8
8
import ast
9
9
from six import string_types
10
10
from six .moves import builtins
11
+ from numbers import Number
11
12
12
13
from bpython import line as line_properties
14
+ from bpython ._py3compat import py3
13
15
14
16
class EvaluationError (Exception ):
15
17
"""Raised if an exception occurred in safe_eval."""
@@ -24,7 +26,7 @@ def safe_eval(expr, namespace):
24
26
# raise
25
27
raise EvaluationError
26
28
27
- # taken from Python 2 stdlib ast.literal_eval
29
+
28
30
def simple_eval (node_or_string , namespace = None ):
29
31
"""
30
32
Safely evaluate an expression node or a string containing a Python
@@ -34,6 +36,9 @@ def simple_eval(node_or_string, namespace=None):
34
36
* variable names causing lookups in the passed in namespace or builtins
35
37
* getitem calls using the [] syntax on objects of the types above
36
38
39
+ Like the Python 3 (and unlike the Python 2) literal_eval, unary and binary
40
+ + and - operations are allowed on all builtin numeric types.
41
+
37
42
The optional namespace dict-like ought not to cause side effects on lookup
38
43
"""
39
44
# Based heavily on stdlib ast.literal_eval
@@ -43,8 +48,13 @@ def simple_eval(node_or_string, namespace=None):
43
48
node_or_string = ast .parse (node_or_string , mode = 'eval' )
44
49
if isinstance (node_or_string , ast .Expression ):
45
50
node_or_string = node_or_string .body
51
+
52
+ string_type_nodes = (ast .Str , ast .Bytes ) if py3 else (ast .Str ,)
53
+ name_type_nodes = (ast .Name , ast .NameConstant ) if py3 else (ast .Name ,)
54
+ numeric_types = (int , float , complex ) + (() if py3 else (long ,))
55
+
46
56
def _convert (node ):
47
- if isinstance (node , ast . Str ):
57
+ if isinstance (node , string_type_nodes ):
48
58
return node .s
49
59
elif isinstance (node , ast .Num ):
50
60
return node .n
@@ -55,23 +65,41 @@ def _convert(node):
55
65
elif isinstance (node , ast .Dict ):
56
66
return dict ((_convert (k ), _convert (v )) for k , v
57
67
in zip (node .keys , node .values ))
58
- elif isinstance (node , ast .Name ):
68
+
69
+ # this is a deviation from literal_eval: we allow non-literals
70
+ elif isinstance (node , name_type_nodes ):
59
71
try :
60
72
return namespace [node .id ]
61
73
except KeyError :
62
- return getattr (builtins , node .id )
74
+ try :
75
+ return getattr (builtins , node .id )
76
+ except AttributeError :
77
+ raise EvaluationError ("can't lookup %s" % node .id )
78
+
79
+ # unary + and - are allowed on any type
80
+ elif isinstance (node , ast .UnaryOp ) and \
81
+ isinstance (node .op , (ast .UAdd , ast .USub )):
82
+ # ast.literal_eval does ast typechecks here, we use type checks
83
+ operand = _convert (node .operand )
84
+ if not type (operand ) in numeric_types :
85
+ raise ValueError ("unary + and - only allowed on builtin nums" )
86
+ if isinstance (node .op , ast .UAdd ):
87
+ return + operand
88
+ else :
89
+ return - operand
63
90
elif isinstance (node , ast .BinOp ) and \
64
- isinstance (node .op , (ast .Add , ast .Sub )) and \
65
- isinstance (node .right , ast .Num ) and \
66
- isinstance (node .right .n , complex ) and \
67
- isinstance (node .left , ast .Num ) and \
68
- isinstance (node .left .n , (int , long , float )):
69
- left = node .left .n
70
- right = node .right .n
91
+ isinstance (node .op , (ast .Add , ast .Sub )):
92
+ # ast.literal_eval does ast typechecks here, we use type checks
93
+ left = _convert (node .left )
94
+ right = _convert (node .right )
95
+ if not (type (left ) in numeric_types and type (right ) in numeric_types ):
96
+ raise ValueError ("binary + and - only allowed on builtin nums" )
71
97
if isinstance (node .op , ast .Add ):
72
98
return left + right
73
99
else :
74
100
return left - right
101
+
102
+ # this is a deviation from literal_eval: we allow indexing
75
103
elif isinstance (node , ast .Subscript ) and \
76
104
isinstance (node .slice , ast .Index ):
77
105
obj = _convert (node .value )
@@ -84,7 +112,10 @@ def _convert(node):
84
112
85
113
def safe_getitem (obj , index ):
86
114
if type (obj ) in (list , tuple , dict , bytes ) + string_types :
87
- return obj [index ]
115
+ try :
116
+ return obj [index ]
117
+ except (KeyError , IndexError ):
118
+ raise EvaluationError ("can't lookup key %r on %r" % (index , obj ))
88
119
raise ValueError ('unsafe to lookup on object of type %s' % (type (obj ), ))
89
120
90
121
@@ -141,5 +172,5 @@ def parse_trees(cursor_offset, line):
141
172
raise EvaluationError ("Corresponding ASTs to right of cursor are invalid" )
142
173
try :
143
174
return simple_eval (largest_ast , namespace )
144
- except ( ValueError , KeyError , IndexError , AttributeError ) :
175
+ except ValueError :
145
176
raise EvaluationError ("Could not safely evaluate" )
0 commit comments