使用嵌套子解析器时,Python argparse创建不正确的用法字符串 [英] Python argparse creates incorrect usage string when using nested sub-parsers
问题描述
我想使用 <代码构建一个(复杂的)命令行参数解析器> 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屋!