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())
приведенный выше код анализирует args в 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
= integer), то он работает так, как вы ожидаете. Он не nargs
is '?'
или '*'
, а иногда, когда это '+'
. Итак, я спустился к коду, чтобы выяснить, что происходит.
это сводится к тому, как аргументы разделяются для потребления. Чтобы выяснить, кто что получает, вызов к 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='+'
is '(-*A[-AO]*)(-*A[A-]*)'
, который работает пока вы передаете только один аргумент. Скажи subpositional a
, поскольку вам требуется хотя бы один позиционный аргумент во второй группе. Опять же, как первая группа жадная, проходящая subpositional a b c d
получает ['AAAA', 'A']
, что не то, что вы хотели.
вкратце: 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
. Я бы сказал, что вы ищете, чтобы иметь позиционный аргумент перед именем subparser. Следовательно, вероятно, то, что вы ищете, - это добавление аргумента перед subparsers:
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