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

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

问题描述

我想使用 <代码构建一个(复杂的)命令行参数解析器> argparse 模块工具.主"脚本可以接受子命令,并且某些子命令也具有自己的子命令.这是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()例程)中,子命令解析器( s1 s2 的解析器)添加到从子解析器的 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

用法包括 prog .如果您不提供它,则 argparse 是从 sys.argv [0] 派生的,对于子解析器,请添加一些字符串以显示子解析器的名称(以及必需的main参数).用于添加的代码在 add_subparses add_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.

但是我可以为子解析器提供我自己的 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 之前创建 s2 parents 机制,我们可以创建 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天全站免登陆