Argparse:如果两者共享父

时间:2018-05-26 14:34:25

标签: python python-3.x argparse

我正在使用argparse和几个subparser。我希望我的程序在args的任何地方都可以选择冗长的选项,包括subparser。

from argparse import ArgumentParser
p = ArgumentParser()
p.add_argument('--verbose', '-v', action='count')

sub = p.add_subparsers()
a = sub.add_parser('a')

print(p.parse_args())

默认情况下,如果用于subparsers,主解析器的选项将抛出错误:

$ python tmp.py -v a
Namespace(verbose=1)

$ python tmp.py a -v
usage: tmp.py [-h] [--verbose] {a} ...
tmp.py: error: unrecognized arguments: -v

我从this answer查看了父解析器。

from argparse import ArgumentParser

parent = ArgumentParser(add_help=False)
parent.add_argument('--verbose', '-v', action='count')

main = ArgumentParser(parents=[parent])

sub = main.add_subparsers()
a = sub.add_parser('a', parents=[parent])

print(main.parse_args())

出于某种原因,没有任何共享标志在主解析器上起作用。

$ python tmp2.py a -vvv
Namespace(verbose=3)
$ python tmp2.py -vvv a
Namespace(verbose=None)

请注意,主解析器肯定有适当的参数,因为当我将其更改为main = ArgumentParser()时,我得到error: unrecognized arguments: -v。我在这里缺少什么?

2 个答案:

答案 0 :(得分:2)

首先,一些一般性评论。

主解析器处理输入到subparser调用,然后调用subparser并给出剩余的argv。完成后,namespace合并回主namespace

parents机制通过引用从parent复制操作。因此,您的主要和子分享器共享相同的verbose Action对象。当subparser尝试设置不同的默认值或帮助时,这是一个问题。这可能不是问题,但请记住它。

即使没有parents机制,在主分析符和子分析符之间共享dest或选项标记也很棘手。是否应该使用subparser Action的默认值?如果两者都被使用怎么办? subparser是否会覆盖主解析器的操作?

最初主namespace被传递给subparser,它被修改并返回。这已经改变了一段时间(如果需要,我可以找到错误/问题)。现在,subparser以默认的空namespace开头,填充它。然后将这些值合并到主文件中。

因此,在您关联的SO问题中,请注意较旧的答案。 argparse此后可能已发生变化。

我认为在你的情况下发生的事情是主要和子分析器verbose分别计算。当你得到None时,你看到的是subparser的默认值。

__call__的{​​{1}}是:

_Count_Action

我怀疑在较早的def __call__(self, parser, namespace, values, option_string=None): new_count = _ensure_value(namespace, self.dest, 0) + 1 setattr(namespace, self.dest, new_count) 中,当共享命名空间时,argparse会累积,但我无法在不重新创建旧版count动作类的情况下对其进行测试。

https://bugs.python.org/issue15327 - 原始开发人员建议将这两个参数设置为subparser。它记录了main和sub的输入。如果需要,您自己的代码可以合并结果。

https://bugs.python.org/issue27859 dest。在这里,我建议一种重建旧款式的方法。

https://bugs.python.org/issue9351 argparse - subparsers does not retain namespace - 这是2014年改变名称空间使用的问题。

答案 1 :(得分:0)

我对此行为的变通方法(在@hpaulj的答案中进行了很好的描述)是创建第二个解析器,该解析器不具有子解析器,而仅包含首先找到的位置参数。

与第一个解析器一起使用的第一个parse_args将验证位置参数和标志,在需要时显示错误消息或显示适当的帮助。

用于第二个解析器的第二个parse_args将正确填写名称空间。

以您的示例为基础:

from argparse import ArgumentParser

parent = ArgumentParser(add_help=False)
parent.add_argument('--verbose', '-v', action='count')

main1 = ArgumentParser(parents=[parent])
sub = main1.add_subparsers()

# eg: tmp.py -vv a -v
a = sub.add_parser('a', parents=[parent])
a.set_defaults(which='a')

# eg: tmp.py -vv v -v --output toto
b = sub.add_parser('b', parents=[parent])
b.add_argument('--output', type=str)
b.set_defaults(which='b')

args = main1.parse_args()
print(args)

# parse a second time with another parser
main2 = ArgumentParser(parents=[parent])
if args.which == 'a':
    main2.add_argument('a')
elif args.which == 'b':
    main2.add_argument('b')
    main2.add_argument('--output', type=str)

print(main2.parse_args())

哪个给:

$ ./tmp.py -vv a -v

Namespace(verbose=1, which='a')
Namespace(a='a', verbose=3)

$ ./tmp.py -vv b -v --output toto
Namespace(output='toto', verbose=1, which='b')
Namespace(b='b', output='toto', verbose=3)

$ ./tmp.py  -vv a  --output
usage: tmp.py [-h] [--verbose] {a,b} ...
tmp.py: error: unrecognized arguments: --output

我将这种技术用于多个嵌套的子解析器。