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

Posted

技术标签:

【中文标题】argparse:声明全局参数后无法获取 subparser_name【英文标题】:argparse: unable to get subparser_name after declaring a global argument 【发布时间】:2022-01-01 16:35:20 【问题描述】:

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

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”。现在,它是“无”。即使在子解析器创建中添加了 parents=[parser] 。我怎样才能解决这个问题?我怎么知道调用了哪个命令?

【问题讨论】:

什么版本的 Python?什么操作系统?并且请将您的问题中的代码设为minimal reproducible example,以便我可以通过复制/粘贴并运行文件进行尝试,而无需添加任何内容 不要将主解析器用作子解析器的父级。并且不要尝试在 main 和 sub 中使用具有相同 dest 的参数。 并不是说它与问题有关,而是将action 指定为foobar 确实使这无法重现。 【参考方案1】:

将公共 args 拆分为单独的 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])

下次您提出问题时,请确保您仅将代码作为可重现的最小示例发布 - 即,可以在不添加任何内容的情况下运行

【讨论】:

更新了如何使用 -d 而无需指定子命令。【参考方案2】:

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

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

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

并且使用主解析器作为子解析器的parent,只是要求混淆。

【讨论】:

以上是关于argparse:声明全局参数后无法获取 subparser_name的主要内容,如果未能解决你的问题,请参考以下文章

Python:正确处理子命令的全局选项的参数解析器

Python argparse 忽略无法识别的参数

为啥直接调用 Python 脚本时 argparse 无法识别参数?

Argparse 无法识别参数

Python Argparse:获取用于命名空间变量的命令行参数

如何在同一个 python 脚本中使用 sys 和 argparse 而不会出现无法识别的参数错误?