简体   繁体   中英

Argparse python, remove subparser list in help menu

I'm writing a command-line utility using Argparse and have added a bunch of sub_parsers (sub commands). In the help menu they appear under a group called "commands" and I get a nice list of all the possible options. However before this list appears, all the same commands appear under the group title in braces like so:

Commands:
    {foo, bar}

    foo          - foo does foo
    bar          - bar does bar

I want to remove the redundant entries which appear in braces. It only appears in this group which is filled with sub_parsers.

My code to handle this looks like so: (where parser is the ArgumentParser() instance)

subparsers = parser.add_subparsers(title="Commands")

foo = subparsers.add_parser("foo", help="- foo does foo")
bar = subparsers.add_parser("bar", help="- bar does bar")

I've looked at the attributes and methods of my commands action group and can't seem to find anything that will solve this for me (at least from what I can make sense of). I'm not sure if anyone else has dealt with this, I realize it's probably a bit obscure. And again, all I'm trying to do is find the way to remove the redundant list of the commands which appear in braces.

The "{foo,bar}" part is the argument 'metavar'. A metavar is how argparse refers to expected argument values in the usage and help strings. argparse treats subcommands like an argument with multiple choices so if you don't specify a metavar, the default is the list of choices (subcommands) in curly braces. It lets the user know the possible options for subcommands but since they're listed just below, it's redundant and if you have lots of subcommands, it's ugly.

You can easily replace with your own chosen metavar:

subparsers = parser.add_subparsers(title="Commands", metavar="<command>")

You can customize your help message formatting by writing your own formatter class, basing on argparse.HelpFormatter 's interface and passing it to parser's constructor using formatter_class argument.

For more details see http://docs.python.org/dev/library/argparse.html#formatter-class

After diving really deep into argparse source code, I have constructed a hack to remove the redundant {cmd1,...} choice list.

The hack implements a custom help formatter which modifies HelpFormatter 's formatting methods when dealing with subparsers action. Specifically, it removes subparsers metavar and help line in subcommand argument group, and removes extra indentation of those subcommands.

Please use with care .

The python 3 version, tested with python3.6

from argparse import ArgumentParser, HelpFormatter, _SubParsersAction

class NoSubparsersMetavarFormatter(HelpFormatter):

    def _format_action(self, action):
        result = super()._format_action(action)
        if isinstance(action, _SubParsersAction):
            # fix indentation on first line
            return "%*s%s" % (self._current_indent, "", result.lstrip())
        return result

    def _format_action_invocation(self, action):
        if isinstance(action, _SubParsersAction):
            # remove metavar and help line
            return ""
        return super()._format_action_invocation(action)

    def _iter_indented_subactions(self, action):
        if isinstance(action, _SubParsersAction):
            try:
                get_subactions = action._get_subactions
            except AttributeError:
                pass
            else:
                # remove indentation
                yield from get_subactions()
        else:
            yield from super()._iter_indented_subactions(action)

parser = ArgumentParser(formatter_class=NoSubparsersMetavarFormatter)
subparsers = parser.add_subparsers(title="Commands")

foo = subparsers.add_parser("foo", help="- foo does foo")
bar = subparsers.add_parser("bar", help="- bar does bar")

parser.parse_args(['-h'])

The python 2 version, tested with python2.7

from argparse import ArgumentParser, HelpFormatter, _SubParsersAction

class NoSubparsersMetavarFormatter(HelpFormatter):

    def _format_action(self, action):
        result = super(NoSubparsersMetavarFormatter,
                       self)._format_action(action)
        if isinstance(action, _SubParsersAction):
            return "%*s%s" % (self._current_indent, "", result.lstrip())
        return result

    def _format_action_invocation(self, action):
        if isinstance(action, _SubParsersAction):
            return ""
        return super(NoSubparsersMetavarFormatter,
                     self)._format_action_invocation(action)

    def _iter_indented_subactions(self, action):
        if isinstance(action, _SubParsersAction):
            try:
                get_subactions = action._get_subactions
            except AttributeError:
                pass
            else:
                for subaction in get_subactions():
                    yield subaction
        else:
            for subaction in super(NoSubparsersMetavarFormatter,
                                   self)._iter_indented_subactions(action):
                yield subaction

parser = ArgumentParser(formatter_class=NoSubparsersMetavarFormatter)
subparsers = parser.add_subparsers(title="Commands")

foo = subparsers.add_parser("foo", help="- foo does foo")
bar = subparsers.add_parser("bar", help="- bar does bar")

parser.parse_args(['-h'])

Sample output:

usage: a.py [-h] {foo,bar} ...

optional arguments:
  -h, --help  show this help message and exit

Commands:
  foo         - foo does foo
  bar         - bar does bar

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM