使用嵌套子解析器时,Python argparse创建不正确的用法字符串

问题描述

我想使用

enter image description here

模块工具来构建(复杂的)命令行参数解析器。 “主”脚本可以接受子命令,并且某些子命令也具有自己的子命令。这是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生成的帮助消息看起来正确:

$ ./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例程拆分为创建解析器的子函数,如下所示:

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

    r.add_argument("arg1",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",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()”)。

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

$ ./main.py -h
usage: main.py [-h] {c1,--help  show this help message and exit
$ ./main.py c2 -h
usage: main.py c2 [-h] {s1,--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,--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的解析器)被添加到{{ 1}}的子解析器方法。但是在第二种情况下,它们被添加到新的“ ArgumentParser”中。

解决方法

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

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。如果不提供,则argparsesys.argv[0]派生,对于子解析器,添加一些字符串以显示子解析器的名称(和必需的主参数)。该添加代码位于add_subparsesadd_parser中。

top_parser案件中添加类似的照片

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绕过该链接。

但是我可以为子解析器提供自己的prog

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

我们还可以提供自定义usage

我们可以创建parents而不是s2机制来创建c2,然后将其传递给创建其参数的函数:

c2

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) 作为向多个子解析器添加相同参数的一种方式(某种程度上)很有用,但在重构较大的内部版本时并不必要。