【问题标题】:Python dependencies between groups using argparse使用 argparse 的组之间的 Python 依赖关系
【发布时间】:2013-01-17 15:01:15
【问题描述】:

我开始学习 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>

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

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

【问题讨论】:

  • 将所有互斥的 args 放在一个组中有什么区别?
  • @TheodrosZelleke 使用多个互斥组,例如,您可以将required 参数传递给其中的一些。拥有一个相互排斥的组你不能这样做。另一个优点是,如果使用多个互斥组,帮助消息会提供更多信息。此外,将来可能会提供有关互斥组的更多信息(例如标题和描述),因此它将显示更多信息帮助消息。
  • @Bakuriu -- 但是如果一个组是required 来提供一个参数,那不是立即排除所有其他组吗?
  • 我同意如果一个参数被定义为required,这将与add_mutually_exclusive 冲突——因为你必须指定这个参数并且不能指定其他论据,我是这样理解的。在上述情况下,我没有使用任何 required 参数。
  • 我不知道为什么这个答案被接受了。 OP 声明您应该能够恰好选择 -m, -p, --list 中的 1 个和 -M, -P, -I, --simulate 中的 1 个。这个答案没有这样做。

标签: python arguments argparse


【解决方案1】:

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

【讨论】:

  • 什么使用文档会产生这种期望的行为?到目前为止,我对 docopt 的销售宣传印象不深。
  • @hpaulj 因为 OP 接受了该答案,正如您所指出的,该答案在使用消息中存在不平衡的括号,并且具有将所有 7 个放入一组的净效果...我不确定是什么他们真的想要。这很难说,但我会试一试。
  • 这是否以argp 为模型,具有多个独立的解析器(每个使用行由不同的解析器处理)? Python 已经有了基于getopt 的解析器,要简单得多。
  • @hpaulj 我不确定内部是如何工作的。我只有在某些事情不起作用时才会深入研究。 argp 是什么?有什么比编写您想要显示的确切用户消息并直接解释它更容易的呢?
  • 我试图跟进您的说法,即知道POSIX 参数处理足以编写一个好的docopt 用法。搜索“posix 参数”将我带到一个 GNU C 库,它有 2 个解析器,getoptargpgnu.org/software/libc/manual/html_node/Argp.html
【解决方案2】:

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

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 只是捷径,并没有真正添加到定义中。在定义大量选项时,快捷方式可以方便,特别是如果选项可以一起使用(例如@ 987654335)。但这里只允许一个选项(-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

【讨论】:

    【解决方案3】:

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

    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
    

    【讨论】:

    • 这正是我想要的!非常感谢Bakuriu
    • 这样的嵌套组有效,但没有做任何重要的事情。当一个动作被添加到group_simulate 时,它也会被添加到root_group。它也被添加到parser 及其optional arguments 组。最终结果是所有 7 个动作都是互斥的。 usage 代码也不处理嵌套组。注意[[ 和嵌套组之间缺少|
    • 事实证明,虽然group_simulate 有一个指向root_groupcontainer 属性,但root_group 没有其嵌套组的列表。有一个_mutually_exclusive_groups 属性,但它在解析器和所有组(独占或非独占)之间共享(相同引用)。 bugs.python.org/issue10984 有一个带有格式化程序的补丁,可以显示重叠的独占组 - 但它独立显示每个组,而不是嵌套。
    猜你喜欢
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2010-10-16
    • 2016-09-10
    • 2010-11-18
    • 1970-01-01
    相关资源
    最近更新 更多