2017-01-02 7 views
1

В настоящее время я тестирую использование argparse, но он не работает должным образом. У меня есть пара subparsers и необязательные аргументы, называют следующим образом:Дополнительные аргументы по всем подпараметрам

python3 myprogram.py positional argument --optional something 

# Outcome 
Namespace(optional='something') 

Программа работает как ожидается, если опция является последним, но если он находится в любом другом порядке, то отбрасываются.

python3 myprogram.py positional --optional argument 
python3 myprogram.py --optional positional argument 

# Outcome 
Namespace(optional=None) 

Глядя на документации argparse я не смог найти способ сделать дополнительный аргумент глобального.

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

import argparse 

class Parsing(object): 

    def __init__(self): 

     parser = argparse.ArgumentParser(prog='python3 myprogram.py', 
      formatter_class=argparse.RawDescriptionHelpFormatter, 
      description='some description') 

     self.subparser = parser.add_subparsers(title='Positional', help='help description') 

     for sub in self.Generate(): # Method with a bunch of subparsers 
      self.Subparser(sub) 

    def Subparser(self, parsers): 

     for each in sorted(parsers): 
      positional = subparser.add_parser(each) 
      self.Optional(positional) # Method with some optional arguments for each of the second subparsers 

     self.Optional(parser) # Adding the optional arguments to the first subparser 

    def Optional(self, parser): 

     # ... Optional arguments 

    def Generate(self): 

     # ... Subparsers 

я, возможно, отсутствует какой-то код в приведенном выше примере, попытался упростить все, что мог и надеяться, что это будет ощутимо.

Вопрос: Есть ли способ сделать необязательные аргументы для всех подпараметров?

ответ

2

Ваше описание и код трудно поддаются, но я пришел к выводу, что ваша проблема заключается в том, как обрабатываются значения по умолчанию, когда основной и подпанели разделяют аргумент 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.

+0

Благодарим вас за подробный ответ Hpaulj, позвольте мне проверить его, чтобы он соответствовал моему коду. – Flippym

+0

Если я правильно понял, 'argparse' не поддерживает глобальные аргументы, я должен был бы контролировать их с помощью' dest', в каждом подпараметре, справа ? – Flippym

+0

Я добавил пример с использованием 'parse_known_args' и второго сеанса синтаксического анализа. – hpaulj

 Смежные вопросы

  • Нет связанных вопросов^_^