argparse子命令错误信息

问题描述 投票:5回答:3

考虑以下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的一个错误吗? 我可以配置 argparse 来给出正确的错误信息吗?

python argparse
3个回答
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 的选择。 所以它现在将解析工作委托给了subparser。sp1. sp1 不承认 --bar所以它将其返回到主解析器的未识别参数列表中。 默认的操作是让主解析器把它传出去,就像它(self)没有识别这个字符串一样。

由于它的位置在 foo, --bar 是不被任何一个解析器识别的。 同样,['foo', '-boo']也是如此。

委托给子解析器的工作是在 __call__ 方式 sp (subparsers行动)。 部分内容是这样的。

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)

因此,根据设计,未识别的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
投票

我不一定会说这是个错误,而是在生成错误信息时采用了简单化的方法。

默认的错误信息只是简单地说明 "{PROG}: error: unrecognized arguments: {ALL_UNRECOGNIZED_ARGS}"无论未识别的参数与哪个(子)解析器相关联。

如果你看看 parse_args() 产生错误,很明显,这些信息不是现成的。

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我看到有明显的方法可以确定它们中的哪一个被赋予了命令行上的哪个命令。


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 反正在那里。

© www.soinside.com 2019 - 2024. All rights reserved.