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

Я использую следующий простой код для анализа некоторых аргументов; обратите внимание, что один из них необходим. К сожалению, когда пользователь запускает script без предоставления аргумента, отображаемый текст использования/помощи не указывает на наличие необязательного аргумента, который я считаю очень запутанным. Как я могу заставить 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

Ответ 1

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

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

В общем случае модуль 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

Ответ 2

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

    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

Здание @Karl Rosaen

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

Ответ 4

Еще раз, построение @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