Python зависимости между группами, использующими argparse

Я начал изучать Python, и теперь я изучаю большие преимущества argparse, С помощью argparseЯ создал две группы аргументов: group_list а также group_simulate, Каждая из групп имеет свои собственные аргументы - пользователь может указать только один аргумент в каждой группе (достигается с помощью parser.add_mutually_exclusive_group()).

И теперь моя цель присутствует синтаксическая ошибка, если пользователь указал аргументы из обеих групп, а не только из одной из них - я хочу добиться этого с помощью возможностей argparse а не путем написания метода, который спрашивает, указана ли эта и указанная ошибка синтаксиса печати.

import argparse
parser = argparse.ArgumentParser(
        description='this is the description',
        epilog="This is the epilog",
        argument_default=argparse.SUPPRESS  
        )

parser.add_argument('-v', '--verbose', help='verbose', action='store_true', default=False)

group_list = parser.add_mutually_exclusive_group()
group_list.add_argument('-m', help='list only modules', action='store_const', dest='list', const='modules', default='all')
group_list.add_argument('-p', help='list only ports', action='store_const', dest='list', const='ports', default='all')
group_list.add_argument('--list', help='list only module or ports', choices=['modules','ports'], metavar='<modules/ports>', default='all')

group_simulate = parser.add_mutually_exclusive_group()
group_simulate.add_argument('-M', help='simulate module down', nargs=1, metavar='module_name', dest='simulate')
group_simulate.add_argument('-P', help='simulate FC port down', nargs=1, metavar='fc_port_name', dest='simulate')
group_simulate.add_argument('-I', help='simulate iSCSI port down', nargs=1, metavar='iSCSI_port_name', dest='simulate')
group_simulate.add_argument('--simulate', help='simulate module or port down', nargs=1, dest='simulate')

args = parser.parse_args()

print args

Итак, говоря более конкретно:

позволил:

test.py
output: Namespace(list='all', verbose=False)
test.py -m
output: Namespace(list='modules', verbose=False)
test.py -P asfasf
output: Namespace(P=['asfasf'], list='all', verbose=False)

не положено:

test.py -m -P asfsaf
expected output: <the help message>
test.py -P asfasf -m
expected output: <the help message>

Я пытался достичь желаемой цели с возможностью add_subparsers от argparse но безуспешно

Итак, мой вопрос, как добиться этой ситуации?

3 ответа

Решение

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

import argparse
parser = argparse.ArgumentParser(
        description='this is the description',
        epilog="This is the epilog",
        argument_default=argparse.SUPPRESS  
        )

parser.add_argument('-v', '--verbose', help='verbose', action='store_true', default=False)

root_group = parser.add_mutually_exclusive_group()

group_list = root_group.add_mutually_exclusive_group()
group_list.add_argument('-m', help='list only modules', action='store_const', dest='list', const='modules', default='all')
group_list.add_argument('-p', help='list only ports', action='store_const', dest='list', const='ports', default='all')
group_list.add_argument('--list', help='list only module or ports', choices=['modules','ports'], metavar='<modules/ports>', default='all')

group_simulate = root_group.add_mutually_exclusive_group()
group_simulate.add_argument('-M', help='simulate module down', nargs=1, metavar='module_name', dest='simulate')
group_simulate.add_argument('-P', help='simulate FC port down', nargs=1, metavar='fc_port_name', dest='simulate')
group_simulate.add_argument('-I', help='simulate iSCSI port down', nargs=1, metavar='iSCSI_port_name', dest='simulate')
group_simulate.add_argument('--simulate', help='simulate module or port down', nargs=1, dest='simulate')

args = parser.parse_args()

print args

Результат:

$ python test.py -m -P asfafs
usage: test.py [-h] [-v] [[-m | -p | --list <modules/ports>]
                [-M module_name | -P fc_port_name | -I iSCSI_port_name | --simulate SIMULATE]
test.py: error: argument -P: not allowed with argument -m 

$ python test.py -m -p
usage: test.py [-h] [-v] [[-m | -p | --list <modules/ports>]
                [-M module_name | -P fc_port_name | -I iSCSI_port_name | --simulate SIMULATE]
test.py: error: argument -p: not allowed with argument -m

Используйте Docopt! Вам не нужно писать документ об использовании, а затем часами пытаться понять, как получить argparse для его создания. Если вы знаете POSIX, вы знаете, как интерпретировать документ об использовании, потому что это стандарт. Докопт знает, как интерпретировать документацию так же, как вы. Нам не нужен слой абстракции.

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


test.py

"""
usage: test.py [-h | --version]
       test.py [-v] (-m | -p)
       test.py [-v] --list (modules | ports)
       test.py [-v] (-M <module_name> | -P <fc_port_name> | -I <iSCSI_port_name>)

this is the description

optional arguments:
  -h, --help                show this help message and exit
  -v, --verbose             verbose
  -m                        list only modules (same as --list modules)
  -p                        list only ports   (same as --list ports)
  --list                    list only module or ports
  -M module_name            simulate module down
  -P fc_port_name           simulate FC port down
  -I iSCSI_port_name        simulate iSCSI port down

This is the epilog
"""

from pprint import pprint
from docopt import docopt

def cli():
    arguments = docopt(__doc__, version='Super Tool 0.2')
    pprint(arguments)

if __name__ == '__main__':
    cli()

В то время как было бы возможно связать все использование в одной строке со сложными вложенными условиями, это более разборчиво. Вот почему докопт имеет такой смысл. Для программы CLI вы хотите убедиться, что вы общаетесь с пользователем четко. Зачем изучать какой-то непонятный синтаксис модуля в надежде, что вы сможете убедить его создать для вас общение с пользователем? Потратьте время, чтобы взглянуть на другие инструменты POSIX с правилами опций, аналогичными вашим потребностям и копировальной пасте.

Более простая версия этого парсера

parser=argparse.ArgumentParser(description="this is the description",
                epilog='this is the epilog')
parser.add_argument('-v', '--vebose', action='count')
g1=parser.add_mutually_exclusive_group()
g1.add_argument('--list', help='list module or ports (default=%(default)s)', choices=['modules','ports','all'], default='all')
g1.add_argument('--simulate', '-M','-P','-C', help='simulate [module down/ FS port down/ iSCSI port down]', dest='simulate', metavar='module/port')

С помощью, которая выглядит как:

usage: stack14660876.py [-h] [-v]
                        [--list {modules,ports,all} | --simulate module/port]

this is the description

optional arguments:
  -h, --help            show this help message and exit
  -v, --vebose
  --list {modules,ports,all}
                        list module or ports (default=all)
  --simulate module/port, -M module/port, -P module/port, -C module/port
                        simulate [module down/ FS port down/ iSCSI port down]

this is the epilog

рядом verbose (здесь я подставил count) ОП устанавливает атрибуты, list а также simulate, list имеет значение по умолчанию allи может быть установлен на modules или же ports, -m а также -p просто короткие пути, и на самом деле не добавить к определению. Ярлыки могут быть полезны при определении множества параметров, особенно если эти параметры могут использоваться вместе (например, -vpm). Но здесь разрешен только один вариант (кроме -v).

simulate принимает неограниченную строку M/P/C Опции - это просто удобство документирования, они не ограничивают значения и не добавляют значения.

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

==========================

Комментарии о docopt а также POSIX Обработка аргументов побудила меня взглянуть на библиотеки аргументов Си. getopt это старый стандарт. Python имеет функциональный эквивалент, https://docs.python.org/2/library/getopt.html

Другой парсер в библиотеке GNU argp,

http://www.gnu.org/software/libc/manual/html_node/Argp.html

Я еще не видел четкого описания того, что он добавляет к getopt синтаксис. Но следующий абзац интересен.

Argp также предоставляет возможность объединять несколько независимо определенных анализаторов опций в один, устраняя конфликты между ними и создавая видимость результата. Библиотека может экспортировать парсер опций argp, который пользовательские программы могут использовать вместе со своими собственными парсерами опций, что приводит к меньшему количеству работы для пользовательских программ. Некоторые программы могут использовать только парсеры аргументов, экспортируемые библиотеками, что обеспечивает согласованный и эффективный анализ параметров для абстракций, реализованных библиотеками.

Это немного похоже на argparse подпарсерный механизм. То есть существует некоторый мета-парсер, который может делегировать действие одному (или нескольким) подпарсеру. Но в argparse подпарсеры должны быть явно названы пользователем.

Возможное расширение состоит в том, чтобы мета-парсер смотрел на контекст. Например, в случае OP, если он видит какой-либо из [--list, -p, -m], используйте list подпарсер, если любой из simulate аргументы, используйте simulate subparser. Это может дать более мощные инструменты группировки. И может быть возможно реализовать такого рода вещи с запасом argparse, Вы можете создать и запустить несколько разных parsers на том же sys.argv,

Другие вопросы по тегам