parser make literal_eval library how example ast python parsing python-2.x abstract-syntax-tree representation

make - parser library python



Malformed String ValueError ast.literal_eval() con representación en cadena de Tuple (2)

Intento leer en una representación de cadena de un Tuple desde un archivo y agregar la tupla a una lista. Aquí está el código relevante.

raw_data = userfile.read().split(''/n'') for a in raw_data : print a btc_history.append(ast.literal_eval(a))

Aquí está el resultado:

(Decimal(''11.66985''), Decimal(''0E-8'')) Traceback (most recent call last): File "./goxnotify.py", line 74, in <module> main() File "./goxnotify.py", line 68, in main local.load_user_file(username,btc_history) File "/home/unix-dude/Code/GoxNotify/local_functions.py", line 53, in load_user_file btc_history.append(ast.literal_eval(a)) File "/usr/lib/python2.7/ast.py", line 80, in literal_eval return _convert(node_or_string) `File "/usr/lib/python2.7/ast.py", line 58, in _convert return tuple(map(_convert, node.elts)) File "/usr/lib/python2.7/ast.py", line 79, in _convert raise ValueError(''malformed string'') ValueError: malformed string


De la documentation para ast.literal_eval() :

Evaluar de forma segura un nodo de expresión o una cadena que contenga una expresión de Python. La cadena o nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, números, tuplas, listas, dictados, booleanos y Ninguno.

Decimal no está en la lista de cosas permitidas por ast.literal_eval() .


ast.literal_eval (ubicado en ast.py ) analiza el árbol con ast.parse primero, luego evalúa el código con una función recursiva bastante fea, interpretando los elementos del árbol de análisis y reemplazándolos con sus equivalentes literales. Lamentablemente, el código no es expansible en absoluto, así que para agregar un código Decimal al código, debe copiar todo el código y comenzar de nuevo.

Para un enfoque un poco más fácil, puede usar el módulo ast.parse para analizar la expresión, y luego el ast.NodeVisitor o ast.NodeTransformer para asegurarse de que no haya una sintaxis no deseada o accesos a variables no deseadas. Luego compila con compile y eval para obtener el resultado.

El código es un poco diferente de literal_eval porque este código realmente usa eval , pero en mi opinión es más simple de entender y no es necesario cavar demasiado profundo en los árboles AST. Específicamente solo permite alguna sintaxis, que prohíbe explícitamente, por ejemplo, lambdas, acceso a atributos ( foo.__dict__ es muy malo) o acceso a nombres que no se consideran seguros. Analiza bien su expresión, y como un extra también agregué Num (flotante y entero), lista y literales de diccionario.

Además, funciona igual en 2.7 y 3.3

import ast import decimal source = "(Decimal(''11.66985''), Decimal(''1e-8''),"/ "(1,), (1,2,3), 1.2, [1,2,3], {1:2})" tree = ast.parse(source, mode=''eval'') # using the NodeTransformer, you can also modify the nodes in the tree, # however in this example NodeVisitor could do as we are raising exceptions # only. class Transformer(ast.NodeTransformer): ALLOWED_NAMES = set([''Decimal'', ''None'', ''False'', ''True'']) ALLOWED_NODE_TYPES = set([ ''Expression'', # a top node for an expression ''Tuple'', # makes a tuple ''Call'', # a function call (hint, Decimal()) ''Name'', # an identifier... ''Load'', # loads a value of a variable with given identifier ''Str'', # a string literal ''Num'', # allow numbers too ''List'', # and list literals ''Dict'', # and dicts... ]) def visit_Name(self, node): if not node.id in self.ALLOWED_NAMES: raise RuntimeError("Name access to %s is not allowed" % node.id) # traverse to child nodes return self.generic_visit(node) def generic_visit(self, node): nodetype = type(node).__name__ if nodetype not in self.ALLOWED_NODE_TYPES: raise RuntimeError("Invalid expression: %s not allowed" % nodetype) return ast.NodeTransformer.generic_visit(self, node) transformer = Transformer() # raises RuntimeError on invalid code transformer.visit(tree) # compile the ast into a code object clause = compile(tree, ''<AST>'', ''eval'') # make the globals contain only the Decimal class, # and eval the compiled object result = eval(clause, dict(Decimal=decimal.Decimal)) print(result)