使用嵌套子解析器时,Python argparse 会创建不正确的用法字符串 [英] Python argparse creates incorrect usage string when using nested sub-parsers

查看:17
本文介绍了使用嵌套子解析器时,Python argparse 会创建不正确的用法字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 argparse 模块设施.'main' 脚本可以接受子命令,一些子命令也有自己的子命令.这是一个 MWE:

I would like to build a (complicated) command-line argument parser using argparse module facilities. The 'main' script can accept sub-commands, and some of the sub-commands also have their own sub-commands. Here is a MWE:

#!/usr/bin/env python3
import argparse

def arg_parser():
    parser = argparse.ArgumentParser()

    subparsers = parser.add_subparsers(required=True, dest="cmd")

    p = subparsers.add_parser("c1", help="Command 1")
    p.add_argument("foo", help="foo argument")
    p.add_argument("bar", help="bar argument")
    p.add_argument("-z", "--baz", help="baz argument")

    p = subparsers.add_parser("c2", help="Command 2")
    q = p.add_subparsers(required=True, dest="sub_command")

    r = q.add_parser("s1", help="Command 1 - Sub-command 1")
    r.add_argument("arg1", help="first argument")
    r.add_argument("-a", "--arg2", help="second argument")

    r = q.add_parser("s2", help="Command 1 - Sub-command 2")

    return parser


def main():
    args = arg_parser().parse_args()

    print(args)

if __name__ == "__main__":
    main()

到目前为止一切正常,argparse 生成的帮助信息看起来是正确的:

So far everything works well, and the help messages generated by argparse look correct:

$ ./main.py
usage: main.py [-h] {c1,c2} ...

positional arguments:
  {c1,c2}
    c1        Command 1
    c2        Command 2

optional arguments:
  -h, --help  show this help message and exit

$ ./main.py c2 -h
usage: main.py c2 [-h] {s1,s2} ...

positional arguments:
  {s1,s2}
    s1        Command 1 - Sub-command 1
    s2        Command 1 - Sub-command 2

optional arguments:
  -h, --help  show this help message and exit
$ ./main.py c2 s1 -h
usage: main.py c2 s1 [-h] [-a ARG2] arg1

positional arguments:
  arg1                  first argument

optional arguments:
  -h, --help            show this help message and exit
  -a ARG2, --arg2 ARG2  second argument

现在我想将长 arg_parser 例程拆分为创建解析器的子函数,如下所示:

Now I would like to split the long arg_parser routine into sub-functions that create the parser as follows:

def sc1_parser():
    r = argparse.ArgumentParser(add_help=False)

    r.add_argument("arg1", help="first argument")
    r.add_argument("-a", "--arg2", help="second argument")

    return r


def c1_parser():
    parser = argparse.ArgumentParser(add_help=None)

    parser.add_argument("foo", help="foo argument")
    parser.add_argument("bar", help="bar argument")
    parser.add_argument("-z", "--baz", help="baz argument")

    return parser


def c2_parser():

    r = argparse.ArgumentParser(add_help=False)

    q = r.add_subparsers(required=True, dest="sub_command")

    q.add_parser("s1", help="Command 1 - Sub-command 1",
                 parents=[sc1_parser()])
    q.add_parser("s2", help="Command 1 - Sub-command 2")

    return r

def top_parser():
    parser = argparse.ArgumentParser()

    subparsers = parser.add_subparsers(required=True, dest="cmd")

    subparsers.add_parser("c1", help="Command 1", parents=[c1_parser()])
    subparsers.add_parser("c2", help="Command 2", parents=[c2_parser()])

    return parser

(并且显然将调用 'arg_parser()' 替换为 'top_parser()').

(and obviously replace the call to 'arg_parser()' by 'top_parser()').

但是,在这种情况下,子命令s1"和s2"的帮助消息不正确:

In this case, however, the help messages for sub-commands 's1' and 's2' are incorrect:

$ ./main.py -h
usage: main.py [-h] {c1,c2} ...

positional arguments:
  {c1,c2}
    c1        Command 1
    c2        Command 2

optional arguments:
  -h, --help  show this help message and exit
$ ./main.py c2 -h
usage: main.py c2 [-h] {s1,s2} ...

positional arguments:
  {s1,s2}
    s1        Command 1 - Sub-command 1
    s2        Command 1 - Sub-command 2

optional arguments:
  -h, --help  show this help message and exit
$ ./main.py c2 s1 -h
usage: main.py s1 [-h] [-a ARG2] arg1

positional arguments:
  arg1                  first argument

optional arguments:
  -h, --help            show this help message and exit
  -a ARG2, --arg2 ARG2  second argument
$ ./main.py c2 s2 -h
usage: main.py s2 [-h]

optional arguments:
  -h, --help  show this help message and exit

知道为什么会这样吗?

我的猜测是在第一个版本(arg_parse() 例程)中,子命令解析器(s1s2 的解析器)) 被添加到从子解析器的 add_parser 方法返回的解析器中.但在第二种情况下,它们被添加到新的ArgumentParser"中.

My guess is that in the first version (arg_parse() routine) the sub-command parser (the parser for s1 and s2) is added to the a parser returned from add_parser method of a sub-parser. But In the second case they are added to a fresh 'ArgumentParser'.

推荐答案

如果我添加到你的第一个脚本中(返回之前):

If I add to your first script (before the return):

print("progs: ")
print("main: ", parser.prog)
print("   p: ", p.prog)
print("   r: ", r.prog)

运行是:

1319:~/mypy$ python3 stack63361458_0.py c2 s2 -h
progs: 
main:  stack63361458_0.py
   p:  stack63361458_0.py c2
   r:  stack63361458_0.py c2 s2
usage: stack63361458_0.py c2 s2 [-h]

optional arguments:
  -h, --help  show this help message and exit

usage 包括 prog.如果您不提供它,argparse 会从 sys.argv[0] 派生它,并且对于子解析器,添加一些字符串以显示子解析器的名称(和必需的主论据).该添加的代码位于 add_subparsesadd_parser 中.

usage includes the prog. If you don't provide it, argparse derives it from sys.argv[0], and for subparsers, adds on some strings to show the subparser's name (and required main arguments). Code for that addition is in add_subparses and add_parser.

top_parser 案例添加类似的打印

Adding similar prints to the top_parser case

1349:~/mypy$ python3 stack63361458_0.py c2 s2 -h
s2 prog:  stack63361458_0.py s2
top prog:  stack63361458_0.py
c2 prog:  stack63361458_0.py c2
usage: stack63361458_0.py s2 [-h]

optional arguments:
  -h, --help  show this help message and exit

c2 得到预期的 prog,但 s2.prog 是在没有知识"的情况下创建的;它将被 c2 子解析器调用.使用 parents 绕过该链接.

c2 gets the expected prog, but s2.prog is created without "knowledge" that it will be called by the c2 subparser. Using parents bypasses that link.

但是我可以为 sub-sub-parser 提供我自己的 prog:

But I could supply my own prog for the sub-sub-parser:

s2 = q.add_parser("s2", help="Command 1 - Sub-command 2", prog=f'MyProg {sys.argv[0]} c2 s2 ')

1358:~/mypy$ python3 stack63361458_0.py c2 s2 -h
s2 prog:  MyProg stack63361458_0.py c2 s2 
top prog:  stack63361458_0.py
c2 prog:  stack63361458_0.py c2
usage: MyProg stack63361458_0.py c2 s2  [-h]

optional arguments:
  -h, --help  show this help message and exit

我们还可以提供自定义的用法.

We can also supply a custom usage.

代替在 c2 之前创建 s2parents 机制,我们可以创建 c2 然后传递它到创建其参数的函数:

Instead of the parents mechanism which creates s2 before c2, we could create c2 and then pass that to the function that creates its arguments:

def c2_parser(q):
    r = q.add_parser("s1", help="Command 1 - Sub-command 1")
    r.add_argument("arg1", help="first argument")
    r.add_argument("-a", "--arg2", help="second argument")

    r = q.add_parser("s2", help="Command 1 - Sub-command 2")

main中:

    p = subparsers.add_parser("c2", help="Command 2")
    c2_parser(p)

parents 作为向多个子解析器添加相同参数的一种方式(有点)有用,但在重构较大的构建时不是必需的.

parents are (somewhat) useful as a means of adding the same arguments to multiple subparsers, but aren't necessary when just refactoring larger builds.

这篇关于使用嵌套子解析器时,Python argparse 会创建不正确的用法字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆