minor PEP8 cleanup (" -> ')
This commit is contained in:
parent
6416b66f6e
commit
61fb3b1044
18 changed files with 456 additions and 456 deletions
|
@ -24,7 +24,7 @@ cdef class Aggregate:
|
|||
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)
|
||||
raise ModuleCollision(module._name, None, 'name', None)
|
||||
|
||||
# check if requirements are satisfied
|
||||
unsatisfied_deps = module._requires - set(self._provided.keys())
|
||||
|
@ -36,10 +36,10 @@ cdef class Aggregate:
|
|||
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)
|
||||
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] != "_"}
|
||||
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()
|
||||
|
@ -50,13 +50,13 @@ cdef class Aggregate:
|
|||
if collision in self._common:
|
||||
colliding_module_names.add(self._common[collision]._name)
|
||||
|
||||
raise ModuleCollision(module._name, colliding_module_names, "attribute", attr_collisions)
|
||||
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)
|
||||
raise ModuleCollision(module._name, colliding_module_names, 'provided keyword', provided_collisions)
|
||||
|
||||
# link the module
|
||||
self._modules[module._name] = module
|
||||
|
@ -81,7 +81,7 @@ cdef class Aggregate:
|
|||
module._top = self
|
||||
|
||||
# call the module's _on_link method, if it has one
|
||||
if hasattr(module, "_on_link"):
|
||||
if hasattr(module, '_on_link'):
|
||||
module._on_link()
|
||||
|
||||
def _unlink_module(Aggregate self, str module_name):
|
||||
|
@ -142,19 +142,19 @@ cdef class Aggregate:
|
|||
module_count = len(self._modules)
|
||||
|
||||
if module_count:
|
||||
lines = ["Aggregate("]
|
||||
lines = ['Aggregate(']
|
||||
|
||||
|
||||
for i, module in enumerate(self._modules.values()):
|
||||
if i + 1 < module_count:
|
||||
comma = ","
|
||||
comma = ','
|
||||
else:
|
||||
comma = ""
|
||||
comma = ''
|
||||
|
||||
lines.append(" %s%s" %(repr(module), comma))
|
||||
lines.append(' %s%s' %(repr(module), comma))
|
||||
|
||||
lines.append(")")
|
||||
lines.append(')')
|
||||
|
||||
return "\n".join(lines)
|
||||
return '\n'.join(lines)
|
||||
else:
|
||||
return "Aggregate()"
|
||||
return 'Aggregate()'
|
||||
|
|
|
@ -23,7 +23,7 @@ cdef class CommonMethod:
|
|||
return return_values
|
||||
|
||||
def __repr__(self):
|
||||
return "CommonMethod(name=%s)" %(self.method_name)
|
||||
return 'CommonMethod(name=%s)' %(self.method_name)
|
||||
|
||||
@property
|
||||
def _name(self):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.
|
||||
|
@ -9,9 +9,9 @@ cdef class Module:
|
|||
* 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
|
||||
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
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""
|
||||
'''
|
||||
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):
|
||||
|
@ -12,7 +12,7 @@ class ModuleCollision(Exception):
|
|||
self.items = items
|
||||
|
||||
def __str__(self):
|
||||
if self.item_type == "name":
|
||||
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,
|
||||
|
|
2
aux.py
2
aux.py
|
@ -5,4 +5,4 @@ import sys
|
|||
|
||||
from . import core
|
||||
|
||||
_print = core.textui.Output("over", stream=sys.stderr)
|
||||
_print = core.textui.Output('over', stream=sys.stderr)
|
||||
|
|
|
@ -10,5 +10,5 @@ from . import textui
|
|||
try:
|
||||
from . import cython_types as types
|
||||
except:
|
||||
aux._print("unable to load C implementation, using python instead", textui.prefix.warn)
|
||||
aux._print('unable to load C implementation, using python instead', textui.prefix.warn)
|
||||
from . import python_types as types
|
||||
|
|
346
core/app.py
346
core/app.py
|
@ -17,27 +17,27 @@ prefix = textui.prefix
|
|||
# --------------------------------------------------
|
||||
|
||||
def _parse(data, dtype):
|
||||
if dtype == "str":
|
||||
if data.startswith("\"") and data.endswith("\""):
|
||||
value = data[1:-1].replace("\\\"", "\"")
|
||||
if dtype == 'str':
|
||||
if data.startswith('\'') and data.endswith('\''):
|
||||
value = data[1:-1].replace('\\\'', '\'')
|
||||
else:
|
||||
raise RuntimeError
|
||||
|
||||
elif dtype == "bool":
|
||||
if data == "True":
|
||||
elif dtype == 'bool':
|
||||
if data == 'True':
|
||||
value = True
|
||||
elif data == "False":
|
||||
elif data == 'False':
|
||||
value = False
|
||||
else:
|
||||
raise RuntimeError
|
||||
|
||||
elif dtype == "int":
|
||||
elif dtype == 'int':
|
||||
try:
|
||||
value = int(data)
|
||||
except ValueError:
|
||||
raise RuntimeError
|
||||
|
||||
elif dtype == "float":
|
||||
elif dtype == 'float':
|
||||
try:
|
||||
value = float(data)
|
||||
except ValueError:
|
||||
|
@ -46,111 +46,111 @@ def _parse(data, dtype):
|
|||
return value
|
||||
|
||||
_over_help_texts = [
|
||||
("over.core: what, how and huh?", ["over.core is a Python 3 module that provides basic functionality for programs. Functionality such as configuration, commandline parsing, text handling and output, file handling, a non-interactive help system and (not all that much) more."]),
|
||||
("Data Types", ["over.core currently supports 4 data types.", "A §bbool§/ is either exactly §mTrue§/ or exactly §mFalse§/. Bool options that are §mTrue§/ look like this: §B--§goption§/. The same name only §mFalse§/ would be §B--no-§goption§/. Makes sense, doesn't it? Their short versions are either §B+§go§/ for §mTrue§/ or §B-§go§/ for §mFalse§/.", "A §bstr§/ing is just any raw text. Remember to enclose it in quotes if it has spaces or other nasty characters in it.", "An §bint§/eger is a whole number. Negative, zero, positive.", "Finally, a §bfloat§/ is any real number."]),
|
||||
("The Commandline Parser", ["This, in conjunction with the configuration system, is the strongest part of over.core and the very reason for its continued existence.", "Each configurable option can be assigned to on the command line. Take an option named §Bres-file§/ as an example. To assign to it, you can use §B--§gres-file§/ §msomething§/ (it's case sensitive). That's it, now its value is §msomething§/! Pretty easy, right?", "Now, you're probably thinking: \"I'm not typing that all over again, Martin!\" Well, you don't have to! Options can have their short names. It's a single letter (again, case sensitive) with a plus or minus sign in front of it. So §B--§gres-file§/ §msomething§/ becomes §B+§gf§/ §msomething§/. That's much better, ain't it? And there's more. Short names can be grouped together. If you have a bunch of bool switches, like §B--§garmed§/ (short: §B+§gA§/), §B--no-§gsound§/ (short: §B-§gS§/), §B--no-§gstore§/ (short: §B-§gs§/) and §B--§gforce§/ (short: §B+§gF§/), you can group their shorts into groups with the same boolean value: §B-§gSs§/ §B+§gAF§/. You can even have a non-bool option in a group, it just has to be on the very right (because it needs to be followed by data): §B-§gSs§/ §B+§gAFf§/ §msomething§/. It doesn't matter if that group begins with §B+§/ or §B-§/.", "If you use an option more than once, its last (rightmost) occurence applies. For example, after §B+§gf§/ §msomething§/ §B+§gf§/ §mor_other§/ is parsed, option §Bres-file§/ holds the value §mor_other§/. It goes the same for bools: after §B+§gA§/ §B--no-§garmed§/ §B+§gA§/, §Barmed§/ is §mTrue§/. However, if an option is §cplural§/, all occurences are used. Sequence §B+§gA§/ §B-§gA§/ §B+§gA§/ would be [§mTrue§/, §mFalse§/, §mTrue§/], and §B--§gnum§/ §m1§/ §B--§gnum§/ §m2§/ would end up looking like [§m1§/, §m2§/]. You don't need to type §B--§gnum§/ for every field either: §B--§gnum§/ §m1§/ §m2§/ would work exactly the same. That's because the parser keeps reading everything after a plural option (that takes data, i.e. not bools) right until it encounters two dashes, like those of a following option. You can use just the two dashes to stop the parsing manually, usually when you don't want to follow with a long option. Example: §B--§gnum§/ §m1§/ §m5§/ §m9§/ §B--§/. I repeat: a plural (non-bool) option needs to be terminated by two dashes. This wouldn't work: §B--§gnum§/ §m1§/ §m5§/ §m9§/ §B+§gf§/ §mthat_one§/, everything after §B--§gnum§/ would be consumed as its data, §Bincluding +§gf§/ §mthat_one§/."]),
|
||||
("The Config File", ["If enabled in the program code, a config file will be generated when you first run it. By default, everything in the config file will be filled with default values and commented out. The general syntax as well as individual options are explained inside. If you run a newer version of the program that offers more configurable options, the config file will be automatically updated. If you'd like to modify an option in the config file, first uncomment it and then change its value. Resolving order for option values is 1) default (hardcoded), 2) config file and 3) commandline arguments."]),
|
||||
("Other", ["When enabled in the program, an over.core program offers two options: §B--§ghelp§/ and §B--§gover-help§/. The first describes the program and its options, including their types, current values and whether is their current value coming from defaults, config files or the command line. The second option displays the help you're reading right now. You may now guess which rank I hold in the Obvious Corps.", "As the brighter amongst you might have noticed, over.core likes colors. A lot. Generally, I use blue for §bdata types§/, magenta for §mvalues§/, white and green for §B--§goptions§/ and reserve red and yellow for when §rshit hits§/ §ythe fan§/, where red letters usually tell you §Bwhy§/ and yellow ones tell you §Bwhat§/.", "And it's not just colors! I like money, too. Push donations to §B1sekErApM4zh35RFW7qGWs5Yeo9EYWjyV§/, or catch me somewhere and force me to accept cash."])
|
||||
('over.core: what, how and huh?', ['over.core is a Python 3 module that provides basic functionality for programs. Functionality such as configuration, commandline parsing, text handling and output, file handling, a non-interactive help system and (not all that much) more.']),
|
||||
('Data Types', ['over.core currently supports 4 data types.', 'A §bbool§/ is either exactly §mTrue§/ or exactly §mFalse§/. Bool options that are §mTrue§/ look like this: §B--§goption§/. The same name only §mFalse§/ would be §B--no-§goption§/. Makes sense, doesn\'t it? Their short versions are either §B+§go§/ for §mTrue§/ or §B-§go§/ for §mFalse§/.', 'A §bstr§/ing is just any raw text. Remember to enclose it in quotes if it has spaces or other nasty characters in it.', 'An §bint§/eger is a whole number. Negative, zero, positive.', 'Finally, a §bfloat§/ is any real number.']),
|
||||
('The Commandline Parser', ['This, in conjunction with the configuration system, is the strongest part of over.core and the very reason for its continued existence.', 'Each configurable option can be assigned to on the command line. Take an option named §Bres-file§/ as an example. To assign to it, you can use §B--§gres-file§/ §msomething§/ (it\'s case sensitive). That\'s it, now its value is §msomething§/! Pretty easy, right?', 'Now, you\'re probably thinking: \'I\'m not typing that all over again!\' Well, you don\'t have to! Options can have their short names. It\'s a single letter (again, case sensitive) with a plus or minus sign in front of it. So §B--§gres-file§/ §msomething§/ becomes §B+§gf§/ §msomething§/. That\'s much better, ain\'t it? And there\'s more. Short names can be grouped together. If you have a bunch of bool switches, like §B--§garmed§/ (short: §B+§gA§/), §B--no-§gsound§/ (short: §B-§gS§/), §B--no-§gstore§/ (short: §B-§gs§/) and §B--§gforce§/ (short: §B+§gF§/), you can group their shorts into groups with the same boolean value: §B-§gSs§/ §B+§gAF§/. You can even have a non-bool option in a group, it just has to be on the very right (because it needs to be followed by data): §B-§gSs§/ §B+§gAFf§/ §msomething§/. It doesn\'t matter if that group begins with §B+§/ or §B-§/.', 'If you use an option more than once, its last (rightmost) occurence applies. For example, after §B+§gf§/ §msomething§/ §B+§gf§/ §mor_other§/ is parsed, option §Bres-file§/ holds the value §mor_other§/. It goes the same for bools: after §B+§gA§/ §B--no-§garmed§/ §B+§gA§/, §Barmed§/ is §mTrue§/. However, if an option is §cplural§/, all occurences are used. Sequence §B+§gA§/ §B-§gA§/ §B+§gA§/ would be [§mTrue§/, §mFalse§/, §mTrue§/], and §B--§gnum§/ §m1§/ §B--§gnum§/ §m2§/ would end up looking like [§m1§/, §m2§/]. You don\'t need to type §B--§gnum§/ for every field either: §B--§gnum§/ §m1§/ §m2§/ would work exactly the same. That\'s because the parser keeps reading everything after a plural option (that takes data, i.e. not bools) right until it encounters two dashes, like those of a following option. You can use just the two dashes to stop the parsing manually, usually when you don\'t want to follow with a long option. Example: §B--§gnum§/ §m1§/ §m5§/ §m9§/ §B--§/. I repeat: a plural (non-bool) option needs to be terminated by two dashes. This wouldn\'t work: §B--§gnum§/ §m1§/ §m5§/ §m9§/ §B+§gf§/ §mthat_one§/, everything after §B--§gnum§/ would be consumed as its data, §Bincluding +§gf§/ §mthat_one§/.']),
|
||||
('The Config File', ['If enabled in the program code, a config file will be generated when you first run it. By default, everything in the config file will be filled with default values and commented out. The general syntax as well as individual options are explained inside. If you run a newer version of the program that offers more configurable options, the config file will be automatically updated. If you\'d like to modify an option in the config file, first uncomment it and then change its value. Resolving order for option values is 1) default (hardcoded), 2) config file and 3) commandline arguments.']),
|
||||
('Other', ['When enabled in the program, an over.core program offers two options: §B--§ghelp§/ and §B--§gover-help§/. The first describes the program and its options, including their types, current values and whether is their current value coming from defaults, config files or the command line. The second option displays the help you\'re reading right now. You may now guess which rank I hold in the Obvious Corps.', 'As the brighter amongst you might have noticed, over.core likes colors. A lot. Generally, I use blue for §bdata types§/, magenta for §mvalues§/, white and green for §B--§goptions§/ and reserve red and yellow for when §rshit hits§/ §ythe fan§/, where red letters usually tell you §Bwhy§/ and yellow ones tell you §Bwhat§/.', 'And it\'s not just colors! I like money, too. Push donations to §B1sekErApM4zh35RFW7qGWs5Yeo9EYWjyV§/, or catch me somewhere and force me to accept cash.'])
|
||||
]
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
def _output(text, indent=0, newlines=1):
|
||||
sys.stdout.write(textui.render(textui.paragraph(text, indent=indent), True))
|
||||
sys.stdout.write(newlines * "\n")
|
||||
sys.stdout.write(newlines * '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
def _print_help(main, help_texts, chapter=None, list_options=False):
|
||||
"""
|
||||
'''
|
||||
Displays a help text and then quits the program.
|
||||
|
||||
str chapter display only this chapter
|
||||
bool list_options follow the static printout by a list of options and their current values
|
||||
"""
|
||||
'''
|
||||
|
||||
# dirty as fuck :-)
|
||||
if not chapter and not main.version is None:
|
||||
if help_texts == _over_help_texts:
|
||||
_output(">>> §yover.core§/ version §y%d§/ (%s), licensed under the §yAO-JSL§/" %_version, newlines=2)
|
||||
_output('>>> §yover.core§/ version §y%d§/ (%s), licensed under the §yAO-JSL§/' %_version, newlines=2)
|
||||
else:
|
||||
_output("§y%s§/ version §y%s§/, licensed under the §y%s§/" %(main.name, main.version, main.license), newlines=2)
|
||||
_output('§y%s§/ version §y%s§/, licensed under the §y%s§/' %(main.name, main.version, main.license), newlines=2)
|
||||
|
||||
# general help texts
|
||||
for help_text in help_texts:
|
||||
_output("[§B%s§/]" %(help_text[0]))
|
||||
_output('[§B%s§/]' %(help_text[0]))
|
||||
for p in help_text[1]:
|
||||
_output(p, indent=4, newlines=2)
|
||||
|
||||
if list_options:
|
||||
_output("[§BAvailable Options§/]")
|
||||
_output('[§BAvailable Options§/]')
|
||||
|
||||
for option in [opt for opt in main.options if not opt.hidden]:
|
||||
if option.dtype == "bool" and not option.callback:
|
||||
long_part = "§B--§g{0}§/ or §B--no-§g{0}§/".format(option.name)
|
||||
if option.dtype == 'bool' and not option.callback:
|
||||
long_part = '§B--§g{0}§/ or §B--no-§g{0}§/'.format(option.name)
|
||||
else:
|
||||
long_part = "§B--§g%s§/" %(option.name)
|
||||
long_part = '§B--§g%s§/' %(option.name)
|
||||
|
||||
if option.short_name:
|
||||
if option.dtype == "bool" and not option.callback:
|
||||
short_part = " (§B+§g{0}§/ or §B-§g{0}§/)".format(option.short_name)
|
||||
if option.dtype == 'bool' and not option.callback:
|
||||
short_part = ' (§B+§g{0}§/ or §B-§g{0}§/)'.format(option.short_name)
|
||||
else:
|
||||
short_part = " (§B+§g%s§/)" %(option.short_name)
|
||||
short_part = ' (§B+§g%s§/)' %(option.short_name)
|
||||
else:
|
||||
short_part = ""
|
||||
short_part = ''
|
||||
|
||||
if option.plural:
|
||||
plural_part = "§cplural§/ "
|
||||
plural_part = '§cplural§/ '
|
||||
else:
|
||||
plural_part = ""
|
||||
plural_part = ''
|
||||
|
||||
# if option.dtype == "boolaaa":
|
||||
# _output("%s%s; §bbool§/" %(long_part, short_part), indent=4)
|
||||
# if option.dtype == 'boolaaa':
|
||||
# _output('%s%s; §bbool§/' %(long_part, short_part), indent=4)
|
||||
# else:
|
||||
_output("%s%s; %s§b%s§/" %(long_part, short_part, plural_part, option.dtype), indent=4)
|
||||
_output('%s%s; %s§b%s§/' %(long_part, short_part, plural_part, option.dtype), indent=4)
|
||||
|
||||
if option.source == "cmdline":
|
||||
src = "§mcmdline§/"
|
||||
elif option.source == "config":
|
||||
src = "§yconfig§/"
|
||||
if option.source == 'cmdline':
|
||||
src = '§mcmdline§/'
|
||||
elif option.source == 'config':
|
||||
src = '§yconfig§/'
|
||||
else:
|
||||
src = "default"
|
||||
src = 'default'
|
||||
|
||||
if option.plural:
|
||||
if option.dtype == "str":
|
||||
value = "§c[§/%s§c]§/" %(", ".join("\"§m%s§/\"" %(x) for x in option.value))
|
||||
if option.dtype == 'str':
|
||||
value = '§c[§/%s§c]§/' %(', '.join('\'§m%s§/\'' %(x) for x in option.value))
|
||||
else:
|
||||
value = "§c[§m%s§c]§/" %("§/, §m".join(str(x) for x in option.value))
|
||||
value = '§c[§m%s§c]§/' %('§/, §m'.join(str(x) for x in option.value))
|
||||
|
||||
else:
|
||||
if option.dtype == "str":
|
||||
value = "\"§m%s§/\"" %(option.value)
|
||||
if option.dtype == 'str':
|
||||
value = '\'§m%s§/\'' %(option.value)
|
||||
else:
|
||||
value = "§m%s§/" %(option.value)
|
||||
value = '§m%s§/' %(option.value)
|
||||
|
||||
if not option.callback:
|
||||
_output("Current value (%s): %s" %(src, value), indent=6)
|
||||
_output('Current value (%s): %s' %(src, value), indent=6)
|
||||
|
||||
if not option.use_cfg_file:
|
||||
_output("§c(ignores config file)§/", indent=6)
|
||||
_output('§c(ignores config file)§/', indent=6)
|
||||
|
||||
_output(option.description, indent=8, newlines=2)
|
||||
|
||||
_output("[§BTargets§/]")
|
||||
_output('[§BTargets§/]')
|
||||
|
||||
if main.targets:
|
||||
_output("§m%s§/" %("§/, §m".join(main.targets)), newlines=2, indent=4)
|
||||
_output('§m%s§/' %('§/, §m'.join(main.targets)), newlines=2, indent=4)
|
||||
else:
|
||||
_output("n/a", newlines=2, indent=4)
|
||||
_output('n/a', newlines=2, indent=4)
|
||||
|
||||
main.exit(1, True)
|
||||
|
||||
_allowed_types = {
|
||||
"bool": [bool],
|
||||
"str": [str],
|
||||
"int": [int],
|
||||
"float": [int, float]
|
||||
'bool': [bool],
|
||||
'str': [str],
|
||||
'int': [int],
|
||||
'float': [int, float]
|
||||
}
|
||||
|
||||
# --------------------------------------------------
|
||||
|
@ -177,42 +177,42 @@ def _verify_type(data, dtype, plural=False):
|
|||
# --------------------------------------------------
|
||||
|
||||
class CfgAccessor:
|
||||
"""
|
||||
'''
|
||||
Provides convenient access to configuration options' values.
|
||||
|
||||
e.g. something = main.cfg.option_name
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(self, main):
|
||||
self.main = main
|
||||
|
||||
def __getattr__(self, opt_name):
|
||||
matches = [opt for opt in self.main.options if opt.name.replace("-", "_") == opt_name]
|
||||
matches = [opt for opt in self.main.options if opt.name.replace('-', '_') == opt_name]
|
||||
|
||||
if matches:
|
||||
if len(matches) == 1:
|
||||
return matches[0].value
|
||||
else:
|
||||
self.main._print("game over, lights out! over.core internal buggaroo", prefix.fail)
|
||||
self.main._print('game over, lights out! over.core internal buggaroo', prefix.fail)
|
||||
raise RuntimeError
|
||||
else:
|
||||
self.main._print("option §r%s§/ doesn't exist" %(opt_name), prefix.fail)
|
||||
self.main._print('option §r%s§/ doesn\'t exist' %(opt_name), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
class Main:
|
||||
"""
|
||||
'''
|
||||
Application backbone. Provides:
|
||||
* A configuration system consisting of
|
||||
* a given set of (hardcoded) default values
|
||||
* a configuration file that overrides the defaults
|
||||
* a command line parser that overrides all of the above
|
||||
* Help system that generates help text based on program's configurable options.
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(self, name, version=None, license=None, cfg_file=None, cmdline=sys.argv[1:], allow_exit=True):
|
||||
"""
|
||||
'''
|
||||
str name program name
|
||||
str version program version
|
||||
str license program license
|
||||
|
@ -220,7 +220,7 @@ class Main:
|
|||
list cmdline command line to parse; defaults to system cmdline (sys.argv)
|
||||
|
||||
Initializes over.Main and opens/creates the config file (if desired).
|
||||
"""
|
||||
'''
|
||||
|
||||
self.cfg = CfgAccessor(self)
|
||||
self.name = name
|
||||
|
@ -229,30 +229,30 @@ class Main:
|
|||
self.cmdline = cmdline
|
||||
self.help_texts = [] # (chapter, [paragraphs])
|
||||
self.allow_exit = allow_exit
|
||||
self._print = textui.Output("over.core.app.Main")
|
||||
self._print = textui.Output('over.core.app.Main')
|
||||
|
||||
if cfg_file:
|
||||
self.cfg_file = file.File(cfg_file, encoding="utf-8")
|
||||
self.cfg_file = file.File(cfg_file, encoding='utf-8')
|
||||
|
||||
if not self.cfg_file.data:
|
||||
self.cfg_file.data = """# Et configuration file for %s
|
||||
self.cfg_file.data = '''# Et configuration file for %s
|
||||
# Licensed under the %s
|
||||
#
|
||||
# Syntax
|
||||
# There are 4 data types: bool, int, float and str (string).
|
||||
# * Bools are exactly True or False.
|
||||
# * Ints are negative integers, positive integers and zero.
|
||||
# * Floats are all real numbers. They don't need to have a decimal point.
|
||||
# * Strings need to be enclosed in double quotes, e.g. "like this".
|
||||
# * Floats are all real numbers. They don\'t need to have a decimal point.
|
||||
# * Strings need to be enclosed in double quotes, e.g. 'like this'.
|
||||
#
|
||||
# An option is either singular or plural. Plurals are arrays, so they ecpect to be
|
||||
# in comma-separated lists: ["this", "is", "a", "str", "example"].
|
||||
# in comma-separated lists: ['this', 'is', 'a', 'str', 'example'].
|
||||
#
|
||||
# Only lines beginning with a '#' are treated as comments.
|
||||
# Only lines beginning with a \'#\' are treated as comments.
|
||||
|
||||
""" %(self.name, self.license)
|
||||
''' %(self.name, self.license)
|
||||
|
||||
self._print("creating configuration file §B%s§/" %(self.cfg_file.path), prefix.start)
|
||||
self._print('creating configuration file §B%s§/' %(self.cfg_file.path), prefix.start)
|
||||
else:
|
||||
self.cfg_file = None
|
||||
|
||||
|
@ -264,73 +264,73 @@ class Main:
|
|||
self.help_texts = [] # (chapter, [paragraphs]) tuples
|
||||
|
||||
def __repr__(self):
|
||||
return "over.Main(name=%s)" %(self.name)
|
||||
return 'over.Main(name=%s)' %(self.name)
|
||||
|
||||
def exit(self, retval=0, silent=False):
|
||||
"""
|
||||
'''
|
||||
int retval return value
|
||||
bool silent don't mention it
|
||||
|
||||
Shutdown the program and exit.
|
||||
|
||||
Can be disabled by setting self.allow_exit to False.
|
||||
"""
|
||||
'''
|
||||
|
||||
if self.allow_exit:
|
||||
if not silent:
|
||||
self._print("exiting with §B%d§/" %(retval), prefix.info)
|
||||
self._print('exiting with §B%d§/' %(retval), prefix.info)
|
||||
|
||||
sys.exit(retval)
|
||||
|
||||
def dump(self, ignore=[]):
|
||||
"""
|
||||
'''
|
||||
return current configuration in Et Commandline format
|
||||
"""
|
||||
'''
|
||||
|
||||
out = []
|
||||
for option in [option for option in self.options if option.name not in ignore]:
|
||||
if option.type == "bool":
|
||||
if option.type == 'bool':
|
||||
if option.plural:
|
||||
for value in option.value:
|
||||
if value:
|
||||
out.append("--%s" %(option.name))
|
||||
out.append('--%s' %(option.name))
|
||||
else:
|
||||
out.append("--no-%s" %(option.name))
|
||||
out.append('--no-%s' %(option.name))
|
||||
elif option.value is not None:
|
||||
if option.value:
|
||||
out.append("--%s" %(option.name))
|
||||
out.append('--%s' %(option.name))
|
||||
else:
|
||||
out.append("--no-%s" %(option.name))
|
||||
out.append('--no-%s' %(option.name))
|
||||
else:
|
||||
if option.plural:
|
||||
out.append("--%s" %(option.name))
|
||||
out.append('--%s' %(option.name))
|
||||
for value in option.value:
|
||||
if option.type == "str":
|
||||
value = '"%s"' %(value.replace('"', r'\"'))
|
||||
if option.type == 'str':
|
||||
value = "'%s'" %(value.replace("'", r"\'"))
|
||||
out.append(str(value))
|
||||
out.append("--")
|
||||
out.append('--')
|
||||
elif option.value is not None:
|
||||
out.append("--%s" %(option.name))
|
||||
if option.type == "str":
|
||||
option.value = '"%s"' %(option.value.replace('"', r'\"'))
|
||||
out.append('--%s' %(option.name))
|
||||
if option.type == 'str':
|
||||
option.value = "'%s'" %(option.value.replace("'", r"\'"))
|
||||
out.append(str(option.value))
|
||||
|
||||
return out
|
||||
|
||||
def reset_to_default(self):
|
||||
"""
|
||||
'''
|
||||
Resets internal state to builtin defaults (i.e. as it is before self.parse() is called).
|
||||
"""
|
||||
'''
|
||||
|
||||
self.targets = []
|
||||
self.unknowns = []
|
||||
|
||||
for option in self.options:
|
||||
option.source = "default"
|
||||
option.source = 'default'
|
||||
option.value = option.default
|
||||
|
||||
def add_option(self, name, dtype, default, description, short_name=None, plural=False, callback=None, use_cfg_file=True, hidden=False):
|
||||
"""
|
||||
'''
|
||||
str name Name of the option. See #0
|
||||
str dtype Option's data type. See #1
|
||||
str default Option's default value. See #2
|
||||
|
@ -345,7 +345,7 @@ class Main:
|
|||
|
||||
#0 Option naming
|
||||
Each option has a name. This name is directly used on the command line prefixed by two dashes.
|
||||
For example option "warp-speed" would be called like so: --warp-speed.
|
||||
For example option 'warp-speed' would be called like so: --warp-speed.
|
||||
Bools take two appearances: --warp-speed is True, while --no-warp-speed is False.
|
||||
Their values can be accessed from the program as main.cfg.option_name. Options with dashes
|
||||
in their names (an operator in python) can be accessed as well, just substitute dashes
|
||||
|
@ -353,10 +353,10 @@ class Main:
|
|||
|
||||
#1 Data types.
|
||||
There are four data types.
|
||||
* "bool" - either exactly True or False.
|
||||
* "str" - a raw text string, nothing special going on here
|
||||
* "int" - an integer
|
||||
* "float" - a float
|
||||
* 'bool' - either exactly True or False.
|
||||
* 'str' - a raw text string, nothing special going on here
|
||||
* 'int' - an integer
|
||||
* 'float' - a float
|
||||
|
||||
#2 Default values
|
||||
These are the values the option will take if it's not called on the command line or
|
||||
|
@ -376,49 +376,49 @@ class Main:
|
|||
An option with a callback will invoke it if it appears on the command line (both +a and -a). This is
|
||||
usually done for options like --help (internally), --list-modules, --show-something and similar.
|
||||
The callback will be passed one argument: the main instance.
|
||||
"""
|
||||
'''
|
||||
|
||||
for option in self.options:
|
||||
if name == option.name:
|
||||
self._print("option §B--§r%s§/ already exists" %(name), prefix.fail)
|
||||
self._print('option §B--§r%s§/ already exists' %(name), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
if option.short_name and short_name == option.short_name:
|
||||
self._print("option §B--§r%s§/'s short name §r%s§/ is already taken by §B--§y%s§/" %(name, short_name, option.name), prefix.fail)
|
||||
self._print('option §B--§r%s§/\'s short name §r%s§/ is already taken by §B--§y%s§/' %(name, short_name, option.name), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
if short_name and len(short_name) > 1:
|
||||
self._print("short name must be a single character; option §B--§r%s§/ has §r%s§/"%(name, short_name), prefix.fail)
|
||||
self._print('short name must be a single character; option §B--§r%s§/ has §r%s§/'%(name, short_name), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
if callback:
|
||||
use_cfg_file = False
|
||||
|
||||
if dtype not in ["bool", "str", "int", "float"]:
|
||||
self._print("option §B--§r%s§/'s dtype is not of a supported type" %(name), prefix.fail)
|
||||
if dtype not in ['bool', 'str', 'int', 'float']:
|
||||
self._print('option §B--§r%s§/\'s dtype is not of a supported type' %(name), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
v = _verify_type(default, dtype, plural)
|
||||
|
||||
if not v:
|
||||
if plural:
|
||||
type_desc = "a list of §m%s§rs" %(dtype)
|
||||
type_desc = 'a list of §m%s§rs' %(dtype)
|
||||
else:
|
||||
if dtype == "int":
|
||||
type_desc = "an int"
|
||||
if dtype == 'int':
|
||||
type_desc = 'an int'
|
||||
else:
|
||||
type_desc = "a %s" %(dtype)
|
||||
type_desc = 'a %s' %(dtype)
|
||||
|
||||
self._print("option §B--§r%s§/'s default must be §y%s§/" %(name, type_desc), prefix.fail)
|
||||
self._print('option §B--§r%s§/\'s default must be §y%s§/' %(name, type_desc), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
o = Option(name, dtype, default, description, short_name, plural, use_cfg_file, callback, hidden)
|
||||
self.options.append(o)
|
||||
|
||||
def parse(self, cmdline=None, reset_to_default=False):
|
||||
"""
|
||||
'''
|
||||
Parses the cfg file and the command line and sets the internal cfg state accordingly.
|
||||
"""
|
||||
'''
|
||||
|
||||
ptr = 0
|
||||
parsing_plural = False
|
||||
|
@ -435,18 +435,18 @@ class Main:
|
|||
to_parse = []
|
||||
word = cmdline[ptr]
|
||||
|
||||
if len(word) > 2 and word[0:2] == "--":
|
||||
if len(word) > 2 and word[0:2] == '--':
|
||||
to_parse.append(word)
|
||||
elif len(word) >= 2 and (word[0] == "+" or (word[0] == "-" and word[1] != "-")):
|
||||
elif len(word) >= 2 and (word[0] == '+' or (word[0] == '-' and word[1] != '-')):
|
||||
# expand and then parse
|
||||
for s in word[1:]:
|
||||
option = [opt for opt in self.options if opt.short_name == s]
|
||||
|
||||
if option:
|
||||
if word[0] == "+" or option[0].dtype != "bool":
|
||||
to_parse.append("--{0.name}".format(option[0]))
|
||||
if word[0] == '+' or option[0].dtype != 'bool':
|
||||
to_parse.append('--{0.name}'.format(option[0]))
|
||||
else:
|
||||
to_parse.append("--no-{0.name}".format(option[0]))
|
||||
to_parse.append('--no-{0.name}'.format(option[0]))
|
||||
else:
|
||||
self.unknowns.append(word[0] + s)
|
||||
else:
|
||||
|
@ -456,7 +456,7 @@ class Main:
|
|||
|
||||
if to_parse:
|
||||
for w in to_parse:
|
||||
if w.startswith("--no-"):
|
||||
if w.startswith('--no-'):
|
||||
bool_on = False
|
||||
name = w[5:]
|
||||
else:
|
||||
|
@ -471,11 +471,11 @@ class Main:
|
|||
if o.callback:
|
||||
callbacks.append(o.callback)
|
||||
|
||||
if o.plural and o.source != "cmdline":
|
||||
if o.plural and o.source != 'cmdline':
|
||||
o.value = []
|
||||
|
||||
if o.dtype == "bool":
|
||||
o.source = "cmdline"
|
||||
if o.dtype == 'bool':
|
||||
o.source = 'cmdline'
|
||||
|
||||
if o.plural:
|
||||
o.value.append(bool_on)
|
||||
|
@ -483,38 +483,38 @@ class Main:
|
|||
o.value = bool_on
|
||||
else:
|
||||
if not bool_on:
|
||||
self._print("§B--no-§r%s§/ makes no sense because this option is of type §y%s§/" %(name, o.dtype), prefix.fail)
|
||||
self._print('§B--no-§r%s§/ makes no sense because this option is of type §y%s§/' %(name, o.dtype), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
# This option takes arguments. These are all words at ptr and higher that don't begin with "--".
|
||||
# This option takes arguments. These are all words at ptr and higher that don't begin with '--'.
|
||||
# If this option is part of a shortgroup it must be at its end.
|
||||
|
||||
if to_parse.index(w) + 1 != len(to_parse):
|
||||
self._print("option §B{0[0]}§r{1.short_name}§/ needs to be at the end of group §y{0}§/".format(word, o), prefix.fail)
|
||||
self._print('option §B{0[0]}§r{1.short_name}§/ needs to be at the end of group §y{0}§/'.format(word, o), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
got_something = False
|
||||
|
||||
# NOTE it shouldn't be hard to implement specific plurality (how many words to chew)
|
||||
# NOTE REPLY: I'm still not doing it.
|
||||
while ptr < len(cmdline) and not cmdline[ptr].startswith("--"):
|
||||
while ptr < len(cmdline) and not cmdline[ptr].startswith('--'):
|
||||
arg = cmdline[ptr]
|
||||
ptr += 1
|
||||
|
||||
if o.dtype == "int":
|
||||
if o.dtype == 'int':
|
||||
try:
|
||||
arg = int(arg)
|
||||
except ValueError:
|
||||
self._print("argument §r%s§/ passed to integer option §B--§y%s§/" %(arg, o.name), prefix.fail, exc=ValueError)
|
||||
self._print('argument §r%s§/ passed to integer option §B--§y%s§/' %(arg, o.name), prefix.fail, exc=ValueError)
|
||||
|
||||
elif o.dtype == "float":
|
||||
elif o.dtype == 'float':
|
||||
try:
|
||||
arg = float(arg)
|
||||
except ValueError:
|
||||
self._print("argument §r%s§/ passed to float option §B--§y%s§/" %(arg, o.name), prefix.fail, exc=ValueError)
|
||||
self._print('argument §r%s§/ passed to float option §B--§y%s§/' %(arg, o.name), prefix.fail, exc=ValueError)
|
||||
|
||||
got_something = True
|
||||
o.source = "cmdline"
|
||||
o.source = 'cmdline'
|
||||
|
||||
if o.plural:
|
||||
o.value.append(arg)
|
||||
|
@ -523,15 +523,15 @@ class Main:
|
|||
break
|
||||
|
||||
#if not got_something:
|
||||
# self._print("option §B--§r%s§/ needs at least one argument" %(o.name), prefix.fail, exc=GeneralError)
|
||||
# self._print('option §B--§r%s§/ needs at least one argument' %(o.name), prefix.fail, exc=GeneralError)
|
||||
|
||||
else:
|
||||
self.unknowns.append(w)
|
||||
|
||||
self.targets = [u for u in unparsed if u != "--"]
|
||||
self.targets = [u for u in unparsed if u != '--']
|
||||
|
||||
if self.unknowns:
|
||||
self._print("unknown options on the command line: §r%s§/" %("§/, §r".join(self.unknowns)), prefix.fail)
|
||||
self._print('unknown options on the command line: §r%s§/' %('§/, §r'.join(self.unknowns)), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
# parse the config file
|
||||
|
@ -539,104 +539,104 @@ class Main:
|
|||
new_lines = []
|
||||
|
||||
for opt in [o for o in self.options if o.use_cfg_file]:
|
||||
match = re.findall("^(#?)\s*%s\s*=\s*(.+)" %(opt.name), self.cfg_file.data, re.M)
|
||||
match = re.findall('^(#?)\s*%s\s*=\s*(.+)' %(opt.name), self.cfg_file.data, re.M)
|
||||
|
||||
if match:
|
||||
if opt.source == "default":
|
||||
if opt.source == 'default':
|
||||
if not match[0][0]: # if it isn't commented out
|
||||
d = match[0][1]
|
||||
|
||||
opt.source = "config"
|
||||
opt.source = 'config'
|
||||
|
||||
if opt.plural:
|
||||
opt.value = []
|
||||
|
||||
if opt.dtype != "str":
|
||||
elements = [x.strip() for x in d[1:-1].split(",")]
|
||||
if opt.dtype != 'str':
|
||||
elements = [x.strip() for x in d[1:-1].split(',')]
|
||||
else:
|
||||
d = re.sub("\",\s*\"", "\",\"", d) # remove spaces
|
||||
d = d[1:-1].strip()[1:-1] # remove [] and outermost ""
|
||||
elements = ["\"%s\"" %(x) for x in d.split("\",\"")] # split and re-pack them with ""
|
||||
d = re.sub('\',\s*\'', '\',\'', d) # remove spaces
|
||||
d = d[1:-1].strip()[1:-1] # remove [] and outermost ''
|
||||
elements = ['\'%s\'' %(x) for x in d.split('\',\'')] # split and re-pack them with ''
|
||||
|
||||
for element in elements:
|
||||
try:
|
||||
opt.value.append(_parse(element, opt.dtype))
|
||||
except RuntimeError:
|
||||
self._print("config file syntax error for option §B--§r%s§/" %(opt.name), prefix.fail)
|
||||
self._print('config file syntax error for option §B--§r%s§/' %(opt.name), prefix.fail)
|
||||
raise
|
||||
|
||||
else:
|
||||
try:
|
||||
opt.value = _parse(d, opt.dtype)
|
||||
except RuntimeError:
|
||||
self._print("config file syntax error for option §B--§r%s§/" %(opt.name), prefix.fail)
|
||||
self._print('config file syntax error for option §B--§r%s§/' %(opt.name), prefix.fail)
|
||||
raise
|
||||
|
||||
else:
|
||||
self._print("updating config file with option §B--§y%s§/" %(opt.name))
|
||||
new_lines.append("")
|
||||
new_lines.append(textui.paragraph(textui.render(opt.description, colors=False), prefix="#", width=80))
|
||||
new_lines.append("# *** data type: %s" %(opt.dtype))
|
||||
self._print('updating config file with option §B--§y%s§/' %(opt.name))
|
||||
new_lines.append('')
|
||||
new_lines.append(textui.paragraph(textui.render(opt.description, colors=False), prefix='#', width=80))
|
||||
new_lines.append('# *** data type: %s' %(opt.dtype))
|
||||
|
||||
if opt.plural:
|
||||
new_lines.append("# *** plural option")
|
||||
new_lines.append('# *** plural option')
|
||||
|
||||
tmp = []
|
||||
for v in opt.default:
|
||||
if opt.dtype == "str":
|
||||
tmp.append("\"%s\"" %(v))
|
||||
if opt.dtype == 'str':
|
||||
tmp.append('\'%s\'' %(v))
|
||||
else:
|
||||
tmp.append(str(v))
|
||||
|
||||
str_value = "[%s]" %(", ".join(tmp))
|
||||
str_value = '[%s]' %(', '.join(tmp))
|
||||
else:
|
||||
if opt.dtype == "str" and opt.default is not None:
|
||||
str_value = "\"%s\"" %(opt.default)
|
||||
if opt.dtype == 'str' and opt.default is not None:
|
||||
str_value = '\'%s\'' %(opt.default)
|
||||
else:
|
||||
str_value = str(opt.default)
|
||||
|
||||
new_lines.append("#%s = %s\n" %(opt.name, str_value))
|
||||
new_lines.append('#%s = %s\n' %(opt.name, str_value))
|
||||
|
||||
self.cfg_file.data += "\n".join(new_lines)
|
||||
self.cfg_file.data += '\n'.join(new_lines)
|
||||
|
||||
# handle callbacks
|
||||
for callback in callbacks:
|
||||
callback(self)
|
||||
|
||||
def dump_cfg(self):
|
||||
"""
|
||||
'''
|
||||
Print a summary of the current cfg state.
|
||||
|
||||
DEPRECATED by --help
|
||||
"""
|
||||
'''
|
||||
|
||||
self._print("state dump", prefix.start)
|
||||
self._print('state dump', prefix.start)
|
||||
|
||||
for opt in self.options:
|
||||
if opt.source == "cmdline":
|
||||
src = "§mcmdline§/"
|
||||
elif opt.source == "config":
|
||||
src = "§yconfig§/"
|
||||
if opt.source == 'cmdline':
|
||||
src = '§mcmdline§/'
|
||||
elif opt.source == 'config':
|
||||
src = '§yconfig§/'
|
||||
else:
|
||||
src = "default"
|
||||
src = 'default'
|
||||
|
||||
if opt.plural:
|
||||
self._print("--§g%s§/ (%s): §b%s§/" %(opt.name, src, "§/, §b".join(str(x) for x in opt.value)))
|
||||
self._print('--§g%s§/ (%s): §b%s§/' %(opt.name, src, '§/, §b'.join(str(x) for x in opt.value)))
|
||||
else:
|
||||
self._print("--§g%s§/ (%s): §b%s§/" %(opt.name, src, opt.value))
|
||||
self._print('--§g%s§/ (%s): §b%s§/' %(opt.name, src, opt.value))
|
||||
|
||||
self._print("Targets: §B%s§/" %("§/, §B".join(self.targets)))
|
||||
self._print('Targets: §B%s§/' %('§/, §B'.join(self.targets)))
|
||||
|
||||
self._print("state dump", prefix.done)
|
||||
self._print('state dump', prefix.done)
|
||||
|
||||
def enable_help(self, short_name=None):
|
||||
"""
|
||||
'''
|
||||
Enable --help, --over-help and --help-over. If a short name is provided, it will be used for --help.
|
||||
"""
|
||||
'''
|
||||
|
||||
self.add_option("help", "bool", False, "Display this help message.", callback=self.help, short_name=short_name, use_cfg_file=False)
|
||||
self.add_option("over-help", "bool", False, "Display general usage information for over.core.", callback=self.help_over, use_cfg_file=False)
|
||||
self.add_option("help-over", "bool", False, "Display general usage information for over.core.", callback=self.help_over, use_cfg_file=False, hidden=True)
|
||||
self.add_option('help', 'bool', False, 'Display this help message.', callback=self.help, short_name=short_name, use_cfg_file=False)
|
||||
self.add_option('over-help', 'bool', False, 'Display general usage information for over.core.', callback=self.help_over, use_cfg_file=False)
|
||||
self.add_option('help-over', 'bool', False, 'Display general usage information for over.core.', callback=self.help_over, use_cfg_file=False, hidden=True)
|
||||
|
||||
def add_help(self, chapter, paragraphs):
|
||||
self.help_texts.append((chapter, paragraphs))
|
||||
|
@ -651,9 +651,9 @@ class Main:
|
|||
|
||||
class Option:
|
||||
def __init__(self, name, dtype, default, description, short_name, plural, use_cfg_file, callback, hidden):
|
||||
"""
|
||||
'''
|
||||
Just a container. Move along...
|
||||
"""
|
||||
'''
|
||||
|
||||
self.name = name
|
||||
self.dtype = dtype # bool, int, str, float
|
||||
|
@ -665,5 +665,5 @@ class Option:
|
|||
self.callback = callback
|
||||
self.hidden = hidden
|
||||
|
||||
self.source = "default"
|
||||
self.source = 'default'
|
||||
self.value = default
|
||||
|
|
|
@ -5,4 +5,4 @@ import sys
|
|||
|
||||
from . import textui
|
||||
|
||||
_print = textui.Output("over.core", stream=sys.stderr)
|
||||
_print = textui.Output('over.core', stream=sys.stderr)
|
||||
|
|
|
@ -4,19 +4,19 @@
|
|||
# --------------------------------------------------
|
||||
|
||||
cdef class ndict(dict):
|
||||
"""
|
||||
'''
|
||||
A dictionary superclass whose keys are exposed as attributes.
|
||||
|
||||
>>> d = ndict()
|
||||
>>> d.alpha = 1
|
||||
>>> d["alpha"]
|
||||
>>> d['alpha']
|
||||
1
|
||||
>>> d["beta"] = 42
|
||||
>>> d['beta'] = 42
|
||||
>>> d.beta
|
||||
42
|
||||
>>> d
|
||||
{'alpha': 1, 'beta': 42}
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
dict.__init__(self, *args, **kwargs)
|
||||
|
@ -33,19 +33,19 @@ cdef class ndict(dict):
|
|||
# --------------------------------------------------
|
||||
|
||||
cdef class map:
|
||||
"""
|
||||
'''
|
||||
An ordered dict.
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(self, source=None):
|
||||
"""
|
||||
'''
|
||||
Initialize the map.
|
||||
|
||||
The source is a sequence of (key, value) tuples.
|
||||
|
||||
TODO fixme bug
|
||||
>>> map(((1, 2, 3, 4), ("a", "b", "c", "d")))
|
||||
"""
|
||||
>>> map(((1, 2, 3, 4), ('a', 'b', 'c', 'd')))
|
||||
'''
|
||||
|
||||
print(repr(source))
|
||||
|
||||
|
@ -106,6 +106,6 @@ cdef class map:
|
|||
pairs = []
|
||||
|
||||
for i in range(len(self.keys)):
|
||||
pairs.append("%s: %s" %(repr(self.keys[i]), repr(self.vals[i])))
|
||||
pairs.append('%s: %s' %(repr(self.keys[i]), repr(self.vals[i])))
|
||||
|
||||
return "<{%s}>" %(", ".join(pairs))
|
||||
return '<{%s}>' %(', '.join(pairs))
|
||||
|
|
38
core/file.py
38
core/file.py
|
@ -9,34 +9,34 @@ from .textui import prefix
|
|||
# --------------------------------------------------
|
||||
|
||||
class File:
|
||||
"""
|
||||
'''
|
||||
A binary r/w file container that abstracts file descriptors away. You just read and write data.
|
||||
|
||||
Nonexistent files will be created, including any directories if necessary.
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(self, path, encoding=None):
|
||||
"""
|
||||
'''
|
||||
encoding which encoding to use when opening files; None means binary (raw)
|
||||
"""
|
||||
'''
|
||||
|
||||
self.encoding = encoding
|
||||
|
||||
if path[0] == "~":
|
||||
self.path = os.path.join(os.getenv("HOME"), path[2:])
|
||||
if path[0] == '~':
|
||||
self.path = os.path.join(os.getenv('HOME'), path[2:])
|
||||
else:
|
||||
self.path = path
|
||||
|
||||
if not os.path.isfile(self.path):
|
||||
if os.path.exists(self.path):
|
||||
_print("path §y%s§/ exists but §ris not a file§/" %(self.path), prefix.fail)
|
||||
_print('path §y%s§/ exists but §ris not a file§/' %(self.path), prefix.fail)
|
||||
raise RuntimeError
|
||||
|
||||
else:
|
||||
dirname = os.path.dirname(self.path)
|
||||
|
||||
if dirname and not os.path.isdir(dirname):
|
||||
_print("creating directory §B%s§/" %(dirname))
|
||||
_print('creating directory §B%s§/' %(dirname))
|
||||
os.makedirs(dirname)
|
||||
|
||||
# create the file
|
||||
|
@ -44,14 +44,14 @@ class File:
|
|||
|
||||
@property
|
||||
def data(self):
|
||||
"""
|
||||
'''
|
||||
Reads the file and returns the contents.
|
||||
"""
|
||||
'''
|
||||
|
||||
if self.encoding:
|
||||
fd = open(self.path, encoding=self.encoding)
|
||||
else:
|
||||
fd = open(self.path, "rb")
|
||||
fd = open(self.path, 'rb')
|
||||
|
||||
data = fd.read()
|
||||
fd.close()
|
||||
|
@ -60,28 +60,28 @@ class File:
|
|||
|
||||
@data.setter
|
||||
def data(self, data):
|
||||
"""
|
||||
'''
|
||||
Writes data into the file.
|
||||
"""
|
||||
'''
|
||||
if self.encoding:
|
||||
fd = open(self.path, "w", encoding=self.encoding)
|
||||
fd = open(self.path, 'w', encoding=self.encoding)
|
||||
else:
|
||||
fd = open(self.path, "wb")
|
||||
fd = open(self.path, 'wb')
|
||||
|
||||
fd.write(data)
|
||||
fd.close()
|
||||
|
||||
def __repr__(self):
|
||||
return "over.core.File(%s %s)" %(self.path, self.encoding if self.encoding else "raw")
|
||||
return 'over.core.File(%s %s)' %(self.path, self.encoding if self.encoding else 'raw')
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
def touch(fname, times=None):
|
||||
"""
|
||||
'''
|
||||
Sets a filename's atime and mtime.
|
||||
|
||||
times is a tuple of (atime, mtime) and defaults to "now".
|
||||
"""
|
||||
times is a tuple of (atime, mtime) and defaults to 'now'.
|
||||
'''
|
||||
|
||||
with open(fname, 'a'):
|
||||
os.utime(fname, times)
|
||||
|
|
18
core/misc.py
18
core/misc.py
|
@ -7,17 +7,17 @@ import os
|
|||
# --------------------------------------------------
|
||||
|
||||
def import_module(path):
|
||||
"""
|
||||
'''
|
||||
Imports a python file as a module. The path can be relative or absolute.
|
||||
|
||||
Based on the work of Yuval Greenfield released into the public domain.
|
||||
"""
|
||||
'''
|
||||
|
||||
# remove the .py suffix
|
||||
mod_dn = os.path.dirname(path)
|
||||
mod_fn = os.path.basename(path)
|
||||
|
||||
if mod_fn.endswith(".py"):
|
||||
if mod_fn.endswith('.py'):
|
||||
mod_name = mod_fn[:-3]
|
||||
else:
|
||||
# packages for example
|
||||
|
@ -39,9 +39,9 @@ def import_module(path):
|
|||
# --------------------------------------------------
|
||||
|
||||
def batch_gen(data, batch_size):
|
||||
"""
|
||||
'''
|
||||
Split data (a sequence) into sequences batch_size elements long.
|
||||
"""
|
||||
'''
|
||||
|
||||
for i in range(0, len(data), batch_size):
|
||||
yield data[i:i+batch_size]
|
||||
|
@ -49,17 +49,17 @@ def batch_gen(data, batch_size):
|
|||
# --------------------------------------------------
|
||||
|
||||
def console(environment, tab_completion=False):
|
||||
"""
|
||||
'''
|
||||
Opens up a Python console.
|
||||
|
||||
environment is a dictionary typically returned by locals() or similar
|
||||
tab_completion enables completion of object names using TAB, however note
|
||||
that this will make pasting formatted snippets of code difficult
|
||||
"""
|
||||
'''
|
||||
|
||||
import code, readline, rlcompleter
|
||||
|
||||
readline.parse_and_bind("tab: complete")
|
||||
readline.parse_and_bind('tab: complete')
|
||||
|
||||
c = code.InteractiveConsole(environment)
|
||||
c.interact(banner="")
|
||||
c.interact(banner='')
|
||||
|
|
|
@ -4,19 +4,19 @@
|
|||
# --------------------------------------------------
|
||||
|
||||
class ndict(dict):
|
||||
"""
|
||||
'''
|
||||
A dictionary superclass whose keys are exposed as attributes.
|
||||
|
||||
>>> d = ndict()
|
||||
>>> d.alpha = 1
|
||||
>>> d["alpha"]
|
||||
>>> d['alpha']
|
||||
1
|
||||
>>> d["beta"] = 42
|
||||
>>> d['beta'] = 42
|
||||
>>> d.beta
|
||||
42
|
||||
>>> d
|
||||
{'alpha': 1, 'beta': 42}
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
dict.__init__(self, *args, **kwargs)
|
||||
|
@ -33,19 +33,19 @@ class ndict(dict):
|
|||
# --------------------------------------------------
|
||||
|
||||
class map:
|
||||
"""
|
||||
'''
|
||||
An ordered dict.
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(self, source=None):
|
||||
"""
|
||||
'''
|
||||
Initialize the map.
|
||||
|
||||
The source is a sequence of (key, value) tuples.
|
||||
|
||||
TODO fixme bug
|
||||
>>> map(((1, 2, 3, 4), ("a", "b", "c", "d")))
|
||||
"""
|
||||
>>> map(((1, 2, 3, 4), ('a', 'b', 'c', 'd')))
|
||||
'''
|
||||
|
||||
print(repr(source))
|
||||
|
||||
|
@ -106,6 +106,6 @@ class map:
|
|||
pairs = []
|
||||
|
||||
for i in range(len(self.keys)):
|
||||
pairs.append("%s: %s" %(repr(self.keys[i]), repr(self.vals[i])))
|
||||
pairs.append('%s: %s' %(repr(self.keys[i]), repr(self.vals[i])))
|
||||
|
||||
return "<{%s}>" %(", ".join(pairs))
|
||||
return '<{%s}>' %(', '.join(pairs))
|
||||
|
|
164
core/textui.py
164
core/textui.py
|
@ -12,19 +12,19 @@ import time
|
|||
# --------------------------------------------------
|
||||
|
||||
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
|
||||
'''
|
||||
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
|
||||
|
@ -40,7 +40,7 @@ class ProgressBar:
|
|||
self.t_start = time.time()
|
||||
|
||||
if self.old_len:
|
||||
sys.stderr.write("\b" * self.old_len)
|
||||
sys.stderr.write('\b' * self.old_len)
|
||||
|
||||
transferred = Unit(self.value, self.unit)
|
||||
dt = time.time() - self.t_start
|
||||
|
@ -50,18 +50,18 @@ class ProgressBar:
|
|||
else:
|
||||
speed = 0.0
|
||||
|
||||
speed = Unit(speed, "%s/s" %(self.unit))
|
||||
speed = Unit(speed, '%s/s' %(self.unit))
|
||||
|
||||
ratio = self.value / self.top
|
||||
pb_done = "=" * int(self.width * ratio)
|
||||
pb_rem = " " * int(self.width * (1 - ratio))
|
||||
symbol = ">"
|
||||
pb_done = '=' * int(self.width * ratio)
|
||||
pb_rem = ' ' * int(self.width * (1 - ratio))
|
||||
symbol = '>'
|
||||
|
||||
if self.reverse:
|
||||
symbol = "<"
|
||||
symbol = '<'
|
||||
pb_done, pb_rem = pb_rem, pb_done
|
||||
|
||||
text = "%s [%s%s%s] %s" %(transferred, pb_done, symbol, pb_rem, speed)
|
||||
text = '%s [%s%s%s] %s' %(transferred, pb_done, symbol, pb_rem, speed)
|
||||
|
||||
sys.stderr.write(text)
|
||||
|
||||
|
@ -70,8 +70,8 @@ class ProgressBar:
|
|||
self.old_len = current_len
|
||||
|
||||
if tail > 0:
|
||||
sys.stderr.write(" " * tail)
|
||||
sys.stderr.write("\b" * tail)
|
||||
sys.stderr.write(' ' * tail)
|
||||
sys.stderr.write('\b' * tail)
|
||||
|
||||
sys.stderr.flush()
|
||||
|
||||
|
@ -81,46 +81,46 @@ class ProgressBar:
|
|||
self.draw()
|
||||
|
||||
def blank(self):
|
||||
sys.stderr.write("\r" + self.old_len * " " + "\r")
|
||||
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
|
||||
|
@ -128,7 +128,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:
|
||||
|
@ -137,7 +137,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:
|
||||
|
@ -148,18 +148,18 @@ 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.textui.Unit(%s)" %(self)
|
||||
return 'over.core.textui.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
|
||||
|
@ -169,7 +169,7 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
|
||||
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()
|
||||
|
@ -187,9 +187,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:
|
||||
|
@ -198,16 +198,16 @@ 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
|
||||
# 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
|
||||
|
@ -234,13 +234,13 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
# --------------------------------------------------
|
||||
|
||||
# def strtrim(text, length, mode=0, dots=True, colors=True):
|
||||
# """
|
||||
# '''
|
||||
# str text text to trim
|
||||
# int length desired length, >1
|
||||
# int mode -1 = cut chars from the left, 0 = from the middle, 1 = from the right
|
||||
# bool dots add an ellipsis to the point of cutting
|
||||
# bool colors dont count color tags into length; also turns the ellipsis blue
|
||||
# """
|
||||
# '''
|
||||
|
||||
# if len(text) <= length:
|
||||
# return text
|
||||
|
@ -253,7 +253,7 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
|
||||
# if mode == -1:
|
||||
# if dots:
|
||||
# return "..." + cut(text, length, 1, colors)
|
||||
# return '...' + cut(text, length, 1, colors)
|
||||
# else:
|
||||
# return cut(text, length, 1, colors)
|
||||
|
||||
|
@ -266,27 +266,27 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
# part2 = cut(text, length/2, 1, colors)
|
||||
|
||||
# if dots:
|
||||
# part1 += "..."
|
||||
# part1 += '...'
|
||||
|
||||
# return part1 + part2
|
||||
|
||||
# else:
|
||||
# if dots:
|
||||
# return cut(text, length, 0, colors) + "..."
|
||||
# return cut(text, length, 0, colors) + '...'
|
||||
# else:
|
||||
# return cut(text, length, 0, colors)
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
# def textfilter(text, set=None):
|
||||
# """
|
||||
# '''
|
||||
# str text text to filter
|
||||
# dict set {to_replace: replacement}
|
||||
|
||||
# Text filter that replaces occurences of to_replace keys with their respective values.
|
||||
|
||||
# Defaults to filtering of 'bad' characters if no translational dictionary is provided.
|
||||
# """
|
||||
# '''
|
||||
|
||||
# if not set:
|
||||
# set = badchars
|
||||
|
@ -299,14 +299,14 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
# --------------------------------------------------
|
||||
|
||||
# def cut(text, size, end=0, colors=True):
|
||||
# """
|
||||
# '''
|
||||
# str text text to cut
|
||||
# int size how many chars to return, >=0
|
||||
# int end return chars from 0 = left, 1 = right
|
||||
# bool colors skip color tags
|
||||
# """
|
||||
# '''
|
||||
|
||||
# out = ""
|
||||
# out = ''
|
||||
|
||||
# strlen = len(text)
|
||||
|
||||
|
@ -317,7 +317,7 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
|
||||
# while size:
|
||||
# if end == 0:
|
||||
# if colors and text[ptr] == "<" and strlen - ptr >= 4 and text[ptr+1] == "C" and text[ptr+3] == ">": # we have a tag
|
||||
# if colors and text[ptr] == '<' and strlen - ptr >= 4 and text[ptr+1] == 'C' and text[ptr+3] == '>': # we have a tag
|
||||
# out += text[ptr:ptr+4]
|
||||
# ptr += 4
|
||||
# else:
|
||||
|
@ -325,7 +325,7 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
# ptr += 1
|
||||
# size -= 1
|
||||
# else:
|
||||
# if colors and text[ptr] == ">" and ptr >= 4 and text[ptr-2] == "C" and text[ptr-3] == "<": # we have a tag
|
||||
# if colors and text[ptr] == '>' and ptr >= 4 and text[ptr-2] == 'C' and text[ptr-3] == '<': # we have a tag
|
||||
# out = text[ptr-3:ptr+1] + out
|
||||
# ptr -= 4
|
||||
# else:
|
||||
|
@ -342,31 +342,31 @@ def paragraph(text, width=0, indent=0, prefix=None, stamp=None):
|
|||
# --------------------------------------------------
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
|
@ -389,9 +389,9 @@ def render(text, colors=True):
|
|||
# --------------------------------------------------
|
||||
|
||||
def char_length(string):
|
||||
"""
|
||||
'''
|
||||
Returns the length of a string minus all formatting tags.
|
||||
"""
|
||||
'''
|
||||
|
||||
plain_string = render(string, colors=False)
|
||||
|
||||
|
@ -400,7 +400,7 @@ def char_length(string):
|
|||
# --------------------------------------------------
|
||||
|
||||
class Output:
|
||||
"""
|
||||
'''
|
||||
Text UI output renderer.
|
||||
|
||||
Prints messages to the stdout with optional eye candy
|
||||
|
@ -408,18 +408,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.stdout):
|
||||
def __init__(self, name, timestamp=True, colors=True, default_prefix=prefix.info, default_suffix='.\n', stream=sys.stdout):
|
||||
self.name = name
|
||||
self.timestamp = timestamp
|
||||
self.colors = colors
|
||||
|
@ -449,13 +449,13 @@ class Output:
|
|||
if timestamp:
|
||||
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))
|
||||
|
@ -463,7 +463,7 @@ class Output:
|
|||
if suffix:
|
||||
output.append(suffix)
|
||||
|
||||
output = "".join(output)
|
||||
output = ''.join(output)
|
||||
|
||||
self.stream.write(render(output, colors))
|
||||
self.stream.flush()
|
||||
|
@ -471,12 +471,12 @@ class Output:
|
|||
# --------------------------------------------------
|
||||
|
||||
def get_terminal_size():
|
||||
"""
|
||||
'''
|
||||
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)
|
||||
|
|
|
@ -8,7 +8,7 @@ import sys
|
|||
try:
|
||||
from .cython_m import mat4, vec3
|
||||
except ImportError:
|
||||
print("!!! [%s] unable to load native implementation, using python instead" %(__name__), file=sys.stderr)
|
||||
print('!!! [%s] unable to load native implementation, using python instead' %(__name__), file=sys.stderr)
|
||||
from .python_m import mat4, vec3
|
||||
|
||||
del sys
|
||||
|
|
110
m/cython_m.pyx
110
m/cython_m.pyx
|
@ -1,11 +1,11 @@
|
|||
#! /bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
"""
|
||||
'''
|
||||
Vector And Matrix Math
|
||||
|
||||
TODO some nice description
|
||||
"""
|
||||
'''
|
||||
|
||||
from libc.stdlib cimport malloc, realloc, free
|
||||
from libc.stdint cimport uint8_t, uint16_t, uint64_t
|
||||
|
@ -26,7 +26,7 @@ class GeneralError(Exception):
|
|||
return self.description
|
||||
|
||||
cdef class mat4:
|
||||
"""
|
||||
'''
|
||||
A float 4x4 matrix.
|
||||
|
||||
All arrays are column-major, i.e. OpenGL style:
|
||||
|
@ -37,30 +37,30 @@ cdef class mat4:
|
|||
3 7 11 15
|
||||
|
||||
The matrix implements stacking useful for graphics.
|
||||
"""
|
||||
'''
|
||||
|
||||
def __cinit__(mat4 self):
|
||||
to_alloc = 16 * sizeof(float)
|
||||
self.stack = <float *>malloc(to_alloc)
|
||||
|
||||
if not self.stack:
|
||||
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||
raise MemoryError('Unable to malloc %d B for mat4.' %(to_alloc))
|
||||
|
||||
self.m = self.stack
|
||||
self.size = 1
|
||||
|
||||
def _debug(mat4 self):
|
||||
print("--- self.stack = %d" %(<uint64_t>self.stack))
|
||||
print("--- self.m = %d (+%d)" %(<uint64_t>self.m, self.m - self.stack))
|
||||
print("--- self.size = %d" %(self.size))
|
||||
print('--- self.stack = %d' %(<uint64_t>self.stack))
|
||||
print('--- self.m = %d (+%d)' %(<uint64_t>self.m, self.m - self.stack))
|
||||
print('--- self.size = %d' %(self.size))
|
||||
|
||||
def __init__(mat4 self, *args):
|
||||
"""
|
||||
'''
|
||||
Create a ma4t.
|
||||
|
||||
Accepts any number of parameters between 0 and 16 to fill the
|
||||
matrix from the upper left corner going down (column-wise).
|
||||
"""
|
||||
'''
|
||||
|
||||
length = len(args)
|
||||
|
||||
|
@ -69,7 +69,7 @@ cdef class mat4:
|
|||
length = len(args)
|
||||
|
||||
if length > 16:
|
||||
raise MathError("Attempt to initialize a mat4 with %d arguments." %(length))
|
||||
raise MathError('Attempt to initialize a mat4 with %d arguments.' %(length))
|
||||
|
||||
self.load_from(args)
|
||||
|
||||
|
@ -89,7 +89,7 @@ cdef class mat4:
|
|||
matrices = length//16
|
||||
|
||||
if not matrices*16 == length:
|
||||
raise GeneralError("mat4 __setstate__ got %d floats as a state" %(length))
|
||||
raise GeneralError('mat4 __setstate__ got %d floats as a state' %(length))
|
||||
|
||||
self.m = self.stack
|
||||
|
||||
|
@ -104,20 +104,20 @@ cdef class mat4:
|
|||
|
||||
def __getitem__(mat4 self, int i):
|
||||
if i > 16 or i < 0:
|
||||
raise IndexError("element index out of range(16)")
|
||||
raise IndexError('element index out of range(16)')
|
||||
|
||||
return self.m[i]
|
||||
|
||||
def __setitem__(self, int i, value):
|
||||
if i > 16 or i < 0:
|
||||
raise IndexError("element index out of range(16)")
|
||||
raise IndexError('element index out of range(16)')
|
||||
|
||||
self.m[i] = value
|
||||
|
||||
def push(mat4 self):
|
||||
"""
|
||||
'''
|
||||
Push the current matrix into the stack and load up an empty one (a zero matrix)
|
||||
"""
|
||||
'''
|
||||
|
||||
# self.m points to the current matrix
|
||||
# self.stack points to the first matrix
|
||||
|
@ -136,7 +136,7 @@ cdef class mat4:
|
|||
if tmp:
|
||||
self.stack = tmp
|
||||
else:
|
||||
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||
raise MemoryError('Unable to malloc %d B for mat4.' %(to_alloc))
|
||||
|
||||
# advance the pointer to the new one
|
||||
self.m = self.stack + 16 * used
|
||||
|
@ -149,12 +149,12 @@ cdef class mat4:
|
|||
self.m[i] = old_m[i]
|
||||
|
||||
def pop(mat4 self):
|
||||
"""
|
||||
'''
|
||||
Pop a matrix from the stack.
|
||||
"""
|
||||
'''
|
||||
|
||||
if self.m == self.stack:
|
||||
raise IndexError("pop from an empty stack")
|
||||
raise IndexError('pop from an empty stack')
|
||||
|
||||
self.m -= 16
|
||||
|
||||
|
@ -167,14 +167,14 @@ cdef class mat4:
|
|||
return L
|
||||
|
||||
def load_from(mat4 self, L):
|
||||
"""
|
||||
'''
|
||||
Fill the current matrix from a either a list of values, column-major,
|
||||
or another matrix. This method doesn't modify the stack, only the
|
||||
current matrix is read and modified.
|
||||
|
||||
If the number of values isn't 16, it will be padded to 16 by zeros.
|
||||
If it's larger, GeneralError will be raised.
|
||||
"""
|
||||
'''
|
||||
|
||||
if isinstance(L, mat4):
|
||||
L = L.get_list()
|
||||
|
@ -183,7 +183,7 @@ cdef class mat4:
|
|||
length = len(L)
|
||||
|
||||
if length > 16:
|
||||
raise GeneralError("supplied list is longer than 16")
|
||||
raise GeneralError('supplied list is longer than 16')
|
||||
|
||||
for i in range(16):
|
||||
if i < length:
|
||||
|
@ -192,13 +192,13 @@ cdef class mat4:
|
|||
self.m[i] = 0.0
|
||||
|
||||
def zero(mat4 self):
|
||||
"""Fill the matrix with zeroes."""
|
||||
'''Fill the matrix with zeroes.'''
|
||||
|
||||
for i in range(16):
|
||||
self.m[i] = 0.0
|
||||
|
||||
def identity(mat4 self):
|
||||
"""Make the matrix an identity."""
|
||||
'''Make the matrix an identity.'''
|
||||
|
||||
self.zero()
|
||||
|
||||
|
@ -208,7 +208,7 @@ cdef class mat4:
|
|||
self.m[15] = 1.0
|
||||
|
||||
def transpose(mat4 self):
|
||||
"""Transpose the matrix."""
|
||||
'''Transpose the matrix.'''
|
||||
|
||||
cdef float tmp
|
||||
|
||||
|
@ -237,7 +237,7 @@ cdef class mat4:
|
|||
self.m[9] = tmp
|
||||
|
||||
def invert(mat4 self):
|
||||
"""Invert the matrix."""
|
||||
'''Invert the matrix.'''
|
||||
|
||||
cdef float tmp[16]
|
||||
cdef float det
|
||||
|
@ -251,7 +251,7 @@ cdef class mat4:
|
|||
|
||||
# epsilon pulled straight out of Uranus
|
||||
if det < 0.00005 and det > -0.00005:
|
||||
print("det=%.1f" %(det))
|
||||
print('det=%.1f' %(det))
|
||||
return
|
||||
|
||||
tmp[1] = -self.m[1]*self.m[10]*self.m[15] + self.m[1]*self.m[11]*self.m[14] + self.m[9]*self.m[2]*self.m[15] - self.m[9]*self.m[3]*self.m[14] - self.m[13]*self.m[2]*self.m[11] + self.m[13]*self.m[3]*self.m[10]
|
||||
|
@ -286,11 +286,11 @@ cdef class mat4:
|
|||
self.m[15] = tmp[15] * det
|
||||
|
||||
def mulm(mat4 self, mat4 B, bint inplace=False):
|
||||
"""
|
||||
'''
|
||||
Return a matrix that is the result of multiplying this matrix by another.
|
||||
|
||||
M = self * mat4 B
|
||||
"""
|
||||
'''
|
||||
|
||||
cdef uint8_t i
|
||||
cdef mat4 tmp = mat4()
|
||||
|
@ -319,11 +319,11 @@ cdef class mat4:
|
|||
return tmp
|
||||
|
||||
def mulv(mat4 self, vec3 v):
|
||||
"""
|
||||
'''
|
||||
Return a vec3 that is the result of multiplying this matrix by a vec3.
|
||||
|
||||
u = self * v
|
||||
"""
|
||||
'''
|
||||
|
||||
cdef mat4 tmp = vec3()
|
||||
|
||||
|
@ -334,11 +334,11 @@ cdef class mat4:
|
|||
return tmp
|
||||
|
||||
def mulf(mat4 self, f):
|
||||
"""
|
||||
'''
|
||||
Return a matrix that is the result of multiplying this matrix by a scalar.
|
||||
|
||||
M = self * f
|
||||
"""
|
||||
'''
|
||||
|
||||
cdef mat4 tmp = mat4()
|
||||
cdef int i
|
||||
|
@ -351,15 +351,15 @@ cdef class mat4:
|
|||
def __repr__(mat4 self):
|
||||
lines = []
|
||||
|
||||
lines.append("mat4(%.1f %.1f %.1f %.1f" %(self.m[0], self.m[4], self.m[8], self.m[12]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[1], self.m[5], self.m[9], self.m[13]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[2], self.m[6], self.m[10], self.m[14]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f)" %(self.m[3], self.m[7], self.m[11], self.m[15]))
|
||||
lines.append('mat4(%.1f %.1f %.1f %.1f' %(self.m[0], self.m[4], self.m[8], self.m[12]))
|
||||
lines.append(' %.1f %.1f %.1f %.1f' %(self.m[1], self.m[5], self.m[9], self.m[13]))
|
||||
lines.append(' %.1f %.1f %.1f %.1f' %(self.m[2], self.m[6], self.m[10], self.m[14]))
|
||||
lines.append(' %.1f %.1f %.1f %.1f)' %(self.m[3], self.m[7], self.m[11], self.m[15]))
|
||||
|
||||
return "\n".join(lines)
|
||||
return '\n'.join(lines)
|
||||
|
||||
cdef class vec3:
|
||||
"""
|
||||
'''
|
||||
A float 3D vector.
|
||||
|
||||
>>> v = vec3(1, 1, 0)
|
||||
|
@ -375,14 +375,14 @@ cdef class vec3:
|
|||
>>> w - v
|
||||
vec4(-1.00, 0.00, 1.00)
|
||||
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(vec3 self, *args):
|
||||
"""
|
||||
'''
|
||||
Create a vec3.
|
||||
|
||||
Accepts any number of parameters between 0 and 3 to fill the vector from the left.
|
||||
"""
|
||||
'''
|
||||
|
||||
length = len(args)
|
||||
|
||||
|
@ -391,7 +391,7 @@ cdef class vec3:
|
|||
length = len(args)
|
||||
|
||||
if length > 3:
|
||||
raise MathError("Attempt to initialize a vec3 with %d arguments." %(length))
|
||||
raise MathError('Attempt to initialize a vec3 with %d arguments.' %(length))
|
||||
|
||||
for i in range(3):
|
||||
if i < length:
|
||||
|
@ -401,18 +401,18 @@ cdef class vec3:
|
|||
|
||||
def __getitem__(vec3 self, int i):
|
||||
if i >= 3 or i < 0:
|
||||
raise IndexError("element index out of range(3)")
|
||||
raise IndexError('element index out of range(3)')
|
||||
|
||||
return self.v[i]
|
||||
|
||||
def __setitem__(vec3 self, int i, float value):
|
||||
if i >= 3 or i < 0:
|
||||
raise IndexError("element index out of range(3)")
|
||||
raise IndexError('element index out of range(3)')
|
||||
|
||||
self.v[i] = value
|
||||
|
||||
def __repr__(vec3 self):
|
||||
return "vec3(%.2f, %.2f, %.2f)" %(self.v[0], self.v[1], self.v[2])
|
||||
return 'vec3(%.2f, %.2f, %.2f)' %(self.v[0], self.v[1], self.v[2])
|
||||
|
||||
def __getstate__(vec3 self):
|
||||
return (self.v[0], self.v[1], self.v[2])
|
||||
|
@ -424,12 +424,12 @@ cdef class vec3:
|
|||
|
||||
@property
|
||||
def length(vec3 self):
|
||||
"""Contains the geometric length of the vector."""
|
||||
'''Contains the geometric length of the vector.'''
|
||||
|
||||
return sqrt(self.v[0]**2 + self.v[1]**2 + self.v[2]**2)
|
||||
|
||||
def normalized(vec3 self):
|
||||
"""Returns this vector, normalized."""
|
||||
'''Returns this vector, normalized.'''
|
||||
|
||||
length = self.length
|
||||
|
||||
|
@ -445,29 +445,29 @@ cdef class vec3:
|
|||
return vec3(-self.v[0], -self.v[1], -self.v[2])
|
||||
|
||||
def dot(vec3 L, vec3 R):
|
||||
"""
|
||||
'''
|
||||
Returns the dot product of the two vectors.
|
||||
|
||||
E.g. u.dot(v) -> u . v
|
||||
"""
|
||||
'''
|
||||
|
||||
return L.v[0] * R.v[0] + L.v[1] * R.v[1] + L.v[2] * R.v[2]
|
||||
|
||||
def cross(vec3 L, vec3 R):
|
||||
"""
|
||||
'''
|
||||
Returns the cross product of the two vectors.
|
||||
|
||||
E.g. u.cross(v) -> u x v
|
||||
|
||||
"""
|
||||
'''
|
||||
|
||||
return vec3(L.v[1]*R.v[2] - L.v[2]*R.v[1], L.v[0]*R.v[2] - L.v[2]*R.v[0], L.v[0]*R.v[1] - L.v[1]*R.v[0])
|
||||
|
||||
def __mul__(vec3 L, R):
|
||||
"""
|
||||
'''
|
||||
Multiplication of a vec3 by a float.
|
||||
|
||||
The float has to be on the right.
|
||||
"""
|
||||
'''
|
||||
|
||||
return vec3(L.v[0] * R, L.v[1] * R, L.v[2] * R)
|
||||
|
|
110
m/python_m.py
110
m/python_m.py
|
@ -1,16 +1,16 @@
|
|||
#! /bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
"""
|
||||
'''
|
||||
Vector And Matrix Math
|
||||
|
||||
Pure Python implementation.
|
||||
"""
|
||||
'''
|
||||
|
||||
from math import sin, cos, sqrt
|
||||
|
||||
class mat4:
|
||||
"""
|
||||
'''
|
||||
A float 4x4 matrix.
|
||||
|
||||
All arrays are column-major, i.e. OpenGL style:
|
||||
|
@ -21,30 +21,30 @@ class mat4:
|
|||
3 7 11 15
|
||||
|
||||
The matrix implements stacking useful for graphics.
|
||||
"""
|
||||
'''
|
||||
|
||||
def __cinit__(mat4 self):
|
||||
to_alloc = 16 * sizeof(float)
|
||||
self.stack = <float *>malloc(to_alloc)
|
||||
|
||||
if not self.stack:
|
||||
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||
raise MemoryError('Unable to malloc %d B for mat4.' %(to_alloc))
|
||||
|
||||
self.m = self.stack
|
||||
self.size = 1
|
||||
|
||||
def _debug(mat4 self):
|
||||
print("--- self.stack = %d" %(<uint64_t>self.stack))
|
||||
print("--- self.m = %d (+%d)" %(<uint64_t>self.m, self.m - self.stack))
|
||||
print("--- self.size = %d" %(self.size))
|
||||
print('--- self.stack = %d' %(<uint64_t>self.stack))
|
||||
print('--- self.m = %d (+%d)' %(<uint64_t>self.m, self.m - self.stack))
|
||||
print('--- self.size = %d' %(self.size))
|
||||
|
||||
def __init__(mat4 self, *args):
|
||||
"""
|
||||
'''
|
||||
Create a ma4t.
|
||||
|
||||
Accepts any number of parameters between 0 and 16 to fill the
|
||||
matrix from the upper left corner going down (column-wise).
|
||||
"""
|
||||
'''
|
||||
|
||||
self.m =
|
||||
|
||||
|
@ -55,7 +55,7 @@ class mat4:
|
|||
length = len(args)
|
||||
|
||||
if length > 16:
|
||||
raise MathError("Attempt to initialize a mat4 with %d arguments." %(length))
|
||||
raise MathError('Attempt to initialize a mat4 with %d arguments.' %(length))
|
||||
|
||||
self.load_from(args)
|
||||
|
||||
|
@ -75,7 +75,7 @@ class mat4:
|
|||
matrices = length//16
|
||||
|
||||
if not matrices*16 == length:
|
||||
raise GeneralError("mat4 __setstate__ got %d floats as a state" %(length))
|
||||
raise GeneralError('mat4 __setstate__ got %d floats as a state' %(length))
|
||||
|
||||
self.m = self.stack
|
||||
|
||||
|
@ -90,20 +90,20 @@ class mat4:
|
|||
|
||||
def __getitem__(mat4 self, int i):
|
||||
if i > 16 or i < 0:
|
||||
raise IndexError("element index out of range(16)")
|
||||
raise IndexError('element index out of range(16)')
|
||||
|
||||
return self.m[i]
|
||||
|
||||
def __setitem__(self, int i, value):
|
||||
if i > 16 or i < 0:
|
||||
raise IndexError("element index out of range(16)")
|
||||
raise IndexError('element index out of range(16)')
|
||||
|
||||
self.m[i] = value
|
||||
|
||||
def push(mat4 self):
|
||||
"""
|
||||
'''
|
||||
Push the current matrix into the stack and load up an empty one (a zero matrix)
|
||||
"""
|
||||
'''
|
||||
|
||||
# self.m points to the current matrix
|
||||
# self.stack points to the first matrix
|
||||
|
@ -122,7 +122,7 @@ class mat4:
|
|||
if tmp:
|
||||
self.stack = tmp
|
||||
else:
|
||||
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||
raise MemoryError('Unable to malloc %d B for mat4.' %(to_alloc))
|
||||
|
||||
# advance the pointer to the new one
|
||||
self.m = self.stack + 16 * used
|
||||
|
@ -135,12 +135,12 @@ class mat4:
|
|||
self.m[i] = old_m[i]
|
||||
|
||||
def pop(mat4 self):
|
||||
"""
|
||||
'''
|
||||
Pop a matrix from the stack.
|
||||
"""
|
||||
'''
|
||||
|
||||
if self.m == self.stack:
|
||||
raise IndexError("pop from an empty stack")
|
||||
raise IndexError('pop from an empty stack')
|
||||
|
||||
self.m -= 16
|
||||
|
||||
|
@ -153,14 +153,14 @@ class mat4:
|
|||
return L
|
||||
|
||||
def load_from(mat4 self, L):
|
||||
"""
|
||||
'''
|
||||
Fill the current matrix from a either a list of values, column-major,
|
||||
or another matrix. This method doesn't modify the stack, only the
|
||||
current matrix is read and modified.
|
||||
|
||||
If the number of values isn't 16, it will be padded to 16 by zeros.
|
||||
If it's larger, GeneralError will be raised.
|
||||
"""
|
||||
'''
|
||||
|
||||
if isinstance(L, mat4):
|
||||
L = L.get_list()
|
||||
|
@ -169,7 +169,7 @@ class mat4:
|
|||
length = len(L)
|
||||
|
||||
if length > 16:
|
||||
raise GeneralError("supplied list is longer than 16")
|
||||
raise GeneralError('supplied list is longer than 16')
|
||||
|
||||
for i in range(16):
|
||||
if i < length:
|
||||
|
@ -178,13 +178,13 @@ class mat4:
|
|||
self.m[i] = 0.0
|
||||
|
||||
def zero(mat4 self):
|
||||
"""Fill the matrix with zeroes."""
|
||||
'''Fill the matrix with zeroes.'''
|
||||
|
||||
for i in range(16):
|
||||
self.m[i] = 0.0
|
||||
|
||||
def identity(mat4 self):
|
||||
"""Make the matrix an identity."""
|
||||
'''Make the matrix an identity.'''
|
||||
|
||||
self.zero()
|
||||
|
||||
|
@ -194,7 +194,7 @@ class mat4:
|
|||
self.m[15] = 1.0
|
||||
|
||||
def transpose(mat4 self):
|
||||
"""Transpose the matrix."""
|
||||
'''Transpose the matrix.'''
|
||||
|
||||
cdef float tmp
|
||||
|
||||
|
@ -223,7 +223,7 @@ class mat4:
|
|||
self.m[9] = tmp
|
||||
|
||||
def invert(mat4 self):
|
||||
"""Invert the matrix."""
|
||||
'''Invert the matrix.'''
|
||||
|
||||
cdef float tmp[16]
|
||||
cdef float det
|
||||
|
@ -237,7 +237,7 @@ class mat4:
|
|||
|
||||
# epsilon pulled straight out of Uranus
|
||||
if det < 0.00005 and det > -0.00005:
|
||||
print("det=%.1f" %(det))
|
||||
print('det=%.1f' %(det))
|
||||
return
|
||||
|
||||
tmp[1] = -self.m[1]*self.m[10]*self.m[15] + self.m[1]*self.m[11]*self.m[14] + self.m[9]*self.m[2]*self.m[15] - self.m[9]*self.m[3]*self.m[14] - self.m[13]*self.m[2]*self.m[11] + self.m[13]*self.m[3]*self.m[10]
|
||||
|
@ -272,11 +272,11 @@ class mat4:
|
|||
self.m[15] = tmp[15] * det
|
||||
|
||||
def mulm(mat4 self, mat4 B, bint inplace=False):
|
||||
"""
|
||||
'''
|
||||
Return a matrix that is the result of multiplying this matrix by another.
|
||||
|
||||
M = self * mat4 B
|
||||
"""
|
||||
'''
|
||||
|
||||
cdef uint8_t i
|
||||
cdef mat4 tmp = mat4()
|
||||
|
@ -305,11 +305,11 @@ class mat4:
|
|||
return tmp
|
||||
|
||||
def mulv(mat4 self, vec3 v):
|
||||
"""
|
||||
'''
|
||||
Return a vec3 that is the result of multiplying this matrix by a vec3.
|
||||
|
||||
u = self * v
|
||||
"""
|
||||
'''
|
||||
|
||||
cdef mat4 tmp = vec3()
|
||||
|
||||
|
@ -320,11 +320,11 @@ class mat4:
|
|||
return tmp
|
||||
|
||||
def mulf(mat4 self, f):
|
||||
"""
|
||||
'''
|
||||
Return a matrix that is the result of multiplying this matrix by a scalar.
|
||||
|
||||
M = self * f
|
||||
"""
|
||||
'''
|
||||
|
||||
cdef mat4 tmp = mat4()
|
||||
cdef int i
|
||||
|
@ -337,15 +337,15 @@ class mat4:
|
|||
def __repr__(mat4 self):
|
||||
lines = []
|
||||
|
||||
lines.append("mat4(%.1f %.1f %.1f %.1f" %(self.m[0], self.m[4], self.m[8], self.m[12]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[1], self.m[5], self.m[9], self.m[13]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[2], self.m[6], self.m[10], self.m[14]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f)" %(self.m[3], self.m[7], self.m[11], self.m[15]))
|
||||
lines.append('mat4(%.1f %.1f %.1f %.1f' %(self.m[0], self.m[4], self.m[8], self.m[12]))
|
||||
lines.append(' %.1f %.1f %.1f %.1f' %(self.m[1], self.m[5], self.m[9], self.m[13]))
|
||||
lines.append(' %.1f %.1f %.1f %.1f' %(self.m[2], self.m[6], self.m[10], self.m[14]))
|
||||
lines.append(' %.1f %.1f %.1f %.1f)' %(self.m[3], self.m[7], self.m[11], self.m[15]))
|
||||
|
||||
return "\n".join(lines)
|
||||
return '\n'.join(lines)
|
||||
|
||||
cdef class vec3:
|
||||
"""
|
||||
'''
|
||||
A float 3D vector.
|
||||
|
||||
>>> v = vec3(1, 1, 0)
|
||||
|
@ -361,14 +361,14 @@ cdef class vec3:
|
|||
>>> w - v
|
||||
vec4(-1.00, 0.00, 1.00)
|
||||
|
||||
"""
|
||||
'''
|
||||
|
||||
def __init__(vec3 self, *args):
|
||||
"""
|
||||
'''
|
||||
Create a vec3.
|
||||
|
||||
Accepts any number of parameters between 0 and 3 to fill the vector from the left.
|
||||
"""
|
||||
'''
|
||||
|
||||
length = len(args)
|
||||
|
||||
|
@ -377,7 +377,7 @@ cdef class vec3:
|
|||
length = len(args)
|
||||
|
||||
if length > 3:
|
||||
raise MathError("Attempt to initialize a vec3 with %d arguments." %(length))
|
||||
raise MathError('Attempt to initialize a vec3 with %d arguments.' %(length))
|
||||
|
||||
for i in range(3):
|
||||
if i < length:
|
||||
|
@ -387,18 +387,18 @@ cdef class vec3:
|
|||
|
||||
def __getitem__(vec3 self, int i):
|
||||
if i >= 3 or i < 0:
|
||||
raise IndexError("element index out of range(3)")
|
||||
raise IndexError('element index out of range(3)')
|
||||
|
||||
return self.v[i]
|
||||
|
||||
def __setitem__(vec3 self, int i, float value):
|
||||
if i >= 3 or i < 0:
|
||||
raise IndexError("element index out of range(3)")
|
||||
raise IndexError('element index out of range(3)')
|
||||
|
||||
self.v[i] = value
|
||||
|
||||
def __repr__(vec3 self):
|
||||
return "vec3(%.2f, %.2f, %.2f)" %(self.v[0], self.v[1], self.v[2])
|
||||
return 'vec3(%.2f, %.2f, %.2f)' %(self.v[0], self.v[1], self.v[2])
|
||||
|
||||
def __getstate__(vec3 self):
|
||||
return (self.v[0], self.v[1], self.v[2])
|
||||
|
@ -410,12 +410,12 @@ cdef class vec3:
|
|||
|
||||
@property
|
||||
def length(vec3 self):
|
||||
"""Contains the geometric length of the vector."""
|
||||
'''Contains the geometric length of the vector.'''
|
||||
|
||||
return sqrt(self.v[0]**2 + self.v[1]**2 + self.v[2]**2)
|
||||
|
||||
def normalized(vec3 self):
|
||||
"""Returns this vector, normalized."""
|
||||
'''Returns this vector, normalized.'''
|
||||
|
||||
length = self.length
|
||||
|
||||
|
@ -431,29 +431,29 @@ cdef class vec3:
|
|||
return vec3(-self.v[0], -self.v[1], -self.v[2])
|
||||
|
||||
def dot(vec3 L, vec3 R):
|
||||
"""
|
||||
'''
|
||||
Returns the dot product of the two vectors.
|
||||
|
||||
E.g. u.dot(v) -> u . v
|
||||
"""
|
||||
'''
|
||||
|
||||
return L.v[0] * R.v[0] + L.v[1] * R.v[1] + L.v[2] * R.v[2]
|
||||
|
||||
def cross(vec3 L, vec3 R):
|
||||
"""
|
||||
'''
|
||||
Returns the cross product of the two vectors.
|
||||
|
||||
E.g. u.cross(v) -> u x v
|
||||
|
||||
"""
|
||||
'''
|
||||
|
||||
return vec3(L.v[1]*R.v[2] - L.v[2]*R.v[1], L.v[0]*R.v[2] - L.v[2]*R.v[0], L.v[0]*R.v[1] - L.v[1]*R.v[0])
|
||||
|
||||
def __mul__(vec3 L, R):
|
||||
"""
|
||||
'''
|
||||
Multiplication of a vec3 by a float.
|
||||
|
||||
The float has to be on the right.
|
||||
"""
|
||||
'''
|
||||
|
||||
return vec3(L.v[0] * R, L.v[1] * R, L.v[2] * R)
|
||||
|
|
|
@ -8,7 +8,7 @@ from . import et
|
|||
|
||||
prefix = et.prefix
|
||||
|
||||
"""
|
||||
'''
|
||||
===== OverTalk protocol spec =====
|
||||
==== Transport layer ====
|
||||
|
||||
|
@ -26,14 +26,14 @@ Payload consists of one or more commands:
|
|||
get_command = [ 0x00 | register (1) | target register (1) ]
|
||||
set_command = [ 0x01 | register (1) | length (1) | value (n) ]
|
||||
|
||||
With a get_command the sender requests the receiver to read its own "register" and issue
|
||||
a set_command that sets the sender's "target register" to that value.
|
||||
With a get_command the sender requests the receiver to read its own 'register' and issue
|
||||
a set_command that sets the sender's 'target register' to that value.
|
||||
|
||||
A set_command does what is says on the box.
|
||||
"""
|
||||
'''
|
||||
|
||||
class Transport:
|
||||
"""
|
||||
'''
|
||||
OverTalk Transport layer implementation.
|
||||
|
||||
Reads data from multiple interfaces and either router frames or receives them.
|
||||
|
@ -41,18 +41,18 @@ class Transport:
|
|||
|
||||
Interfaces are objects that implement methods read() (returns one byte of data as int), write(buffer, len),
|
||||
and waiting property (contains number of bytes waiting).
|
||||
"""
|
||||
'''
|
||||
def __init__(self, my_id, interfaces, def_route):
|
||||
"""
|
||||
'''
|
||||
@param my_id OverTalk address of this Transport instance
|
||||
@param interfaces a dict of interfaces keyed by their IDs
|
||||
@param def_route ID of default interface for sending
|
||||
"""
|
||||
'''
|
||||
|
||||
assert my_id not in interfaces
|
||||
assert def_route in interfaces
|
||||
|
||||
self._print = et.Output("over.com.Transport", default_suffix="\n", timestamp=True)
|
||||
self._print = et.Output('over.com.Transport', default_suffix='\n', timestamp=True)
|
||||
|
||||
self.my_id = my_id
|
||||
self.def_route = def_route
|
||||
|
@ -61,7 +61,7 @@ class Transport:
|
|||
self.destination_unknown = 0
|
||||
self.incoming = None
|
||||
|
||||
self._print("<Cg>on-line<C/>")
|
||||
self._print('<Cg>on-line<C/>')
|
||||
|
||||
def update(self):
|
||||
self.incoming = None
|
||||
|
@ -106,7 +106,7 @@ class Transport:
|
|||
|
||||
else:
|
||||
interface.malformed_frames += 1
|
||||
self._print("broken frame received: <Cr>%s<C/>" %(interface.rxbuffer))
|
||||
self._print('broken frame received: <Cr>%s<C/>' %(interface.rxbuffer))
|
||||
interface.rxbuffer = []
|
||||
|
||||
return False
|
||||
|
@ -133,15 +133,15 @@ class Transport:
|
|||
self.incoming = (frame[2], payload)
|
||||
else:
|
||||
if destination in self.interfaces:
|
||||
self._print("routing frame to [%d]" %(destination))
|
||||
self._print('routing frame to [%d]' %(destination))
|
||||
self.interfaces[destination].write(self.escape_frame(frame))
|
||||
else:
|
||||
if source_interface_id == self.def_route:
|
||||
self.destination_unknown += 1
|
||||
self._print("unknown destination <Cr>%d<C/> for frame: <Cy>%s<C/>" %(destination,
|
||||
self._print('unknown destination <Cr>%d<C/> for frame: <Cy>%s<C/>' %(destination,
|
||||
repr(frame)), prefix.fail)
|
||||
else:
|
||||
self._print("routing frame to default route [%d]" %(self.def_route))
|
||||
self._print('routing frame to default route [%d]' %(self.def_route))
|
||||
self.interfaces[self.def_route].write(self.escape_frame(frame))
|
||||
|
||||
def send_data(self, destination, data):
|
||||
|
@ -157,7 +157,7 @@ class Transport:
|
|||
s.write(frame)
|
||||
|
||||
class TTL_Interface:
|
||||
def __init__(self, interface="/dev/ttyUSB0", baudrate=57600):
|
||||
def __init__(self, interface='/dev/ttyUSB0', baudrate=57600):
|
||||
try:
|
||||
self.s = serial.Serial(interface, baudrate, timeout=1)
|
||||
except serial.serialutil.SerialException:
|
||||
|
@ -184,7 +184,7 @@ class TTL_Interface:
|
|||
return 0
|
||||
|
||||
def write(self, data):
|
||||
print("Sending:", ''.join([hex(x)[2:].zfill(2) for x in data]))
|
||||
print('Sending:', ''.join([hex(x)[2:].zfill(2) for x in data]))
|
||||
|
||||
if self.s:
|
||||
self.s.write(bytes(data))
|
||||
|
|
|
@ -10,7 +10,7 @@ SUBMARKER = 'OVER_VERSION_IDENTIFIER'
|
|||
|
||||
if __name__ == "__main__":
|
||||
action = sys.argv[1]
|
||||
data = (sys.argv[2], sys.argv[3][:8]) if action == "--set" else ('0', '00000000')
|
||||
data = (sys.argv[2], sys.argv[3][:8]) if action == '--set' else ('0', '00000000')
|
||||
|
||||
for line in sys.stdin.readlines():
|
||||
if SUBMARKER in line:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue