получить доступ к «неявному» значению метавара в строке справки аргумента - PullRequest
0 голосов
/ 28 июня 2018

Когда вы вызываете add_argument на argparse.ArgumentParser() без явного действия, вы получаете действие "store". В автоматически сгенерированном выводе --help вы получите верхний регистр длинной опции, если вы не установили metavar:

import argparse

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

отображает:

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

В этом я бы назвал X.Y.Z явным метаваром, а DATE неявным метаваром.

Если вы хотите получить более полезную помощь, вы можете сделать:

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

, который дает (показаны только измененные строки):

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

и возможность использовать это %(metavar)s в строке справки - это хорошо, потому что когда вы изменяете metavar='MAJOR.MINOR', справка не нуждается в обновлении (что вы обязательно забудете).

Но если вы добавите справку для аргумента --date с неявным метаваром:

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

вы получите:

  --date DATE      use None instead of today

И это None не то, что я ожидал, ни то, что я хочу.

Конечно, я всегда могу жестко кодировать 'DATE' в справке или явно указывать метавар (особенно, когда он используется в строке справки). Но когда я это сделаю, я обязательно забуду обновить метавар при изменении имени длинной опции.

Есть ли "автоматический" способ получить DATE в строке справки вместо None?
Или я использую %(metavar)s, где я должен использовать что-то еще (и если да, то что)?

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Это будет непросто, по крайней мере, в пределах argparse.

Когда вы add_argument создаете объект класса Action и присваиваете атрибуты:

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

a1.metavar будет 'X.Y.Z', a2.metavar будет по умолчанию None.

Это значение, которое используется в строке помощи, что-то вроде:

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

Этот атрибут action.metavar можно изменить после создания действия, как показано в другом ответе.

Но для usage и первой части help это выглядит примерно так:

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 отличается для positionals и optionals, но в основном происходит от action.dest. Таким образом, отображаемое metavar генерируется на лету и нигде не сохраняется.


Обрабатывается %(metavar)s:

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) составляет словарь из всех атрибутов action.

Я могу представить создание подкласса Formatter, который модифицирует один или несколько методов. Существующие подклассы работают, изменяя только один или два низкоуровневых метода. Но для этого нужно изучить код.


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>}

Справка с несколькими параметрами:

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
0 голосов
/ 28 июня 2018

Одна вещь, которую вы можете сделать перед вызовом parser.parse_args(), это обновить те действия, добавленные к parser, которые имеют атрибут metavar, равный 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()

Это генерирует вывод как:

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

Но, как мы говорим на родном языке BDFL: «Муи Андерс» 101


¹ красиво выглядит по-другому

...