diff --git a/__init__.py b/__init__.py index 7752746..f414966 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,3 @@ from . import aux #from . import m #from . import serial -_version = 2.999 diff --git a/core/app.py b/core/app.py index 0863692..ce0ab6c 100644 --- a/core/app.py +++ b/core/app.py @@ -2,6 +2,13 @@ # encoding: utf-8 import sys +import re + +from . import file +from . import textui +from ..version import _revision + +prefix = textui.prefix # FIXME This very seriously needs to be heavily simplified and de-duplicated # TODO same parser for cmdline and Config @@ -14,7 +21,7 @@ def _parse(data, dtype): if data.startswith("\"") and data.endswith("\""): value = data[1:-1].replace("\\\"", "\"") else: - raise GeneralError + raise RuntimeError elif dtype == "bool": if data == "True": @@ -22,34 +29,34 @@ def _parse(data, dtype): elif data == "False": value = False else: - raise GeneralError + raise RuntimeError elif dtype == "int": try: value = int(data) except ValueError: - raise GeneralError + raise RuntimeError elif dtype == "float": try: value = float(data) except ValueError: - raise GeneralError + raise RuntimeError return value _over_help_texts = [ - ("OverCore: what, how and huh?", ["OverCore 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", ["OverCore 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 OverCore 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§/."]), + ("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 OverCore 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, OverCore 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."]) + ("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(render(paragraph(text, indent=indent), True)) + sys.stdout.write(textui.render(textui.paragraph(text, indent=indent), True)) sys.stdout.write(newlines * "\n") sys.stdout.flush() @@ -66,9 +73,9 @@ def _print_help(main, help_texts, chapter=None, list_options=False): # dirty as fuck :-) if not chapter and not main.version is None: if help_texts == _over_help_texts: - _output(">>> §yOverCore§/ version §y%s§/, licensed under §yAO-JSL§/" %(_version), newlines=2) + _output(">>> §yover.core§/ version §y%d§/ (%s), licensed under the §yAO-JSL§/" %_revision, newlines=2) else: - _output("§y%s§/ version §y%s§/, licensed under §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: @@ -186,9 +193,11 @@ class CfgAccessor: if len(matches) == 1: return matches[0].value else: - self.main._print("game over, lights out! OverCore internal buggaroo", prefix.fail, exc=GeneralError) + 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, exc=GeneralError) + self.main._print("option §r%s§/ doesn't exist" %(opt_name), prefix.fail) + raise RuntimeError # -------------------------------------------------- @@ -220,14 +229,14 @@ class Main: self.cmdline = cmdline self.help_texts = [] # (chapter, [paragraphs]) self.allow_exit = allow_exit - self._print = Output("over.Main", default_suffix=".\n", timestamp=True) + self._print = textui.Output("over.core.app.Main") if cfg_file: - self.cfg_file = File(cfg_file) + 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 -# Licensed under %s +# Licensed under the %s # # Syntax # There are 4 data types: bool, int, float and str (string). @@ -515,7 +524,8 @@ class Main: 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, exc=GeneralError) + self._print("unknown options on the command line: §r%s§/" %("§/, §r".join(self.unknowns)), prefix.fail) + raise RuntimeError # parse the config file if self.cfg_file: @@ -544,19 +554,21 @@ class Main: for element in elements: try: opt.value.append(_parse(element, opt.dtype)) - except GeneralError: - self._print("config file syntax error for option §B--§r%s§/" %(opt.name), prefix.fail, exc=GeneralError) + except RuntimeError: + 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 GeneralError: - self._print("config file syntax error for option §B--§r%s§/" %(opt.name), prefix.fail, exc=GeneralError) + except RuntimeError: + 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(paragraph(render(opt.description, colors=False), prefix="#", width=80)) + 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: @@ -616,8 +628,8 @@ class Main: """ 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 OverCore.", callback=self.help_over, use_cfg_file=False) - self.add_option("help-over", "bool", False, "Display general usage information for OverCore.", callback=self.help_over, use_cfg_file=False, hidden=True) + 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)) diff --git a/core/build.sh b/core/build.sh index 6e0ba70..52264e3 100755 --- a/core/build.sh +++ b/core/build.sh @@ -15,8 +15,8 @@ echo "- translating from Python to C" cython -3 cython_types.pyx -o cython_types.c || die "translating" echo "- compiling and linking" -gcc $CFLAGS -I/usr/include/python3.3 -pthread -c cython_types.c || die "compilation" -gcc $LFLAGS -L/usr/lib -lpython3.3 cython_types.o -o cython_types.so || die "linking" +gcc $CFLAGS -I/usr/include/python3.4 -pthread -c cython_types.c || die "compilation" +gcc $LFLAGS -L/usr/lib -lpython3.4 cython_types.o -o cython_types.so || die "linking" rm -f cython_types.{c,o} echo "- done" diff --git a/core/cython_types.pyx b/core/cython_types.pyx index d735c18..9d07aff 100644 --- a/core/cython_types.pyx +++ b/core/cython_types.pyx @@ -109,40 +109,3 @@ cdef class map: pairs.append("%s: %s" %(repr(self.keys[i]), repr(self.vals[i]))) return "<{%s}>" %(", ".join(pairs)) - -# -------------------------------------------------- - -cdef class enum: - """ - Emulates a C++-like enum type. - - Based on a py2 enum function by Alec Thomas and acjohnson55. - """ - - def __init__(self, str name, words, int start=0): - """ - Initializes the enum. - - name is used for __repr__ only - words is a sequence of enum keys - start is the numerical value of the first key - """ - - self.typename = name - self.reverse_enums = dict(enumerate(words, start)) - self.enums = dict((value, key) for key, value in self.reverse_enums.items()) - - def name(self, int value): - if value in self.reverse_enums: - return self.reverse_enums[value] - else: - raise AttributeError("no %s has a value of %s" %(self, value)) - - def __getattr__(self, aname): - if aname in self.enums: - return self.enums[aname] - else: - raise AttributeError("%s not in %s" %(aname, self)) - - def __repr__(self): - return "" %(self.typename) diff --git a/core/python_types.py b/core/python_types.py index 5c4f226..291d720 100644 --- a/core/python_types.py +++ b/core/python_types.py @@ -109,43 +109,3 @@ class map: pairs.append("%s: %s" %(repr(self.keys[i]), repr(self.vals[i]))) return "<{%s}>" %(", ".join(pairs)) - -# -------------------------------------------------- - -class enum: - """ - Emulates a C++-like enum type. - - Based on a py2 enum function by Alec Thomas and acjohnson55. - """ - - def __init__(self, name, words, start=0): - """ - Initializes the enum. - - name is used for __repr__ only - words is a sequence of enum keys - start is the numerical value of the first key - """ - - self.typename = name - self.reverse_enums = dict(enumerate(words, start)) - self.enums = dict((value, key) for key, value in self.reverse_enums.items()) - - def name(self, value): - if value in self.reverse_enums: - return self.reverse_enums[value] - else: - raise AttributeError("no %s has a value of %s" %(self, value)) - - def __getattr__(self, aname): - if aname in self.enums: - return self.enums[aname] - else: - raise AttributeError("%s not in %s" %(aname, self)) - - def __repr__(self): - return "" %(self.typename) - -# -------------------------------------------------- - diff --git a/core/textui.py b/core/textui.py index 49e2199..858d364 100644 --- a/core/textui.py +++ b/core/textui.py @@ -4,6 +4,9 @@ import math import re import sys +import struct +import fcntl +import termios import time # -------------------------------------------------- @@ -76,6 +79,10 @@ class ProgressBar: self.value = value self.draw() + + def blank(self): + sys.stderr.write("\r" + self.old_len * " " + "\r") + sys.stderr.flush() # -------------------------------------------------- diff --git a/version.py b/version.py new file mode 100644 index 0000000..7f5fc19 --- /dev/null +++ b/version.py @@ -0,0 +1,4 @@ +#! /bin/env python3 +# encoding: utf-8 + +_revision = (0, '00000000') # GIT_REVISION_IDENTIFIER