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
,