2011-12-29 1 views
11

Я работаю с argparse и пытаюсь смешать подкоманды и позиционные аргументы, и возникла следующая проблема.Python 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 

Почему это?

+0

Btw, кажется, [известная ошибка ] (http://bugs.python.org/issue9340), который был исправлен для последних версий Python. –

ответ

9

Сначала я думал так же, как 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'], что не то, что вы хотели.

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

+0

Обратите внимание, что, конечно, добавление подпарантов после того, как весь верхний уровень, как было предложено документацией jcollado и argparses, нарушит двусмысленность и будет работать по назначению! –

5

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

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

+0

Он, к сожалению, не работает. Помощь действительно выглядит так, как должна, но на практике - это не меняет процесс синтаксического анализа. – kcpr

6
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), а не позиционным аргументом.

+0

Что делать, если у субпараметра были позиционные аргументы? Как мы можем разрешить 'print (parser.parse_args (['subpositional', 'subparserarg']))' print: '# -> Пространство имен (positional = None)'? Это должно было бы сказать, что мы выбираем подкоманду «подпозиционирование», а аргумент «positional» является необязательным. Это возможно? – jmlopez

0

Это все еще беспорядок в 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

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

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