我开始学习Python,现在我正在学习argparse
的巨大好处。使用argparse
,我创建了两组参数:group_list
和group_simulate
。每个组都有自己的参数 - 用户只能在每个组中指定一个参数(使用 parser.add_mutually_exclusive_group()
实现)。
现在,如果用户指定了来自两个 groupg 而不是仅来自其中一个的参数,我的目标是出现语法错误——我想通过使用 argparse
的功能来实现这一点,而不是通过编写一个询问是否指定了这个和这个的方法打印语法错误。
import argparse
parser = argparse.ArgumentParser(
description='this is the description',
epilog="This is the epilog",
argument_default=argparse.SUPPRESS
)
parser.add_argument('-v', '--verbose', help='verbose', action='store_true', default=False)
group_list = parser.add_mutually_exclusive_group()
group_list.add_argument('-m', help='list only modules', action='store_const', dest='list', const='modules', default='all')
group_list.add_argument('-p', help='list only ports', action='store_const', dest='list', const='ports', default='all')
group_list.add_argument('--list', help='list only module or ports', choices=['modules','ports'], metavar='<modules/ports>', default='all')
group_simulate = parser.add_mutually_exclusive_group()
group_simulate.add_argument('-M', help='simulate module down', nargs=1, metavar='module_name', dest='simulate')
group_simulate.add_argument('-P', help='simulate FC port down', nargs=1, metavar='fc_port_name', dest='simulate')
group_simulate.add_argument('-I', help='simulate iSCSI port down', nargs=1, metavar='iSCSI_port_name', dest='simulate')
group_simulate.add_argument('--simulate', help='simulate module or port down', nargs=1, dest='simulate')
args = parser.parse_args()
print args
所以更具体地说:
允许:
test.py
output: Namespace(list='all', verbose=False)
test.py -m
output: Namespace(list='modules', verbose=False)
test.py -P asfasf
output: Namespace(P=['asfasf'], list='all', verbose=False)
不允许:
test.py -m -P asfsaf
expected output: <the help message>
test.py -P asfasf -m
expected output: <the help message>
我试图通过选择argparse
add_subparsers
来实现想要的目标,但没有任何成功。
所以我的问题是如何实现这种情况?
您可以使用一个共同的互斥组作为两个子组的"根":
import argparse
parser = argparse.ArgumentParser(
description='this is the description',
epilog="This is the epilog",
argument_default=argparse.SUPPRESS
)
parser.add_argument('-v', '--verbose', help='verbose', action='store_true', default=False)
root_group = parser.add_mutually_exclusive_group()
group_list = root_group.add_mutually_exclusive_group()
group_list.add_argument('-m', help='list only modules', action='store_const', dest='list', const='modules', default='all')
group_list.add_argument('-p', help='list only ports', action='store_const', dest='list', const='ports', default='all')
group_list.add_argument('--list', help='list only module or ports', choices=['modules','ports'], metavar='<modules/ports>', default='all')
group_simulate = root_group.add_mutually_exclusive_group()
group_simulate.add_argument('-M', help='simulate module down', nargs=1, metavar='module_name', dest='simulate')
group_simulate.add_argument('-P', help='simulate FC port down', nargs=1, metavar='fc_port_name', dest='simulate')
group_simulate.add_argument('-I', help='simulate iSCSI port down', nargs=1, metavar='iSCSI_port_name', dest='simulate')
group_simulate.add_argument('--simulate', help='simulate module or port down', nargs=1, dest='simulate')
args = parser.parse_args()
print args
结果:
$ python test.py -m -P asfafs
usage: test.py [-h] [-v] [[-m | -p | --list <modules/ports>]
[-M module_name | -P fc_port_name | -I iSCSI_port_name | --simulate SIMULATE]
test.py: error: argument -P: not allowed with argument -m
$ python test.py -m -p
usage: test.py [-h] [-v] [[-m | -p | --list <modules/ports>]
[-M module_name | -P fc_port_name | -I iSCSI_port_name | --simulate SIMULATE]
test.py: error: argument -p: not allowed with argument -m
使用 Docopt!您不必编写一个使用文档,然后花费数小时试图弄清楚如何让 argparse 为您创建它。如果您了解 POSIX,您就知道如何解释使用文档,因为它是一个标准。Docopt知道如何像您一样解释使用文档。我们不需要抽象层。
我认为OP未能根据我在他们的帮助文本中读到的内容来描述自己的意图。我将尝试推测他们试图做什么。
test.py
"""
usage: test.py [-h | --version]
test.py [-v] (-m | -p)
test.py [-v] --list (modules | ports)
test.py [-v] (-M <module_name> | -P <fc_port_name> | -I <iSCSI_port_name>)
this is the description
optional arguments:
-h, --help show this help message and exit
-v, --verbose verbose
-m list only modules (same as --list modules)
-p list only ports (same as --list ports)
--list list only module or ports
-M module_name simulate module down
-P fc_port_name simulate FC port down
-I iSCSI_port_name simulate iSCSI port down
This is the epilog
"""
from pprint import pprint
from docopt import docopt
def cli():
arguments = docopt(__doc__, version='Super Tool 0.2')
pprint(arguments)
if __name__ == '__main__':
cli()
虽然可以使用复杂的嵌套条件在一行中传达所有用法,但这更清晰。这就是为什么docopt如此有意义。对于 CLI 程序,您希望确保清楚地与用户通信。为什么要学习一些晦涩难懂的模块语法,希望你能说服它为你创建与用户的通信?花点时间看看其他POSIX工具,其选项规则与您的需求和复制粘贴相似。
这个解析器的更简单版本是
parser=argparse.ArgumentParser(description="this is the description",
epilog='this is the epilog')
parser.add_argument('-v', '--vebose', action='count')
g1=parser.add_mutually_exclusive_group()
g1.add_argument('--list', help='list module or ports (default=%(default)s)', choices=['modules','ports','all'], default='all')
g1.add_argument('--simulate', '-M','-P','-C', help='simulate [module down/ FS port down/ iSCSI port down]', dest='simulate', metavar='module/port')
在看起来像这样的帮助下:
usage: stack14660876.py [-h] [-v]
[--list {modules,ports,all} | --simulate module/port]
this is the description
optional arguments:
-h, --help show this help message and exit
-v, --vebose
--list {modules,ports,all}
list module or ports (default=all)
--simulate module/port, -M module/port, -P module/port, -C module/port
simulate [module down/ FS port down/ iSCSI port down]
this is the epilog
在 verbose
旁边(这里我替换了一个count
),OP 设置为属性、list
和 simulate
。 list
的默认值为 all
,可以设置为 modules
或 ports
。 -m
和-p
只是捷径,并没有真正增加定义。 在定义许多选项时,快捷方式会很方便,特别是如果选项可以一起使用(例如 -vpm
)。 但是这里只允许一个选项(除了-v
)。
simulate
采用不受约束的字符串。 M/P/C
选项只是文档上的便利,不会限制值或添加含义。
这是突破argparse
(或任何其他解析器)界限的一个很好的练习,但我认为它太复杂了,没有用。 尽管有所有分组,但归根结底只允许一种选择。
====
=========================关于docopt
和POSIX
参数处理的评论促使我查看 C 参数库。 getopt
是旧标准。 Python有一个功能等价物,https://docs.python.org/2/library/getopt.html
GNU 库中的另一个解析器是 argp
。
http://www.gnu.org/software/libc/manual/html_node/Argp.html
我还没有看到它添加到getopt
语法中的清晰描述。 但下面一段很有趣。
Argp 还提供了将多个独立定义的选项解析器合并为一个选项解析器的能力,调解它们之间的冲突并使结果看起来无缝。库可以导出 argp 选项解析器,用户程序可以将其与自己的选项解析器结合使用,从而减少用户程序的工作量。某些程序可能仅使用库导出的参数解析器,从而为库实现的抽象实现一致且高效的选项解析。
这听起来有点像argparse
子解析器机制。 也就是说,有某种元解析器可以将操作委托给一个(或多个)子解析器。 但是在argparse
子解析器必须由用户显式命名。
一个可能的扩展是让元解析器查看上下文。 例如,在 OP 情况下,如果它看到 [--list, -p, -m] 中的任何一个,请使用 list
子解析器,如果有任何simulate
参数,请使用 simulate
子解析器。 这可能会提供一些更强大的分组工具。 并且有可能用股票argparse
实现这种事情。 您可以在同一sys.argv
上创建和运行多个不同的parsers
。