access “implicit” metavar value in argument help string

ぐ巨炮叔叔 提交于 2021-02-05 06:46:05

问题


When you call add_argument on an argparse.ArgumentParser() without an explicit action, you get the "store" action. In the auto-generated --help output you get the uppercase of the long option, unless you set metavar:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--version', metavar='X.Y.Z')
parser.add_argument('--date'),
parser.parse_args(['--help'])

displays:

usage: try.py [-h] [--version X.Y.Z] [--date DATE]

optional arguments:
  -h, --help       show this help message and exit
  --version X.Y.Z
  --date DATE

In this I would call X.Y.Z an explicit metavar, and DATE an implicit metavar.

If you want to have more useful help you can do:

parser.add_argument('--version', metavar='X.Y.Z', 
                    help = "set version to % (metavar)s")

which gives (only changed lines shown):

  --version X.Y.Z  set version to X.Y.Z

and being able to use that %(metavar)s in the help string is nice because when you change metavar='MAJOR.MINOR', the help doesn't need to be updated (which you are bound to forget).

But if you add the help for the --date argument, with the implicit metavar:

parser.add_argument('--date', 
                    help="use %(metavar)s instead of today's date")

you get:

  --date DATE      use None instead of today

And that None is not what I expected, nor what I want.

Of course I can always hard-code 'DATE' in the help, or explicitly provide the metavar (especially when it is used in the help string). But when I do that, I am bound to forget to update the metavar when I change the name of the long option.

Is there an "automatic" way to get DATE in the help string instead of None ?
Or am I using %(metavar)s where I should be using something else (and if so, what)?


回答1:


One thing you can do before calling parser.parse_args() is update those actions added to the parser that have a metavar attribute that is None:

for action in parser._actions:
    if not hasattr(action, 'metavar') or not hasattr(action, 'dest'):
        continue
    metavar = getattr(action, 'metavar')
    if metavar is None:
        action.metavar = action.dest.upper()

That generates output like:

  --version X.Y.Z  set version to X.Y.Z
  --date DATE      use DATE instead of today

But as we say in the BDFL's native language: "mooi is anders" ¹


¹ beautiful looks different




回答2:


It's not going to be easy, at least not within argparse.

When you add_argument it creates an Action class object, and assigns attributes:

 a1 = parser.add_argument('--version', metavar='X.Y.Z')
 a2 = parser.add_argument('--date')

a1.metavar will be 'X.Y.Z', a2.metavar will be the default None.

That's the value that is used in the help line, something like:

`'help %(metavar)`%{'metavar':action.metavar}'

That action.metavar attribute can be modified after creating the Action, as demonstrated in the other answer.

But for the usage and first part of help it does something like:

def _metavar_formatter(self, action, default_metavar):
        if action.metavar is not None:
            result = action.metavar
        elif action.choices is not None:
            choice_strs = [str(choice) for choice in action.choices]
            result = '{%s}' % ','.join(choice_strs)
        else:
            result = default_metavar
        ...

default_metavar is different for positionals and optionals, but basically is derived from action.dest. So the displayed metavar is generated on the fly, and not stored anywhere.


The %(metavar)s is handled in:

def _expand_help(self, action):
    params = dict(vars(action), prog=self._prog)
    for name in list(params):
        if params[name] is SUPPRESS:
            del params[name]
    for name in list(params):
        if hasattr(params[name], '__name__'):
            params[name] = params[name].__name__
    if params.get('choices') is not None:
        choices_str = ', '.join([str(c) for c in params['choices']])
        params['choices'] = choices_str
    return self._get_help_string(action) % params

vars(action) makes a dictionary from all the attributes of the action.

I can imagine creating a Formatter subclass that modifies one or more of the methods. The existing subclasses work by modifying just one or two low level methods. But to do that requires studying the code.


In [329]: p = argparse.ArgumentParser()
In [330]: a1 = p.add_argument('--version', metavar='X.Y.Z')
In [331]: a1
Out[331]: _StoreAction(option_strings=['--version'], dest='version', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar='X.Y.Z')
In [332]: vars(a1)
Out[332]: 
{'option_strings': ['--version'],
 'dest': 'version',
 'nargs': None,
 'const': None,
 'default': None,
 'type': None,
 'choices': None,
 'required': False,
 'help': None,
 'metavar': 'X.Y.Z',
 'container': <argparse._ArgumentGroup at 0x7f72ecc4b4a8>}

A help with several parameters:

In [333]: a1.help='help %(metavar)s, %(dest)s, %(required)s'
In [334]: p.print_help()
usage: ipython3 [-h] [--version X.Y.Z]

optional arguments:
  -h, --help       show this help message and exit
  --version X.Y.Z  help X.Y.Z, version, False


来源:https://stackoverflow.com/questions/51085465/access-implicit-metavar-value-in-argument-help-string

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!