考虑以下Python 2代码:
from argparse import ArgumentParser
p = ArgumentParser(prog="test")
p.add_argument('--bar')
sp = p.add_subparsers()
sp1 = sp.add_parser('foo')
sp1.add_argument('--baz')
p.parse_args(['foo', '--bar'])
我得到的错误信息是:
usage: test [-h] [--bar BAR] {foo} ...
test: error: unrecognized arguments: --bar
这似乎意味着--bar
是test
无法识别的论据。但实际上它是foo
子命令的一个无法识别的参数。
我认为错误信息应为:
usage: test foo [-h] [--baz BAZ]
foo: error: unrecognized arguments: --bar
这是argparse中的错误吗?我可以使用configure argparse来提供正确的错误消息吗?
答案 0 :(得分:2)
如果我调整你的剧本
p = ArgumentParser(prog="test")
p.add_argument('--bar')
sp = p.add_subparsers(dest='cmd')
sp1 = sp.add_parser('foo')
sp1.add_argument('--baz')
print p.parse_known_args()
输出
1517:~/mypy$ python2.7 stack25333847.py foo --bar
(Namespace(bar=None, baz=None, cmd='foo'), ['--bar'])
解析器p
遇到foo
,其中一个允许的sp
选项。因此它现在将解析委托给子分析器sp1
。 sp1
无法识别--bar
,因此它会将其返回到无法识别的参数列表中的主解析器。默认操作是主解析器将其传递出去,就好像它(self)没有识别出字符串一样。
由于foo
之后的位置,任何一个解析器都无法识别--bar
。同样适用于[' foo',' - boo']。
subparser的委派是在__call__
sp
方法(subparsers action)中完成的。部分内容如下:
def __call__(self, parser, namespace, values, option_string=None):
...
# parse all the remaining options into the namespace
# store any unrecognized options on the object, so that the top
# level parser can decide what to do with them
namespace, arg_strings = parser.parse_known_args(arg_strings, namespace)
if arg_strings:
vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
因此,设计中将unrecognized_args的处理留给了主解析器(调用parse_args
而不是parse_known_args
的人)。
另一个错误,例如省略--baz
的值会在subparser中生成错误消息:
1523:~/mypy$ python2.7 stack25333847.py foo --baz
usage: test foo [-h] [--baz BAZ]
test foo: error: argument --baz: expected one argument
我找到了一种产生方式:
usage: test foo [-h] [--baz BAZ]
test foo: error: unrecognized arguments: --bar
尽管它不短而且甜美。我是argparse._SubParsersAction
的子类;给它一个使用__call__
而不是parse_args
的新parse_known_args
。我还必须更改主解析器注册表。 (如果需要,我可以添加代码。)
答案 1 :(得分:1)
我不一定会说这是一个错误,而是一种简单的方法来生成错误消息。
默认错误消息只表示"{PROG}: error: unrecognized arguments: {ALL_UNRECOGNIZED_ARGS}"
,无论与无法识别的参数关联的是什么(子)解析器。
如果你看一下parse_args()
generates the error的方式,很明显这些信息并不容易获得:
def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
return args
调用args, argv = parse_known_args(args)
将简单地解析已知参数并将其作为命名空间args
返回,并将所有剩余的未知参数返回为argv
。
所以你可以自己直接使用parse_known_args()
。您还可以使用
sp = p.add_subparsers(dest='cmd')
然后使用args.cmd
访问它以确定用户尝试调用哪个子命令。
基于此,并且根据解析器层次结构的简单程度,您可以生成更有用的错误消息(请参阅parser.error()
)。
但它不会是完美的。如果用户要为主解析器和子解析器指定一个未知参数,那么这两个无法识别的参数将保留在argv
中,并且我看到明确的方法来确定哪个命令被赋予命令中的哪个命令线。
答案 2 :(得分:0)
我已经解决了以下问题:
from argparse import ArgumentParser, _
class _ArgumentParser(ArgumentParser):
# "parse_known_args" is made to behave exactly like "parse_args".
def parse_known_args(self, args=None, namespace=None):
args, argv = super().parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
return args, argv
p = ArgumentParser(prog="test")
p.add_argument('--bar')
sp = p.add_subparsers(parser_class=_ArgumentParser)
sp1 = sp.add_parser('foo')
sp1.add_argument('--baz')
p.parse_args(['foo', '--bar'])
我收到以下错误消息:
usage: test foo [-h] [--baz BAZ]
test foo: error: unrecognized arguments: --bar
我想这正是想要的,不是吗?
我不知道为什么Python在那里有parse_known_args
。我认为那里的用处为零,无论如何我应该声称那里应该parse_args
。