Python argparse 断言错误

16 投票
5 回答
8766 浏览
提问于 2025-04-18 03:18

我刚开始使用argparse模块。我写了下面这个简化的代码片段来展示我遇到的问题。

from argparse import ArgumentParser

if __name__ == '__main__':
    parser = ArgumentParser('Test argparse. This string needs to be relatively long to trigger the issue.')
    parser.add_argument('-f', '--fin', help='a', required = True)
    parser.add_argument('-o', '--out ', help='b', required = True)
    parser.add_argument('-t', '--trans', help='c', required = True)

    args = parser.parse_args()
    print(repr(vars(args)))

当运行这个脚本并加上-h这个参数时,会出现AssertionError错误。

Traceback (most recent call last):
  File "arg.py", line 10, in <module>
    args = parser.parse_args()
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 1707, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 1739, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 1945, in _parse_known_args
    start_index = consume_optional(start_index)
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 1885, in consume_optional
    take_action(action, args, option_string)
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 1813, in take_action
    action(self, namespace, argument_values, option_string)
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 1017, in __call__
    parser.print_help()
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 2341, in print_help
    self._print_message(self.format_help(), file)
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 2325, in format_help
    return formatter.format_help()
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 278, in format_help
    help = self._root_section.format_help()
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 208, in format_help
    func(*args)
  File "C:\Users\user\AppData\Local\Continuum\Anaconda\envs\py3k\lib\argparse.py", line 329, in _format_usage
    assert ' '.join(opt_parts) == opt_usage
AssertionError

如果把传给ArgumentParser的描述字符串缩短,就能正常工作。删除其中一个参数也会有帮助。

我是不是做错了什么?我的环境是:

Python 3.3.5 | Anaconda 1.9.2 (64位) | (默认,2014年3月10日,11:25:04) [MSC v.1600 64位 (AMD64)] 在win32上

5 个回答

4

对我来说,是因为同时设置了required=True和metavar=''。去掉其中一个,保留另一个,就解决了问题。

4

Python 3.5.2

这让我困扰了一段时间,但我终于找到了问题所在。这个问题确实是因为使用的行长度太长了,如果超过了控制台或终端设置的 COLUMNS 环境变量,就会出现这个错误。我在命令行中尝试:

$ COLUMNS=80 python <myprog.py> -h

结果我得到了这个异常:

...
  File "/usr/lib/python3.5/argparse.py", line 1735, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/usr/lib/python3.5/argparse.py", line 1767, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/usr/lib/python3.5/argparse.py", line 1973, in _parse_known_args
    start_index = consume_optional(start_index)
  File "/usr/lib/python3.5/argparse.py", line 1913, in consume_optional
    take_action(action, args, option_string)
  File "/usr/lib/python3.5/argparse.py", line 1841, in take_action
    action(self, namespace, argument_values, option_string)
  File "/usr/lib/python3.5/argparse.py", line 1025, in __call__
    parser.print_help()
  File "/usr/lib/python3.5/argparse.py", line 2367, in print_help
    self._print_message(self.format_help(), file)
  File "/usr/lib/python3.5/argparse.py", line 2351, in format_help
    return formatter.format_help()
  File "/usr/lib/python3.5/argparse.py", line 287, in format_help
    help = self._root_section.format_help()
  File "/usr/lib/python3.5/argparse.py", line 217, in format_help
    func(*args)
  File "/usr/lib/python3.5/argparse.py", line 338, in _format_usage
    assert ' '.join(opt_parts) == opt_usage
AssertionError

但是如果我这样做:

$ COLUMNS=<XX> python <myprog.py> -h

其中 XX 大于生成的使用行的长度,一切就正常了,它会打印出使用说明和帮助信息,然后退出。所以你要么缩短你的使用行,要么增加 COLUMNS 的值。

编辑:

我在我的情况下找到了错误:我在程序或参数描述中使用了方括号 []

正如其他人正确指出的那样,查看出现异常的 Python 代码,你会发现 argparse 有一个自动将使用说明/帮助折叠到 $COLUMNS 列数的功能。但为了拆分长行,它使用了以下正则表达式:

(文件 "/usr/lib/python3.5/argparse.py",第 333 行:)

`part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'`

当它重新连接行进行检查时,如果用户输入了方括号,断言就会失败,因为方括号是 argparse 用来标记可选值的特殊字符。

总之,我把文本中的多余方括号去掉了,一切就正常了,使用说明/帮助信息也根据 $COLUMNS 的值进行了正确的折叠和格式化。

4

问题并不是你加的那个多余的 -h。看看错误信息和 -o 这个参数:

assert ' '.join(opt_parts) == opt_usage

它把 '--out ' 里的空格给连在一起了。如果你把空格去掉,其他的就应该没问题了。

9

我来这里是因为遇到了完全一样的问题/错误,但我在--out后面没有多余的空格。我的问题是metavar被设置成了一个空字符串(metavar='')。把这个改了之后,问题就解决了。

15

在代码中,--out 后面多了一个空格。你需要把:

parser.add_argument('-o', '--out ', help='b', required = True)

改成:

parser.add_argument('-o', '--out', help='b', required = True)

问题的根本原因是 Python 代码中的一个 assert 检查,这个检查只在 Python 尝试把帮助文本分成多行时发生,因为文本太长了。分行后,Python 会把这些行合并回去,并和原来的文本进行比较,以确保内容是正确的。不过,负责分行的代码在处理时去掉了相邻的空格,导致比较时出现了问题。

我在代码中添加了一些打印语句(argparse.py,Python 2.7):

# wrap the usage parts if it's too long
text_width = self._width - self._current_indent
if len(prefix) + len(usage) > text_width:
    # break usage into wrappable parts
    part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
    opt_usage = format(optionals, groups)
    pos_usage = format(positionals, groups)
    opt_parts = _re.findall(part_regexp, opt_usage)
    pos_parts = _re.findall(part_regexp, pos_usage)
    print ' '.join(opt_parts)
    print opt_usage
    assert ' '.join(opt_parts) == opt_usage

结果如下:

[-h] -f FIN -o OUT -t TRANS
[-h] -f FIN -o OUT  -t TRANS
Traceback (most recent call last):
  File "blah.py", line 9, in <module>
    args = parser.parse_args()

注意到 OUT 后面有多余的空格。

这就解释了所有观察到的行为:

  • 文本必须足够长,才能触发换行的行为。
  • 删除 --trans 参数后,--out 被移动到最后,导致不再出现这种行为。
  • 删除 --out 参数也使这种行为不再出现。

撰写回答