|
| 1 | +# encoding: utf-8 |
| 2 | +"""simple evaluation of side-effect free code |
| 3 | +
|
| 4 | +In order to provide fancy completion, some code can be executed safely. |
| 5 | +
|
| 6 | +""" |
| 7 | + |
| 8 | +from ast import * |
| 9 | +from six import string_types |
| 10 | + |
| 11 | +class EvaluationError(Exception): |
| 12 | + """Raised if an exception occurred in safe_eval.""" |
| 13 | + |
| 14 | + |
| 15 | +def safe_eval(expr, namespace): |
| 16 | + """Not all that safe, just catches some errors""" |
| 17 | + try: |
| 18 | + return eval(expr, namespace) |
| 19 | + except (NameError, AttributeError, SyntaxError): |
| 20 | + # If debugging safe_eval, raise this! |
| 21 | + # raise |
| 22 | + raise EvaluationError |
| 23 | + |
| 24 | +def simple_eval(node_or_string, namespace={}): |
| 25 | + """ |
| 26 | + Safely evaluate an expression node or a string containing a Python |
| 27 | + expression. The string or node provided may only consist of: |
| 28 | + * the following Python literal structures: strings, numbers, tuples, |
| 29 | + lists, dicts, booleans, and None. |
| 30 | + * variable names causing lookups in the passed in namespace or builtins |
| 31 | + * getitem calls using the [] syntax on objects of the types above |
| 32 | + * getitem calls on subclasses of the above types if they do not override |
| 33 | + the __getitem__ method and do not override __getattr__ or __getattribute__ |
| 34 | + (or maybe we'll try to clean those up?) |
| 35 | +
|
| 36 | + The optional namespace dict-like ought not to cause side effects on lookup |
| 37 | + """ |
| 38 | + if isinstance(node_or_string, string_types): |
| 39 | + node_or_string = parse(node_or_string, mode='eval') |
| 40 | + if isinstance(node_or_string, Expression): |
| 41 | + node_or_string = node_or_string.body |
| 42 | + def _convert(node): |
| 43 | + if isinstance(node, Str): |
| 44 | + return node.s |
| 45 | + elif isinstance(node, Num): |
| 46 | + return node.n |
| 47 | + elif isinstance(node, Tuple): |
| 48 | + return tuple(map(_convert, node.elts)) |
| 49 | + elif isinstance(node, List): |
| 50 | + return list(map(_convert, node.elts)) |
| 51 | + elif isinstance(node, Dict): |
| 52 | + return dict((_convert(k), _convert(v)) for k, v |
| 53 | + in zip(node.keys, node.values)) |
| 54 | + elif isinstance(node, Name): |
| 55 | + try: |
| 56 | + return namespace[node.id] |
| 57 | + except KeyError: |
| 58 | + return __builtins__[node.id] |
| 59 | + elif isinstance(node, BinOp) and \ |
| 60 | + isinstance(node.op, (Add, Sub)) and \ |
| 61 | + isinstance(node.right, Num) and \ |
| 62 | + isinstance(node.right.n, complex) and \ |
| 63 | + isinstance(node.left, Num) and \ |
| 64 | + isinstance(node.left.n, (int, long, float)): |
| 65 | + left = node.left.n |
| 66 | + right = node.right.n |
| 67 | + if isinstance(node.op, Add): |
| 68 | + return left + right |
| 69 | + else: |
| 70 | + return left - right |
| 71 | + elif isinstance(node, Subscript) and \ |
| 72 | + isinstance(node.slice, Index): |
| 73 | + obj = _convert(node.value) |
| 74 | + index = _convert(node.slice.value) |
| 75 | + return safe_getitem(obj, index) |
| 76 | + |
| 77 | + raise ValueError('malformed string') |
| 78 | + return _convert(node_or_string) |
| 79 | + |
| 80 | +def safe_getitem(obj, index): |
| 81 | + if type(obj) in (list, tuple, dict, bytes) + string_types: |
| 82 | + return obj[index] |
| 83 | + raise ValueError('unsafe to lookup on object of type %s' % (type(obj), )) |
0 commit comments