144
144
# PyInstaller environment doesn't include this module.
145
145
DISALLOW_FUNCTIONS .add (help )
146
146
147
+ # Opt-in type safety experiment. Will be opt-out in 2.x
148
+
149
+ BASIC_ALLOWED_ATTRS = {
150
+ int : {
151
+ "as_integer_ratio" ,
152
+ "bit_length" ,
153
+ "conjugate" ,
154
+ "denominator" ,
155
+ "from_bytes" ,
156
+ "imag" ,
157
+ "numerator" ,
158
+ "real" ,
159
+ "to_bytes" ,
160
+ },
161
+ float : {
162
+ "as_integer_ratio" ,
163
+ "conjugate" ,
164
+ "fromhex" ,
165
+ "hex" ,
166
+ "imag" ,
167
+ "is_integer" ,
168
+ "real" ,
169
+ },
170
+ str : {
171
+ "capitalize" ,
172
+ "casefold" ,
173
+ "center" ,
174
+ "count" ,
175
+ "encode" ,
176
+ "endswith" ,
177
+ "expandtabs" ,
178
+ "find" ,
179
+ "format" ,
180
+ "format_map" ,
181
+ "index" ,
182
+ "isalnum" ,
183
+ "isalpha" ,
184
+ "isascii" ,
185
+ "isdecimal" ,
186
+ "isdigit" ,
187
+ "isidentifier" ,
188
+ "islower" ,
189
+ "isnumeric" ,
190
+ "isprintable" ,
191
+ "isspace" ,
192
+ "istitle" ,
193
+ "isupper" ,
194
+ "join" ,
195
+ "ljust" ,
196
+ "lower" ,
197
+ "lstrip" ,
198
+ "maketrans" ,
199
+ "partition" ,
200
+ "removeprefix" ,
201
+ "removesuffix" ,
202
+ "replace" ,
203
+ "rfind" ,
204
+ "rindex" ,
205
+ "rjust" ,
206
+ "rpartition" ,
207
+ "rsplit" ,
208
+ "rstrip" ,
209
+ "split" ,
210
+ "splitlines" ,
211
+ "startswith" ,
212
+ "strip" ,
213
+ "swapcase" ,
214
+ "title" ,
215
+ "translate" ,
216
+ "upper" ,
217
+ "zfill" ,
218
+ },
219
+ bool : {
220
+ "as_integer_ratio" ,
221
+ "bit_length" ,
222
+ "conjugate" ,
223
+ "denominator" ,
224
+ "from_bytes" ,
225
+ "imag" ,
226
+ "numerator" ,
227
+ "real" ,
228
+ "to_bytes" ,
229
+ },
230
+ None : {},
231
+ dict : {
232
+ "clear" ,
233
+ "copy" ,
234
+ "fromkeys" ,
235
+ "get" ,
236
+ "items" ,
237
+ "keys" ,
238
+ "pop" ,
239
+ "popitem" ,
240
+ "setdefault" ,
241
+ "update" ,
242
+ "values" ,
243
+ },
244
+ list : {
245
+ "pop" ,
246
+ "append" ,
247
+ "index" ,
248
+ "reverse" ,
249
+ "count" ,
250
+ "sort" ,
251
+ "copy" ,
252
+ "extend" ,
253
+ "clear" ,
254
+ "insert" ,
255
+ "remove" ,
256
+ },
257
+ set : {
258
+ "pop" ,
259
+ "intersection_update" ,
260
+ "intersection" ,
261
+ "issubset" ,
262
+ "symmetric_difference_update" ,
263
+ "discard" ,
264
+ "isdisjoint" ,
265
+ "difference_update" ,
266
+ "issuperset" ,
267
+ "add" ,
268
+ "copy" ,
269
+ "union" ,
270
+ "clear" ,
271
+ "update" ,
272
+ "symmetric_difference" ,
273
+ "difference" ,
274
+ "remove" ,
275
+ },
276
+ tuple : {"index" , "count" },
277
+ }
278
+
147
279
148
280
########################################
149
281
# Exceptions:
150
282
151
283
284
+ class TypeNotSpecified (Exception ):
285
+ pass
286
+
287
+
152
288
class InvalidExpression (Exception ):
153
289
"""Generic Exception"""
154
290
@@ -346,7 +482,7 @@ class SimpleEval(object): # pylint: disable=too-few-public-methods
346
482
347
483
expr = ""
348
484
349
- def __init__ (self , operators = None , functions = None , names = None ):
485
+ def __init__ (self , operators = None , functions = None , names = None , allowed_attrs = None ):
350
486
"""
351
487
Create the evaluator instance. Set up valid operators (+,-, etc)
352
488
functions (add, random, get_val, whatever) and names."""
@@ -361,6 +497,7 @@ def __init__(self, operators=None, functions=None, names=None):
361
497
self .operators = operators
362
498
self .functions = functions
363
499
self .names = names
500
+ self .allowed_attrs = allowed_attrs
364
501
365
502
self .nodes = {
366
503
ast .Expr : self ._eval_expr ,
@@ -589,6 +726,17 @@ def _eval_subscript(self, node):
589
726
return container [key ]
590
727
591
728
def _eval_attribute (self , node ):
729
+ if self .allowed_attrs is not None :
730
+ allowed_attrs = self .allowed_attrs .get (type (node .value .value ), TypeNotSpecified )
731
+ if allowed_attrs == TypeNotSpecified :
732
+ raise FeatureNotAvailable (
733
+ f"Sorry, attribute access not allowed on '{ type (node .value .value )} '"
734
+ )
735
+ if node .attr not in allowed_attrs :
736
+ raise FeatureNotAvailable (
737
+ f"Sorry, '{ node .attr } ' access not allowed on '{ type (node .value .value )} '"
738
+ )
739
+
592
740
for prefix in DISALLOW_PREFIXES :
593
741
if node .attr .startswith (prefix ):
594
742
raise FeatureNotAvailable (
@@ -764,7 +912,12 @@ def do_generator(gi=0):
764
912
return to_return
765
913
766
914
767
- def simple_eval (expr , operators = None , functions = None , names = None ):
915
+ def simple_eval (expr , operators = None , functions = None , names = None , allowed_attrs = None ):
768
916
"""Simply evaluate an expresssion"""
769
- s = SimpleEval (operators = operators , functions = functions , names = names )
917
+ s = SimpleEval (
918
+ operators = operators ,
919
+ functions = functions ,
920
+ names = names ,
921
+ allowed_attrs = allowed_attrs ,
922
+ )
770
923
return s .eval (expr )
0 commit comments