【问题标题】:How to share arguments between the toplevel and a subcommand如何在顶层和子命令之间共享参数
【发布时间】:2021-11-19 15:03:09
【问题描述】:

在使用带有子命令的argparse 的程序中,我想添加参数来控制详细程度。我希望这些充当“全局”参数,这意味着它们是在子命令之前还是之后传递都无关紧要。换句话说,我希望以相同的方式解释以下调用:

> ./test.py -v -v foo
> ./test.py foo -v -v
> ./test.py -v foo -v

我尝试过的事情

将参数添加到父解析器和子命令解析器:

main = arg.ArgumentParser()
main.add_argument ("-v", "--verbose", default = 0, action = "count")
s = main.add_subparsers()
foo = s.add_parser ("foo")
foo.add_argument ("-v", "--verbose", default = 0, action = "count")

print (main.parse_args().verbose)

这给出(都应该是2):

Invocation Result
./test.py -v -v foo 0
./test.py foo -v -v 2
./test.py -v foo -v 1

向主命令解析器和子命令解析器添加一个通用的“父”解析器:

this question 启发,不同之处在于在另一个问题中一次只有一个子命令处于活动状态,而这里的主解析器和子命令解析器同时处于活动状态。

p = arg.ArgumentParser (add_help = False)
p.add_argument ("-v", "--verbose", default = 0, action = "count")
main = arg.ArgumentParser (parents = [ p ])
s = main.add_subparsers()
foo = s.add_parser ("foo", parents = [ p ])

print (main.parse_args().verbose)

这给出了与上面相同的结果。

将主解析器声明为子命令解析器的父级:

main = arg.ArgumentParser()
main.add_argument ("-v", "--verbose", default = 0, action = "count")
s = main.add_subparsers()
foo = s.add_parser ("foo", parents = [ main ], add_help = False)

print (main.parse_args().verbose)

这会导致foo 子命令本身期望的无限递归:

> ./test.py -v -v foo
usage: test.py foo [-h] [-v] {foo} ...
test.py foo: error: too few arguments

将参数添加到具有不同目的地的父和子命令解析器:

main = arg.ArgumentParser()
main.add_argument ("-v", "--verbose", default = 0, action = "count", dest = "main_verbose")
s = main.add_subparsers()
foo = s.add_parser ("foo")
foo.add_argument ("-v", "--verbose", default = 0, action = "count")

args = main.parse_args()
print (args.main_verbose + args.verbose)

这行得通,但它增加了复杂性:现在我需要组合每个公共参数的值,以及与嵌套子命令一样多的层。

有没有一种方法可以定义“全局”参数,无论它们放在命令行的什么位置,它的作用都是一样的?

【问题讨论】:

  • 别偷懒。使用最后一个有效的

标签: python argparse


【解决方案1】:

主解析器和子解析器不“共享”参数。每个都是一个独立的解析器,有自己的参数列表。 parents 机制只是将参数定义(通过引用)从父级复制到子级。它不会改变解析。

主解析器将值放在其命名空间中,直到调用子解析器为止。子解析器做它的事情,解析剩余的字符串并将值放在它自己的命名空间中。完成后,子解析器命名空间被复制到主命名空间。

结果是对于任何具有公共dest 的参数,子解析器值(包括默认值)会覆盖主解析器的值。因此,如果您想查看主要设置,您可以使用不同的dest

【讨论】:

    【解决方案2】:

    我使用过的一个具有这种行为的工具是AWS CLI v2。使用该命令,您可以运行

    aws --profile myprofile s3 ls s3://mybucket
    aws s3 ls s3://mybucket --profile myprofile
    

    你会得到同样的结果。

    在仔细阅读 aws cli v2 源代码后,主要是 argparser.pyclidriver.py,似乎他们创建了一个主解析器和服务解析器。主解析器处理aws [options] command subcommand ,服务解析器处理aws s3 command。不过,服务解析器不是主解析器的父级。

    他们首先用主解析器解析参数,然后将剩余的参数传递给服务解析器。这是一个适用于您的问题的示例。

    import argparse
    
    main = argparse.ArgumentParser()
    main.add_argument("-v", "--verbose", default=0, action="count")
    main.add_argument("command")
    sub = argparse.ArgumentParser()
    sub.add_argument("operation")
    
    
    def test_parser(cli_string):
        cli_args = cli_string.split()
        parsed_args, remaining = main.parse_known_args(cli_args)
        sub_args, remaining = sub.parse_known_args(remaining)
        print("Verbose:", parsed_args.verbose)
        print("Subcommand:", sub_args.operation)
    
    calls = ["./test.py foo -v -v", "./test.py -v foo -v", "./test.py -v -v foo"]
    
    for c in calls:
        print(c)
        test_parser(c)
        print()
    

    导致

    ./test.py foo -v -v
    Verbose: 2
    Subcommand: foo
    
    ./test.py -v foo -v
    Verbose: 2
    Subcommand: foo
    
    ./test.py -v -v foo
    Verbose: 2
    Subcommand: foo
    

    不过,我不确定您可能还需要哪些其他功能以及它将如何与此模式交互。

    【讨论】:

    • 有趣的方法。缺点是--help 不会显示子命令,因为它会被main 拦截(或者如果我们将add_help = False 添加到main 定义中,它将不会显示全局选项)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-15
    • 1970-01-01
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    相关资源
    最近更新 更多