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
参数添加到p
和s
。 ***.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 --- 命令行选项参数解析器