Python argparse позиционные аргументы и подкоманды

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

Этот код работает нормально:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

Приведенный выше код анализирует аргументы в Namespace(positional='positional')Однако, когда я изменяю позиционный аргумент, чтобы иметь Nargs='?' в качестве таких:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional', nargs='?')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

Это ошибки с:

usage: [-h] {subpositional} ... [positional]
: error: unrecognized arguments: positional

Почему это?

4 ответа

Решение

Сначала я думал так же, как jcollado, но потом есть тот факт, что, если последующие позиционные аргументы (верхнего уровня) имеют определенный nargs (nargs знак равно None, nargs = целое число), тогда он работает, как вы ожидаете. Это не удается, когда nargs является '?' или же '*'и иногда, когда это '+', Итак, я пошел к коду, чтобы выяснить, что происходит.

Это сводится к тому, как аргументы разделяются для использования. Чтобы выяснить, кто что получает, позвоните parse_args суммирует аргументы в виде строки 'AA', в твоем случае ('A' для позиционных аргументов, 'O' для необязательного) и заканчивает тем, что создает шаблон регулярного выражения для сопоставления с этой строкой резюме, в зависимости от действий, которые вы добавили в анализатор через .add_argument а также .add_subparsers методы.

В каждом случае, например, строка аргумента заканчивается 'AA', Какие изменения - это шаблон, который нужно сопоставить (вы можете увидеть возможные шаблоны в _get_nargs_pattern в argparse.py, За subpositional это в конечном итоге '(-*A[-AO]*)', что означает, что допускается один аргумент, за которым следует любое количество опций или аргументов. За positional, это зависит от значения, переданного nargs:

  • None => '(-*A-*)'
  • 3 => '(-*A-*A-*A-*)' (один '-*A' за ожидаемый аргумент)
  • '?' => '(-*A?-*)'
  • '*' => '(-*[A-]*)'
  • '+' => '(-*A[A-]*)'

Эти шаблоны добавляются и, для nargs=None (ваш рабочий пример), вы в конечном итоге '(-*A[-AO]*)(-*A-*)', которая соответствует двум группам ['A', 'A'], Сюда, subpositional будет разбирать только subpositional (что ты хотел), пока positional будет соответствовать его действию.

За nargs='?'тем не менее, вы в конечном итоге '(-*A[-AO]*)(-*A?-*)', Вторая группа полностью состоит из необязательных шаблонов и * будучи жадным, это означает, что первая группа теряет все в строке, заканчивая тем, что распознала две группы ['AA', ''], Это означает subpositional получает два аргумента и в конце концов задыхается, конечно.

Достаточно забавно, шаблон для nargs='+' является '(-*A[-AO]*)(-*A[A-]*)', который работает до тех пор, пока вы передаете только один аргумент. Сказать subpositional a, так как вам требуется хотя бы один позиционный аргумент во второй группе. Опять же, поскольку первая группа жадная, прохождение subpositional a b c d получает вас ['AAAA', 'A']что не то, что вы хотели.

Вкратце: беспорядок. Я думаю, это следует считать ошибкой, но я не уверен, как это повлияет, если шаблоны превратятся в не жадных...

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional', nargs='?')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

print(parser.parse_args(['positional', 'subpositional']))
# -> Namespace(positional='positional')
print(parser.parse_args(['subpositional']))
# -> Namespace(positional=None)
parser.print_usage()
# -> usage: bpython [-h] [positional] {subpositional} ...

Обычная практика заключается в том, что аргументы перед командой (слева) принадлежат основной программе, а после (справа) - команде. Следовательно positional должен идти перед командой subpositional, Примеры программ: git, twistd,

Дополнительно аргумент с narg=? должно быть вариант (--opt=value), а не позиционный аргумент.

Я думаю, что проблема в том, что когда add_subparsers вызывается, новый параметр добавляется в исходный анализатор для передачи имени подпарамера.

Например, с этим кодом:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')                                             
subparsers.add_parser('subpositional')                                             

parser.parse_args()

Вы получите следующую строку справки:

usage: test.py [-h] {subpositional} ... positional

positional arguments:
  {subpositional}
  positional

optional arguments:
  -h, --help       show this help message and exit

Обратите внимание, что subpositional отображается перед positional, Я бы сказал, что вам нужно иметь позиционный аргумент перед именем подпарапера. Следовательно, вероятно, что вы ищете, это добавить аргумент перед подпарсерами:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

parser.parse_args()

Строка справки, полученная с помощью этого кода:

usage: test.py [-h] positional {subpositional} ...

positional arguments:
  positional
  {subpositional}

optional arguments:
  -h, --help       show this help message and exit

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

Это все еще беспорядок в Python 3.5.

Я предлагаю подклассу ArgumentParser сохранить все оставшиеся позиционные аргументы и разобраться с ними позже:

import argparse

class myArgumentParser(argparse.ArgumentParser):
    def parse_args(self, args=None, namespace=None):
       args, argv = self.parse_known_args(args, namespace)
       args.remaining_positionnals = argv
       return args

parser = myArgumentParser()

options = parser.parse_args()

Остальные позиционные аргументы находятся в списке options.remaining_positionals

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