如何在 python argparse 中使用子解析器定义全局选项?

How can I define global options with sub-parsers in python argparse?

我正在尝试弄清楚如何使用 pythons arparse 库在子解析器场景中添加全局选项。

现在我的代码如下所示:

def parseArgs(self):
    parent_parser = argparse.ArgumentParser(add_help=False)
    parent_parser.add_argument('--debug', default=False, required=False,
        action='store_true', dest="debug", help='debug flag')

    main_parser = argparse.ArgumentParser()
    main_parser.add_argument('--debug', default=False, required=False,
        action='store_true', dest="debug", help='debug flag')

    service_subparsers = main_parser.add_subparsers(title="category",
        dest="category")
    agent_parser = service_subparsers.add_parser("agent",
        help="agent commands", parents=[parent_parser])
    return main_parser.parse_args()

这适用于命令行 ./test --help 并且 --debug 选项被列为全局:

usage: test [-h] [--debug] {agent} ...

optional arguments:
  -h, --help  show this help message and exit
  --debug     debug flag

category:
  {agent}
    agent     agent commands

然而,当我使用命令行 ./test agent --help 触发代理子解析器时,--debug 选项现在不再列为全局选项,而是列为子解析器的选项。此外,它现在必须指定为 ./test agent --debug 并且 ./test --debug agent 不再有效:

usage: test agent [-h] [--debug]

optional arguments:
  -h, --help  show this help message and exit
  --debug     debug flag

我想做的是定义 --debug 是全局的,这样它就可以始终为所有子解析器指定,并在帮助输出中适当地列出。

main_parser填入默认值namespaceFalse for debug);如果遇到 --debug,它会将 debug 更改为 True。当它看到 agent 字符串时,它会调用子解析器,将剩余的参数字符串和它一直在使用的命名空间传递给它。

现在子解析器执行正常的解析器操作 - 如果为其参数填充默认值,则将 default 设置为 False。如果它在剩余的字符串中遇到 --debug,它会将其更改为 True。否则它会保持原样。完成后,它会将命名空间传回主解析器,然后 returns 将其传递给您的代码。

所以

myprog.py --debug agent --debug

namespace(debug=False) 已从 False 变为 True 再变为 False 再变为 True。

这是主解析器(我不喜欢在这种情况下使用 'global')和子解析器共享相同 dest 的结果。

有一个 bug/issue 试图稍微改变行为,将 'virgin' 命名空间传递给子解析器,然后以某种方式将其结果与主解析器合并。但这产生了一些向后兼容性问题。需要的话我可以查一下。

目前,尝试在主解析器和子解析器中定义相同的可选参数势必会给您和您的用户造成混淆。

如果我将父级更改为

parent_parser.add_argument('--Debug', action='store_true', help='debug flag')

(不需要默认,如果与选项标志相同则为dest)

生成的命名空间类似于

1721:~/mypy$ python stack37933480.py --debug agent --Debug
Namespace(Debug=True, category='agent', debug=True)

或者我可以定义

parent_parser.add_argument('--debug', dest='debug1', action='store_true', help='debug flag')

并得到:

1724:~/mypy$ python stack37933480.py --debug agent --debug
Namespace(category='agent', debug=True, debug1=True)

两个地方的标志相同,但名称空间中的条目不同。解析后我可以做类似的事情:

args.debug = args.debug or args.debug1

统一两旗。无论请求哪种帮助,您的用户都会看到“--debug”。

抱歉,如果描述有点冗长,但我认为首先了解行为很重要。然后解决方案变得更加明显。

在这种情况下,使用父项不会使问题复杂化。我假设您使用它只是为了将此调试添加到所有子解析器。

另一种选择是只为主解析器定义 debug。是的,子解析器帮助中会缺少它,但您始终可以在描述中添加注释。

===================

子解析器定义采用 prog 参数。如果没有给出,它是基于主要 prog.

定义的

如果我将 prog 添加为:

agent_parser = service_subparsers.add_parser("agent",
    prog='myprog.py [--debug] agent',
    help="agent commands", parents=[parent_parser])

子解析器用法变为:

1824:~/mypy$ python3 stack37933480.py agent -h
usage: myprog.py [--debug] agent [-h] [--debug]

或者我可以将 prog 添加到 add_subparsers 定义中

service_subparsers = main_parser.add_subparsers(title="category",
    prog='myprog.py [--debug]',
    dest="category")

检查该方法的代码以了解它如何构造默认用法前缀。它包括 main 个位置元素,但不包括可选元素。

http://bugs.python.org/issue9351 - 在此补丁中,最初的开发人员认为用户希望参数的子解析器定义应该覆盖主解析器的值和操作。另一方面,您希望主要定义具有优先权。

http://bugs.python.org/issue24251 - 但 9351 中提出的更正给其他用户带来了问题。这就是为什么我认为最好不要在 main 和 sub 中定义相同的 dest。很难满足大家的期待