我如何调试`ast.literal_eval`中的错误?

9
我使用pprint.PrettyPrinter将数据写入文件,现在我正在使用ast.literal_eval读取它。这种方式一直在我身上运行了很长一段时间,并且我对生成的文本表示比较满意。
然而,今天我在反序列化时遇到了这个错误:
  File "/...mypath.../store.py", line 82, in <lambda>
    reader=(lambda fd: ast.literal_eval(fd.read())),
  File "/usr/lib64/python2.7/ast.py", line 80, in literal_eval
    return _convert(node_or_string)
  File "/usr/lib64/python2.7/ast.py", line 60, in _convert
    return list(map(_convert, node.elts))
  File "/usr/lib64/python2.7/ast.py", line 63, in _convert
    in zip(node.keys, node.values))
  File "/usr/lib64/python2.7/ast.py", line 62, in <genexpr>
    return dict((_convert(k), _convert(v)) for k, v
  File "/usr/lib64/python2.7/ast.py", line 63, in _convert
    in zip(node.keys, node.values))
  File "/usr/lib64/python2.7/ast.py", line 62, in <genexpr>
    return dict((_convert(k), _convert(v)) for k, v
  File "/usr/lib64/python2.7/ast.py", line 79, in _convert
    raise ValueError('malformed string')
ValueError: malformed string

如何修复这个特定的文件?

这个文件有17k行/700kb。

我将它加载到Emacs中,括号是平衡的。

文件中没有非ASCII字符。

我可以“分而治之”(将文件分成两半并尝试读取每一半),但这相当繁琐。

有更好的方法吗?

我修改了ast.literal_eval:_convert以打印出问题节点,结果显示为<_ast.UnaryOp object at 0x110696510>。不太有帮助。

如何确保将来不会再出现这种情况?

我希望JSON不是答案。;-)

我没有使用JSON,因为:

  1. JSON无法处理非字符串字典键
  2. JSON插入太多换行符或根本没有换行符

6
如果您能获取到出错节点的引用,打印出它的 linenocol_offset 属性 - 这应该能够告诉您问题的准确位置。 - jasonharper
你能展示一下你的文件(简化版)的例子吗? - Serge Ballesta
2
@SergeBallesta 这个问题是想要找出文件中的问题所在。简化版本的文件可能根本没有错误。 - Daniel H
快速猜测,可能会或可能不会得到回报:在您的文件中搜索“set”。ast.literal_eval无法处理空集合。 - user2357112
2
@jasonharper:没错,就是这个。谢谢!问题在于我触发了一个已知的bugast.literal_eval(str(float("inf")))(请将您的评论转换为答案,我会很乐意接受它)。 - sds
1个回答

9

快速且简单

应用此补丁:

--- /...../2.7/lib/python2.7/ast.py.old 2018-03-25 12:17:11.000000000 -0400
+++ /...../2.7/lib/python2.7/ast.py 2018-03-25 12:17:18.000000000 -0400
@@ -76,7 +76,7 @@ def literal_eval(node_or_string):
                 return left + right
             else:
                 return left - right
-        raise ValueError('malformed string')
+        raise ValueError('malformed string', node.lineno, node.col_offset)
     return _convert(node_or_string)
 

重新加载 ast

>>> reload(ast)

重试加载有问题的文件

获取

ValueError: ('malformed string', 21161, 10)

错误位于第21161行,第10列。

已提交错误报告

复杂的

将代码放在 try/except 中,捕获错误并使用 inspect/traceback 访问相关的 node

try:
    ast.literal_eval(...)
except ValueError as ex:
    _exc_type, exc_value, exc_traceback = sys.exc_info()
    print("ERROR: %r" % (exc_value))
    # traceback.print_tb(exc_traceback)
    last_tb = exc_traceback
    while last_tb.tb_next:
        last_tb = last_tb.tb_next
    print("Error location: line=%d, col=%d" % (
        last_tb.tb_frame.f_locals["node"].lineno,
        last_tb.tb_frame.f_locals["node"].col_offset))

打印

ERROR: ValueError('malformed string')
Error location: line=21933, col=15

3
很棒的回答。这确实应该成为该模块的默认行为。 - lys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接