diff options
Diffstat (limited to '__init__.py')
-rw-r--r-- | __init__.py | 281 |
1 files changed, 215 insertions, 66 deletions
diff --git a/__init__.py b/__init__.py index af658b3..06e9803 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,15 @@ # coding: utf-8 +from __future__ import print_function + +# from six +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + string_types = str, +else: + string_types = basestring, # < from ruamel.util.new import _convert_version def _convert_version(tup): @@ -61,9 +71,9 @@ class SubParsersAction(argparse._SubParsersAction): return parser -from _action.checksinglestore import CheckSingleStoreAction -from _action.count import CountAction -from _action.splitappend import SplitAppendAction +from ._action.checksinglestore import CheckSingleStoreAction +from ._action.count import CountAction +from ._action.splitappend import SplitAppendAction class SmartFormatter(argparse.HelpFormatter): @@ -74,25 +84,28 @@ class SmartFormatter(argparse.HelpFormatter): The SmartFormatter has sensible defaults (RawDescriptionFormatter) and the individual help text can be marked ( help="R|" ) for variations in formatting. + version string is formatted using _split_lines and preserves any + line breaks in the version string. """ def __init__(self, *args, **kw): - self._add_defaults = False + self._add_defaults = None super(SmartFormatter, self).__init__(*args, **kw) 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 if text.startswith('D|'): self._add_defaults = True text = text[2:] + elif text.startswith('*|'): + text = text[2:] if text.startswith('R|'): return text[2:].splitlines() return argparse.HelpFormatter._split_lines(self, text, width) def _get_help_string(self, action): - if not self._add_defaults: + if self._add_defaults is None: return argparse.HelpFormatter._get_help_string(self, action) help = action.help if '%(default)' not in action.help: @@ -102,7 +115,21 @@ class SmartFormatter(argparse.HelpFormatter): help += ' (default: %(default)s)' return help - + def _expand_help(self, action): + """mark a password help with '*|' at the start, so that + when global default adding is activated (e.g. through a helpstring + starting with 'D|') no password is show by default. + Orginal marking used in repo cannot be used because of decorators. + """ + hs = self._get_help_string(action) + if hs.startswith('*|'): + params = dict(vars(action), prog=self._prog) + if params.get('default') is not None: + # you can update params, this will change the default, but we + # are printing help only + params['default'] = '*' * len(params['default']) + return self._get_help_string(action) % params + return super(SmartFormatter, self)._expand_help(action) class ProgramBase(object): @@ -110,66 +137,46 @@ class ProgramBase(object): ToDo: - grouping - mutual exclusion + - Original order/sorted (by kw) + """ + _methods_with_sub_parsers = [] + def __init__(self, *args, **kw): self._verbose = kw.pop('verbose', 0) self._parser = argparse.ArgumentParser(*args, **kw) cls = self self._sub_parsers = None methods_with_sub_parsers = [] # list to process, multilevel - for x in dir(cls): - if x.startswith('_'): - continue - method = getattr(self, x) - if hasattr(method, "_sub_parser"): - if self._sub_parsers is None: - # create the top level subparsers - self._sub_parsers = self._parser.add_subparsers( - dest="subparser_level_0", help=None) - methods_with_sub_parsers.append(method) - max_depth = 10 - level = 0 - all_methods_with_sub_parsers = methods_with_sub_parsers[:] - while methods_with_sub_parsers: - level += 1 - if level > max_depth: - raise NotImplementedError - for method in methods_with_sub_parsers: - parent = method._sub_parser['kw'].get('_parent', None) - sub_parsers = self._sub_parsers - if parent is None: - method._sub_parser['level'] = 0 - # parent sub parser - elif 'level' not in parent._sub_parser: - print 'skipping', parent.__name__, method.__name__ - continue - else: # have a parent - # make sure _parent is no longer in kw + all_methods_with_sub_parsers = [] + + def add_subparsers(method_name_list, parser, level=0): + if not method_name_list: + return None + ssp = parser.add_subparsers( + dest="subparser_level_{0}".format(level),) + for method_name in method_name_list: + #print('method', ' ' * level, method_name) + method = getattr(self, method_name) + all_methods_with_sub_parsers.append(method) + info = method._sub_parser + info['level'] = level + if level > 0: method._sub_parser['parent'] = \ method._sub_parser['kw'].pop('_parent') - level = parent._sub_parser['level'] + 1 - method._sub_parser['level'] = level - print 'level', level - ssp = parent._sub_parser.get('sp') - if ssp is None: - pparser = parent._sub_parser['parser'] - ssp = pparser.add_subparsers( - dest="subparser_level_{}".format(level), - ) - parent._sub_parser['sp'] = ssp - sub_parsers = ssp arg = method._sub_parser['args'] - if not arg or not isinstance(arg[0], basestring): + if not arg or not isinstance(arg[0], string_types): arg = list(arg) arg.insert(0, method.__name__) - sp = sub_parsers.add_parser(*arg, - **method._sub_parser['kw']) - # add parser primarily for being able to add subparsers - method._sub_parser['parser'] = sp - sp.set_defaults(func=method) - - # print x, method._sub_parser - for o in method._sub_parser['options']: + parser = ssp.add_parser(*arg, **method._sub_parser['kw']) + info['parser'] = parser + res = add_subparsers(info.get('ordering', []), + parser, level=level+1) + if res is None: + # only set default if there are no subparsers, otherwise + # defaults override + parser.set_defaults(func=method) + for o in info['options']: arg = list(o['args']) fun_name = o.get('fun') if arg: @@ -186,11 +193,32 @@ class ProgramBase(object): else: # add long option based on function name arg.insert(0, '--' + fun_name) - sp.add_argument(*arg, **o['kw']) - #print 'removing', method.__name__ - methods_with_sub_parsers.remove(method) + parser.add_argument(*arg, **o['kw']) + return ssp + + def dump(method_name_list, level=0): + if not method_name_list: + return None + for method_name in method_name_list: + print('method', ' ' * level, method_name) + method = getattr(self, method_name) + info = method._sub_parser + for k in sorted(info): + if k == 'parser': + v = 'ArgumentParser()' + elif k == 'sp': + v = '_SubParserAction()' + else: + v = info[k] + print(' ' + ' ' * level, k, '->', v) + dump(info.get('ordering', []), level=level+1) + + self._sub_parsers = add_subparsers( + ProgramBase._methods_with_sub_parsers, self._parser) + + # this only does toplevel and global options for x in dir(self): - if x.startswith('_') and not x in ['__init__', '_pb_init']: + if x.startswith('_') and x not in ['__init__', '_pb_init']: continue method = getattr(self, x) if hasattr(method, "_options"): # not transfered to sub_parser @@ -203,14 +231,116 @@ class ProgramBase(object): except TypeError: print('args, kw', arg, kw) if global_option: + #print('global option', arg, len(all_methods_with_sub_parsers)) for m in all_methods_with_sub_parsers: sp = m._sub_parser['parser'] sp.add_argument(*arg, **kw) - def _parse_args(self, *args): - self._args = self._parser.parse_args(*args) + #print('-------------------') + #dump(ProgramBase._methods_with_sub_parsers) + if False: + for x in dir(cls): + #for x in ProgramBase._methods_with_sub_parsers: + if x.startswith('_'): + continue + method = getattr(self, x) + if hasattr(method, "_sub_parser"): + if self._sub_parsers is None: + # create the top level subparsers + self._sub_parsers = self._parser.add_subparsers( + dest="subparser_level_0", help=None) + methods_with_sub_parsers.append(method) + max_depth = 10 + level = 0 + all_methods_with_sub_parsers = methods_with_sub_parsers[:] + while methods_with_sub_parsers: + level += 1 + if level > max_depth: + raise NotImplementedError + for method in all_methods_with_sub_parsers: + if not method in methods_with_sub_parsers: + continue + parent = method._sub_parser['kw'].get('_parent', None) + sub_parsers = self._sub_parsers + if parent is None: + method._sub_parser['level'] = 0 + # parent sub parser + elif 'level' not in parent._sub_parser: + #print('skipping', parent.__name__, method.__name__) + continue + else: # have a parent + # make sure _parent is no longer in kw + method._sub_parser['parent'] = \ + method._sub_parser['kw'].pop('_parent') + level = parent._sub_parser['level'] + 1 + method._sub_parser['level'] = level + ssp = parent._sub_parser.get('sp') + if ssp is None: + pparser = parent._sub_parser['parser'] + ssp = pparser.add_subparsers( + dest="subparser_level_{0}".format(level), + ) + parent._sub_parser['sp'] = ssp + sub_parsers = ssp + arg = method._sub_parser['args'] + if not arg or not isinstance(arg[0], basestring): + arg = list(arg) + arg.insert(0, method.__name__) + sp = sub_parsers.add_parser(*arg, + **method._sub_parser['kw']) + # add parser primarily for being able to add subparsers + method._sub_parser['parser'] = sp + # and make self._args.func callable + sp.set_defaults(func=method) + + # print(x, method._sub_parser) + for o in method._sub_parser['options']: + 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) + sp.add_argument(*arg, **o['kw']) + methods_with_sub_parsers.remove(method) + for x in dir(self): + if x.startswith('_') and x not in ['__init__', '_pb_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: + #print('global option', arg, len(all_methods_with_sub_parsers)) + for m in all_methods_with_sub_parsers: + sp = m._sub_parser['parser'] + sp.add_argument(*arg, **kw) + + def _parse_args(self, *args, **kw): + self._args = self._parser.parse_args(*args, **kw) return self._args + #def _parse_known_args(self, *args, **kw): + # self._args, self._unknown_args = \ + # self._parser.parse_known_args(*args, **kw) + # return self._args + @staticmethod def _pb_option(*args, **kw): def decorator(target): @@ -236,6 +366,11 @@ class ProgramBase(object): a = self._parent[1] k = self._parent[2].copy() k['_parent'] = self._parent[0] + pi = self._parent[0]._sub_parser + ordering = pi.setdefault('ordering', []) + else: + ordering = ProgramBase._methods_with_sub_parsers + ordering.append(target.__name__) # move options to sub_parser o = getattr(target, '_options', []) if o: @@ -255,8 +390,7 @@ class ProgramBase(object): if arguments is not given the name will be the method name """ decorator = Decorator() - print '>>>>', self.target.__name__, self - decorator._parent = (self.target, a, k) + decorator._parent = (self.target, a, k, []) return decorator decorator = Decorator() @@ -265,8 +399,23 @@ class ProgramBase(object): # decorators -def option(*args, **kw): - return ProgramBase._pb_option(*args, **kw) +def option(*args, **keywords): + """\ +args: + name or flags - Either a name or a list of option strings, e.g. foo or -f, --foo. +keywords: + action - The basic type of action to be taken when this argument is encountered at the command line. + nargs - The number of command-line arguments that should be consumed. + const - A constant value required by some action and nargs selections. + default - The value produced if the argument is absent from the command line. + type - The type to which the command-line argument should be converted. + choices - A container of the allowable values for the argument. + required - Whether or not the command-line option may be omitted (optionals only). + help - A brief description of what the argument does. + metavar - A name for the argument in usage messages. + dest - The name of the attribute to be added to the object returned by parse_args(). + """ + return ProgramBase._pb_option(*args, **keywords) def sub_parser(*args, **kw): @@ -275,4 +424,4 @@ def sub_parser(*args, **kw): def version(version_string): return ProgramBase._pb_option( - '--version', action='version', version=version_string)
\ No newline at end of file + '--version', action='version', version=version_string) |