def test_group_expressions(): x = (1, 2) evaluator = Evaluator({'x': x}) tree = ast.parse('x[0] + x[x[0]]').body[0].value expressions = evaluator.find_expressions(tree) grouped = set((frozenset(nodes), value) for nodes, value in group_expressions(expressions)) expected = { (frozenset([tree.left, subscript_item(tree.right)]), x[0]), (frozenset([ tree.left.value, subscript_item(tree.right).value, tree.right.value ]), x), (frozenset([ subscript_item(tree.left), subscript_item(subscript_item(tree.right)) ]), 0), (frozenset([tree.right]), x[x[0]]), (frozenset([tree]), x[0] + x[x[0]]), } assert grouped == expected grouped = set( (frozenset(nodes), value) for nodes, value in evaluator.interesting_expressions_grouped(tree)) expected = set((nodes, value) for nodes, value in expected if value != 0) assert grouped == expected
def test_cannot_subscript(expr): with pytest.raises(Exception): eval(expr) evaluator = Evaluator({}) tree = ast.parse(expr) node = tree.body[0].value assert isinstance(node, ast.Subscript) with pytest.raises(CannotEval): str(evaluator[node])
def test_evaluator_wrong_getitem(): evaluator = Evaluator({}) with pytest.raises(TypeError, match="node should be an ast.expr, not 'str'"): # noinspection PyTypeChecker str(evaluator["foo"])
def get_all_objects(line, frame): """Given a (partial) line of code and a frame, obtains a dict containing all the relevant information about objects found on that line so that they can be formatted as part of the answer to "where()" or they can be used during the analysis of the cause of the exception. The dict returned has four keys. The first three, 'locals', 'globals', 'nonlocals', each containing a list of tuples, each tuple being of the form (name, repr(obj), obj) where name --> obj. The fourth key, 'literals', contains a list of tuples of the form ('name', obj). It is only occasionally used in helping to make suggestions regarding the cause of some exception. """ objects = { "locals": [], "globals": [], "literals": [], "builtins": [], "name, obj": [], } scopes = ( ("locals", frame.f_locals), # always have locals before globals ("globals", frame.f_globals), ) names = set([]) try: atok = ASTTokens(line, parse=True) except SyntaxError: # this should not happen atok = None if atok is not None: for scope, scope_dict in scopes: for nodes, obj in Evaluator( scope_dict).interesting_expressions_grouped(atok.tree): name = atok.get_text(nodes[0]) if name in names: continue names.add(name) objects[scope].append((name, repr(obj), obj)) objects["name, obj"].append((name, obj)) Evaluator.literal_expressions_grouped = literal_expressions_grouped for nodes, obj in Evaluator({}).literal_expressions_grouped( atok.tree): # noqa name = atok.get_text(nodes[0]) objects["literals"].append((name, obj)) objects["name, obj"].append((name, obj)) tokens = token_utils.get_significant_tokens(line) for tok in tokens: if tok.is_identifier(): name = tok.string if name in names: continue for scope, scope_dict in scopes: if name in scope_dict: names.add(name) obj = scope_dict[name] objects[scope].append((name, repr(obj), obj)) objects["name, obj"].append((name, obj)) break else: if name in dir(builtins): obj = getattr(builtins, name) objects["builtins"].append((name, repr(obj), obj)) objects["name, obj"].append((name, obj)) dotted_names = get_dotted_names(line) for name in dotted_names: for scope, scope_dict in scopes: if name not in scope_dict: continue obj = scope_dict[name] if (name, obj) not in objects["name, obj"]: objects[scope].append((name, repr(obj), obj)) objects["name, obj"].append((name, obj)) # TODO: check to see if this is still needed objects["nonlocals"] = get_nonlocal_objects(frame) return objects