Ваше описание и код трудно поддаются, но я пришел к выводу, что ваша проблема заключается в том, как обрабатываются значения по умолчанию, когда основной и подпанели разделяют аргумент dest
.
Я конденсируется свой код немного, так что я мог бы сделать пробный запуск:
import argparse
class Parsing(object):
def __init__(self):
self.parser = argparse.ArgumentParser(prog='prog',
description='some description')
self.subparser = self.parser.add_subparsers(dest='cmd', title='Cmds', help='help description')
self.make_subparsers(['cmd1','cmd2'])
def make_subparsers(self, parsers):
for each in parsers:
subp = self.subparser.add_parser(each)
self.optional(subp, default='sub')
self.optional(self.parser, default='main')
def optional(self, parser, default=None):
parser.add_argument('--foo', default=default)
args = Parsing().parser.parse_args()
print(args)
Я получаю 2 пробегов
1315:~/mypy$ python3.5 stack41431025.py cmd1 --foo 1
Namespace(cmd='cmd1', foo='1')
1316:~/mypy$ python3.5 stack41431025.py --foo 1 cmd1
Namespace(cmd='cmd1', foo='sub')
В первом, foo
задается струнам помощью синтаксического анализа cmd1
subparser.
Во втором, foo
получает значение по умолчанию, установленное подпараметром. Основной синтаксический анализатор разобрал --foo
, но его значение было написано субпараметром.
Обсуждалось это с ошибкой/проблемами. http://bugs.python.org/issue9351 изменил обработку таким образом, чтобы по умолчанию субпараметр имел приоритет над значениями основного парсера. Я думаю, что есть проблемы с этим патчем, но это действует уже пару лет.
Вы сохраняете больше контроля, если им заданы разные dest
.
def make_subparsers(self, parsers):
for each in parsers:
subp = self.subparser.add_parser(each)
self.optional(subp, default='sub')
self.optional(self.parser, default='main', dest='main_foo')
def optional(self, parser, default=None, dest=None):
parser.add_argument('--foo', default=default, dest=dest)
1325:~/mypy$ python3.5 stack41431025.py --foo 1 cmd1
Namespace(cmd='cmd1', foo='sub', main_foo='1')
1325:~/mypy$ python3.5 stack41431025.py cmd1
Namespace(cmd='cmd1', foo='sub', main_foo='main')
1325:~/mypy$ python3.5 stack41431025.py --foo 1 cmd1 --foo 2
Namespace(cmd='cmd1', foo='2', main_foo='1')
====================
(ранее ответ)
Я попытаюсь обрисовать возможные комбинации аргументов
parser = argparse.ArgumentParser()
parser.add_argument('mainpos', help='positional for main')
parser.add_argument('--mainopt', help='optional defined for main')
sp = parser.add_subparser(dest='cmd')
p1 = sp.add_parser('cmd1')
p1.add_argument('subpos', help='postional for sub')
p1.add_argument('--subopt', help='optional defined for sub')
Составной usage
будет выглядеть так:
python prog.py foo [--mainopt bar] cmd1 sfoo [--subopt baz]
Соответствующие positionals
должны быть указаны в правильном порядке. Подпараметр cmd
фактически является позиционным для main
.
Необязательный, определенный для основного, должен произойти до имени подпараметра. Необязательный, определенный для подпарамера, должен произойти после. Они могут иметь одинаковые flag
или dest
, но они должны быть определены отдельно. И если у них одинаковые dest
, может возникнуть конфликт над значениями, особенно значения по умолчанию.
parser.parse_args()
начинает сопоставление входных строк с аргументами. Если он видит, что --mainopt
анализирует этот необязательный аргумент. В противном случае он ожидает двух постов. Второй должен быть одним из подпаранных имен.
Как только он получает имя подпараметра, он передает оставшиеся строки этому подпараметру. Подпараметр обрабатывает остальное и помещает значения в основное пространство имен. И первое, что делает subparser, устанавливается по умолчанию. Независимо от того, перезаписывает ли это действие значения, заданные основным синтаксическим анализатором, или нет, зависит только от того, как передается namespace
между ними.
================
Синтаксический приводится порядок аргументов в командной строке. Он пытается разрешить помеченные аргументы в любом порядке. Но как только синтаксический анализ передается подпараметру, главный синтаксический анализатор не получает другого анализа при анализе. Он просто выполняет несколько задач очистки.
Но если я использую parse_known_args
, я могу собрать строки, которые не обработал ни один из парсеров, и сделать еще один удар по их разборке.
parser1 = argparse.ArgumentParser()
parser1.add_argument('--foo')
sp = parser1.add_subparsers(dest='cmd')
sp1 = sp.add_parser('cmd1')
args, extra = parser1.parse_known_args()
parser2 = argparse.ArgumentParser()
parser2.add_argument('--foo')
if extra:
args = parser2.parse_args(extra)
print(args)
работает
1815:~/mypy$ python stack41431025.py --foo 1 cmd1
Namespace(cmd='cmd1', foo='1')
1815:~/mypy$ python stack41431025.py cmd1 --foo 2
Namespace(foo='2')
1815:~/mypy$ python stack41431025.py --foo 1 cmd1 --foo 3
Namespace(foo='3')
Я не проверял эту идею в чем более сложной, так что могут быть некоторые взаимодействия, которые я не подумал. Но это самый близкий я могу прийти к помеченному аргументу, который может произойти где угодно, и не подвергать конфликтующим проблемам default
.
Благодарим вас за подробный ответ Hpaulj, позвольте мне проверить его, чтобы он соответствовал моему коду. – Flippym
Если я правильно понял, 'argparse' не поддерживает глобальные аргументы, я должен был бы контролировать их с помощью' dest', в каждом подпараметре, справа ? – Flippym
Я добавил пример с использованием 'parse_known_args' и второго сеанса синтаксического анализа. – hpaulj