diff options
Diffstat (limited to 'cmd2')
-rw-r--r-- | cmd2/cmd2.py | 65 |
1 files changed, 43 insertions, 22 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 7d2cad20..5cae2a0f 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -177,14 +177,37 @@ def with_argument_list(*args: List[Callable], preserve_quotes: bool = False) -> return arg_decorator -def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, *, +def set_parser_prog(parser: argparse.ArgumentParser, prog: str): + """ + Recursively set prog attribute of a parser and all of its subparsers so that the root command + is a command name and not sys.argv[0]. + :param parser: the parser being edited + :param prog: value for the current parsers prog attribute + """ + # Set the prog value for this parser + parser.prog = prog + + # Set the prog value for the parser's subcommands + for action in parser._actions: + if isinstance(action, argparse._SubParsersAction): + + # Set the prog value for each subcommand + for sub_cmd, sub_cmd_parser in action.choices.items(): + sub_cmd_prog = parser.prog + ' ' + sub_cmd + set_parser_prog(sub_cmd_parser, sub_cmd_prog) + + # We can break since argparse only allows 1 group of subcommands per level + break + + +def with_argparser_and_unknown_args(parser: argparse.ArgumentParser, *, ns_provider: Optional[Callable[..., argparse.Namespace]] = None, preserve_quotes: bool = False) -> \ Callable[[argparse.Namespace, List], Optional[bool]]: """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given instance of argparse.ArgumentParser, but also returning unknown args as a list. - :param argparser: unique instance of ArgumentParser + :param parser: unique instance of ArgumentParser :param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument and returns an argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that affects parsing. @@ -209,27 +232,26 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, *, namespace = ns_provider(cmd2_app) try: - args, unknown = argparser.parse_known_args(parsed_arglist, namespace) + args, unknown = parser.parse_known_args(parsed_arglist, namespace) except SystemExit: return else: setattr(args, '__statement__', statement) return func(cmd2_app, args, unknown) - # argparser defaults the program name to sys.argv[0] - # we want it to be the name of our command + # argparser defaults the program name to sys.argv[0], but we want it to be the name of our command command_name = func.__name__[len(COMMAND_FUNC_PREFIX):] - argparser.prog = command_name + set_parser_prog(parser, command_name) # If the description has not been set, then use the method docstring if one exists - if argparser.description is None and func.__doc__: - argparser.description = func.__doc__ + if parser.description is None and func.__doc__: + parser.description = func.__doc__ # Set the command's help text as argparser.description (which can be None) - cmd_wrapper.__doc__ = argparser.description + cmd_wrapper.__doc__ = parser.description # Set some custom attributes for this command - setattr(cmd_wrapper, CMD_ATTR_ARGPARSER, argparser) + setattr(cmd_wrapper, CMD_ATTR_ARGPARSER, parser) setattr(cmd_wrapper, CMD_ATTR_PRESERVE_QUOTES, preserve_quotes) return cmd_wrapper @@ -238,13 +260,13 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, *, return arg_decorator -def with_argparser(argparser: argparse.ArgumentParser, *, +def with_argparser(parser: argparse.ArgumentParser, *, ns_provider: Optional[Callable[..., argparse.Namespace]] = None, preserve_quotes: bool = False) -> Callable[[argparse.Namespace], Optional[bool]]: """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given instance of argparse.ArgumentParser. - :param argparser: unique instance of ArgumentParser + :param parser: unique instance of ArgumentParser :param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument and returns an argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that affects parsing. @@ -268,27 +290,26 @@ def with_argparser(argparser: argparse.ArgumentParser, *, namespace = ns_provider(cmd2_app) try: - args = argparser.parse_args(parsed_arglist, namespace) + args = parser.parse_args(parsed_arglist, namespace) except SystemExit: return else: setattr(args, '__statement__', statement) return func(cmd2_app, args) - # argparser defaults the program name to sys.argv[0] - # we want it to be the name of our command + # argparser defaults the program name to sys.argv[0], but we want it to be the name of our command command_name = func.__name__[len(COMMAND_FUNC_PREFIX):] - argparser.prog = command_name + set_parser_prog(parser, command_name) # If the description has not been set, then use the method docstring if one exists - if argparser.description is None and func.__doc__: - argparser.description = func.__doc__ + if parser.description is None and func.__doc__: + parser.description = func.__doc__ # Set the command's help text as argparser.description (which can be None) - cmd_wrapper.__doc__ = argparser.description + cmd_wrapper.__doc__ = parser.description # Set some custom attributes for this command - setattr(cmd_wrapper, CMD_ATTR_ARGPARSER, argparser) + setattr(cmd_wrapper, CMD_ATTR_ARGPARSER, parser) setattr(cmd_wrapper, CMD_ATTR_PRESERVE_QUOTES, preserve_quotes) return cmd_wrapper @@ -2396,7 +2417,7 @@ class Cmd(cmd.Cmd): "An alias is a command that enables replacement of a word by another string.") alias_epilog = ("See also:\n" " macro") - alias_parser = Cmd2ArgumentParser(description=alias_description, epilog=alias_epilog, prog='alias') + alias_parser = Cmd2ArgumentParser(description=alias_description, epilog=alias_epilog) # Add subcommands to alias alias_subparsers = alias_parser.add_subparsers(dest='subcommand') @@ -2573,7 +2594,7 @@ class Cmd(cmd.Cmd): "A macro is similar to an alias, but it can contain argument placeholders.") macro_epilog = ("See also:\n" " alias") - macro_parser = Cmd2ArgumentParser(description=macro_description, epilog=macro_epilog, prog='macro') + macro_parser = Cmd2ArgumentParser(description=macro_description, epilog=macro_epilog) # Add subcommands to macro macro_subparsers = macro_parser.add_subparsers(dest='subcommand') |