Соответствующая ошибка Python: Issue 15112.
argparse: nargs='*'
positional argument doesn't accept any items if preceded by an option and another positional
Когда argparse разбирает ['1', '2', '--spam', '8', '8', '9']
он сначала пытается соответствовать ['1','2']
, как многие из позиционных аргументов, как это возможно. С вашими аргументами строка соответствия шаблону равна AAA*
: 1 аргумент для pos
и foo
, а нулевые аргументы для vars
(запомнить *
означает ZERO_OR_MORE).
['--spam','8']
обрабатывается вашим аргументом --spam
. Так как vars
уже установлен в []
, ничего не остается, чтобы обрабатывать ['8','9']
.
Изменение программирования на argparse
проверяет на случай, когда строки аргументов 0
удовлетворяют шаблону, но все еще есть optionals
для анализа. Затем он отменяет обработку этого аргумента *
.
Возможно, вам удастся обойти это, сначала разобрав ввод с parse_known_args
, а затем обработав remainder
с помощью другого вызова parse_args
.
Чтобы иметь полную свободу в пересыпая среди positionals УСТРОЙСТВА, в issue 14191, я предлагаю использовать parse_known_args
только с optionals
, а затем parse_args
, который знает только о positionals. Функция parse_intermixed_args
, которую я разместил там, может быть реализована в подклассе ArgumentParser
без изменения самого кода argparse.py
.
Вот способ обработки подпарантов.Я взял функцию parse_known_intermixed_args
, упростил ее ради презентации, а затем сделал ее функцией parse_known_args
подкласса Parser. Мне пришлось сделать дополнительный шаг, чтобы избежать рекурсии.
Наконец-то я изменил _parser_class
подпараметров Action, поэтому каждый подпараметр использует эту альтернативу parse_known_args
. Альтернативой может быть подкласс _SubParsersAction
, возможно модифицирующий его __call__
.
from argparse import ArgumentParser
def parse_known_intermixed_args(self, args=None, namespace=None):
# self - argparse parser
# simplified from http://bugs.python.org/file30204/test_intermixed.py
parsefn = super(SubParser, self).parse_known_args # avoid recursion
positionals = self._get_positional_actions()
for action in positionals:
# deactivate positionals
action.save_nargs = action.nargs
action.nargs = 0
namespace, remaining_args = parsefn(args, namespace)
for action in positionals:
# remove the empty positional values from namespace
if hasattr(namespace, action.dest):
delattr(namespace, action.dest)
for action in positionals:
action.nargs = action.save_nargs
# parse positionals
namespace, extras = parsefn(remaining_args, namespace)
return namespace, extras
class SubParser(ArgumentParser):
parse_known_args = parse_known_intermixed_args
parser = ArgumentParser()
parser.add_argument('foo')
sp = parser.add_subparsers(dest='cmd')
sp._parser_class = SubParser # use different parser class for subparsers
spp1 = sp.add_parser('cmd1')
spp1.add_argument('-x')
spp1.add_argument('bar')
spp1.add_argument('vars',nargs='*')
print parser.parse_args('foo cmd1 bar -x one 8 9'.split())
# Namespace(bar='bar', cmd='cmd1', foo='foo', vars=['8', '9'], x='one')
О, это хорошая новость. Так вы говорите, что если я возьму две функции, которые вы добавили в http://bugs.python.org/file30422/intermixed.patch, и я реализую в своем подклассе, я смогу обойти проблему? Как насчет функции '_get_values ()', является ли необходимость в двух строках строго обязательной? Мне трудно понять, как используется «SUPPRESS». – rubik
О, нет, плохие новости только что прибыли. Я читал, что это несовместимо с подпарщиками, особенностью, которая мне абсолютно необходима. Это временное или просто невозможно реализовать? Инстинктивно я бы сказал, что это должно быть возможно, потому что subparser указан как второй аргумент, тогда разбор происходит нормально. Я ошибаюсь? – rubik
Я пробовал 2 разных способа временно отключить аргументы 'positional'. Первым было «nargs = 0». Второй - «nargs = SUPPRESS». Я использовал бы первый, если бы реализовал свой собственный подкласс. «SUPPRESS» имеет некоторые преимущества, но требует более глубоких изменений в 'argparse. – hpaulj