From 6198e7e496bc4c840f2a75a973b6a852a2484389 Mon Sep 17 00:00:00 2001 From: Anthon van der Neut Date: Wed, 26 Mar 2014 20:19:39 +0100 Subject: initial ProgramBase --- __init__.py | 177 ++++++++++++++++++++++++++++++++++++++++++++++----- test/test_program.py | 41 ++++++++++++ 2 files changed, 201 insertions(+), 17 deletions(-) create mode 100644 test/test_program.py diff --git a/__init__.py b/__init__.py index 8bc424f..b69c8ca 100644 --- a/__init__.py +++ b/__init__.py @@ -60,6 +60,7 @@ class SubParsersAction(argparse._SubParsersAction): return parser + from _action.checksinglestore import CheckSingleStoreAction from _action.count import CountAction from _action.splitappend import SplitAppendAction @@ -81,9 +82,8 @@ class SmartFormatter(argparse.HelpFormatter): def _fill_text(self, text, width, indent): return ''.join([indent + line for line in text.splitlines(True)]) - def _split_lines(self, text, width): - #print 'TEXT', text + # print 'TEXT', text if text.startswith('D|'): self._add_defaults = True text = text[2:] @@ -106,7 +106,7 @@ class SmartFormatter(argparse.HelpFormatter): # decorators # decorate method as a subparser, ToDo: signature -def sub_parser(*args, **kw): +def sub_parser1(*args, **kw): class Decorator(object): def __init__(self): self.target = None @@ -134,7 +134,7 @@ def sub_parser(*args, **kw): """called with the original function that is decorated add function name to last options element """ - self.target.options[-1]['fun'] = args[0].__name__ + self.target.options[-1]['fun'] = args[0].__name__ pass if not hasattr(self.target, "options"): @@ -146,7 +146,7 @@ def sub_parser(*args, **kw): # global options can come before and after a sub_parser, ToDo: signature -def global_option(*args, **kw): +def global_option1(*args, **kw): def decorator(target): target.global_option = {'args': args, 'kw': kw} return target @@ -154,14 +154,14 @@ def global_option(*args, **kw): # global only options can come before a sub_parser only, ToDo: signature -def global_only_option(*args, **kw): +def global_only_option1(*args, **kw): def decorator(target): target.global_only_option = {'args': args, 'kw': kw} return target return decorator -def create_argument_parser(self, *args, **keywords): +def create_argument_parser1(self, *args, **keywords): argument_parser = argparse.ArgumentParser(**keywords) self._subparsers = None for x in dir(self): @@ -171,8 +171,8 @@ def create_argument_parser(self, *args, **keywords): if hasattr(method, "sub_parser"): if self._subparsers is None: self._subparsers = argument_parser.add_subparsers( - dest="subparser_name", help='sub-command help') - #(name, aliases=aliases, help=help) + dest="subparser_level_0", help='sub-command help') + # (name, aliases=aliases, help=help) arg = method.sub_parser['args'] if not arg or not isinstance(arg[0], basestring): arg = list(arg) @@ -181,10 +181,10 @@ def create_argument_parser(self, *args, **keywords): **method.sub_parser['kw']) sp.set_defaults(func=method) - #print x, method.sub_parser - if hasattr(method, "options"): - for o in method.options: - #print 'arg1', o + # print x, method.sub_parser + if hasattr(method, "_options"): + for o in method._options: + # print 'arg1', o arg = list(o['args']) fun_name = o.get('fun') if arg: @@ -201,13 +201,19 @@ def create_argument_parser(self, *args, **keywords): else: # add long option based on function name arg.insert(0, '--' + fun_name) - #print 'arg2', arg + # print 'arg2', arg sp.add_argument(*arg, **o['kw']) - #print ' opt:', x, method.options + # print ' opt:', x, method.options if hasattr(method, "global_only_option"): arg = method.global_only_option['args'] kw = method.global_only_option['kw'] - argument_parser.add_argument(*arg, **kw) + if not arg or not arg[0].startswith('--'): + arg = list(arg) + arg.insert(0, '--' + x) + try: + argument_parser.add_argument(*arg, **kw) + except TypeError: + print('args, kw', arg, kw) for x in dir(self): if x.startswith('_'): continue @@ -216,9 +222,146 @@ def create_argument_parser(self, *args, **keywords): for name in self._subparsers._name_parser_map: sp = self._subparsers._name_parser_map[name] arg = method.global_option['args'] - if arg and not arg[0].startswith('--'): + if not arg or not arg[0].startswith('--'): arg = list(arg) arg.insert(0, '--' + x) sp.add_argument(*arg, **method.global_option['kw']) return argument_parser + +class ProgramBase(object): + def __init__(self, *args, **kw): + self._verbose = kw.pop('verbose', 0) + self._parser = argparse.ArgumentParser(*args, **kw) + cls = self + self._subparsers = None + for x in dir(cls): + if x.startswith('_'): + continue + method = getattr(self, x) + if hasattr(method, "_sub_parser"): + if self._subparsers is None: + self._subparsers = self._parser.add_subparsers( + dest="subparser_level_0", help='sub-command help') + # (name, aliases=aliases, help=help) + arg = method._sub_parser['args'] + if not arg or not isinstance(arg[0], basestring): + arg = list(arg) + arg.insert(0, x) + sp = self._subparsers.add_parser(*arg, + **method._sub_parser['kw']) + sp.set_defaults(func=method) + + # print x, method._sub_parser + for o in method._sub_parser['options']: + # print 'arg1', o + arg = list(o['args']) + fun_name = o.get('fun') + if arg: + # short option name only, add long option name + # based on function name + if len(arg[0]) == 2 and arg[0][0] == '-': + if (fun_name): + arg.insert(0, '--' + fun_name) + else: + # no option name + if o['kw'].get('nargs') == '+': + # file names etc, no leading dashes + arg.insert(0, fun_name) + else: + # add long option based on function name + arg.insert(0, '--' + fun_name) + # print 'arg2', arg + sp.add_argument(*arg, **o['kw']) + # print ' opt:', x, method._options + if False: + if hasattr(method, "global_only_option"): + arg = method.global_only_option['args'] + kw = method.global_only_option['kw'] + if not arg or not arg[0].startswith('--'): + arg = list(arg) + arg.insert(0, '--' + x) + try: + self._parser.add_argument(*arg, **kw) + except TypeError: + print('args, kw', arg, kw) + for x in dir(self): + if x.startswith('_') and not x == '__init__': + continue + method = getattr(self, x) + if hasattr(method, "_options"): # not transfered to sub_parser + for o in method._options: + arg = o['args'] + kw = o['kw'] + global_option = kw.pop('global_option', False) + try: + self._parser.add_argument(*arg, **kw) + except TypeError: + print('args, kw', arg, kw) + if global_option: + for name in self._subparsers._name_parser_map: + sp = self._subparsers._name_parser_map[name] + sp.add_argument(*arg, **kw) + + def _parse_args(self, *args): + self._args = self._parser.parse_args(*args) + return self._args + + @staticmethod + def option(*args, **kw): + def decorator(target): + if not hasattr(target, '_options'): + target._options = [] + # insert to reverse order of list + target._options.insert(0, {'args': args, 'kw': kw}) + return target + return decorator + + @staticmethod + def sub_parser(*args, **kw): + class Decorator(object): + def __init__(self): + self.target = None + + def __call__(self, target): + self.target = target + # move options to sub_parser + o = getattr(target, '_options', []) + if o: + del target._options + target._sub_parser = {'args': args, 'kw': kw, 'options': o} + return target + + def XXXoption(self, *a, **k): + """ after a method xyz is decorated as sub_parser, you can add + options with: + @xyz.option('--force') + def force(self): + pass + + if option argument is a short option ('-F'), add the long + option based on the function name + if no option argument, add function_name if 'nargs' in k + else add long option based on function name + """ + # here check if a is option name else take from function + def option_func(*args): + """called with the original function that is decorated + add function name to last options element + """ + self.target.options[-1]['fun'] = args[0].__name__ + pass + + if not hasattr(self.target, "options"): + self.target.options = [] + self.target.options.append({'args': a, 'kw': k}) + return option_func + decorator = Decorator() + return decorator + + +def option(*args, **kw): + return ProgramBase.option(*args, **kw) + +def sub_parser(*args, **kw): + return ProgramBase.sub_parser(*args, **kw) \ No newline at end of file diff --git a/test/test_program.py b/test/test_program.py new file mode 100644 index 0000000..719e653 --- /dev/null +++ b/test/test_program.py @@ -0,0 +1,41 @@ + +import pytest + +from ruamel.std.argparse import ProgramBase, option, sub_parser + +class Program(ProgramBase): + @option('--verbose', global_option=True, action='store_true') + @option('--quiet', action='store_true') + def __init__(self): + ProgramBase.__init__(self) + + @sub_parser(help="call mercurial") + @option('--show', action='store_true') + @option('--no-show', help='do not show', metavar='NSHO') + @option('file-name', nargs='*') + def hg(self): + pass + + #@hg.sub_parser() + #def check(self): + # pass + + @sub_parser(help="call git") + def git(self): + pass + + +def test_program(capsys): + print '------------- hallo' + p = Program() + + with pytest.raises(SystemExit): + p._parse_args('-h'.split()) + out, err = capsys.readouterr() + print out + print '++++++++++++++' + with pytest.raises(SystemExit): + p._parse_args('hg -h'.split()) + out, err = capsys.readouterr() + print out + assert False \ No newline at end of file -- cgit v1.2.1