def _handle(self, node: ast.expr) -> Any: """ This is where the evaluation happens. Users should use `__getitem__`, i.e. `evaluator[node]`, as it provides caching. :param node: an AST expression to evaluate :return: the value of the node """ with suppress(ValueError): return ast.literal_eval(node) if isinstance(node, ast.Name): try: return self.names[node.id] except KeyError: raise CannotEval if isinstance(node, ast.Attribute): value = self[node.value] attr = node.attr return getattr_static(value, attr) if isinstance(node, ast.Subscript): value = self[node.value] index = node.slice if is_any(type(value), list, tuple, str, bytes, bytearray): if isinstance(index, ast.Index): key = of_type(self[index.value], int, bool) try: return value[key] except IndexError: raise CannotEval elif isinstance(index, ast.Slice): return value[slice(*[ None if p is None else of_type(self[p], int, bool) for p in [index.lower, index.upper, index.step] ])] elif is_any(type(value), dict) and isinstance(index, ast.Index): key = self[index.value] if ( safe_hash_key(key) # Have to ensure that the dict only contains keys that # can safely be compared via __eq__ to the index. # Don't bother for massive dicts to not kill performance and len(value) < 10000 and all(map(safe_hash_key, value)) ): try: return value[key] except KeyError: raise CannotEval raise CannotEval
def _handle_binop(self, node): op_type = type(node.op) op = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv, ast.Mod: operator.mod, ast.Pow: operator.pow, ast.LShift: operator.lshift, ast.RShift: operator.rshift, ast.BitOr: operator.or_, ast.BitXor: operator.xor, ast.BitAnd: operator.and_, }.get(op_type) if not op: raise CannotEval left = self[node.left] hash_type = is_any(type(left), set, frozenset, dict, OrderedDict) left = of_standard_types(left, check_dict_values=False, deep=hash_type) formatting = type(left) in (str, bytes) and op_type == ast.Mod right = of_standard_types( self[node.right], check_dict_values=formatting, deep=formatting or hash_type, ) try: return op(left, right) except Exception as e: raise CannotEval from e
def _handle_subscript(self, node): value = self[node.value] of_standard_types(value, check_dict_values=False, deep=is_any(type(value), dict, OrderedDict)) index = node.slice if isinstance(index, ast.Slice): index = slice(*[ None if p is None else self[p] for p in [index.lower, index.upper, index.step] ]) elif isinstance(index, ast.ExtSlice): raise CannotEval else: if isinstance(index, ast.Index): index = index.value index = self[index] of_standard_types(index, check_dict_values=False, deep=True) try: return value[index] except Exception: raise CannotEval
def _handle_call(self, node): if node.keywords: raise CannotEval func = self[node.func] args = [self[arg] for arg in node.args] if (is_any( func, slice, int, range, round, complex, list, tuple, abs, hex, bin, oct, bool, ord, float, len, chr, ) or len(args) == 0 and is_any(func, set, dict, str, frozenset, bytes, bytearray, object) or len(args) >= 2 and is_any(func, str, divmod, bytes, bytearray, pow)): args = [ of_standard_types(arg, check_dict_values=False, deep=False) for arg in args ] try: return func(*args) except Exception as e: raise CannotEval from e if len(args) == 1: arg = args[0] if is_any(func, id, type): try: return func(arg) except Exception as e: raise CannotEval from e if is_any(func, all, any, sum): of_type(arg, tuple, frozenset, list, set, dict, OrderedDict, deque) for x in arg: of_standard_types(x, check_dict_values=False, deep=False) try: return func(arg) except Exception as e: raise CannotEval from e if is_any(func, sorted, min, max, hash, set, dict, ascii, str, repr, frozenset): of_standard_types(arg, check_dict_values=True, deep=True) try: return func(arg) except Exception as e: raise CannotEval from e raise CannotEval