# Copyright (c) 2007-2013 LOGILAB S.A. (Paris, FRANCE) # Copyright (c) 2010 Daniel Harding # Copyright (c) 2014-2016, 2018-2020 Claudiu Popa # Copyright (c) 2014 Google, Inc. # Copyright (c) 2015-2016 Ceridwen # Copyright (c) 2019 Ashley Whetter # Copyright (c) 2019 Hugo van Kemenade # Copyright (c) 2020 hippo91 # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER """tests for the astroid variable lookup capabilities """ import functools import unittest from astroid import builder from astroid import exceptions from astroid import nodes from astroid import scoped_nodes from . import resources class LookupTest(resources.SysPathSetup, unittest.TestCase): def setUp(self): super().setUp() self.module = resources.build_file("data/module.py", "data.module") self.module2 = resources.build_file("data/module2.py", "data.module2") self.nonregr = resources.build_file("data/nonregr.py", "data.nonregr") def test_limit(self): code = """ l = [a for a,b in list] a = 1 b = a a = None def func(): c = 1 """ astroid = builder.parse(code, __name__) # a & b a = next(astroid.nodes_of_class(nodes.Name)) self.assertEqual(a.lineno, 2) self.assertEqual(len(astroid.lookup("b")[1]), 1) self.assertEqual(len(astroid.lookup("a")[1]), 1) b = astroid.locals["b"][0] stmts = a.lookup("a")[1] self.assertEqual(len(stmts), 1) self.assertEqual(b.lineno, 6) b_infer = b.infer() b_value = next(b_infer) self.assertEqual(b_value.value, 1) # c self.assertRaises(StopIteration, functools.partial(next, b_infer)) func = astroid.locals["func"][0] self.assertEqual(len(func.lookup("c")[1]), 1) def test_module(self): astroid = builder.parse("pass", __name__) # built-in objects none = next(astroid.ilookup("None")) self.assertIsNone(none.value) obj = next(astroid.ilookup("object")) self.assertIsInstance(obj, nodes.ClassDef) self.assertEqual(obj.name, "object") self.assertRaises( exceptions.InferenceError, functools.partial(next, astroid.ilookup("YOAA")) ) # XXX self.assertEqual(len(list(self.nonregr.ilookup("enumerate"))), 2) def test_class_ancestor_name(self): code = """ class A: pass class A(A): pass """ astroid = builder.parse(code, __name__) cls1 = astroid.locals["A"][0] cls2 = astroid.locals["A"][1] name = next(cls2.nodes_of_class(nodes.Name)) self.assertEqual(next(name.infer()), cls1) ### backport those test to inline code def test_method(self): method = self.module["YOUPI"]["method"] my_dict = next(method.ilookup("MY_DICT")) self.assertTrue(isinstance(my_dict, nodes.Dict), my_dict) none = next(method.ilookup("None")) self.assertIsNone(none.value) self.assertRaises( exceptions.InferenceError, functools.partial(next, method.ilookup("YOAA")) ) def test_function_argument_with_default(self): make_class = self.module2["make_class"] base = next(make_class.ilookup("base")) self.assertTrue(isinstance(base, nodes.ClassDef), base.__class__) self.assertEqual(base.name, "YO") self.assertEqual(base.root().name, "data.module") def test_class(self): klass = self.module["YOUPI"] my_dict = next(klass.ilookup("MY_DICT")) self.assertIsInstance(my_dict, nodes.Dict) none = next(klass.ilookup("None")) self.assertIsNone(none.value) obj = next(klass.ilookup("object")) self.assertIsInstance(obj, nodes.ClassDef) self.assertEqual(obj.name, "object") self.assertRaises( exceptions.InferenceError, functools.partial(next, klass.ilookup("YOAA")) ) def test_inner_classes(self): ddd = list(self.nonregr["Ccc"].ilookup("Ddd")) self.assertEqual(ddd[0].name, "Ddd") def test_loopvar_hiding(self): astroid = builder.parse( """ x = 10 for x in range(5): print (x) if x > 0: print ('#' * x) """, __name__, ) xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == "x"] # inside the loop, only one possible assignment self.assertEqual(len(xnames[0].lookup("x")[1]), 1) # outside the loop, two possible assignments self.assertEqual(len(xnames[1].lookup("x")[1]), 2) self.assertEqual(len(xnames[2].lookup("x")[1]), 2) def test_list_comps(self): astroid = builder.parse( """ print ([ i for i in range(10) ]) print ([ i for i in range(10) ]) print ( list( i for i in range(10) ) ) """, __name__, ) xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == "i"] self.assertEqual(len(xnames[0].lookup("i")[1]), 1) self.assertEqual(xnames[0].lookup("i")[1][0].lineno, 2) self.assertEqual(len(xnames[1].lookup("i")[1]), 1) self.assertEqual(xnames[1].lookup("i")[1][0].lineno, 3) self.assertEqual(len(xnames[2].lookup("i")[1]), 1) self.assertEqual(xnames[2].lookup("i")[1][0].lineno, 4) def test_list_comp_target(self): """test the list comprehension target""" astroid = builder.parse( """ ten = [ var for var in range(10) ] var """ ) var = astroid.body[1].value self.assertRaises(exceptions.NameInferenceError, var.inferred) def test_dict_comps(self): astroid = builder.parse( """ print ({ i: j for i in range(10) for j in range(10) }) print ({ i: j for i in range(10) for j in range(10) }) """, __name__, ) xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == "i"] self.assertEqual(len(xnames[0].lookup("i")[1]), 1) self.assertEqual(xnames[0].lookup("i")[1][0].lineno, 2) self.assertEqual(len(xnames[1].lookup("i")[1]), 1) self.assertEqual(xnames[1].lookup("i")[1][0].lineno, 3) xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == "j"] self.assertEqual(len(xnames[0].lookup("i")[1]), 1) self.assertEqual(xnames[0].lookup("i")[1][0].lineno, 2) self.assertEqual(len(xnames[1].lookup("i")[1]), 1) self.assertEqual(xnames[1].lookup("i")[1][0].lineno, 3) def test_set_comps(self): astroid = builder.parse( """ print ({ i for i in range(10) }) print ({ i for i in range(10) }) """, __name__, ) xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == "i"] self.assertEqual(len(xnames[0].lookup("i")[1]), 1) self.assertEqual(xnames[0].lookup("i")[1][0].lineno, 2) self.assertEqual(len(xnames[1].lookup("i")[1]), 1) self.assertEqual(xnames[1].lookup("i")[1][0].lineno, 3) def test_set_comp_closure(self): astroid = builder.parse( """ ten = { var for var in range(10) } var """ ) var = astroid.body[1].value self.assertRaises(exceptions.NameInferenceError, var.inferred) def test_generator_attributes(self): tree = builder.parse( """ def count(): "test" yield 0 iterer = count() num = iterer.next() """ ) next_node = tree.body[2].value.func gener = next_node.expr.inferred()[0] self.assertIsInstance(gener.getattr("__next__")[0], nodes.FunctionDef) self.assertIsInstance(gener.getattr("send")[0], nodes.FunctionDef) self.assertIsInstance(gener.getattr("throw")[0], nodes.FunctionDef) self.assertIsInstance(gener.getattr("close")[0], nodes.FunctionDef) def test_explicit___name__(self): code = """ class Pouet: __name__ = "pouet" p1 = Pouet() class PouetPouet(Pouet): pass p2 = Pouet() class NoName: pass p3 = NoName() """ astroid = builder.parse(code, __name__) p1 = next(astroid["p1"].infer()) self.assertTrue(p1.getattr("__name__")) p2 = next(astroid["p2"].infer()) self.assertTrue(p2.getattr("__name__")) self.assertTrue(astroid["NoName"].getattr("__name__")) p3 = next(astroid["p3"].infer()) self.assertRaises(exceptions.AttributeInferenceError, p3.getattr, "__name__") def test_function_module_special(self): astroid = builder.parse( ''' def initialize(linter): """initialize linter with checkers in this package """ package_load(linter, __path__[0]) ''', "data.__init__", ) path = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == "__path__"][ 0 ] self.assertEqual(len(path.lookup("__path__")[1]), 1) def test_builtin_lookup(self): self.assertEqual(scoped_nodes.builtin_lookup("__dict__")[1], ()) intstmts = scoped_nodes.builtin_lookup("int")[1] self.assertEqual(len(intstmts), 1) self.assertIsInstance(intstmts[0], nodes.ClassDef) self.assertEqual(intstmts[0].name, "int") # pylint: disable=no-member; Infers two potential values self.assertIs(intstmts[0], nodes.const_factory(1)._proxied) def test_decorator_arguments_lookup(self): code = """ def decorator(value): def wrapper(function): return function return wrapper class foo: member = 10 #@ @decorator(member) #This will cause pylint to complain def test(self): pass """ member = builder.extract_node(code, __name__).targets[0] it = member.infer() obj = next(it) self.assertIsInstance(obj, nodes.Const) self.assertEqual(obj.value, 10) self.assertRaises(StopIteration, functools.partial(next, it)) def test_inner_decorator_member_lookup(self): code = """ class FileA: def decorator(bla): return bla @__(decorator) def funcA(): return 4 """ decname = builder.extract_node(code, __name__) it = decname.infer() obj = next(it) self.assertIsInstance(obj, nodes.FunctionDef) self.assertRaises(StopIteration, functools.partial(next, it)) def test_static_method_lookup(self): code = """ class FileA: @staticmethod def funcA(): return 4 class Test: FileA = [1,2,3] def __init__(self): print (FileA.funcA()) """ astroid = builder.parse(code, __name__) it = astroid["Test"]["__init__"].ilookup("FileA") obj = next(it) self.assertIsInstance(obj, nodes.ClassDef) self.assertRaises(StopIteration, functools.partial(next, it)) def test_global_delete(self): code = """ def run2(): f = Frobble() class Frobble: pass Frobble.mumble = True del Frobble def run1(): f = Frobble() """ astroid = builder.parse(code, __name__) stmts = astroid["run2"].lookup("Frobbel")[1] self.assertEqual(len(stmts), 0) stmts = astroid["run1"].lookup("Frobbel")[1] self.assertEqual(len(stmts), 0) if __name__ == "__main__": unittest.main()