web-dev-qa-db-fra.com

String ValueError malformée ast.literal_eval () avec représentation String de Tuple

J'essaie de lire une représentation sous forme de chaîne d'un tuple à partir d'un fichier et d'ajouter le tuple à une liste. Voici le code pertinent.

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

Voici la sortie:

(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
36
Sinthet

ast.literal_eval (situé dans ast.py) analyse l'arbre avec ast.parse d'abord, puis il évalue le code avec une fonction récursive assez laide, interprétant les éléments de l'arbre d'analyse et les remplaçant par leurs équivalents littéraux. Malheureusement, le code n'est pas du tout extensible, donc pour ajouter Decimal au code, vous devez copier tout le code et recommencer.

Pour une approche un peu plus simple, vous pouvez utiliser ast.parse module pour analyser l'expression, puis le ast.NodeVisitor ou ast.NodeTransformer pour garantir qu'il n'y a pas de syntaxe indésirable ou d'accès aux variables indésirable. Compilez ensuite avec compile et eval pour obtenir le résultat.

Le code est un peu différent de literal_eval en ce que ce code utilise en fait eval, mais à mon avis est plus simple à comprendre et il n'est pas nécessaire de creuser trop profondément dans les arbres AST. Il autorise spécifiquement certains syntaxe, interdisant explicitement par exemple les lambdas, les accès aux attributs (foo.__dict__ est très diabolique), ou accède à des noms qui ne sont pas considérés comme sûrs. Il analyse votre expression très bien, et en plus j'ai également ajouté Num (float et entier), list et dictionnaire littéraux.

Fonctionne de même sur 2.7 et 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)
35
Antti Haapala

De la documentation pour ast.literal_eval():

Évaluez en toute sécurité un nœud d'expression ou une chaîne contenant une expression Python. La chaîne ou le nœud fourni ne peut être composé que des structures littérales Python suivantes: chaînes, nombres, tuples, listes, dict, booléens et Aucun.

Decimal n'est pas sur la liste des choses autorisées par ast.literal_eval().

21
NPE

Je sais que c'est une vieille question, mais je pense avoir trouvé une réponse très simple, au cas où quelqu'un en aurait besoin.

Si vous mettez des guillemets dans votre chaîne ("'bonjour'"), ast_literaleval () le comprendra parfaitement.

Vous pouvez utiliser une fonction simple:

    def doubleStringify(a):
        b = "\'" + a + "\'"
        return b

Ou probablement plus approprié pour cet exemple:

    def perfectEval(anonstring):
        try:
            ev = ast.literal_eval(anonstring)
            return ev
        except ValueError:
            corrected = "\'" + anonstring + "\'"
            ev = ast.literal_eval(corrected)
            return ev
0
nojco