使用 argparse 的组之间的 Python 依赖关系



我开始学习Python,现在我正在学习argparse的巨大好处。使用argparse,我创建了两组参数:group_listgroup_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 设置为属性、listsimulatelist的默认值为 all ,可以设置为 modulesports-m-p只是捷径,并没有真正增加定义。 在定义许多选项时,快捷方式会很方便,特别是如果选项可以一起使用(例如 -vpm )。 但是这里只允许一个选项(除了-v)。

simulate采用不受约束的字符串。 M/P/C选项只是文档上的便利,不会限制值或添加含义。

这是突破argparse(或任何其他解析器)界限的一个很好的练习,但我认为它太复杂了,没有用。 尽管有所有分组,但归根结底只允许一种选择。

====

=========================

关于docoptPOSIX参数处理的评论促使我查看 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

相关内容

  • 没有找到相关文章

最新更新