Как заставить Python argparse принять флаг "-a" вместо позиционного аргумента? - PullRequest
0 голосов
/ 05 февраля 2020

Я пишу Python программу с использованием argparse. У меня есть аргумент для значения идентификатора. Пользователь может указать значение идентификатора для обработки в программе. Или они могут указать -a, чтобы указать, что все идентификаторы должны быть обработаны.

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

myprog 5
myprog -a

Но если вы не указали спецификацию c ID, тогда -a требуется, и это должно выдать ошибку.

Я поиграл с взаимоисключающей группой:

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-a', action='store_true', help=argparse.SUPPRESS)
group.add_argument("ID", action='store', nargs='?')

, которая работает, но мои разобранные аргументы заканчивают тем, что были два аргумента:

{'a': True, 'ID': None}

и если я попытаюсь добавить подобную группу после этого, скажем для другого аргумента "max", который может быть максимальным значением или -i, что означает игнорирование максимального значения:

group2 = parser.add_mutually_exclusive_group(required=True)
group2.add_argument('-i', action='store_true', help=argparse.SUPPRESS)
group2.add_argument("max", action='store', nargs='?')

Затем, если я пытаюсь разобрать аргументы ['-a', '2'], выдается сообщение об ошибке:

usage: args_exclusive_group.py [-h] [ID] [max]
args_exclusive_group.py: error: argument ID: not allowed with argument -a

Поскольку он обрабатывает 2 как ID, а не как max , Есть ли что-то действительно простое, что я упускаю, что просто позволило бы указанному позиционному аргументу (ID или max) также взять строку, которая "выглядит как" необязательная, потому что она начинается с "-"?

Ответы [ 2 ]

1 голос
/ 05 февраля 2020

Если вы хотите сохранить его как 2 позиционных аргумента, одним из подходов может быть инкапсуляция флагов -a и -i внутри их соответствующих аргументов и выполнение некоторой постобработки. Проблема в том, что argparse будет автоматически рассматривать строки, начинающиеся с -, в качестве аргументов :

позиционные аргументы могут начинаться только с -, если они выглядят как отрицательные числа и в парсере нет опций, которые выглядят как отрицательные числа.

Так что если вы измените свои ключевые слова на "all" и "1017 *", вы можете сделать что-то вроде:

parser = argparse.ArgumentParser()
parser.add_argument("ID")
parser.add_argument("max")

args = parser.parse_args()

if args.ID == 'all':
    print("processing all")
elif args.ID.isdigit():
    print(f"processing {args.ID}")
else:
    parser.error("ID must be a number or 'all' to use all IDs")

if args.max == 'ign':
    print("ignoring max")
elif args.max.isdigit():
    print(f"max is {args.max}")
else:
    parser.error("max must be a number or 'ign' to disable max")

И некоторые примеры запуска будут:

>>> tests.py 5 ign
processing 5
ignoring max

>>> tests.py all 7
processing all
max is 7

>>> tests.py blah 7
tests.py: error: ID must be a number or 'all' to use all IDs

>>> tests.py 5 blah
tests.py: error: max must be a number or 'ign' to disable max

Если вам действительно нужно использовать -a и -i:

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

Просто измените строку анализа на:

import sys
...
args = parser.parse_args(['--'] + sys.argv[1:])
0 голосов
/ 05 февраля 2020

Самое простое было бы иметь только один позиционный аргумент, значение которого или специальный токен, такой как all, или номер определенного процесса. Вы можете справиться с этим с пользовательским типом.

def process_id(s):
    if s == "all":
        return s

    try:
        return int(s)
    except ValueError:
        raise argparse.ArgumentTypeError("Must be 'all' or an integer")

p = argparse.ArgumentParser()
p.add_argument("ID", type=process_id)

args = p.parse_args()
if args.ID == "all":
    # process everything
else:
    # process just args.ID
...