9

我开始学习 Python,现在我正在学习argparse. 使用argparse,我创建了两组参数:group_listgroup_simulate。每个组都有自己的参数——用户只能在每个组中指定一个参数(使用 实现parser.add_mutually_exclusive_group())。

现在,如果用户从两个 groupgs 而不是仅从其中一个指定参数,我的目标是出现语法错误——我想通过使用的功能argparse而不是通过编写询问是否指定 this 和 this 的方法来实现这一点打印语法错误。

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>

我试图通过add_subparsersfrom选项实现想要的目标,argparse但没有任何成功。

所以我的问题是如何实现这种情况?

4

3 回答 3

9

您可以使用一个共同的互斥组作为两个子组的“根”:

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
于 2013-02-02T10:40:59.370 回答
1

使用Docopt!您不必编写使用文档,然后花费数小时试图弄清楚如何让 argparse 为您创建它。如果您了解 POSIX,您就会知道如何解释使用文档,因为它是一个标准。Docopt 知道如何像您一样解释使用文档。我们不需要抽象层。

我认为 OP 未能根据我在他们的帮助文本中读到的内容来描述他们自己的意图。我将尝试推测他们正在尝试做什么。


测试.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 工具,其选项规则类似于您的需求和复制粘贴。

于 2016-05-12T05:58:04.110 回答
0

这个解析器的一个更简单的版本是

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(这里我替换了 a count)OP 设置为属性,list并且simulate. list具有默认值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 选项解析器,从而减少用户程序的工作量。一些程序可能只使用库导出的参数解析器,从而对库实现的抽象实现一致且高效的选项解析。

这听起来有点像argparsesubparser 机制。也就是说,有某种元解析器可以将操作委托给一个(或多个)子解析器。但是在argparse子解析器中必须由用户显式命名。

一个可能的扩展是让元解析器查看上下文。例如在 OP 案例中,如果它看到任何 [--list, -p, -m] 使用list子解析器,如果有任何simulate参数,则使用simulate子解析器。这可能会提供一些更强大的分组工具。并且有可能用 stock 实现那种东西argparse。你可以在同一个上创建和运行几个不同parserssys.argv.

于 2016-05-20T00:35:19.660 回答