| # Copyright (c) 2007-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> |
| # Copyright (c) 2014 Google, Inc. |
| # Copyright (c) 2014-2016 Claudiu Popa <pcmanticore@gmail.com> |
| # Copyright (c) 2015-2016 Cara Vinson <ceridwenv@gmail.com> |
| |
| # 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 sys |
| import unittest |
| |
| from astroid import builder |
| from astroid import exceptions |
| from astroid.interpreter import lookup |
| from astroid import nodes |
| from astroid.tree import scoped_nodes |
| from astroid import util |
| from astroid.tests import resources |
| |
| |
| class LookupTest(resources.SysPathSetup, unittest.TestCase): |
| |
| def setUp(self): |
| super(LookupTest, self).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) |
| if sys.version_info < (3, 0): |
| self.assertEqual(len(astroid.lookup('b')[1]), 1) |
| self.assertEqual(len(astroid.lookup('a')[1]), 1) |
| b = astroid.locals['b'][1] |
| else: |
| 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 |
| if sys.version_info < (3, 0): |
| self.assertEqual(var.inferred(), [util.Uninferable]) |
| else: |
| 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] |
| if sys.version_info < (3, 0): |
| self.assertIsInstance(gener.getattr('next')[0], nodes.FunctionDef) |
| else: |
| 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(lookup.builtin_lookup('__dict__')[1], ()) |
| intstmts = lookup.builtin_lookup('int')[1] |
| self.assertEqual(len(intstmts), 1) |
| self.assertIsInstance(intstmts[0], nodes.ClassDef) |
| self.assertEqual(intstmts[0].name, 'int') |
| self.assertIs(intstmts[0], nodes.Const(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() |