Python argparse: укажите хотя бы один аргумент - PullRequest
76 голосов
/ 17 июля 2011

Я использовал 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 для принудительного выбора хотя бы одного параметра?

UPDATE:

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

Ответы [ 11 ]

85 голосов
/ 17 июля 2011
if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')
27 голосов
/ 02 марта 2013
args = vars(parser.parse_args())
if not any(args.values()):
    parser.error('No arguments provided.')
20 голосов
/ 17 июля 2011

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

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")

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

14 голосов
/ 10 октября 2015

Я знаю, что это старый как грязь, но способ требовать одну опцию, но запретить более одного (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  
8 голосов
/ 10 июня 2014

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

  • использование 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 голосов
/ 10 июня 2017

Лучший способ сделать это - использовать встроенный модуль 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()

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

group = parser.add_mutually_exclusive_group(required=True)
4 голосов
/ 11 июня 2014

Для 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' и т. Д.

В результате запускается тест, аналогичный https://stackoverflow.com/a/6723066/901925,, хотя сообщение об ошибке должно быть более четким:

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

Интересно:

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

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

  • использует группу, подобную этой, более интуитивно, чем phihag's простой тест?

4 голосов
/ 20 июля 2013

Если вам требуется, чтобы программа на python выполнялась хотя бы с одним параметром, добавьте аргумент, что не имеет префикс опции (- или - по умолчанию) и установите nargs=+ (минимумтребуется один аргумент).Проблема с этим методом, который я обнаружил, заключается в том, что если вы не укажете аргумент, 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()

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

1 голос
/ 09 апреля 2019

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

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', вы также можете связать функции для непосредственного вызова из данной подкоманды (см. Документы).

1 голос
/ 10 июня 2014

Используйте 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()
...