我正在使用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
。我在这里缺少什么?
答案 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
我将这种技术用于多个嵌套的子解析器。