¿Cómo depuro un error en `ast.literal_eval`?

Escribí mis datos en un archivo usando pprint.PrettyPrinter y estoy tratando de leerlo usando ast.literal_eval . Esto me ha funcionado durante bastante tiempo y estoy razonablemente satisfecho con la representación del texto producido.

Sin embargo, hoy he recibido este error en la deserialización:

  File "/...mypath.../store.py", line 82, in  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  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  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 

¿Cómo arreglo este archivo específico?

El archivo en cuestión es de 17k líneas / 700kb. Lo cargué en Emacs – los parens están equilibrados. No hay caracteres que no sean ASCII en el archivo. Puedo “dividir y vencer” (dividir el archivo por la mitad e intentar realzar cada mitad), pero esto es bastante tedioso. ¿Hay algo mejor?

ast.literal_eval:_convert para imprimir el nodo ofensivo – resultó ser el . No muy útil.

¿Cómo me aseguro de que esto no suceda en el futuro?

Espero que JSON no sea la respuesta. 😉

No estoy usando JSON porque

  1. JSON no puede manejar claves de dictado sin cadena
  2. JSON inserta demasiadas líneas nuevas o ninguna en absoluto

Rápido y sucio

Aplique este parche:

 --- /...../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) 

Recargar ast :

 >>> reload(ast) 

Vuelva a intentar cargar el archivo infractor

Obtener

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

luego la línea 21161, columna 10 es donde está el error.

Sofisticado

Envuelva el código en try/except , detecte el error y use inspect / traceback para acceder al node en cuestión:

 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)) 

huellas dactilares

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