[Zope] Re: How to convert string to list without eval or exec

Evan Simpson evan at 4-am.com
Wed Mar 10 17:15:41 EST 2004


Josef Meile wrote:
> I've seen this question here several times. Even I made it once
> long time ago. I could solve it easily because I didn't have a
> complex list.

Here's another way (in Python 2.3):

from compiler import parse, ast

class LiteralExEval(object):
     """
     Evaluate a string as a single expression consisting of literals.
     """
     def __call__(self, s):
         tree = parse(s)
         if tree.doc is not None:
             if tree.node.nodes:
                 raise ValueError, 'Too many expressions.'
             return tree.doc
         nodes = tree.node.nodes
         if len(nodes) < 1:
             raise ValueError, 'Empty expression.'
         if len(nodes) > 1:
             raise ValueError, 'Too many expressions.'
         node = nodes[0]
         if node.__class__ <> ast.Discard:
             raise ValueError, 'String is not an expression.'
         return self.dispatch(node.getChildNodes()[0])

     def dispatch(self, node):
         kind = node.__class__.__name__
         f = getattr(self, 'eval' + kind, None)
         if f is None:
             raise ValueError, '%s not allowed in expression.' % kind
         return f(node)

     def evalConst(self, node):
         return node.value

     def evalList(self, node):
         e = self.dispatch
         return [e(n) for n in node.nodes]

     def evalTuple(self, node):
         return tuple(self.evalList(node))

     def evalDict(self, node):
         e = self.dispatch
         return dict([(e(k), e(v)) for k, v in node.items])

class NameExEval(LiteralExEval):
     """
     Evaluate a string as a single expression that contains literals
     and pre-defined names.
     """
     def __init__(self,
                  names={'None': None, 'True': True, 'False': False},
                  **kwnames):
         kwnames.update(names)
         self._names = kwnames

     def evalName(self, node):
         try:
             return self._names[node.name]
         except KeyError:
             raise ValueError, 'Undefined name %s' % `node.name`

class NameCallExEval(NameExEval):
     """
     Evaluate a string as a single expression that contains literals
     and pre-defined names, including callable names.
     """
     def evalCallFunc(self, node):
         if node.star_args or node.dstar_args:
             raise ValueError, 'Function uses * or **-style arguments.'
         e = self.dispatch
         f = e(node.node)
         args = []
         kwargs = {}
         for arg in node.args:
             if arg.__class__ == ast.Keyword:
                 kwargs[arg.name] = e(arg.expr)
             else:
                 args.append(e(arg))
         return f(*args, **kwargs)

def _debug(Eval, s):
     try:
         return Eval(s)
     except ValueError, e:
         print '%s: %s' % (`s`, str(e))

if __name__ == '__main__':
     le = LiteralExEval()
     for s in (
         "'string'",
         "42",
         "1, 0x2",
         "['one', 0.2]",
         "{1j: ()}",
         ):
         print _debug(le, s)

     ne = NameExEval(x=42)
     for s in (
         "None",
         "[x, 2, True]",
         "{False: (2, ['x']),}",
         ):
         print _debug(ne, s)

     nc = NameCallExEval(x='42', Int=int, Str=str)
     for s in (
         "Str(4)",
         "Int(x)",
         "Str(Int(x,),)",
         ):
         print _debug(nc, s)




More information about the Zope mailing list