argparse子命令错误消息

时间:2014-08-15 20:36:10

标签: python argparse

考虑以下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

这似乎意味着--bartest无法识别的论据。但实际上它是foo子命令的一个无法识别的参数。

我认为错误信息应为:

usage: test foo [-h] [--baz BAZ]
foo: error: unrecognized arguments: --bar

这是argparse中的错误吗?我可以使用configure argparse来提供正确的错误消息吗?

3 个答案:

答案 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选项。因此它现在将解析委托给子分析器sp1sp1无法识别--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