Python:正确处理子命令的全局选项的参数解析器

Posted

技术标签:

【中文标题】Python:正确处理子命令的全局选项的参数解析器【英文标题】:Python: argument parser that handles global options to sub-commands properly 【发布时间】:2012-06-09 09:44:44 【问题描述】:

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参数解析器可以正确处理子命令的全局选项吗?

【问题讨论】:

“失败”是什么意思?你想发生什么?有哪些全局选项? 失败,因为抱怨未知参数 --arg 【参考方案1】:

Give docopt a try:

>>> 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

【讨论】:

@Roony,太棒了!感谢您的反馈,欢迎提供更多反馈。【参考方案2】:

您可以轻松地将此参数添加到两个解析器(主解析器和子命令解析器):

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

更多示例/文档:

looking for best way of giving command line arguments in python, where some params are req for some option and some params are req for other options

Python argparse - Add argument to multiple subparsers(我不确定这个问题是否与这个问题重叠太多)

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

【讨论】:

如果有更多可选项,可以在parent 解析器中定义它们,并通过parents 参数添加到ps。 ***.com/a/18346152/901925 有一个向许多子解析器添加相同的选项集的示例。 @hpaulj - 我认为这是更好的答案!我要更新我的回复... 在 Python 2.7.16 上,第二个示例在第一种情况下打印 False,我相信这根本不是所需的行为?因此,我认为第二个经过编辑的解决方案是不正确的。【参考方案3】:

Python 世界中有大量的参数解析库。以下是我见过的一些,所有这些都应该能够解决您试图解决的问题(基于我上次玩它们时对它们的模糊回忆):

opster—我认为这就是 mercurial 的用途,IIRC docopt——这个是新的,但使用了一种有趣的方法 cliff——这是 Doug Hellmann(PSF 成员,virtualenvwrapper 作者,普通黑客非凡)的一个相对较新的项目,它不仅仅是一个参数解析器,而是从头开始设计用于处理多级命令 clint——另一个旨在成为“参数解析等”的项目,这个项目由 Kenneth Reitz(以 Requests 闻名)。

【讨论】:

哇——我没有意识到有这么多选择——我想这可能是因为我总是能够哄 argparse 提交。 +1 用于找到所有这些... 我做了一个快速调查,docopt 和 clint 不会做悬崖是一件非常奇怪和复杂的事情,我不想理解 opster 看起来很有趣,尝试一下 @Ronny,docopt 应该可以工作——你可以添加[options] 快捷方式,它允许传递全局选项而不管子命令层次结构。 @Ronny 即'test --arg' 将在您指定模式my_program test [options]my_program test [--arg] 的情况下被识别。【参考方案4】:

这是一个肮脏的解决方法--

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 并用 my_parse_args 替换 parse_args 方法,然后你永远不会知道其中的区别——尽管用子类替换单个方法对我来说似乎有点矫枉过正。

然而,我认为这是使用子解析器的一种非标准方式。一般来说,全局选项应该出现在子解析器选项之前,而不是之后。

【讨论】:

【参考方案5】:

解析器有一个特定的语法:command <global options> subcommand <subcommand ptions>,您正在尝试为子命令提供一个选项,但您没有定义一个选项。

【讨论】:

我认为 Ronny 知道失败的原因(或者至少他不在乎)——他正在寻找解决方法(使用 argparse 或其他方法)。

以上是关于Python:正确处理子命令的全局选项的参数解析器的主要内容,如果未能解决你的问题,请参考以下文章

[Python系列-25]:argparse --- 命令行选项参数解析器

argparse 未正确处理子解析器中的缩写

python-argparse.ArgumentParser()用法解析

带有通用子解析器命令的 Python argparse

Python Argparse 子解析器

Python 选项解析器:带有可选参数的布尔标志