Argparse - не перехватывать позиционные аргументы с помощью `nargs`.
Я пытаюсь написать функцию wo, которая позволяет анализировать переменное количество аргументов через argparse - я знаю, что могу сделать это через nargs="+"
, К сожалению, способ, которым работает argparse help (и то, как люди обычно пишут аргументы в CLI), ставит позиционные аргументы последними. Это приводит к тому, что мой позиционный аргумент будет пойман как часть необязательных аргументов.
#!/usr/bin/python
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("positional", help="my positional arg", type=int)
parser.add_argument("-o", "--optional", help="my optional arg", nargs='+', type=float)
args = parser.parse_args()
print args.positional, args.optional
работает как ./test.py -h
показывает следующую инструкцию по использованию:
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] positional
но если я бегу ./test.py -o 0.21 0.11 0.33 0.13 100
дает мне
test.py: error: too few arguments
чтобы получить правильный анализ аргументов, мне нужно запустить ./test.py 100 -o 0.21 0.11 0.33 0.13
Так как же мне:
заставьте argparse переформатировать вывод об использовании, чтобы он не вводил в заблуждение, ИЛИ даже лучше:
скажите argparse не перехватывать последний элемент для необязательного аргумента
-o
если это последний в списке
?
2 ответа
Об этом есть сообщение об ошибке: http://bugs.python.org/issue9338
опциональные аргументы argparse с nargs='?', '*' или '+' не могут сопровождаться позиционированием
Простое (пользовательское) исправление заключается в использовании --
отделить позиции от дополнительных:
./test.py -o 0.21 0.11 0.33 0.13 -- 100
Я написал патч, который резервирует некоторые аргументы для использования позиционным. Но это не тривиально.
Что касается изменения строки использования - проще всего написать свою собственную, например:
usage: test.py [-h] positional [-o OPTIONAL [OPTIONAL ...]]
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] -- positional
Я бы не рекомендовал добавлять логику в модуль форматирования, чтобы вносить подобные изменения. Я думаю, что это будет слишком сложно.
Другое быстрое решение состоит в том, чтобы превратить это положение в (обязательный) необязательный. Это дает пользователю полную свободу в отношении их заказа и может уменьшить путаницу. Если вы не хотите путать "обязательный необязательный", просто дайте ему логическое значение по умолчанию.
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] -p POSITIONAL
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] [-p POS_WITH_DEFAULT]
Одно простое изменение в Help_Formatter - просто перечислить аргументы в порядке их определения. Обычный способ изменить поведение форматера - это создать его подкласс и изменить один или два метода. Большинство этих методов являются "приватными" (префикс _), поэтому вы делаете это с пониманием того, что будущий код может изменяться (медленно).
В этом методе actions
список аргументов в том порядке, в котором они были определены. Поведение по умолчанию состоит в том, чтобы отделить 'optionals' от 'positionals', и заново собрать список с позициями в конце. Есть дополнительный код, который обрабатывает длинные строки, которые нуждаются в переносе. Обычно он размещает позиционеров на отдельной строке. Я пропустил это.
class Formatter(argparse.HelpFormatter):
# use defined argument order to display usage
def _format_usage(self, usage, actions, groups, prefix):
if prefix is None:
prefix = 'usage: '
# if usage is specified, use that
if usage is not None:
usage = usage % dict(prog=self._prog)
# if no optionals or positionals are available, usage is just prog
elif usage is None and not actions:
usage = '%(prog)s' % dict(prog=self._prog)
elif usage is None:
prog = '%(prog)s' % dict(prog=self._prog)
# build full usage string
action_usage = self._format_actions_usage(actions, groups) # NEW
usage = ' '.join([s for s in [prog, action_usage] if s])
# omit the long line wrapping code
# prefix with 'usage:'
return '%s%s\n\n' % (prefix, usage)
parser = argparse.ArgumentParser(formatter_class=Formatter)
Который производит строку использования как:
usage: stack26985650.py [-h] positional [-o OPTIONAL [OPTIONAL ...]]
Вместо того, чтобы использовать nargs="+"
рассмотрите возможность использования action="append"
, Это требует прохождения -o
перед каждым числом, но он не будет использовать аргументы, если вы на самом деле не хотите этого.