diff --git a/Changes b/Changes new file mode 100644 index 0000000..4f6e27c --- /dev/null +++ b/Changes @@ -0,0 +1,3 @@ +replaced text.ProgressBar with text.ProgressBar2 +simplified text.paragraph +color tags changed from §g...§/ to g$...$ diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 2a2409b..0000000 --- a/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env python3 -# encoding: utf-8 - -import time -import sys - -#from . import ag -from . import core -from . import aux -#from . import m -#from . import serial - diff --git a/ag/ag.Aggregate.pyx b/ag/ag.Aggregate.pyx deleted file mode 100644 index 080028c..0000000 --- a/ag/ag.Aggregate.pyx +++ /dev/null @@ -1,160 +0,0 @@ -from collections import OrderedDict - -cdef class Aggregate: - def __init__(Aggregate self, *modules): - self._attrs = {} # attr_name, module - self._modules = OrderedDict() # module_name, module - self._provided = {} # keyword, module - self._common = {} # method_name, method_wrapper - - for module in modules: - self._link_module(module) - - def _list_modules(Aggregate self): - d = {} - - for attr, module in self._attrs.items(): - if module not in d: - d[module] = set() - - d[module].add(attr) - - return d - - def _link_module(Aggregate self, Module module): - # check for name (=type) collision - if module._name in self._modules.keys(): - raise ModuleCollision(module._name, None, 'name', None) - - # check if requirements are satisfied - unsatisfied_deps = module._requires - set(self._provided.keys()) - if unsatisfied_deps: - raise ModuleDependencyError(module._name, unsatisfied_deps) - - # check for new module declaring a common method that we already provide as non-common - new_commons = module._common - set(self._common.keys()) - common_collisions = {nc for nc in new_commons if nc in self._attrs and not nc in self._common.keys()} - if common_collisions: - colliding_module_names = {self._attrs[x]._name for x in common_collisions} - raise ModuleCollision(module._name, colliding_module_names, 'non-common method', common_collisions) - - # check for an attr collision - module_attrs = {x for x in dir(module) if x[0] != '_'} - attr_collisions = (module_attrs - module._common) & (set(self._attrs.keys()) | set(self._common.keys())) - if attr_collisions: - colliding_module_names = set() - for collision in attr_collisions: - if collision in self._attrs: - colliding_module_names.add(self._attrs[collision]._name) - - if collision in self._common: - colliding_module_names.add(self._common[collision]._name) - - raise ModuleCollision(module._name, colliding_module_names, 'attribute', attr_collisions) - - # check for a provided keyword collision - provided_collisions = module._provides & set(self._provided.keys()) - if provided_collisions: - colliding_module_names = {self._provided[x]._name for x in provided_collisions} - raise ModuleCollision(module._name, colliding_module_names, 'provided keyword', provided_collisions) - - # link the module - self._modules[module._name] = module - - for keyword in module._provides: - self._provided[keyword] = module - - for attr in (module_attrs - module._common): - self._attrs[attr] = module - - # create and/or populate CommonMethod wrappers to common methods - for method_name in module._common: - if method_name not in module_attrs: - raise CommonMethodMissing(method_name, module._name) - - if method_name not in self._common: - self._common[method_name] = CommonMethod(method_name) - - self._common[method_name].link_module(module) - - # hand the module a reference to us - module._top = self - - # call the module's _on_link method, if it has one - if hasattr(module, '_on_link'): - module._on_link() - - def _unlink_module(Aggregate self, str module_name): - if not module_name in self._modules: - raise ModuleDoesntExist(module_name) - - module = self._modules[module_name] - - # check reverse dependencies - global_deps = set() - for m in self._modules.values(): - global_deps.update(m._requires) - - reverse_deps = module._provides & global_deps - if reverse_deps: - raise ModuleDependencyError(module_name, reverse_deps, unlink=True) - - # remove from all pools - for aname, mod in list(self._attrs.items()): - if mod._name == module_name: - del self._attrs[aname] - - del self._modules[module_name] - - for ename in module._provides: - del self._provided[ename] - - # remove _common wrappers - for method_name in module._common: - self._common[method_name].unlink_module(module_name) - - # clear _top reference - module._top = None - - def _merge_in(Aggregate self, Aggregate other_ag): - for module_name, module in other_ag._modules.items(): - if module_name not in self._modules: - self._link_module(module) - - def __getattr__(Aggregate self, str aname): - if aname in self._attrs: - return getattr(self._attrs[aname], aname) - elif aname in self._common: - return self._common[aname] - else: - raise AttributeError("Aggregate has no attribute '%s'" %(aname)) - - def __setattr__(Aggregate self, str aname, avalue): - if aname not in self._attrs: - raise AttributeError("Aggregate has no attribute '%s'" %(aname)) - else: - setattr(self._attrs[aname], aname, avalue) - - def _get_type(Aggregate self): - return tuple(self._modules.keys()) - - def __repr__(Aggregate self): - module_count = len(self._modules) - - if module_count: - lines = ['Aggregate('] - - - for i, module in enumerate(self._modules.values()): - if i + 1 < module_count: - comma = ',' - else: - comma = '' - - lines.append(' %s%s' %(repr(module), comma)) - - lines.append(')') - - return '\n'.join(lines) - else: - return 'Aggregate()' diff --git a/ag/ag.CommonMethod.pyx b/ag/ag.CommonMethod.pyx deleted file mode 100644 index 4b8e9fd..0000000 --- a/ag/ag.CommonMethod.pyx +++ /dev/null @@ -1,30 +0,0 @@ -cdef class CommonMethod: - def __init__(CommonMethod self, str method_name): - self.method_name = method_name - self.module_names = [] - self.modules = [] - - def link_module(CommonMethod self, Module module): - self.module_names.append(module._name) - self.modules.append(module) - - def unlink_module(CommonMethod self, module_name): - i = self.module_names.index(module_name) - - del self.module_names[i] - del self.modules[i] - - def __call__(CommonMethod self, *args): - return_values = [] - - for module in self.modules: - return_values.append(getattr(module, self.method_name).__call__(*args)) - - return return_values - - def __repr__(self): - return 'CommonMethod(name=%s)' %(self.method_name) - - @property - def _name(self): - return repr(self) diff --git a/ag/ag.Module.pyx b/ag/ag.Module.pyx deleted file mode 100644 index c1a81d1..0000000 --- a/ag/ag.Module.pyx +++ /dev/null @@ -1,20 +0,0 @@ -cdef class Module: - def __init__(self, name, provides=set(), requires=set(), common=set()): - ''' - * Modules are identified by their name which has to be unique within the Aggregate's namespace. - * The provides sequence contains keywords which are copied into Aggregate's _provided list. - Two modules providing the same export can't be linked into the same Aggregate. - * The requires sequence contains keywords that have to be already present in the Aggregate's - _provided list before linking. - * All names in the Module's namespace that don't begin with an underscore will be exported - into the Aggregate's namespace. - * If you want to call one method on multiple modules, these modules must all export the method name - in their 'common' set. Otherwise a name collision is raised. Their methods will be called in the - same order in which the modules were linked. - ''' - - self._top = None - self._name = name - self._provides = set(provides) - self._requires = set(requires) - self._common = set(common) diff --git a/ag/ag.header.pyx b/ag/ag.header.pyx deleted file mode 100644 index 435f61e..0000000 --- a/ag/ag.header.pyx +++ /dev/null @@ -1,49 +0,0 @@ -''' -Overwatch Aggregate Object type - -TODO Method overrides: _common gets renamed to _call_all and we add _call_last. -''' - -class ModuleCollision(Exception): - def __init__(self, colliding_module_name, resident_module_names, item_type, items): - self.colliding_module_name = colliding_module_name - self.resident_module_names = resident_module_names - self.item_type = item_type - self.items = items - - def __str__(self): - if self.item_type == 'name': - return "Unable to link module '%s': name already present in Aggregate." %(self.colliding_module_name) - else: - return "Unable to link module '%s': %s(s) '%s' already provided by module(s) '%s'." %(self.colliding_module_name, - self.item_type, "', '".join(self.items), "', '".join(self.resident_module_names)) - -class ModuleDependencyError(Exception): - def __init__(self, module_name, dependencies, unlink=False): - self.module_name = module_name - self.dependencies = dependencies - self.unlink = unlink - - def __str__(self): - if self.unlink: - return "Unable to unlink module '%s': Aggregate depends on its export(s) '%s'." %(self.module_name, - "', '".join(self.dependencies)) - else: - return "Unable to link module '%s': requirement '%s' unsatisfied by Aggregate." %(self.module_name, - "', '".join(self.dependencies)) - -class CommonMethodMissing(Exception): - def __init__(self, method_name, module_name): - self.method_name = method_name - self.module_name = module_name - - def __str__(self): - return "Unable to link module '%s': method '%s' (declared as common) does not exist in module." %( - self.module_name, self.method_name) - -class ModuleDoesntExist(Exception): - def __init__(self, module_name): - self.module_name = module_name - - def __str__(self): - return "Unable to unlink module '%s': not linked to Aggregate." %(self.module_name) diff --git a/ag/build.sh b/ag/build.sh deleted file mode 100755 index 901160a..0000000 --- a/ag/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/zsh -# encoding: utf-8 - -echo "- nothing to do" diff --git a/aux.py b/aux.py deleted file mode 100644 index d2aa393..0000000 --- a/aux.py +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python3 -# encoding: utf-8 - -import sys - -from . import core - -_print = core.text.Output('over', stream=sys.stderr) diff --git a/build-all.sh b/build-all.sh deleted file mode 100755 index 933aa21..0000000 --- a/build-all.sh +++ /dev/null @@ -1,10 +0,0 @@ -#! /bin/zsh -# encoding: utf-8 - -for dir in ag core m serial -do - echo "Building Cython implementations in ${dir}:" - cd "$dir" - ./build.sh - cd .. -done diff --git a/core/build.sh b/core/build.sh deleted file mode 100755 index 5cbfc38..0000000 --- a/core/build.sh +++ /dev/null @@ -1,22 +0,0 @@ -#! /bin/zsh -# encoding: utf-8 - -setopt extendedglob - -function die() { - echo "\n\n>>> Failed during ${1}, aborting." - exit 1 -} - -CFLAGS=(-Wall -pedantic -std=c99 -fPIC) -LFLAGS=(-shared) - -echo "- translating from Python to C" -cython -f -3 --fast-fail -X embedsignature=True cython_types.pyx -o cython_types.c || die "translating" - -echo "- compiling and linking" -gcc $CFLAGS -I/usr/include/python3.5m -pthread -c cython_types.c || die "compilation" -gcc $LFLAGS -L/usr/lib -lpython3.5m cython_types.o -o cython_types.so || die "linking" -rm -f cython_types.{c,o} - -echo "- done" diff --git a/m/build.sh b/m/build.sh deleted file mode 100755 index 901160a..0000000 --- a/m/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/zsh -# encoding: utf-8 - -echo "- nothing to do" diff --git a/core/__init__.py b/over/__init__.py similarity index 75% rename from core/__init__.py rename to over/__init__.py index 116ef0e..19889fc 100644 --- a/core/__init__.py +++ b/over/__init__.py @@ -1,12 +1,9 @@ #! /usr/bin/env python3 # encoding: utf-8 -from . import m -from . import app +import sys + from . import aux -from . import cmd -from . import file -from . import misc from . import text try: @@ -15,4 +12,7 @@ except: aux._print('unable to load C implementation, using python instead', text.prefix.warn) from . import python_types as types -textui = aux.DeprecationForwarder(text, 'over.core.textui', 'over.core.text') +core = aux.DeprecationForwarder(sys.modules[__name__], 'over.core', 'over') +textui = aux.DeprecationForwarder(text, 'over.core.textui', 'over.text') + +del sys diff --git a/core/app.py b/over/app.py similarity index 99% rename from core/app.py rename to over/app.py index 3930ce7..43008f8 100644 --- a/core/app.py +++ b/over/app.py @@ -12,13 +12,13 @@ except: from . import file from . import text -from ..version import _version +from . import version prefix = text.prefix # FIXME This very seriously needs to be heavily simplified and de-duplicated # TODO same parser for cmdline and Config -# TODO zsh and fish integration +# TODO zsh integration # -------------------------------------------------- diff --git a/core/aux.py b/over/aux.py similarity index 100% rename from core/aux.py rename to over/aux.py diff --git a/core/cmd.py b/over/cmd.py similarity index 100% rename from core/cmd.py rename to over/cmd.py diff --git a/core/cython_types.pyx b/over/cython_types.pyx similarity index 100% rename from core/cython_types.pyx rename to over/cython_types.pyx diff --git a/core/file.py b/over/file.py similarity index 100% rename from core/file.py rename to over/file.py diff --git a/core/m.py b/over/m.py similarity index 100% rename from core/m.py rename to over/m.py diff --git a/m/__init__.py b/over/m/__init__.py similarity index 100% rename from m/__init__.py rename to over/m/__init__.py diff --git a/m/cython_m.pyx b/over/m/cython_m.pyx similarity index 100% rename from m/cython_m.pyx rename to over/m/cython_m.pyx diff --git a/m/python_m.py b/over/m/python_m.py similarity index 100% rename from m/python_m.py rename to over/m/python_m.py diff --git a/core/misc.py b/over/misc.py similarity index 100% rename from core/misc.py rename to over/misc.py diff --git a/core/python_types.py b/over/python_types.py similarity index 100% rename from core/python_types.py rename to over/python_types.py diff --git a/ag/__init__.py b/over/serial/__init__.py similarity index 100% rename from ag/__init__.py rename to over/serial/__init__.py diff --git a/serial/com.py b/over/serial/com.py similarity index 100% rename from serial/com.py rename to over/serial/com.py diff --git a/template.py b/over/template.py similarity index 71% rename from template.py rename to over/template.py index de9065a..c11053e 100644 --- a/template.py +++ b/over/template.py @@ -3,10 +3,10 @@ # library imports import over -prefix = over.core.text.prefix +prefix = over.text.prefix # local imports -#import version +import version # -------------------------------------------------- # Exceptions @@ -23,9 +23,9 @@ class ConfigurationError(Exception): # -------------------------------------------------- if __name__ == "__main__": - main = over.core.app.Main("short-name", "Human Readable Name", version.str, "LICENSE", use_cfg_file=False) + main = over.app.Main("short-name", "Human Readable Name", version.str, "LICENSE", use_cfg_file=False) main.add_option("option", "type", "default", "Description.", short_name="s") - main.add_help("Description", ["What it does."]) + main.add_help("Description", ["What it does.", "Another paragraph."]) main.enable_help("h") main.parse() diff --git a/core/text.py b/over/text.py similarity index 72% rename from core/text.py rename to over/text.py index 3f80532..50bb52a 100644 --- a/core/text.py +++ b/over/text.py @@ -12,32 +12,32 @@ import time # -------------------------------------------------- def lexical_join(words, oxford=False): - ''' + """ Joins an iterable of words or sentence fragments into a lexical list: - >>> lexical_join(['this', 'that', 'one of them too']) - 'this, that and one of them too' + >>> lexical_join(["this", "that", "one of them too"]) + "this, that and one of them too" - >>> lexical_join(['this', 'that', 'one of them too'], oxford=True) - 'this, that, and one of them too' + >>> lexical_join(["this", "that", "one of them too"], oxford=True) + "this, that, and one of them too" - >>> lexical_join(['this', 'that']) - 'this and that' + >>> lexical_join(["this", "that"]) + "this and that" - >>> lexical_join(['this']) - 'this' - ''' + >>> lexical_join(["this"]) + "this" + """ l = len(words) if l == 0: - return '' + return "" elif l == 1: return words[0] elif l == 2: - return '%s and %s' %(str(words[0]), str(words[1])) + return "%s and %s" %(str(words[0]), str(words[1])) else: - return '%s%s and %s' %(', '.join(str(w) for w in words[:-1]), ',' if oxford else '', str(words[-1])) + return "%s%s and %s" %(", ".join(str(w) for w in words[:-1]), "," if oxford else "", str(words[-1])) # -------------------------------------------------- @@ -75,7 +75,7 @@ class _ProgressBarChannel: u = Unit(value, unit, format="%.{:d}f pU".format(self.precision)) if not self.use_prefixes or just == "percent": - u._prefixes = (('', 0),) # Unit needs fixin' + u._prefixes = (("", 0),) # Unit needs fixin" s = str(u) @@ -118,8 +118,8 @@ class ProgressBar2: any other character - a bar consisting of these characters filling the line so that at 100% it fills the entirety of the remaining space - A channel ID as a lowercase letter is the channel's actual value. An uppercase letter - is that channel's complement. Operations z, s, m and h cannot have a complement. + A channel ID as a lowercase letter is the channel"s actual value. An uppercase letter + is that channel"s complement. Operations z, s, m and h cannot have a complement. Use §§ to display a literal §. @@ -163,7 +163,7 @@ class ProgressBar2: Properties "unit" and "prefix_base2" are passed to over.core.text.Unit. "top" is the value of the channel that corresponds to 100%. Channels are allowed to exceed this value. It can either be a number or a callable. If - it's a callable, it will be called without arguments and shall return a number. + it"s a callable, it will be called without arguments and shall return a number. "precision" is the displayed floating point precision. Use 0 to force an integer. """ @@ -174,7 +174,7 @@ class ProgressBar2: def set(self, channel_id, value): """ - Sets the channel's value. + Sets the channel"s value. """ c = self.channels[channel_id] @@ -301,120 +301,43 @@ class ProgressBar2: return output.rjust(just) -class ProgressBar: - ''' - An animated progress bar. - - TODO derive Wait() from this - ''' - - def __init__(self, width, top, unit, reverse=False): - ''' - width width of the 'widget' including all text - top the 100% value - unit name of the unit to display - reverse True to expand the progress bar from right to left - ''' - - self.width = width - self.value = 0 - self.top = top - self.unit = unit - self.reverse = reverse - - self.old_len = 0 - self.t_start = None - - _print("over.core.text.ProgressBar is deprecated and will be replaced by ProgressBar2 soon", prefix.warn) - - def draw(self): - if not self.t_start: - self.t_start = time.time() - - if self.old_len: - sys.stderr.write('\b' * self.old_len) - - transferred = str(Unit(self.value, self.unit)) - dt = time.time() - self.t_start - - if dt > 0: - speed = self.value / dt - else: - speed = 0.0 - - speed = str(Unit(speed, '%s/s' %(self.unit))) - - available_width = self.width - len(transferred) - len(speed) - 5 - - ratio = self.value / self.top - pb_done = '=' * int(available_width * ratio) - pb_rem = ' ' * int(available_width * (1 - ratio)) - symbol = '>' - - if self.reverse: - symbol = '<' - pb_done, pb_rem = pb_rem, pb_done - - text = '%s [%s%s%s] %s' %(transferred, pb_done, symbol, pb_rem, speed) - - sys.stderr.write(text) - - current_len = len(text) - tail = self.old_len - current_len - self.old_len = current_len - - if tail > 0: - sys.stderr.write(' ' * tail) - sys.stderr.write('\b' * tail) - - sys.stderr.flush() - - def update(self, value): - self.value = value - - self.draw() - - def blank(self): - sys.stderr.write('\r' + self.old_len * ' ' + '\r') - sys.stderr.flush() - # -------------------------------------------------- class Unit: - ''' + """ A object that represents numbers and units in human-readable form. TODO use significant digits instead of rigid order boundaries TODO float superclass? TODO base_2 prefixes (Ki, Mi, ...) - ''' + """ _prefixes = ( - ('Y', 24), ('Z', 21), ('E', 18), ('P', 15), ('T', 12), ('G', 9), ('M', 6), ('k', 3), - ('h', 2), ('D', 1), ('', 0), ('d', -1), ('c', -2), ('m', -3), ('μ', -6), ('n', -9), - ('p', -12), ('f', -15), ('a', -18), ('z', -21), ('y', -24) + ("Y", 24), ("Z", 21), ("E", 18), ("P", 15), ("T", 12), ("G", 9), ("M", 6), ("k", 3), + ("h", 2), ("D", 1), ("", 0), ("d", -1), ("c", -2), ("m", -3), ("μ", -6), ("n", -9), + ("p", -12), ("f", -15), ("a", -18), ("z", -21), ("y", -24) ) def __init__(self, value, unit=None, dimension=1, - use_prefixes='YZEPTGMkmμnpfazy', format='%.2f pU', + use_prefixes="YZEPTGMkmμnpfazy", format="%.2f pU", logarithmic=False, log_base=10 ): - ''' + """ value the numerical value of the variable (int or float) unit the symbol to use, if any (str or None) dimension the dimensionality of the value (1, 2, 3, ...) use_prefixes which multiplier prefixes to use - - the default 'YZEPTGMkmμnpfazy' omits 'c' for centi- and 'D' for deca- + - the default "YZEPTGMkmμnpfazy" omits "c" for centi- and "D" for deca- format use printf notation for value (e.g. %010.5f), p for prefix and U for unit logarithmic when True, log_10 prefixes are used log_base logarithm base value - note that deca- is correctly rendered as 'da', the 'D' is used in use_prefixes only - ''' + note that deca- is correctly rendered as "da", the "D" is used in use_prefixes only + """ self.value = float(value) - self.unit = unit if unit else '' + self.unit = unit if unit else "" self.dimension = dimension self.use_prefixes = use_prefixes self.format = format @@ -422,7 +345,7 @@ class Unit: self.log_base = log_base if self.logarithmic and (self.value < 0 or self.log_base < 0): - raise ValueError('math domain error (negative values can\'t be represented in dB)') + raise ValueError("math domain error (negative values can't be represented in dB)") def __str__(self): if self.value == 0.0: @@ -431,7 +354,7 @@ class Unit: e = round(math.log(abs(self.value), 10), 6) if self.logarithmic: - prefix = 'dB' + prefix = "dB" value = math.log(self.value, self.log_base) * 10 else: @@ -442,30 +365,27 @@ class Unit: value = self.value / 10**(mul*self.dimension) output = self.format %(value) - output = output.replace('p', prefix if prefix != 'D' else 'da') # deca- handler - output = output.replace('U', self.unit) + output = output.replace("p", prefix if prefix != "D" else "da") # deca- handler + output = output.replace("U", self.unit) return output def __repr__(self): - return 'over.core.text.Unit(%s)' %(self) + return "over.text.Unit(%s)" %(self) # -------------------------------------------------- def paragraph(text, width=0, indent=0, prefix=None, stamp=None): - ''' + """ str text text to format - int width required line length; if 0, current terminal width will be used; - - if negative, current terminal width minus supplied amount will be used + int width required line length; if 0, current terminal width will be used int indent how many spaces to indent the text with - str prefix prefix each line with it; not counted in width (offsets the lines) str stamp placed over the first line's indent (stretching the indent if necessary) Formats text into an indented paragraph that fits the terminal and returns it. Correctly handles color tags. - ''' + """ - #words = [x.strip() for x in text.split() if x.strip()] words = text.split() term_width = get_terminal_size()[1] @@ -481,9 +401,9 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None): while words: if indent: if first and stamp: - lines.append([stamp + ' '*(indent-1-len(stamp))]) + lines.append([stamp + " "*(indent-1-len(stamp))]) else: - lines.append([' '*(indent-1)]) # first word = indent minus one space (that's gonna get back while joining) + lines.append([" "*(indent-1)]) # first word = indent minus one space (that's gonna get back while joining) first = False else: @@ -492,23 +412,23 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None): while words: word_added = False - if len(re.sub('§.', '', ' '.join(lines[-1]))) + len(re.sub('§.', '', words[0])) + 1 <= width: + if len(re.sub("§.", "", " ".join(lines[-1]))) + len(re.sub("§.", "", words[0])) + 1 <= width: lines[-1].append(words.pop(0)) word_added = True elif not word_added and len(lines[-1]) == 1 and indent: - # no word added and just the indent's in here = word's too long -> screw indent + # no word added and just the indent"s in here = word"s too long -> screw indent # we might try to keep at least a part of the indent - if possible - len_word = len(re.sub('§.', '', words[0])) + len_word = len(re.sub("§.", "", words[0])) if len_word < width: - lines[-1] = [' '*(width - len_word - 1), words.pop(0)] + lines[-1] = [" "*(width - len_word - 1), words.pop(0)] else: lines[-1] = [words.pop(0)] word_added = True break elif not word_added and not lines[-1] and not indent: - # no word added, empty line = word's too long -> screw indent + # no word added, empty line = word"s too long -> screw indent lines[-1] = [words.pop(0)] word_added = True break @@ -521,41 +441,47 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None): if prefix: line.insert(0, prefix) - lines_tmp.append(' '.join(line)) # put words together + lines_tmp.append(" ".join(line)) # put words together - return '\n'.join(lines_tmp) # put lines together + return "\n".join(lines_tmp) # put lines together class prefix: - info = (' ', 'INFO') - debug = (' §b?§/', '§bDEBG§/') - start = ('§B>>>§/', '§BEXEC§/') + info = (" ", "INFO") + debug = (" §b?§/", "§bDEBG§/") + start = ("§B>>>§/", "§BEXEC§/") exec = start - warn = (' §y#§/', '§yWARN§/') - fail = ('§r!!!§/', '§rFAIL§/') - done = (' §g*§/', '§gDONE§/') + warn = (" §y#§/", "§yWARN§/") + fail = ("§r!!!§/", "§rFAIL§/") + done = (" §g*§/", "§gDONE§/") colortags = { - '§r': '\x1b[31;01m', - '§g': '\x1b[32;01m', - '§y': '\x1b[33;01m', - '§b': '\x1b[34;01m', - '§m': '\x1b[35;01m', - '§c': '\x1b[36;01m', - '§B': '\x1b[01m', - '§/': '\x1b[39;49;00m' + "r": "\x1b[31;01m", + "g": "\x1b[32;01m", + "y": "\x1b[33;01m", + "b": "\x1b[34;01m", + "m": "\x1b[35;01m", + "c": "\x1b[36;01m", + "B": "\x1b[01m", + "": "\x1b[39;49;00m" # reset } def render(text, colors=True): - ''' + """ Processes text with color tags and either removes them (with colors=False) or replaces them with terminal color codes. - ''' + """ text = str(text) + tags = set(colortags.keys()) + output = [] + + for + + if colors: - tags = re.findall('§[^§]', text) + tags = re.findall(r"[{:s}]\$".format("".join(tags)), text) for tag in tags: try: @@ -563,19 +489,19 @@ def render(text, colors=True): except KeyError: pass else: - text = re.sub('§[^§]', '', text) + text = re.sub("§[^§]", "", text) # unescape actual paragraphs - text = re.sub('§§', '§', text) + text = re.sub("§§", "§", text) return text # -------------------------------------------------- def char_length(string): - ''' + """ Returns the length of a string minus all formatting tags. - ''' + """ plain_string = render(string, colors=False) @@ -584,7 +510,7 @@ def char_length(string): # -------------------------------------------------- class Output: - ''' + """ Text UI output renderer. Prints messages to the stdout with optional eye candy @@ -592,18 +518,18 @@ class Output: Usage: >>> from over import Output, prefix - >>> say = Output('test', timestamp=True) - >>> say('system initialized') + >>> say = Output("test", timestamp=True) + >>> say("system initialized") [2013-02-28 16:41:28] INFO -- test, system initialized - >>> say('system is FUBAR', prefix.fail) + >>> say("system is FUBAR", prefix.fail) [2013-02-28 16:41:46] FAIL -- test, system is FUBAR - >>> say('I just realized this will not work', prefix.fail, timestamp=False) + >>> say("I just realized this will not work", prefix.fail, timestamp=False) !!! I just realized this will not work TODO use a generic format string - ''' + """ - def __init__(self, name, timestamp=True, colors=True, default_prefix=prefix.info, default_suffix='.\n', stream=sys.stderr): + def __init__(self, name, timestamp=True, colors=True, default_prefix=prefix.info, default_suffix=".\n", stream=sys.stderr): self.name = name self.timestamp = timestamp self.colors = colors @@ -631,15 +557,15 @@ class Output: # [2012-11-11 16:52:06] INFO -- ahoj if timestamp: - output.append(time.strftime('[%Y-%m-%d %H:%M:%S] ')) + output.append(time.strftime("[%Y-%m-%d %H:%M:%S] ")) output.append(prefix[1]) - output.append(' -- ') + output.append(" -- ") elif prefix: output.append(prefix[0]) - output.append(' ') + output.append(" ") if display_name and self.name: - output.append('%s, ' %(self.name)) + output.append("%s, " %(self.name)) #output.append(paragraph(str(text), indent=indent)) output.append(str(text)) @@ -647,7 +573,7 @@ class Output: if suffix: output.append(suffix) - output = ''.join(output) + output = "".join(output) self.stream.write(render(output, colors)) self.stream.flush() @@ -655,19 +581,19 @@ class Output: # -------------------------------------------------- def get_terminal_size(): - ''' - Returns current terminal's (rows, cols). - ''' + """ + Returns current terminal"s (rows, cols). + """ terminal = sys.stdout.fileno() try: - return struct.unpack('HHHH', fcntl.ioctl(terminal, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0))) + return struct.unpack("HHHH", fcntl.ioctl(terminal, termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0))) except IOError: return (40, 80) # -------------------------------------------------- -_print = Output('over.core.text', stream=sys.stderr) +_print = Output("over.core.text", stream=sys.stderr) # -------------------------------------------------- diff --git a/deploy-template.sh b/over/tools/deploy-template.sh similarity index 100% rename from deploy-template.sh rename to over/tools/deploy-template.sh diff --git a/over/version.py b/over/version.py new file mode 100644 index 0000000..748f49a --- /dev/null +++ b/over/version.py @@ -0,0 +1,7 @@ +#! /usr/bin/env python3 +# encoding: utf-8 + +major = 0 +minor = 0 +patch = 0 # OVER_VERSION_PATCH_IDENTIFIER +str = ".".join(str(v) for v in (major, minor, patch)) diff --git a/serial/__init__.py b/serial/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/serial/build.sh b/serial/build.sh deleted file mode 100755 index 901160a..0000000 --- a/serial/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/zsh -# encoding: utf-8 - -echo "- nothing to do" diff --git a/.over_inhibit_deployment b/setup.py similarity index 100% rename from .over_inhibit_deployment rename to setup.py diff --git a/version.py b/version.py deleted file mode 100644 index 5b83190..0000000 --- a/version.py +++ /dev/null @@ -1,4 +0,0 @@ -#! /usr/bin/env python3 -# encoding: utf-8 - -_version = (0, '00000000') # OVER_VERSION_IDENTIFIER