Argparse необязательный аргумент перед позиционным аргументом

Мне было интересно, возможно ли иметь позиционный аргумент после аргумента с необязательным параметром. В идеале последний аргумент, введенный в командную строку, всегда применяется к "testname".

import argparse
parser = argparse.ArgumentParser(description='TAF')
parser.add_argument('-r','--release',nargs='?',dest='release',default='trunk')
parser.add_argument('testname',nargs='+')
args = parser.parse_args()

Я хотел бы, чтобы оба этих вызова smelletest применялись к testname, а во втором - ошибка.

>> python TAF.py -r 1.0 smoketest
>> python TAF.py -r smoketest
TAF.py: error: too few arguments

Я понимаю, что перенос позиционного аргумента на фронт приведет к правильному поведению необязательного параметра, однако это не совсем тот формат, который я ищу. Флаг выбора выглядит привлекательной альтернативой, однако он выдает ошибку вместо игнорирования непревзойденного элемента.

EDIT: Я нашел хакерский путь вокруг этого. Если у кого-то будет более приятное решение, я был бы признателен.

import argparse
parser = argparse.ArgumentParser(description='TAF')
parser.add_argument('-r','--release',nargs='?',dest='release',default='trunk')
parser.add_argument('testname',nargs=argparse.REMAINDER)
args = parser.parse_args()

if not args.testname:
    args.testname = args.release
    args.release = ''

Ответ 1

Как указано в документации:

'?'. Один аргумент будет потребляться из командной строки, если это возможно, и производится как отдельный элемент. Если аргумент командной строки отсутствует, будет произведено значение по умолчанию. Обратите внимание, что для необязательного аргументов, есть дополнительный случай - присутствует строка опций но не следует аргумент командной строки. В этом случае значение из const будет произведено.

Таким образом, поведение, которое вы хотите, невозможно получить, используя '?'. Вероятно, вы могли бы написать хак, используя argparse.Action и вмешиваясь в предыдущие результаты. (1)

Я думаю, что лучшим решением является разделить функциональность этой опции. Сделайте его опцией, которая требует аргумента (но сама опция необязательна) и добавьте параметр без аргумента, который устанавливает выпуск в 'trunk'. Таким образом, вы можете получить те же результаты без каких-либо взломов. Также я считаю, что интерфейс проще.

В вашем примере:

python TAF.py -r smoketest

Совершенно ясно, что smoketest будет интерпретироваться как аргумент -r. По крайней мере, после соглашений unix. Если вы хотите сохранить nargs='?', то пользователь должен использовать --:

$ python TAF.py -r -- sometest
Namespace(release=None, testname=['sometest'])   #parsed result

(1) Идея о том, как это сделать: проверьте, имеет ли параметр аргумент. Если у него есть одна проверка, является ли это действительным тестовым именем. Если это положить вручную, testname и установить release значение по умолчанию. Вам также придется установить "флаг", который сообщает вам, что это произошло.

Теперь, прежде чем разобрать sys.argv, вы должны перенаправить sys.stderr. Когда вы выполняете синтаксический анализ, вы должны поймать SystemExit, проверьте stderr и посмотрите, была ли ошибка "слишком мало аргументов", проверьте, был ли установлен флаг, если так игнорировать ошибку и продолжить работу, иначе вы должны перепечатать ее оригинал stderr сообщение об ошибке и выход.

Этот подход не выглядит надежным и, вероятно, ошибкой.