Simple and safe evaluator
bvdp
bob at mellowood.ca
Wed Jun 11 17:49:57 EDT 2008
More information about the Python-list mailing list
Wed Jun 11 17:49:57 EDT 2008
- Previous message (by thread): Simple and safe evaluator
- Next message (by thread): Simple and safe evaluator
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Matimus wrote: > On Jun 11, 1:25 pm, bvdp <b... at mellowood.ca> wrote: >> Is there a simple/safe expression evaluator I can use in a python >> program. I just want to pass along a string in the form "1 + 44 / 3" or >> perhaps "1 + (-4.3*5)" and get a numeric result. >> >> I can do this with eval() but I really don't want to subject my users to >> the problems with that method. >> >> In this use I don't need python to worry about complex numbers, >> variables or anything else. Just do the math on a set of values. Would >> eval() with some restricted list of permitted operators do the trick? >> >> I'm feeling too lazy to write/debug my own parser for this :) >> >> Thanks, Bob. > > Here is something that I wrote using the _ast module. It works pretty > well, and might be a good example for others wanting to experiment > with the _ast module. On a related note... if anybody wants to provide > feedback on this code it would be much appreciated. It involves a lot > of if/elif branches, and feels ugly. > > Matt > > [code] > import _ast > > class SafeEvalError(Exception): > pass > > class UnsafeCode(SafeEvalError): > pass > > # safe types: > # Sequences: > # list, tuple, dict, set, frozen_set* > # Literals: str, unicode, int, long, complex, float > def safe_eval(text): > "similar to eval, but only works on literals" > ast = compile(text, "<string>", 'exec', _ast.PyCF_ONLY_AST) > return _traverse(ast.body[0].value) > > def _traverse(ast): > if isinstance(ast, _ast.List): > return [_traverse(el) for el in ast.elts] > elif isinstance(ast, _ast.Tuple): > return tuple(_traverse(el) for el in ast.elts) > elif isinstance(ast, _ast.Dict): > return dict( > zip( > (_traverse(k) for k in ast.keys), > (_traverse(v) for v in ast.values) > ) > ) > elif isinstance(ast, _ast.Str): > return ast.s > elif isinstance(ast, _ast.Num): > return ast.n > elif isinstance(ast, _ast.Expr): > return _traverse(ast.value) > elif isinstance(ast, _ast.BinOp): > if isinstance(ast.op, _ast.Add): > return _traverse(ast.left) + _traverse(ast.right) > elif isinstance(ast.op, _ast.Sub): > return _traverse(ast.left) - _traverse(ast.right) > elif isinstance(ast.op, _ast.Div): > return _traverse(ast.left) / _traverse(ast.right) > elif isinstance(ast.op, _ast.FloorDiv): > return _traverse(ast.left) // _traverse(ast.right) > elif isinstance(ast.op, _ast.Mod): > return _traverse(ast.left) % _traverse(ast.right) > elif isinstance(ast.op, _ast.Mult): > return _traverse(ast.left) * _traverse(ast.right) > elif isinstance(ast.op, _ast.Pow): > return _traverse(ast.left) ** _traverse(ast.right) > elif isinstance(ast.op, _ast.BitAnd): > return _traverse(ast.left) & _traverse(ast.right) > elif isinstance(ast.op, _ast.BitOr): > return _traverse(ast.left) | _traverse(ast.right) > elif isinstance(ast.op, _ast.BitXor): > return _traverse(ast.left) ^ _traverse(ast.right) > elif isinstance(ast.op, _ast.LShift): > return _traverse(ast.left) << _traverse(ast.right) > elif isinstance(ast.op, _ast.RShift): > return _traverse(ast.left) >> _traverse(ast.right) > elif isinstance(ast, _ast.BoolOp): > if isinstance(ast.op, _ast.And): > return all(_traverse(v) for v in ast.values) > if isinstance(ast.op, _ast.Or): > return any(_traverse(v) for v in ast.values) > elif isinstance(ast, _ast.UnaryOp): > if isinstance(ast.op, _ast.Invert): > return _traverse(ast.operand) > if isinstance(ast.op, _ast.USub): > return -_traverse(ast.operand) > if isinstance(ast.op, _ast.UAdd): > return +_traverse(ast.operand) > if isinstance(ast.op, _ast.Not): > return not _traverse(ast.operand) > > > raise UnsafeCode() > > if __name__ == "__main__": > print safe_eval("[1,2,3,{'hello':1}, (1,-2,3)], 4j, 1+5j, ~1+2*3") > [/code] Oh, this is interesting. Similar to some other code I found on the web which grabs a list of permitted token values using the dis module: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/286134 I was really hoping for a builtin on this :) Thanks.
- Previous message (by thread): Simple and safe evaluator
- Next message (by thread): Simple and safe evaluator
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Python-list mailing list