Argparse: Обязательные аргументы перечислены в разделе «необязательные аргументы»?

229

Я использую следующий простой код для разбора некоторых аргументов; обратите внимание, что один из них требуется. К сожалению, когда пользователь запускает сценарий без предоставления аргумента, отображаемый текст использования / справки не указывает на наличие необязательного аргумента, который я нахожу очень запутанным. Как я могу получить Python, чтобы указать, что аргумент не является необязательным?

Вот код:

import argparse
if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Foo')
    parser.add_argument('-i','--input', help='Input file name', required=True)
    parser.add_argument('-o','--output', help='Output file name', default="stdout")
    args = parser.parse_args()
    print ("Input file: %s" % args.input )
    print ("Output file: %s" % args.output )

При выполнении кода выше без предоставления обязательного аргумента я получаю следующий вывод:

usage: foo.py [-h] -i INPUT [-o OUTPUT]

Foo

optional arguments:
    -h, --help            show this help message and exit
    -i INPUT, --input INPUT
                          Input file name
    -o OUTPUT, --output OUTPUT
                          Output file name
Морт
источник
5
В строке использования -i INPUTдеталь не заключена в квадратные скобки, что указывает на то, что действительно требуется. Кроме того, вы можете вручную объяснить это через helpпараметр
Хайме RGP
7
@JaimeRGP Да, но этого не достаточно, конечно, и это также менее чем заметно. Назначенное имя группы optional argumentsдля требуемых аргументов по-прежнему вводит в заблуждение.
Acumenus

Ответы:

316

Параметры, начинающиеся с -или --обычно считаются необязательными. Все остальные параметры являются позиционными параметрами и, как таковые, требуются при разработке (например, аргументы позиционной функции). Можно требовать необязательные аргументы, но это немного противоречит их замыслу. Так как они все еще являются частью непозиционных аргументов, они все равно будут перечислены под вводящим в заблуждение заголовком «необязательные аргументы», даже если они необходимы. Отсутствующие квадратные скобки в части использования показывают, что они действительно необходимы.

Смотрите также документацию :

В общем, модуль argparse предполагает, что флаги, такие как -f и --bar, указывают необязательные аргументы, которые всегда можно опустить в командной строке.

Примечание. Обязательные параметры обычно считаются неверными, поскольку пользователи ожидают, что параметры являются необязательными, поэтому их следует избегать, когда это возможно.

При этом заголовки «позиционные аргументы» и «необязательные аргументы» в справке генерируются двумя группами аргументов, в которые автоматически разделяются аргументы. Теперь вы можете «взломать его» и изменить имя дополнительных, но гораздо более элегантным решением будет создание другой группы для «требуемых именованных аргументов» (или как вы хотите их называть):

parser = argparse.ArgumentParser(description='Foo')
parser.add_argument('-o', '--output', help='Output file name', default='stdout')
requiredNamed = parser.add_argument_group('required named arguments')
requiredNamed.add_argument('-i', '--input', help='Input file name', required=True)
parser.parse_args(['-h'])
usage: [-h] [-o OUTPUT] -i INPUT

Foo

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        Output file name

required named arguments:
  -i INPUT, --input INPUT
                        Input file name
совать
источник
У меня была такая же проблема. Я попробовал тебе решение. Он добавляет аргументы в новую группу, но мой код, похоже, не работает после этого. Любые решения будут оценены. Ссылка на мой код - pastebin.com/PvC2aujz
Зарар Махмуд
1
@ZararMahmud: вы передаете пустые аргументы в строке 24 вашего кода: parser.parse_args([])вместо этого используйте parser.parse_args()без аргументов для захвата содержимого sys.argv. Per argparse
Девин
@poke: Хорошее решение! Но это не поможет, если вам нужны взаимоисключающие группы, или я что-то упустил?
судья
@ Судите, я бы порекомендовал прочитать этот pymotw.com/3/argparse/#mutually-exclusive-options
Питер Мур
79

Так как я предпочитаю перечислять обязательные аргументы перед необязательными, я взломаю их через:

    parser = argparse.ArgumentParser()
    parser._action_groups.pop()
    required = parser.add_argument_group('required arguments')
    optional = parser.add_argument_group('optional arguments')
    required.add_argument('--required_arg', required=True)
    optional.add_argument('--optional_arg')
    return parser.parse_args()

и это выводит:

usage: main.py [-h] [--required_arg REQUIRED_ARG]
               [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  --optional_arg OPTIONAL_ARG

Я могу жить без «помощи», появляющейся в группе необязательных аргументов.

Карл Росаен
источник
3
Действительно ли это заставляет argparse обрабатывать любой из аргументов как требуется?
Энтони
6
Я думаю, что «обязательный» аргумент все еще должен быть установлен при добавлении аргумента.
Карл Росаен
Это действительно мило.
Пол Сезанн
7
@Anthony - нет, для этого вам нужно использовать 'required = True' в add_argument. Приведенный выше ответ просто иллюстрирует группировку аргументов.
user2275693
47

Здание от Карла Росаена

parser = argparse.ArgumentParser()
optional = parser._action_groups.pop() # Edited this line
required = parser.add_argument_group('required arguments')
# remove this line: optional = parser...
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')
parser._action_groups.append(optional) # added this line
return parser.parse_args()

и это выводит:

usage: main.py [-h] [--required_arg REQUIRED_ARG]
           [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  -h, --help                    show this help message and exit
  --optional_arg OPTIONAL_ARG
RalphyZ
источник
1
Кстати, есть ли способы (методы), как получить доступ _action_groupбез доступа к защищенному члену? В моем случае мне нужно добавить некоторый аргумент в уже существующую (пользовательскую) группу.
Мачин
Это круто. Решает элемент --help, отображаемый во втором необязательном списке.
Джереми
Примечание : этот ответ нарушает открытый API, проверьте ответ Bryan_D ниже.
LOL
18

Еще раз, построение @RalphyZ

Этот не нарушает выставленный API.

from argparse import ArgumentParser, SUPPRESS
# Disable default help
parser = ArgumentParser(add_help=False)
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')

# Add back help 
optional.add_argument(
    '-h',
    '--help',
    action='help',
    default=SUPPRESS,
    help='show this help message and exit'
)
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')

Который будет показывать то же, что и выше, и должен выжить в будущих версиях:

usage: main.py [-h] [--required_arg REQUIRED_ARG]
           [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  -h, --help                    show this help message and exit
  --optional_arg OPTIONAL_ARG
Bryan_D
источник
Можете ли вы объяснить, как ответ RalphyZ нарушает открытый API?
Jeremysprofile
5
_action_groupsпредназначен только для внутреннего использования. Таким образом, нет гарантии совместимости между версиями.
Bryan_D