argparse:声明全局参数后无法获得 subparser_name

argparse: unable to get subparser_name after declaring a global argument

我有一个带有一些子解析器的解析器。我设置了一个全局参数以用于所有子解析器。这是相关的片段

parser = argparse.ArgumentParser(prog="my_prog", add_help=False)
parser.add_argument('-d', '--debug', action='store_true', help='debug flag')

subparsers =  parser.add_subparsers(dest="subparser_name", help='some help notes')

parser_cmd1 = subparsers.add_parser('cmd1', parents=[parser])
parser_cmd1.add_argument('-f', '-foo', type=str, action=foo, required=False, help='foo command')

parser_cmd2 = subparsers.add_parser('cmd2', parents=[parser])
parser_cmd2.add_argument('-b', '-bar', type=str, action=bar, required=False, help='bar command')

args = parser.parse_args()
parser = args.subparser_name

print(args)

if args.debug:
    logging.basicConfig(level=logging.INFO)

if parser == 'cmd1':
    if args.foo:
        //do foo stuff

if parser == 'cmd2':
    if args.bar:
        //do bar stuff

因此您可以使用这样的命令 my_prog.py cmd1 -d -f inp_str。问题是:subparser_name 是 None。 print(args) 的输出看起来有点像这样

Namespace(debug=True, foo="inp_str", subparser_name=None)

在我添加全局调试参数之前,subparser_name 将是我 运行 命令的名称,即 'cmd1' 或 'cmd2'。现在,它是 'None'。即使在子解析器创建中添加了 parents=[parser] 。我怎样才能解决这个问题?我怎么知道调用了哪个命令?

子解析器的默认值优先于主解析器设置的任何值 - 默认值或用户输入。 main 将 set the subparser_name 改为 'cmd1',但 subparser 将其更改回默认值 None

虽然在您的测试用例中不明显,但在两个级别定义 debug 存在相同的问题。子解析器的默认值会覆盖主解析器中设置的任何内容。

一般来说,在主解析器和子解析器中使用相同的 dest 并不是一个好主意。标志可以相同,但 dest 应该不同 - 至少如果您想查看 main 设置的任何内容。

并且使用主解析器作为子解析器的 parent,只是在自找麻烦。

将公共参数拆分为单独的 ArgumentParser,然后将其用作子解析器的父级。此外,您的 foo 和 bar 选项是使用 -foo 和 -bar 指定的,而应该是 --foo 和 --bar。此外,您没有这些的默认值,例如当未指定 -f/--foo 时 args.foo 正确地不存在。

这个效果更好:

import argparse

common_args = argparse.ArgumentParser(prog="my_prog", add_help=False)
common_args.add_argument('-d', '--debug', action='store_true', help='debug flag')

parser = argparse.ArgumentParser(prog="my_prog", add_help=True)

subparsers =  parser.add_subparsers(dest="subparser_name", help='some help notes')

parser_cmd1 = subparsers.add_parser('cmd1', parents=[common_args])
parser_cmd1.add_argument('-f', '--foo', type=str, default='', required=False, help='foo command')

parser_cmd2 = subparsers.add_parser('cmd2', parents=[common_args])
parser_cmd2.add_argument('-b', '--bar', type=str, default='', required=False, help='bar command')

args = parser.parse_args()
parser = args.subparser_name

print(args)

if args.debug:
    logging.basicConfig(level=logging.INFO)

if parser == 'cmd1':
    if args.foo:
        #//do foo stuff
        print( f"foo {args.foo}" )

if parser == 'cmd2':
    if args.bar:
        #//do bar stuff
        print( f"bar {args.bar}" )

运行 与:

args.py cmd1 -f asd

输出:

Namespace(subparser_name='cmd1', debug=False, foo='asd')
foo asd

更新:

如果您希望能够使用例如args.py -d cmd1 然后在创建 parser 时指定 parents=[common_args]

parser = argparse.ArgumentParser(prog="my_prog", add_help=True, parents=[common_args])

下次您提问时,请确保您只post 将代码作为最小的可重现示例 - 即可以 运行 不添加任何内容