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 перед каждым числом, но он не будет использовать аргументы, если вы на самом деле не хотите этого.

Другие вопросы по тегам