Argparse - не улавливайте позиционные аргументы с `nargs`.

Я пытаюсь написать функцию wo, в которой вы можете анализировать переменное количество аргументов через argparse - я знаю, что могу сделать это через nargs="+". К сожалению, способ работы справки argparse (и то, как люди обычно пишут аргументы в 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

Итак, как я:

  • make argparse переформатировать выход использования так, что он менее вводит в заблуждение, OR, еще лучше:

  • сообщить argparse, чтобы не улавливать последний элемент для необязательного аргумента -o, если он является последним в списке

?

Ответ 1

Об этом сообщается сообщение об ошибке: 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 - просто перечислить аргументы в том порядке, в котором они определены. Обычный способ изменения поведения форматирования заключается в подклассе и изменении одного или двух методов. Большинство из этих методов 'private' (_ prefix), поэтому вы делаете это с осознанием того, что будущий код может меняться (медленно).

В этом методе actions - это список аргументов в том порядке, в котором они были определены. Поведение по умолчанию состоит в том, чтобы разделить "дополнительные параметры" на "позиционные элементы" и собрать список с позициями в конце. Там есть дополнительный код, который обрабатывает длинные строки, которые требуют обертывания. Обычно он позиционирует позиции на отдельной строке. Я пропустил это.

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 ...]]

Ответ 2

Вместо использования nargs="+" рассмотрите возможность использования action="append". Для этого требуется передать -o перед каждым номером, но он не будет использовать аргументы, если вы на самом деле этого не хотите.