Python argparse: требуется хотя бы один аргумент

Я использовал argparse для программы на Python, которая может -process, -upload или обоих:

parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('-process', action='store_true')
parser.add_argument('-upload',  action='store_true')
args = parser.parse_args()

Программа бессмысленна без хотя бы одного параметра. Как я могу настроить argparse для принудительного argparse хотя бы одного параметра?

ОБНОВИТЬ:

После комментариев: Какой Pythonic способ параметризовать программу хотя бы с одним вариантом?

Ответ 1

if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')

Ответ 2

args = vars(parser.parse_args())
if not any(args.values()):
    parser.error('No arguments provided.')

Ответ 3

Если не часть "или обе" (изначально я пропустил это), вы можете использовать что-то вроде этого:

parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('--process', action='store_const', const='process', dest='mode')
parser.add_argument('--upload',  action='store_const', const='upload', dest='mode')
args = parser.parse_args()
if not args.mode:
    parser.error("One of --process or --upload must be given")

Хотя, вероятно, было бы лучше использовать подкоманды.

Ответ 4

Я знаю, что это старое, как грязь, но способ требовать один вариант, но запрещать более одного (XOR) выглядит следующим образом:

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-process', action='store_true')
group.add_argument('-upload',  action='store_true')
args = parser.parse_args()
print args

Выход:

>opt.py  
usage: multiplot.py [-h] (-process | -upload)  
multiplot.py: error: one of the arguments -process -upload is required  

>opt.py -upload  
Namespace(process=False, upload=True)  

>opt.py -process  
Namespace(process=True, upload=False)  

>opt.py -upload -process  
usage: multiplot.py [-h] (-process | -upload)  
multiplot.py: error: argument -process: not allowed with argument -upload  

Ответ 5

Обзор требований

  • используйте argparse (я проигнорирую это)
  • позволяет вызывать одно или два действия (по крайней мере, один из них требуется).
  • попробуйте Pythonic (я бы скорее назвал его "POSIX" -подобным)

Также существуют некоторые неявные требования при проживании в командной строке:

  • объясняет использование пользователю таким образом, который легко понять
  • параметры должны быть необязательными
  • позволяет указывать флаги и опции
  • позволяют комбинировать с другими параметрами (например, имя файла или имена).

Пример решения с использованием docopt (файл managelog.py):

"""Manage logfiles
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  Password

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
    from docopt import docopt
    args = docopt(__doc__)
    print args

Попробуйте запустить его:

$ python managelog.py
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Показать справку:

$ python managelog.py -h
Manage logfiles
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  P    managelog.py [options] upload -- <logfile>...

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>

И используйте его:

$ python managelog.py -V -U user -P secret upload -- alfa.log beta.log
{'--': True,
 '--pswd': 'secret',
 '--user': 'user',
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': False,
 'upload': True}

Короткая альтернатива short.py

Может быть еще более короткий вариант:

"""Manage logfiles
Usage:
    short.py [options] (process|upload)... -- <logfile>...
    short.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  Password

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
    from docopt import docopt
    args = docopt(__doc__)
    print args

Использование выглядит следующим образом:

$ python short.py -V process upload  -- alfa.log beta.log
{'--': True,
 '--pswd': None,
 '--user': None,
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': 1,
 'upload': 1}

Обратите внимание, что вместо логических значений для ключей "process" и "upload" есть счетчики.

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

$ python short.py -V process process upload  -- alfa.log beta.log
{'--': True,
 '--pswd': None,
 '--user': None,
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': 2,
 'upload': 1}

Заключение

Разработка хорошего интерфейса командной строки может быть сложной.

Существует несколько аспектов программы на основе командной строки:

  • хороший дизайн командной строки
  • выбор/использование собственного парсера

argparse предлагает много, но ограничивает возможные сценарии и может стать очень сложным.

С docopt дела идут намного короче, сохраняя удобочитаемость и предлагая высокую степень гибкости. Если вам удастся получить анализируемые аргументы из словаря и выполнить некоторые преобразования (целые, открывать файлы..) вручную (или другой библиотекой, называемой schema), вы можете найти docopt подходящую для синтаксического анализа командной строки.

Ответ 6

Лучший способ сделать это - использовать встроенный модуль python add_mutually_exclusive_group.

parser = argparse.ArgumentParser(description='Log archiver arguments.')
group = parser.add_mutually_exclusive_group()
group.add_argument('-process', action='store_true')
group.add_argument('-upload',  action='store_true')
args = parser.parse_args()

Если вы хотите, чтобы только один аргумент выбирался по командной строке, просто используйте value = True в качестве аргумента для группы

group = parser.add_mutually_exclusive_group(required=True)

Ответ 7

Если вам требуется запустить программу python с хотя бы одним параметром, добавьте аргумент, в котором не имеет префикс опции (или по умолчанию) и установите nargs=+ (Minimum требуется один аргумент). Проблема с этим методом, который я нашел, состоит в том, что если вы не укажете аргумент, argparse будет генерировать ошибку "слишком мало аргументов" и не распечатывать меню справки. Если вам не нужны эти функции, вот как это сделать в коде:

import argparse

parser = argparse.ArgumentParser(description='Your program description')
parser.add_argument('command', nargs="+", help='describe what a command is')
args = parser.parse_args()

Я думаю, что при добавлении аргумента с префиксами параметров nargs управляет всем парсером аргументов, а не только опцией. (Я имею в виду, если у вас есть флаг --option с nargs="+", то флаг --option ожидает хотя бы один аргумент. Если у вас есть option с nargs="+", он ожидает хотя бы один аргумент в целом.)

Ответ 8

Для http://bugs.python.org/issue11588 Я изучаю способы обобщения концепции mutually_exclusive_group для обработки таких случаев.

С этим развитием argparse.py, https://github.com/hpaulj/argparse_issues/blob/nested/argparse.py Я могу написать:

parser = argparse.ArgumentParser(prog='PROG', 
    description='Log archiver arguments.')
group = parser.add_usage_group(kind='any', required=True,
    title='possible actions (at least one is required)')
group.add_argument('-p', '--process', action='store_true')
group.add_argument('-u', '--upload',  action='store_true')
args = parser.parse_args()
print(args)

который производит следующее help:

usage: PROG [-h] (-p | -u)

Log archiver arguments.

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

possible actions (at least one is required):
  -p, --process
  -u, --upload

Это принимает входы, такие как "-u", "-up", "-proc -up" и т.д.

В результате выполняется тест, аналогичный fooobar.com/questions/76507/..., хотя сообщение об ошибке должно быть более четким:

usage: PROG [-h] (-p | -u)
PROG: error: some of the arguments process upload is required

Интересно:

  • - это параметры kind='any', required=True достаточно ясно (принять любую из групп, требуется хотя бы один)?

  • используется (-p | -u) clear? Требуемая взаимная_exclusive_group производит то же самое. Есть ли альтернативные обозначения?

  • использует такую ​​группу, которая более интуитивно понятна, чем phihag's простая проверка?

Ответ 9

Используйте append_const для списка действий, а затем проверьте, что этот список заполнен:

parser.add_argument('-process', dest=actions, const="process", action='append_const')
parser.add_argument('-upload',  dest=actions, const="upload", action='append_const')

args = parser.parse_args()

if(args.actions == None):
    parser.error('Error: No actions requested')

Вы даже можете указать методы непосредственно внутри констант.

def upload:
    ...

parser.add_argument('-upload',  dest=actions, const=upload, action='append_const')
args = parser.parse_args()

if(args.actions == None):
    parser.error('Error: No actions requested')

else:
    for action in args.actions:
        action()

Ответ 10

Может быть, использовать суб-парсеры?

import argparse

parser = argparse.ArgumentParser(description='Log archiver arguments.')
subparsers = parser.add_subparsers(dest='subparser_name', help='sub-command help')
parser_process = subparsers.add_parser('process', help='Process logs')
parser_upload = subparsers.add_parser('upload', help='Upload logs')
args = parser.parse_args()

print("Subparser: ", args.subparser_name)

Теперь --help показывает:

$ python /tmp/aaa.py --help
usage: aaa.py [-h] {process,upload} ...

Log archiver arguments.

positional arguments:
  {process,upload}  sub-command help
    process         Process logs
    upload          Upload logs

optional arguments:
  -h, --help        show this help message and exit
$ python /tmp/aaa.py
usage: aaa.py [-h] {process,upload} ...
aaa.py: error: too few arguments
$ python3 /tmp/aaa.py upload
Subparser:  upload

Вы также можете добавить дополнительные опции к этим подпарсерам. Также вместо использования dest='subparser_name' вы также можете привязать функции, которые будут напрямую вызываться для данной dest='subparser_name' (см. Документы).

Ответ 11

Это достигает цели, и это также будет отражено в автоматически --help выводе --help, который является тем, чего хочет большинство здравомыслящих программистов (также работает с необязательными аргументами):

parser.add_argument(
    'commands',
    nargs='+',                      # require at least 1
    choices=['process', 'upload'],  # restrict the choice
    help='commands to execute'
)

Официальные документы по этому вопросу: https://docs.python.org/3/library/argparse.html#choices.