def _autocomplete(root_parser, cwords, cword): def _collect_choices(parser, word): for a in parser._actions: if a.choices: for choice in a.choices: if word: if choice.startswith(word): yield choice else: yield choice choices = [] # dig into the tree of parsers until we can yield no more choices # 1 [''] root parser -> 'help fixtures' # 2 ['', 'fi'] root parser -> 'fixtures' # 2 ['', 'fixtures'] subparser -> 'load dump' # 3 ['', 'fixtures', 'lo'] subparser -> 'load' # 3 ['', 'fixtures', 'load'] subparser -> '' parser = root_parser choices = _collect_choices(parser, '') for word in cwords: # find the subparser and switch to it subparsers = get_subparsers(parser) if not subparsers: break if word in subparsers.choices: parser = subparsers.choices[word] word = '' choices = _collect_choices(parser, word) return choices
def add_commands(parser, functions, namespace=None, title=None, description=None, help=None): """Adds given functions as commands to given parser. :param parser: an :class:`argparse.ArgumentParser` instance. :param functions: a list of functions. A subparser is created for each of them. If the function is decorated with :func:`~argh.decorators.arg`, the arguments are passed to :class:`argparse.ArgumentParser.add_argument`. See also :func:`~argh.dispatching.dispatch` for requirements concerning function signatures. The command name is inferred from the function name. Note that the underscores in the name are replaced with hyphens, i.e. function name "foo_bar" becomes command name "foo-bar". :param namespace: an optional string representing the group of commands. For example, if a command named "hello" is added without the namespace, it will be available as "prog.py hello"; if the namespace if specified as "greet", then the command will be accessible as "prog.py greet hello". The namespace itself is not callable, so "prog.py greet" will fail and only display a help message. Help message for a namespace can be also tuned with these params (provided that you specify the `namespace`): :param title: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `title`. :param description: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `description`. :param help: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `help`. .. note:: This function modifies the parser object. Generally side effects are bad practice but we don't seem to have any choice as ArgumentParser is pretty opaque. You may prefer :class:`~argh.helpers.ArghParser.add_commands` for a bit more predictable API. .. admonition:: Design flaw This function peeks into the parser object using its internal API. Unfortunately the public API does not allow to *get* the subparsers, it only lets you *add* them, and do that *once*. So you would have to toss the subparsers object around to add something later. That said, I doubt that argparse will change a lot in the future as it's already pretty stable. If some implementation details would change and break `argh`, we'll simply add a workaround a keep it compatibile. .. note:: An attempt to add commands to a parser which already has a default function (e.g. added with :func:`~argh.assembling.set_default_command`) results in a `RuntimeError`. """ if 'function' in parser._defaults: raise RuntimeError('Cannot add commands to a single-command parser') subparsers = get_subparsers(parser, create=True) if namespace: # make a namespace placeholder and register the commands within it subsubparser = subparsers.add_parser(namespace, help=title) subparsers = subsubparser.add_subparsers(title=title, description=description, help=help) else: assert not any([title, description, help]), ( 'Arguments "title", "description" or "extra_help" only make sense ' 'if provided along with a namespace.') for func in functions: # use explicitly defined name; if none, use function name (a_b → a-b) cmd_name = getattr(func, ATTR_NAME, func.__name__.replace('_','-')) parser_kwargs = { # add command help from function's docstring 'help': func.__doc__, # set default formatter 'formatter_class': PARSER_FORMATTER, } # try adding aliases for command name if SUPPORTS_ALIASES: parser_kwargs['aliases'] = getattr(func, ATTR_ALIASES, []) # create and set up the parser for this command command_parser = subparsers.add_parser(cmd_name, **parser_kwargs) set_default_command(command_parser, func)
def add_commands(parser, functions, namespace=None, namespace_kwargs=None, func_kwargs=None, # deprecated args: title=None, description=None, help=None): """ Adds given functions as commands to given parser. :param parser: an :class:`argparse.ArgumentParser` instance. :param functions: a list of functions. A subparser is created for each of them. If the function is decorated with :func:`~argh.decorators.arg`, the arguments are passed to :class:`argparse.ArgumentParser.add_argument`. See also :func:`~argh.dispatching.dispatch` for requirements concerning function signatures. The command name is inferred from the function name. Note that the underscores in the name are replaced with hyphens, i.e. function name "foo_bar" becomes command name "foo-bar". :param namespace: an optional string representing the group of commands. For example, if a command named "hello" is added without the namespace, it will be available as "prog.py hello"; if the namespace if specified as "greet", then the command will be accessible as "prog.py greet hello". The namespace itself is not callable, so "prog.py greet" will fail and only display a help message. :param func_kwargs: a `dict` of keyword arguments to be passed to each nested ArgumentParser instance created per command (i.e. per function). Members of this dictionary have the highest priority, so a function's docstring is overridden by a `help` in `func_kwargs` (if present). :param namespace_kwargs: a `dict` of keyword arguments to be passed to the nested ArgumentParser instance under given `namespace`. Deprecated params that should be moved into `namespace_kwargs`: :param title: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `title`. .. deprecated:: 0.26.0 Please use `namespace_kwargs` instead. :param description: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `description`. .. deprecated:: 0.26.0 Please use `namespace_kwargs` instead. :param help: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `help`. .. deprecated:: 0.26.0 Please use `namespace_kwargs` instead. .. note:: This function modifies the parser object. Generally side effects are bad practice but we don't seem to have any choice as ArgumentParser is pretty opaque. You may prefer :class:`~argh.helpers.ArghParser.add_commands` for a bit more predictable API. .. note:: An attempt to add commands to a parser which already has a default function (e.g. added with :func:`~argh.assembling.set_default_command`) results in `AssemblingError`. """ # FIXME "namespace" is a correct name but it clashes with the "namespace" # that represents arguments (argparse.Namespace and our ArghNamespace). # We should rename the argument here. if DEST_FUNCTION in parser._defaults: _require_support_for_default_command_with_subparsers() namespace_kwargs = namespace_kwargs or {} # FIXME remove this by 1.0 # if title: warnings.warn('argument `title` is deprecated in add_commands(),' ' use `parser_kwargs` instead', DeprecationWarning) namespace_kwargs['description'] = title if help: warnings.warn('argument `help` is deprecated in add_commands(),' ' use `parser_kwargs` instead', DeprecationWarning) namespace_kwargs['help'] = help if description: warnings.warn('argument `description` is deprecated in add_commands(),' ' use `parser_kwargs` instead', DeprecationWarning) namespace_kwargs['description'] = description # # / subparsers_action = get_subparsers(parser, create=True) if namespace: # Make a nested parser and init a deeper _SubParsersAction under it. # Create a named group of commands. It will be listed along with # root-level commands in ``app.py --help``; in that context its `title` # can be used as a short description on the right side of its name. # Normally `title` is shown above the list of commands # in ``app.py my-namespace --help``. subsubparser_kw = { 'help': namespace_kwargs.get('title'), } subsubparser = subparsers_action.add_parser(namespace, **subsubparser_kw) subparsers_action = subsubparser.add_subparsers(**namespace_kwargs) else: assert not namespace_kwargs, ('`parser_kwargs` only makes sense ' 'with `namespace`.') for func in functions: cmd_name, func_parser_kwargs = _extract_command_meta_from_func(func) # override any computed kwargs by manually supplied ones if func_kwargs: func_parser_kwargs.update(func_kwargs) # create and set up the parser for this command command_parser = subparsers_action.add_parser(cmd_name, **func_parser_kwargs) set_default_command(command_parser, func)
def add_commands(parser, functions, namespace=None, title=None, description=None, help=None): """Adds given functions as commands to given parser. :param parser: an :class:`argparse.ArgumentParser` instance. :param functions: a list of functions. A subparser is created for each of them. If the function is decorated with :func:`arg`, the arguments are passed to the :class:`~argparse.ArgumentParser.add_argument` method of the parser. See also :func:`dispatch` for requirements concerning function signatures. The command name is inferred from the function name. Note that the underscores in the name are replaced with hyphens, i.e. function name "foo_bar" becomes command name "foo-bar". :param namespace: an optional string representing the group of commands. For example, if a command named "hello" is added without the namespace, it will be available as "prog.py hello"; if the namespace if specified as "greet", then the command will be accessible as "prog.py greet hello". The namespace itself is not callable, so "prog.py greet" will fail and only display a help message. Help message for a namespace can be also tuned with these params (provided that you specify the `namespace`): :param title: passed to :meth:`argsparse.ArgumentParser.add_subparsers` as `title`. :param description: passed to :meth:`argsparse.ArgumentParser.add_subparsers` as `description`. :param help: passed to :meth:`argsparse.ArgumentParser.add_subparsers` as `help`. .. note:: This function modifies the parser object. Generally side effects are bad practice but we don't seem to have any choice as ArgumentParser is pretty opaque. You may prefer :class:`ArghParser.add_commands` for a bit more predictable API. .. admonition:: Design flaw This function peeks into the parser object using its internal API. Unfortunately the public API does not allow to *get* the subparsers, it only lets you *add* them, and do that *once*. So you would have to toss the subparsers object around to add something later. That said, I doubt that argparse will change a lot in the future as it's already pretty stable. If some implementation details would change and break `argh`, we'll simply add a workaround a keep it compatibile. """ subparsers = get_subparsers(parser, create=True) if namespace: # make a namespace placeholder and register the commands within it assert isinstance(namespace, string_types) subsubparser = subparsers.add_parser(namespace, help=title) subparsers = subsubparser.add_subparsers(title=title, description=description, help=help) else: assert not any([title, description, help]), ( 'Arguments "title", "description" or "extra_help" only make sense ' 'if provided along with a namespace.') for func in functions: # XXX we could add multiple aliases here but it's a bit of a hack cmd_name = getattr(func, ATTR_ALIAS, func.__name__.replace('_','-')) cmd_help = func.__doc__ command_parser = subparsers.add_parser(cmd_name, help=cmd_help) for a_args, a_kwargs in getattr(func, ATTR_ARGS, []): command_parser.add_argument(*a_args, **a_kwargs) command_parser.set_defaults(function=func)
def add_commands( parser, functions, namespace=None, namespace_kwargs=None, func_kwargs=None, # deprecated args: title=None, description=None, help=None): """ Adds given functions as commands to given parser. :param parser: an :class:`argparse.ArgumentParser` instance. :param functions: a list of functions. A subparser is created for each of them. If the function is decorated with :func:`~argh.decorators.arg`, the arguments are passed to :class:`argparse.ArgumentParser.add_argument`. See also :func:`~argh.dispatching.dispatch` for requirements concerning function signatures. The command name is inferred from the function name. Note that the underscores in the name are replaced with hyphens, i.e. function name "foo_bar" becomes command name "foo-bar". :param namespace: an optional string representing the group of commands. For example, if a command named "hello" is added without the namespace, it will be available as "prog.py hello"; if the namespace if specified as "greet", then the command will be accessible as "prog.py greet hello". The namespace itself is not callable, so "prog.py greet" will fail and only display a help message. :param func_kwargs: a `dict` of keyword arguments to be passed to each nested ArgumentParser instance created per command (i.e. per function). Members of this dictionary have the highest priority, so a function's docstring is overridden by a `help` in `func_kwargs` (if present). :param namespace_kwargs: a `dict` of keyword arguments to be passed to the nested ArgumentParser instance under given `namespace`. Deprecated params that should be moved into `namespace_kwargs`: :param title: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `title`. .. deprecated:: 0.26.0 Please use `namespace_kwargs` instead. :param description: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `description`. .. deprecated:: 0.26.0 Please use `namespace_kwargs` instead. :param help: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `help`. .. deprecated:: 0.26.0 Please use `namespace_kwargs` instead. .. note:: This function modifies the parser object. Generally side effects are bad practice but we don't seem to have any choice as ArgumentParser is pretty opaque. You may prefer :class:`~argh.helpers.ArghParser.add_commands` for a bit more predictable API. .. note:: An attempt to add commands to a parser which already has a default function (e.g. added with :func:`~argh.assembling.set_default_command`) results in `AssemblingError`. """ # FIXME "namespace" is a correct name but it clashes with the "namespace" # that represents arguments (argparse.Namespace and our ArghNamespace). # We should rename the argument here. if DEST_FUNCTION in parser._defaults: _require_support_for_default_command_with_subparsers() namespace_kwargs = namespace_kwargs or {} # FIXME remove this by 1.0 # if title: warnings.warn( 'argument `title` is deprecated in add_commands(),' ' use `parser_kwargs` instead', DeprecationWarning) namespace_kwargs['description'] = title if help: warnings.warn( 'argument `help` is deprecated in add_commands(),' ' use `parser_kwargs` instead', DeprecationWarning) namespace_kwargs['help'] = help if description: warnings.warn( 'argument `description` is deprecated in add_commands(),' ' use `parser_kwargs` instead', DeprecationWarning) namespace_kwargs['description'] = description # # / subparsers_action = get_subparsers(parser, create=True) if namespace: # Make a nested parser and init a deeper _SubParsersAction under it. # Create a named group of commands. It will be listed along with # root-level commands in ``app.py --help``; in that context its `title` # can be used as a short description on the right side of its name. # Normally `title` is shown above the list of commands # in ``app.py my-namespace --help``. subsubparser_kw = { 'help': namespace_kwargs.get('title'), } subsubparser = subparsers_action.add_parser(namespace, **subsubparser_kw) subparsers_action = subsubparser.add_subparsers(**namespace_kwargs) else: assert not namespace_kwargs, ('`parser_kwargs` only makes sense ' 'with `namespace`.') for func in functions: cmd_name, func_parser_kwargs = _extract_command_meta_from_func(func) # override any computed kwargs by manually supplied ones if func_kwargs: func_parser_kwargs.update(func_kwargs) # create and set up the parser for this command command_parser = subparsers_action.add_parser(cmd_name, **func_parser_kwargs) set_default_command(command_parser, func)
def add_commands(parser, functions, namespace=None, title=None, description=None, help=None): """Adds given functions as commands to given parser. :param parser: an :class:`argparse.ArgumentParser` instance. :param functions: a list of functions. A subparser is created for each of them. If the function is decorated with :func:`~argh.decorators.arg`, the arguments are passed to :class:`argparse.ArgumentParser.add_argument`. See also :func:`~argh.dispatching.dispatch` for requirements concerning function signatures. The command name is inferred from the function name. Note that the underscores in the name are replaced with hyphens, i.e. function name "foo_bar" becomes command name "foo-bar". :param namespace: an optional string representing the group of commands. For example, if a command named "hello" is added without the namespace, it will be available as "prog.py hello"; if the namespace if specified as "greet", then the command will be accessible as "prog.py greet hello". The namespace itself is not callable, so "prog.py greet" will fail and only display a help message. Help message for a namespace can be also tuned with these params (provided that you specify the `namespace`): :param title: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `title`. :param description: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `description`. :param help: passed to :meth:`argparse.ArgumentParser.add_subparsers` as `help`. .. note:: This function modifies the parser object. Generally side effects are bad practice but we don't seem to have any choice as ArgumentParser is pretty opaque. You may prefer :class:`~argh.helpers.ArghParser.add_commands` for a bit more predictable API. .. admonition:: Design flaw This function peeks into the parser object using its internal API. Unfortunately the public API does not allow to *get* the subparsers, it only lets you *add* them, and do that *once*. So you would have to toss the subparsers object around to add something later. That said, I doubt that argparse will change a lot in the future as it's already pretty stable. If some implementation details would change and break `argh`, we'll simply add a workaround a keep it compatibile. .. note:: An attempt to add commands to a parser which already has a default function (e.g. added with :func:`~argh.assembling.set_default_command`) results in a `RuntimeError`. """ if 'function' in parser._defaults: raise RuntimeError('Cannot add commands to a single-command parser') subparsers = get_subparsers(parser, create=True) if namespace: # make a namespace placeholder and register the commands within it subsubparser = subparsers.add_parser(namespace, help=title) subparsers = subsubparser.add_subparsers(title=title, description=description, help=help) else: assert not any([title, description, help]), ( 'Arguments "title", "description" or "extra_help" only make sense ' 'if provided along with a namespace.') for func in functions: # use explicitly defined name; if none, use function name (a_b → a-b) cmd_name = getattr(func, ATTR_NAME, func.__name__.replace('_', '-')) parser_kwargs = { # add command help from function's docstring 'help': func.__doc__, # set default formatter 'formatter_class': PARSER_FORMATTER, } # try adding aliases for command name if SUPPORTS_ALIASES: parser_kwargs['aliases'] = getattr(func, ATTR_ALIASES, []) # create and set up the parser for this command command_parser = subparsers.add_parser(cmd_name, **parser_kwargs) set_default_command(command_parser, func)