Python: анализатор аргументов, который правильно обрабатывает глобальные опции для подкоманд

argparse не работает с подкомандами, получающими глобальные параметры:

import argparse
p = argparse.ArgumentParser()
p.add_argument('--arg', action='store_true')
s = p.add_subparsers()
s.add_parser('test')

буду иметь p.parse_args('--arg test'.split()) Работа,
но терпит неудачу на p.parse_args('test --arg'.split()),

Кто-нибудь знает о парсере аргументов Python, который правильно обрабатывает глобальные опции для подкоманд?

5 ответов

Решение

Дайте докопту попробовать:

>>> from docopt import docopt

>>> usage = """
... usage: prog.py command [--test]
...        prog.py another [--test]
... 
... --test  Perform the test."""

>>> docopt(usage, argv='command --test')
{'--test': True,
 'another': False,
 'command': True}

>>> docopt(usage, argv='--test command')
{'--test': True,
 'another': False,
 'command': True}

Вы можете легко добавить этот аргумент для обоих анализаторов (основной анализатор и анализатор подкоманд):

import argparse                                                                  

main = argparse.ArgumentParser()                                                    
subparser = main.add_subparsers().add_parser('test')                                        

for p in [main,subparser]:                                                                  
   p.add_argument('--arg', action='store_true')                                 

print main.parse_args('--arg test'.split()).arg                                     
print main.parse_args('test --arg'.split()).arg

Редактировать: как указал @hpaulj в комментарии, есть также parents аргумент, который вы можете передать ArgumentParser конструктор или add_parser метод. Вы можете перечислить в этом значении парсеры, которые являются основой для нового.

import argparse

base = argparse.ArgumentParser(add_help=False)
base.add_argument('--arg', action='store_true')

main = argparse.ArgumentParser(parents=[base])
subparser = main.add_subparsers().add_parser('test', parents=[base])

print main.parse_args('--arg test'.split()).arg
print main.parse_args('test --arg'.split()).arg

Больше примеров / документов:

ищет лучший способ предоставления аргументов командной строки в python, где некоторые параметры требуются для некоторых параметров, а некоторые параметры требуются для других параметров

Python argparse - добавить аргумент в несколько подпарсеров (я не уверен, что этот вопрос не слишком пересекается с этим)

http://docs.python.org/dev/library/argparse.html

В мире Python есть масса разборов аргументов. Вот некоторые из них, которые я видел, и все они должны быть в состоянии решить проблему, которую вы пытаетесь решить (основываясь на моем нечетком воспоминании о них, когда я играл с ними в последний раз):

  • opster - я думаю, что это то, что использует ртуть, IIRC
  • docopt- это новый, но использует интересный подход
  • cliff- это относительно новый проект Дуга Хеллманна (член PSF, автор virtualenvwrapper, общий экстраординарный хакер) - это нечто большее, чем просто анализатор аргументов, но с самого начала он предназначен для обработки многоуровневых команд
  • Клинт - еще один проект, целью которого является "разбор аргументов и многое другое", этот - Кеннет Рейтц (из Requests Fame).

Вот грязный обходной путь -

import argparse
p = argparse.ArgumentParser()
p.add_argument('--arg', action='store_true')
s = p.add_subparsers()
s.add_parser('test')

def my_parse_args(ss):
    #parse the info the subparser knows about; don't issue an error on unknown stuff
    namespace,leftover=p.parse_known_args(ss) 
    #reparse the unknown as global options and add it to the namespace.
    if(leftover):
        s.add_parser('null',add_help=False)
        p.parse_args(leftover+['null'],namespace=namespace)

    return namespace

#print my_parse_args('-h'.split())  #This works too, but causes the script to stop.
print my_parse_args('--arg test'.split())
print my_parse_args('test --arg'.split())

Это работает - и вы можете довольно легко изменить его, чтобы работать с sys.argv (просто удалите разделенную строку "ss"). Вы могли бы даже подкласс argparse.ArgumentParser и заменить parse_args метод с my_parse_args и тогда вы никогда не узнаете разницу - хотя подклассы для замены одного метода кажутся мне излишними.

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

Парсер имеет определенный синтаксис: command <global options> subcommand <subcommand ptions>, вы пытаетесь передать подкоманде опцию, но вы ее не определили.

Другие вопросы по тегам