| # Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> |
| # Copyright (c) 2011, 2013-2015 Google, Inc. |
| # Copyright (c) 2013-2016 Claudiu Popa <pcmanticore@gmail.com> |
| # Copyright (c) 2015 Rene Zhang <rz99@cornell.edu> |
| # Copyright (c) 2015 Philip Lorenz <philip@bithub.de> |
| # 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 specific behaviour of astroid scoped nodes (i.e. module, class and |
| function) |
| """ |
| import os |
| import sys |
| from functools import partial |
| import unittest |
| import warnings |
| |
| import six |
| |
| from astroid import builder |
| from astroid import context |
| from astroid.interpreter import lookup |
| from astroid import nodes |
| from astroid.tree import scoped_nodes |
| from astroid import util |
| from astroid.exceptions import ( |
| InferenceError, AttributeInferenceError, |
| NoDefault, ResolveError, MroError, |
| InconsistentMroError, DuplicateBasesError, |
| TooManyLevelsError, |
| ) |
| from astroid.interpreter.objects import ( |
| Instance, BoundMethod, UnboundMethod, Generator |
| ) |
| from astroid import test_utils |
| from astroid.tests import resources |
| |
| |
| BUILTINS = six.moves.builtins.__name__ |
| |
| |
| def _test_dict_interface(self, node, test_attr): |
| self.assertIs(node[test_attr], node[test_attr]) |
| self.assertIn(test_attr, node) |
| node.keys() |
| node.values() |
| node.items() |
| iter(node) |
| |
| |
| class ModuleLoader(resources.SysPathSetup): |
| def setUp(self): |
| super(ModuleLoader, 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') |
| self.pack = resources.build_file('data/__init__.py', 'data') |
| |
| |
| class ModuleNodeTest(ModuleLoader, unittest.TestCase): |
| |
| def test_special_attributes(self): |
| self.assertEqual(len(self.module.getattr('__name__')), 1) |
| self.assertIsInstance(self.module.getattr('__name__')[0], nodes.Const) |
| self.assertEqual(self.module.getattr('__name__')[0].value, 'data.module') |
| self.assertEqual(len(self.module.getattr('__doc__')), 1) |
| self.assertIsInstance(self.module.getattr('__doc__')[0], nodes.Const) |
| self.assertEqual(self.module.getattr('__doc__')[0].value, 'test module for astroid\n') |
| self.assertEqual(len(self.module.getattr('__file__')), 1) |
| self.assertIsInstance(self.module.getattr('__file__')[0], nodes.Const) |
| self.assertEqual(self.module.getattr('__file__')[0].value, |
| os.path.abspath(resources.find('data/module.py'))) |
| self.assertEqual(len(self.module.getattr('__dict__')), 1) |
| self.assertIsInstance(self.module.getattr('__dict__')[0], nodes.Dict) |
| self.assertRaises(AttributeInferenceError, self.module.getattr, '__path__') |
| self.assertEqual(len(self.pack.getattr('__path__')), 1) |
| self.assertIsInstance(self.pack.getattr('__path__')[0], nodes.List) |
| |
| def test_dict_interface(self): |
| _test_dict_interface(self, self.module, 'YO') |
| |
| def test_getattr(self): |
| yo = self.module.getattr('YO')[0] |
| self.assertIsInstance(yo, nodes.ClassDef) |
| self.assertEqual(yo.name, 'YO') |
| red = next(self.module.igetattr('redirect')) |
| self.assertIsInstance(red, nodes.FunctionDef) |
| self.assertEqual(red.name, 'four_args') |
| namenode = next(self.module.igetattr('NameNode')) |
| self.assertIsInstance(namenode, nodes.ClassDef) |
| self.assertEqual(namenode.name, 'Name') |
| # resolve packageredirection |
| mod = resources.build_file('data/appl/myConnection.py', |
| 'data.appl.myConnection') |
| ssl = next(mod.igetattr('SSL1')) |
| cnx = next(ssl.igetattr('Connection')) |
| self.assertEqual(cnx.__class__, nodes.ClassDef) |
| self.assertEqual(cnx.name, 'Connection') |
| self.assertEqual(cnx.root().name, 'data.SSL1.Connection1') |
| self.assertEqual(len(self.nonregr.getattr('enumerate')), 2) |
| self.assertRaises(InferenceError, self.nonregr.igetattr, 'YOAA') |
| |
| def test_public_names(self): |
| m = builder.parse(''' |
| name = 'a' |
| _bla = 2 |
| other = 'o' |
| class Aaa: pass |
| def func(): print('yo') |
| __all__ = 'Aaa', '_bla', 'name' |
| ''') |
| values = sorted(['Aaa', 'name', 'other', 'func']) |
| self.assertEqual(sorted(m.public_names()), values) |
| m = builder.parse(''' |
| name = 'a' |
| _bla = 2 |
| other = 'o' |
| class Aaa: pass |
| |
| def func(): return 'yo' |
| ''') |
| res = sorted(m.public_names()) |
| self.assertEqual(res, values) |
| |
| m = builder.parse(''' |
| from missing import tzop |
| trop = "test" |
| __all__ = (trop, "test1", tzop, 42) |
| ''') |
| res = sorted(m.public_names()) |
| self.assertEqual(res, ["trop", "tzop"]) |
| |
| m = builder.parse(''' |
| test = tzop = 42 |
| __all__ = ('test', ) + ('tzop', ) |
| ''') |
| res = sorted(m.public_names()) |
| self.assertEqual(res, ['test', 'tzop']) |
| |
| def test_module_getattr(self): |
| data = ''' |
| appli = application |
| appli += 2 |
| del appli |
| ''' |
| astroid = builder.parse(data, __name__) |
| # test del statement not returned by getattr |
| self.assertEqual(len(astroid.getattr('appli')), 2, |
| astroid.getattr('appli')) |
| |
| def test_relative_to_absolute_name(self): |
| # package |
| mod = nodes.Module('very.multi.package', 'doc') |
| mod.package = True |
| modname = mod.relative_to_absolute_name('utils', 1) |
| self.assertEqual(modname, 'very.multi.package.utils') |
| modname = mod.relative_to_absolute_name('utils', 2) |
| self.assertEqual(modname, 'very.multi.utils') |
| modname = mod.relative_to_absolute_name('utils', 0) |
| self.assertEqual(modname, 'very.multi.package.utils') |
| modname = mod.relative_to_absolute_name('', 1) |
| self.assertEqual(modname, 'very.multi.package') |
| # non package |
| mod = nodes.Module('very.multi.module', 'doc') |
| mod.package = False |
| modname = mod.relative_to_absolute_name('utils', 0) |
| self.assertEqual(modname, 'very.multi.utils') |
| modname = mod.relative_to_absolute_name('utils', 1) |
| self.assertEqual(modname, 'very.multi.utils') |
| modname = mod.relative_to_absolute_name('utils', 2) |
| self.assertEqual(modname, 'very.utils') |
| modname = mod.relative_to_absolute_name('', 1) |
| self.assertEqual(modname, 'very.multi') |
| |
| def test_relative_to_absolute_name_beyond_top_level(self): |
| mod = nodes.Module('a.b.c', '') |
| mod.package = True |
| for level in (5, 4): |
| with self.assertRaises(TooManyLevelsError) as cm: |
| mod.relative_to_absolute_name('test', level) |
| |
| expected = ("Relative import with too many levels " |
| "({level}) for module {name!r}".format( |
| level=level - 1, name=mod.name)) |
| self.assertEqual(expected, str(cm.exception)) |
| |
| def test_import_1(self): |
| data = '''from . import subpackage''' |
| sys.path.insert(0, resources.find('data')) |
| astroid = builder.parse(data, 'package', 'data/package/__init__.py') |
| try: |
| m = astroid.import_module('', level=1) |
| self.assertEqual(m.name, 'package') |
| inferred = list(astroid.igetattr('subpackage')) |
| self.assertEqual(len(inferred), 1) |
| self.assertEqual(inferred[0].name, 'package.subpackage') |
| finally: |
| del sys.path[0] |
| |
| |
| def test_import_2(self): |
| data = '''from . import subpackage as pouet''' |
| astroid = builder.parse(data, 'package', 'data/package/__init__.py') |
| sys.path.insert(0, resources.find('data')) |
| try: |
| m = astroid.import_module('', level=1) |
| self.assertEqual(m.name, 'package') |
| inferred = list(astroid.igetattr('pouet')) |
| self.assertEqual(len(inferred), 1) |
| self.assertEqual(inferred[0].name, 'package.subpackage') |
| finally: |
| del sys.path[0] |
| |
| |
| def test_file_stream_in_memory(self): |
| data = '''irrelevant_variable is irrelevant''' |
| astroid = builder.parse(data, 'in_memory') |
| with warnings.catch_warnings(record=True): |
| with astroid.stream() as stream: |
| self.assertEqual(stream.read().decode(), data) |
| |
| def test_file_stream_physical(self): |
| path = resources.find('data/absimport.py') |
| astroid = builder.AstroidBuilder().file_build(path, 'all') |
| with open(path, 'rb') as file_io: |
| with astroid.stream() as stream: |
| self.assertEqual(stream.read(), file_io.read()) |
| |
| def test_stream_api(self): |
| path = resources.find('data/absimport.py') |
| astroid = builder.AstroidBuilder().file_build(path, 'all') |
| stream = astroid.stream() |
| self.assertTrue(hasattr(stream, 'close')) |
| with stream: |
| with open(path, 'rb') as file_io: |
| self.assertEqual(stream.read(), file_io.read()) |
| |
| |
| class FunctionNodeTest(ModuleLoader, unittest.TestCase): |
| |
| def test_special_attributes(self): |
| func = self.module2['make_class'] |
| self.assertEqual(len(func.getattr('__name__')), 1) |
| self.assertIsInstance(func.getattr('__name__')[0], nodes.Const) |
| self.assertEqual(func.getattr('__name__')[0].value, 'make_class') |
| self.assertEqual(len(func.getattr('__doc__')), 1) |
| self.assertIsInstance(func.getattr('__doc__')[0], nodes.Const) |
| self.assertEqual(func.getattr('__doc__')[0].value, 'check base is correctly resolved to Concrete0') |
| self.assertEqual(len(self.module.getattr('__dict__')), 1) |
| self.assertIsInstance(self.module.getattr('__dict__')[0], nodes.Dict) |
| |
| def test_dict_interface(self): |
| _test_dict_interface(self, self.module['global_access'], 'local') |
| |
| def test_default_value(self): |
| func = self.module2['make_class'] |
| self.assertIsInstance(func.args.default_value('base'), nodes.Attribute) |
| self.assertRaises(NoDefault, func.args.default_value, 'args') |
| self.assertRaises(NoDefault, func.args.default_value, 'kwargs') |
| self.assertRaises(NoDefault, func.args.default_value, 'any') |
| #self.assertIsInstance(func.mularg_class('args'), nodes.Tuple) |
| #self.assertIsInstance(func.mularg_class('kwargs'), nodes.Dict) |
| #self.assertIsNone(func.mularg_class('base')) |
| |
| def test_navigation(self): |
| function = self.module['global_access'] |
| self.assertEqual(function.statement(), function) |
| l_sibling = function.previous_sibling() |
| # check taking parent if child is not a stmt |
| self.assertIsInstance(l_sibling, nodes.Assign) |
| child = function.args.args[0] |
| self.assertIs(l_sibling, child.previous_sibling()) |
| r_sibling = function.next_sibling() |
| self.assertIsInstance(r_sibling, nodes.ClassDef) |
| self.assertEqual(r_sibling.name, 'YO') |
| self.assertIs(r_sibling, child.next_sibling()) |
| last = r_sibling.next_sibling().next_sibling().next_sibling() |
| self.assertIsInstance(last, nodes.Assign) |
| self.assertIsNone(last.next_sibling()) |
| first = l_sibling.root().body[0] |
| self.assertIsNone(first.previous_sibling()) |
| |
| def test_nested_args(self): |
| if sys.version_info >= (3, 0): |
| self.skipTest("nested args has been removed in py3.x") |
| code = ''' |
| def nested_args(a, (b, c, d)): |
| "nested arguments test" |
| ''' |
| tree = builder.parse(code) |
| func = tree['nested_args'] |
| self.assertEqual(sorted(func.locals), ['a', 'b', 'c', 'd']) |
| self.assertEqual(func.args.format_args(), 'a, b, c, d') |
| |
| def test_four_args(self): |
| func = self.module['four_args'] |
| #self.assertEqual(func.args.args, ['a', ('b', 'c', 'd')]) |
| local = sorted(func.keys()) |
| self.assertEqual(local, ['a', 'b', 'c', 'd']) |
| self.assertEqual(func.type, 'function') |
| |
| def test_format_args(self): |
| func = self.module2['make_class'] |
| self.assertEqual(func.args.format_args(), |
| 'any, base=data.module.YO, *args, **kwargs') |
| func = self.module['four_args'] |
| self.assertEqual(func.args.format_args(), 'a, b, c, d') |
| |
| def test_is_generator(self): |
| self.assertTrue(self.module2['generator'].is_generator()) |
| self.assertFalse(self.module2['not_a_generator'].is_generator()) |
| self.assertFalse(self.module2['make_class'].is_generator()) |
| |
| def test_is_abstract(self): |
| method = self.module2['AbstractClass']['to_override'] |
| self.assertTrue(method.is_abstract(pass_is_abstract=False)) |
| self.assertEqual(method.qname(), 'data.module2.AbstractClass.to_override') |
| self.assertEqual(method.pytype(), '%s.instancemethod' % BUILTINS) |
| method = self.module2['AbstractClass']['return_something'] |
| self.assertFalse(method.is_abstract(pass_is_abstract=False)) |
| # non regression : test raise "string" doesn't cause an exception in is_abstract |
| func = self.module2['raise_string'] |
| self.assertFalse(func.is_abstract(pass_is_abstract=False)) |
| |
| def test_is_abstract_decorated(self): |
| methods = builder.extract_node(""" |
| import abc |
| |
| class Klass(object): |
| @abc.abstractproperty |
| def prop(self): #@ |
| pass |
| |
| @abc.abstractmethod |
| def method1(self): #@ |
| pass |
| |
| some_other_decorator = lambda x: x |
| @some_other_decorator |
| def method2(self): #@ |
| pass |
| """) |
| self.assertTrue(methods[0].is_abstract(pass_is_abstract=False)) |
| self.assertTrue(methods[1].is_abstract(pass_is_abstract=False)) |
| self.assertFalse(methods[2].is_abstract(pass_is_abstract=False)) |
| |
| ## def test_raises(self): |
| ## method = self.module2['AbstractClass']['to_override'] |
| ## self.assertEqual([str(term) for term in method.raises()], |
| ## ["Call(Name('NotImplementedError'), [], None, None)"] ) |
| |
| ## def test_returns(self): |
| ## method = self.module2['AbstractClass']['return_something'] |
| ## # use string comp since Node doesn't handle __cmp__ |
| ## self.assertEqual([str(term) for term in method.returns()], |
| ## ["Const('toto')", "Const(None)"]) |
| |
| def test_lambda_pytype(self): |
| data = ''' |
| def f(): |
| g = lambda: None |
| ''' |
| astroid = builder.parse(data) |
| g = list(astroid['f'].ilookup('g'))[0] |
| self.assertEqual(g.pytype(), '%s.function' % BUILTINS) |
| |
| def test_lambda_qname(self): |
| astroid = builder.parse('lmbd = lambda: None', __name__) |
| self.assertEqual('%s.<lambda>' % __name__, astroid['lmbd'].parent.value.qname()) |
| |
| def test_is_method(self): |
| data = ''' |
| class A: |
| def meth1(self): |
| return 1 |
| @classmethod |
| def meth2(cls): |
| return 2 |
| @staticmethod |
| def meth3(): |
| return 3 |
| |
| def function(): |
| return 0 |
| |
| @staticmethod |
| def sfunction(): |
| return -1 |
| ''' |
| astroid = builder.parse(data) |
| self.assertTrue(astroid['A']['meth1'].is_method()) |
| self.assertTrue(astroid['A']['meth2'].is_method()) |
| self.assertTrue(astroid['A']['meth3'].is_method()) |
| self.assertFalse(astroid['function'].is_method()) |
| self.assertFalse(astroid['sfunction'].is_method()) |
| |
| def test_argnames(self): |
| if sys.version_info < (3, 0): |
| code = 'def f(a, (b, c), *args, **kwargs): pass' |
| else: |
| code = 'def f(a, b, c, *args, **kwargs): pass' |
| astroid = builder.parse(code, __name__) |
| self.assertEqual(astroid['f'].argnames(), ['a', 'b', 'c', 'args', 'kwargs']) |
| |
| def test_return_nothing(self): |
| """test inferred value on a function with empty return""" |
| data = ''' |
| def func(): |
| return |
| |
| a = func() |
| ''' |
| astroid = builder.parse(data) |
| call = astroid.body[1].value |
| func_vals = call.inferred() |
| self.assertEqual(len(func_vals), 1) |
| self.assertIsInstance(func_vals[0], nodes.Const) |
| self.assertIsNone(func_vals[0].value) |
| |
| def test_func_instance_attr(self): |
| """test instance attributes for functions""" |
| data = """ |
| def test(): |
| print(test.bar) |
| |
| test.bar = 1 |
| test() |
| """ |
| astroid = builder.parse(data, 'mod') |
| func = astroid.body[2].value.func.inferred()[0] |
| self.assertIsInstance(func, nodes.FunctionDef) |
| self.assertEqual(func.name, 'test') |
| one = func.getattr('bar')[0].inferred()[0] |
| self.assertIsInstance(one, nodes.Const) |
| self.assertEqual(one.value, 1) |
| |
| def test_type_builtin_descriptor_subclasses(self): |
| astroid = builder.parse(""" |
| class classonlymethod(classmethod): |
| pass |
| class staticonlymethod(staticmethod): |
| pass |
| |
| class Node: |
| @classonlymethod |
| def clsmethod_subclass(cls): |
| pass |
| @classmethod |
| def clsmethod(cls): |
| pass |
| @staticonlymethod |
| def staticmethod_subclass(cls): |
| pass |
| @staticmethod |
| def stcmethod(cls): |
| pass |
| """) |
| node = astroid.locals['Node'][0] |
| self.assertEqual(node.locals['clsmethod_subclass'][0].type, |
| 'classmethod') |
| self.assertEqual(node.locals['clsmethod'][0].type, |
| 'classmethod') |
| self.assertEqual(node.locals['staticmethod_subclass'][0].type, |
| 'staticmethod') |
| self.assertEqual(node.locals['stcmethod'][0].type, |
| 'staticmethod') |
| |
| def test_decorator_builtin_descriptors(self): |
| astroid = builder.parse(""" |
| def static_decorator(platform=None, order=50): |
| def wrapper(f): |
| f.cgm_module = True |
| f.cgm_module_order = order |
| f.cgm_module_platform = platform |
| return staticmethod(f) |
| return wrapper |
| |
| def long_classmethod_decorator(platform=None, order=50): |
| def wrapper(f): |
| def wrapper2(f): |
| def wrapper3(f): |
| f.cgm_module = True |
| f.cgm_module_order = order |
| f.cgm_module_platform = platform |
| return classmethod(f) |
| return wrapper3(f) |
| return wrapper2(f) |
| return wrapper |
| |
| def classmethod_decorator(platform=None): |
| def wrapper(f): |
| f.platform = platform |
| return classmethod(f) |
| return wrapper |
| |
| def classmethod_wrapper(fn): |
| def wrapper(cls, *args, **kwargs): |
| result = fn(cls, *args, **kwargs) |
| return result |
| |
| return classmethod(wrapper) |
| |
| def staticmethod_wrapper(fn): |
| def wrapper(*args, **kwargs): |
| return fn(*args, **kwargs) |
| return staticmethod(wrapper) |
| |
| class SomeClass(object): |
| @static_decorator() |
| def static(node, cfg): |
| pass |
| @classmethod_decorator() |
| def classmethod(cls): |
| pass |
| @static_decorator |
| def not_so_static(node): |
| pass |
| @classmethod_decorator |
| def not_so_classmethod(node): |
| pass |
| @classmethod_wrapper |
| def classmethod_wrapped(cls): |
| pass |
| @staticmethod_wrapper |
| def staticmethod_wrapped(): |
| pass |
| @long_classmethod_decorator() |
| def long_classmethod(cls): |
| pass |
| """) |
| node = astroid.locals['SomeClass'][0] |
| self.assertEqual(node.locals['static'][0].type, |
| 'staticmethod') |
| self.assertEqual(node.locals['classmethod'][0].type, |
| 'classmethod') |
| self.assertEqual(node.locals['not_so_static'][0].type, |
| 'method') |
| self.assertEqual(node.locals['not_so_classmethod'][0].type, |
| 'method') |
| self.assertEqual(node.locals['classmethod_wrapped'][0].type, |
| 'classmethod') |
| self.assertEqual(node.locals['staticmethod_wrapped'][0].type, |
| 'staticmethod') |
| self.assertEqual(node.locals['long_classmethod'][0].type, |
| 'classmethod') |
| |
| def test_igetattr(self): |
| func = builder.extract_node(''' |
| def test(): |
| pass |
| ''') |
| func.instance_attrs['value'] = [nodes.Const(42)] |
| value = func.getattr('value') |
| self.assertEqual(len(value), 1) |
| self.assertIsInstance(value[0], nodes.Const) |
| self.assertEqual(value[0].value, 42) |
| inferred = next(func.igetattr('value')) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| @test_utils.require_version(minver='3.0') |
| def test_return_annotation_is_not_the_last(self): |
| func = builder.extract_node(''' |
| def test() -> bytes: |
| pass |
| pass |
| return |
| ''') |
| last_child = func.last_child() |
| self.assertIsInstance(last_child, nodes.Return) |
| self.assertEqual(func.tolineno, 5) |
| |
| @test_utils.require_version(minver='3.6') |
| def test_method_init_subclass(self): |
| klass = builder.extract_node(''' |
| class MyClass: |
| def __init_subclass__(cls): |
| pass |
| ''') |
| method = klass['__init_subclass__'] |
| self.assertEqual([n.name for n in method.args.args], ['cls']) |
| self.assertEqual(method.type, 'classmethod') |
| |
| |
| class ClassNodeTest(ModuleLoader, unittest.TestCase): |
| |
| def test_dict_interface(self): |
| _test_dict_interface(self, self.module['YOUPI'], 'method') |
| |
| def test_cls_special_attributes_1(self): |
| cls = self.module['YO'] |
| self.assertEqual(len(cls.getattr('__bases__')), 1) |
| self.assertEqual(len(cls.getattr('__name__')), 1) |
| self.assertIsInstance(cls.getattr('__name__')[0], nodes.Const) |
| self.assertEqual(cls.getattr('__name__')[0].value, 'YO') |
| self.assertEqual(len(cls.getattr('__doc__')), 1) |
| self.assertIsInstance(cls.getattr('__doc__')[0], nodes.Const) |
| self.assertEqual(cls.getattr('__doc__')[0].value, 'hehe') |
| self.assertEqual(len(cls.getattr('__module__')), 1) |
| self.assertIsInstance(cls.getattr('__module__')[0], nodes.Const) |
| self.assertEqual(cls.getattr('__module__')[0].value, 'data.module') |
| self.assertEqual(len(cls.getattr('__dict__')), 1) |
| if not cls.newstyle: |
| self.assertRaises(AttributeInferenceError, cls.getattr, '__mro__') |
| |
| ast_list = builder.extract_node("[1]") |
| for cls in (ast_list._proxied, |
| nodes.Const(1)._proxied): |
| self.assertEqual(len(cls.getattr('__bases__')), 1) |
| self.assertEqual(len(cls.getattr('__name__')), 1) |
| self.assertEqual(len(cls.getattr('__doc__')), 1, (cls, cls.getattr('__doc__'))) |
| self.assertEqual(cls.getattr('__doc__')[0].value, cls.doc) |
| self.assertEqual(len(cls.getattr('__module__')), 1) |
| self.assertEqual(len(cls.getattr('__dict__')), 1) |
| self.assertEqual(len(cls.getattr('__mro__')), 1) |
| |
| def test__mro__attribute(self): |
| node = builder.extract_node(''' |
| class A(object): pass |
| class B(object): pass |
| class C(A, B): pass |
| ''') |
| mro = node.getattr('__mro__')[0] |
| self.assertIsInstance(mro, nodes.Tuple) |
| self.assertEqual(mro.elts, node.mro()) |
| |
| def test__bases__attribute(self): |
| node = builder.extract_node(''' |
| class A(object): pass |
| class B(object): pass |
| class C(A, B): pass |
| class D(C): pass |
| ''') |
| bases = node.getattr('__bases__')[0] |
| self.assertIsInstance(bases, nodes.Tuple) |
| self.assertEqual(len(bases.elts), 1) |
| self.assertIsInstance(bases.elts[0], nodes.ClassDef) |
| self.assertEqual(bases.elts[0].name, 'C') |
| |
| def test_cls_special_attributes_2(self): |
| astroid = builder.parse(''' |
| class A(object): pass |
| class B(object): pass |
| |
| A.__bases__ += (B,) |
| ''', __name__) |
| self.assertEqual(len(astroid['A'].getattr('__bases__')), 2) |
| self.assertIsInstance(astroid['A'].getattr('__bases__')[0], nodes.Tuple) |
| self.assertIsInstance(astroid['A'].getattr('__bases__')[1], nodes.AssignAttr) |
| |
| def test_instance_special_attributes(self): |
| instance = self.module['YO'].instantiate_class() |
| self.assertEqual(len(instance.getattr('__dict__')), 1) |
| self.assertEqual(len(instance.getattr('__doc__')), 1) |
| |
| # Don't have these special attributes, because they belong to the Instance, |
| # not BaseInstance. |
| for inst in (nodes.List(), nodes.Const(1)): |
| self.assertRaises(AttributeInferenceError, inst.getattr, '__mro__') |
| self.assertRaises(AttributeInferenceError, inst.getattr, '__bases__') |
| self.assertRaises(AttributeInferenceError, inst.getattr, '__name__') |
| self.assertRaises(AttributeInferenceError, inst.getattr, '__dict__') |
| |
| def test_navigation(self): |
| klass = self.module['YO'] |
| self.assertEqual(klass.statement(), klass) |
| l_sibling = klass.previous_sibling() |
| self.assertTrue(isinstance(l_sibling, nodes.FunctionDef), l_sibling) |
| self.assertEqual(l_sibling.name, 'global_access') |
| r_sibling = klass.next_sibling() |
| self.assertIsInstance(r_sibling, nodes.ClassDef) |
| self.assertEqual(r_sibling.name, 'YOUPI') |
| |
| def test_local_attr_ancestors(self): |
| module = builder.parse(''' |
| class A(): |
| def __init__(self): pass |
| class B(A): pass |
| class C(B): pass |
| class D(object): pass |
| class F(): pass |
| class E(F, D): pass |
| ''') |
| # Test old-style (Python 2) / new-style (Python 3+) ancestors lookups |
| klass2 = module['C'] |
| it = klass2.local_attr_ancestors('__init__') |
| anc_klass = next(it) |
| self.assertIsInstance(anc_klass, nodes.ClassDef) |
| self.assertEqual(anc_klass.name, 'A') |
| if sys.version_info[0] == 2: |
| self.assertRaises(StopIteration, partial(next, it)) |
| else: |
| anc_klass = next(it) |
| self.assertIsInstance(anc_klass, nodes.ClassDef) |
| self.assertEqual(anc_klass.name, 'object') |
| self.assertRaises(StopIteration, partial(next, it)) |
| |
| it = klass2.local_attr_ancestors('method') |
| self.assertRaises(StopIteration, partial(next, it)) |
| |
| # Test mixed-style ancestor lookups |
| klass2 = module['E'] |
| it = klass2.local_attr_ancestors('__init__') |
| anc_klass = next(it) |
| self.assertIsInstance(anc_klass, nodes.ClassDef) |
| self.assertEqual(anc_klass.name, 'object') |
| self.assertRaises(StopIteration, partial(next, it)) |
| |
| def test_local_attr_mro(self): |
| module = builder.parse(''' |
| class A(object): |
| def __init__(self): pass |
| class B(A): |
| def __init__(self, arg, arg2): pass |
| class C(A): pass |
| class D(C, B): pass |
| ''') |
| dclass = module['D'] |
| init = dclass.local_attr('__init__')[0] |
| self.assertIsInstance(init, nodes.FunctionDef) |
| self.assertEqual(init.parent.name, 'B') |
| |
| cclass = module['C'] |
| init = cclass.local_attr('__init__')[0] |
| self.assertIsInstance(init, nodes.FunctionDef) |
| self.assertEqual(init.parent.name, 'A') |
| |
| ancestors = list(dclass.local_attr_ancestors('__init__')) |
| self.assertEqual([node.name for node in ancestors], ['B', 'A', 'object']) |
| |
| def test_instance_attr_ancestors(self): |
| klass2 = self.module['YOUPI'] |
| it = klass2.instance_attr_ancestors('yo') |
| anc_klass = next(it) |
| self.assertIsInstance(anc_klass, nodes.ClassDef) |
| self.assertEqual(anc_klass.name, 'YO') |
| self.assertRaises(StopIteration, partial(next, it)) |
| klass2 = self.module['YOUPI'] |
| it = klass2.instance_attr_ancestors('member') |
| self.assertRaises(StopIteration, partial(next, it)) |
| |
| def test_methods(self): |
| expected_methods = {'__init__', 'class_method', 'method', 'static_method'} |
| klass2 = self.module['YOUPI'] |
| methods = {m.name for m in klass2.methods()} |
| self.assertTrue(methods.issuperset(expected_methods)) |
| methods = {m.name for m in klass2.mymethods()} |
| self.assertSetEqual(expected_methods, methods) |
| klass2 = self.module2['Specialization'] |
| methods = {m.name for m in klass2.mymethods()} |
| self.assertSetEqual(set([]), methods) |
| method_locals = klass2.local_attr('method') |
| self.assertEqual(len(method_locals), 1) |
| self.assertEqual(method_locals[0].name, 'method') |
| self.assertRaises(AttributeInferenceError, klass2.local_attr, 'nonexistant') |
| methods = {m.name for m in klass2.methods()} |
| self.assertTrue(methods.issuperset(expected_methods)) |
| |
| #def test_rhs(self): |
| # my_dict = self.module['MY_DICT'] |
| # self.assertIsInstance(my_dict.rhs(), nodes.Dict) |
| # a = self.module['YO']['a'] |
| # value = a.rhs() |
| # self.assertIsInstance(value, nodes.Const) |
| # self.assertEqual(value.value, 1) |
| |
| @unittest.skipIf(sys.version_info[0] >= 3, "Python 2 class semantics required.") |
| def test_ancestors(self): |
| klass = self.module['YOUPI'] |
| self.assertEqual(['YO'], [a.name for a in klass.ancestors()]) |
| klass = self.module2['Specialization'] |
| self.assertEqual(['YOUPI', 'YO'], [a.name for a in klass.ancestors()]) |
| |
| @unittest.skipIf(sys.version_info[0] < 3, "Python 3 class semantics required.") |
| def test_ancestors_py3(self): |
| klass = self.module['YOUPI'] |
| self.assertEqual(['YO', 'object'], [a.name for a in klass.ancestors()]) |
| klass = self.module2['Specialization'] |
| self.assertEqual(['YOUPI', 'YO', 'object'], [a.name for a in klass.ancestors()]) |
| |
| def test_type(self): |
| klass = self.module['YOUPI'] |
| self.assertEqual(klass.type, 'class') |
| klass = self.module2['Metaclass'] |
| self.assertEqual(klass.type, 'metaclass') |
| klass = self.module2['MyException'] |
| self.assertEqual(klass.type, 'exception') |
| klass = self.module2['MyError'] |
| self.assertEqual(klass.type, 'exception') |
| # the following class used to be detected as a metaclass |
| # after the fix which used instance._proxied in .ancestors(), |
| # when in fact it is a normal class |
| klass = self.module2['NotMetaclass'] |
| self.assertEqual(klass.type, 'class') |
| |
| def test_inner_classes(self): |
| eee = self.nonregr['Ccc']['Eee'] |
| self.assertEqual([n.name for n in eee.ancestors()], ['Ddd', 'Aaa', 'object']) |
| |
| |
| def test_classmethod_attributes(self): |
| data = ''' |
| class WebAppObject(object): |
| def registered(cls, application): |
| cls.appli = application |
| cls.schema = application.schema |
| cls.config = application.config |
| return cls |
| registered = classmethod(registered) |
| ''' |
| astroid = builder.parse(data, __name__) |
| cls = astroid['WebAppObject'] |
| self.assertEqual(list(cls.locals.keys()), ['registered']) |
| self.assertEqual(sorted(cls.external_attrs.keys()), |
| ['appli', 'config', 'schema']) |
| |
| def test_class_getattr(self): |
| data = ''' |
| class WebAppObject(object): |
| appli = application |
| appli += 2 |
| del self.appli |
| ''' |
| astroid = builder.parse(data, __name__) |
| cls = astroid['WebAppObject'] |
| # test del statement not returned by getattr |
| self.assertEqual(len(cls.getattr('appli')), 2) |
| |
| |
| def test_instance_getattr(self): |
| data = ''' |
| class WebAppObject(object): |
| def __init__(self, application): |
| self.appli = application |
| self.appli += 2 |
| del self.appli |
| ''' |
| astroid = builder.parse(data) |
| inst = Instance(astroid['WebAppObject']) |
| # test del statement not returned by getattr |
| self.assertEqual(len(inst.getattr('appli')), 2) |
| |
| |
| def test_instance_getattr_with_class_attr(self): |
| data = ''' |
| class Parent: |
| aa = 1 |
| cc = 1 |
| |
| class Klass(Parent): |
| aa = 0 |
| bb = 0 |
| |
| def incr(self, val): |
| self.cc = self.aa |
| if val > self.aa: |
| val = self.aa |
| if val < self.bb: |
| val = self.bb |
| self.aa += val |
| ''' |
| astroid = builder.parse(data) |
| inst = Instance(astroid['Klass']) |
| self.assertEqual(len(inst.getattr('aa')), 3, inst.getattr('aa')) |
| self.assertEqual(len(inst.getattr('bb')), 1, inst.getattr('bb')) |
| self.assertEqual(len(inst.getattr('cc')), 2, inst.getattr('cc')) |
| |
| |
| def test_getattr_method_transform(self): |
| data = ''' |
| class Clazz(object): |
| |
| def m1(self, value): |
| self.value = value |
| m2 = m1 |
| |
| def func(arg1, arg2): |
| "function that will be used as a method" |
| return arg1.value + arg2 |
| |
| Clazz.m3 = func |
| inst = Clazz() |
| inst.m4 = func |
| ''' |
| astroid = builder.parse(data) |
| cls = astroid['Clazz'] |
| # test del statement not returned by getattr |
| for method in ('m1', 'm2', 'm3'): |
| inferred = list(cls.igetattr(method)) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], UnboundMethod if six.PY2 else nodes.FunctionDef) |
| inferred = list(Instance(cls).igetattr(method)) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], BoundMethod) |
| inferred = list(Instance(cls).igetattr('m4')) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.FunctionDef) |
| |
| def test_getattr_from_grandpa(self): |
| data = ''' |
| class Future: |
| attr = 1 |
| |
| class Present(Future): |
| pass |
| |
| class Past(Present): |
| pass |
| ''' |
| astroid = builder.parse(data) |
| past = astroid['Past'] |
| attr = past.getattr('attr') |
| self.assertEqual(len(attr), 1) |
| attr1 = attr[0] |
| self.assertIsInstance(attr1, nodes.AssignName) |
| self.assertEqual(attr1.name, 'attr') |
| |
| def test_function_with_decorator_lineno(self): |
| data = ''' |
| @f(a=2, |
| b=3) |
| def g1(x): |
| print(x) |
| |
| @f(a=2, |
| b=3) |
| def g2(): |
| pass |
| ''' |
| astroid = builder.parse(data) |
| self.assertEqual(astroid['g1'].fromlineno, 4) |
| self.assertEqual(astroid['g1'].tolineno, 5) |
| self.assertEqual(astroid['g2'].fromlineno, 9) |
| self.assertEqual(astroid['g2'].tolineno, 10) |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_simple_metaclass(self): |
| astroid = builder.parse(""" |
| class Test(object): |
| __metaclass__ = type |
| """) |
| klass = astroid['Test'] |
| metaclass = klass.metaclass() |
| self.assertIsInstance(metaclass, scoped_nodes.ClassDef) |
| self.assertEqual(metaclass.name, 'type') |
| |
| def test_metaclass_error(self): |
| astroid = builder.parse(""" |
| class Test(object): |
| __metaclass__ = typ |
| """) |
| klass = astroid['Test'] |
| self.assertFalse(klass.metaclass()) |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_metaclass_imported(self): |
| astroid = builder.parse(""" |
| from abc import ABCMeta |
| class Test(object): |
| __metaclass__ = ABCMeta |
| """) |
| klass = astroid['Test'] |
| |
| metaclass = klass.metaclass() |
| self.assertIsInstance(metaclass, scoped_nodes.ClassDef) |
| self.assertEqual(metaclass.name, 'ABCMeta') |
| |
| def test_metaclass_yes_leak(self): |
| astroid = builder.parse(""" |
| # notice `ab` instead of `abc` |
| from ab import ABCMeta |
| |
| class Meta(object): |
| __metaclass__ = ABCMeta |
| """) |
| klass = astroid['Meta'] |
| self.assertIsNone(klass.metaclass()) |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_newstyle_and_metaclass_good(self): |
| astroid = builder.parse(""" |
| from abc import ABCMeta |
| class Test: |
| __metaclass__ = ABCMeta |
| """) |
| klass = astroid['Test'] |
| self.assertTrue(klass.newstyle) |
| self.assertEqual(klass.metaclass().name, 'ABCMeta') |
| astroid = builder.parse(""" |
| from abc import ABCMeta |
| __metaclass__ = ABCMeta |
| class Test: |
| pass |
| """) |
| klass = astroid['Test'] |
| self.assertTrue(klass.newstyle) |
| self.assertEqual(klass.metaclass().name, 'ABCMeta') |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_nested_metaclass(self): |
| astroid = builder.parse(""" |
| from abc import ABCMeta |
| class A(object): |
| __metaclass__ = ABCMeta |
| class B: pass |
| |
| __metaclass__ = ABCMeta |
| class C: |
| __metaclass__ = type |
| class D: pass |
| """) |
| a = astroid['A'] |
| b = a.locals['B'][0] |
| c = astroid['C'] |
| d = c.locals['D'][0] |
| self.assertEqual(a.metaclass().name, 'ABCMeta') |
| self.assertFalse(b.newstyle) |
| self.assertIsNone(b.metaclass()) |
| self.assertEqual(c.metaclass().name, 'type') |
| self.assertEqual(d.metaclass().name, 'ABCMeta') |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_parent_metaclass(self): |
| astroid = builder.parse(""" |
| from abc import ABCMeta |
| class Test: |
| __metaclass__ = ABCMeta |
| class SubTest(Test): pass |
| """) |
| klass = astroid['SubTest'] |
| self.assertTrue(klass.newstyle) |
| metaclass = klass.metaclass() |
| self.assertIsInstance(metaclass, scoped_nodes.ClassDef) |
| self.assertEqual(metaclass.name, 'ABCMeta') |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_metaclass_ancestors(self): |
| astroid = builder.parse(""" |
| from abc import ABCMeta |
| |
| class FirstMeta(object): |
| __metaclass__ = ABCMeta |
| |
| class SecondMeta(object): |
| __metaclass__ = type |
| |
| class Simple(object): |
| pass |
| |
| class FirstImpl(FirstMeta): pass |
| class SecondImpl(FirstImpl): pass |
| class ThirdImpl(Simple, SecondMeta): |
| pass |
| """) |
| classes = { |
| 'ABCMeta': ('FirstImpl', 'SecondImpl'), |
| 'type': ('ThirdImpl', ) |
| } |
| for metaclass, names in classes.items(): |
| for name in names: |
| impl = astroid[name] |
| meta = impl.metaclass() |
| self.assertIsInstance(meta, nodes.ClassDef) |
| self.assertEqual(meta.name, metaclass) |
| |
| def test_metaclass_type(self): |
| klass = builder.extract_node(""" |
| def with_metaclass(meta, base=object): |
| return meta("NewBase", (base, ), {}) |
| |
| class ClassWithMeta(with_metaclass(type)): #@ |
| pass |
| """) |
| self.assertEqual( |
| ['NewBase', 'object'], |
| [base.name for base in klass.ancestors()]) |
| |
| def test_no_infinite_metaclass_loop(self): |
| klass = builder.extract_node(""" |
| class SSS(object): |
| |
| class JJJ(object): |
| pass |
| |
| @classmethod |
| def Init(cls): |
| cls.JJJ = type('JJJ', (cls.JJJ,), {}) |
| |
| class AAA(SSS): |
| pass |
| |
| class BBB(AAA.JJJ): |
| pass |
| """) |
| self.assertFalse(scoped_nodes._is_metaclass(klass)) |
| ancestors = [base.name for base in klass.ancestors()] |
| self.assertIn('object', ancestors) |
| self.assertIn('JJJ', ancestors) |
| |
| def test_no_infinite_metaclass_loop_with_redefine(self): |
| nodes = builder.extract_node(""" |
| import datetime |
| |
| class A(datetime.date): #@ |
| @classmethod |
| def now(cls): |
| return cls() |
| |
| class B(datetime.date): #@ |
| pass |
| |
| datetime.date = A |
| datetime.date = B |
| """) |
| for klass in nodes: |
| self.assertEqual(None, klass.metaclass()) |
| |
| def test_metaclass_generator_hack(self): |
| klass = builder.extract_node(""" |
| import six |
| |
| class WithMeta(six.with_metaclass(type, object)): #@ |
| pass |
| """) |
| self.assertEqual( |
| ['object'], |
| [base.name for base in klass.ancestors()]) |
| self.assertEqual( |
| 'type', klass.metaclass().name) |
| |
| def test_using_six_add_metaclass(self): |
| klass = builder.extract_node(''' |
| import six |
| import abc |
| |
| @six.add_metaclass(abc.ABCMeta) |
| class WithMeta(object): |
| pass |
| ''') |
| inferred = next(klass.infer()) |
| metaclass = inferred.metaclass() |
| self.assertIsInstance(metaclass, scoped_nodes.ClassDef) |
| self.assertEqual(metaclass.qname(), 'abc.ABCMeta') |
| |
| def test_using_invalid_six_add_metaclass_call(self): |
| klass = builder.extract_node(''' |
| import six |
| @six.add_metaclass() |
| class Invalid(object): |
| pass |
| ''') |
| inferred = next(klass.infer()) |
| self.assertIsNone(inferred.metaclass()) |
| |
| def test_nonregr_infer_callresult(self): |
| astroid = builder.parse(""" |
| class Delegate(object): |
| def __get__(self, obj, cls): |
| return getattr(obj._subject, self.attribute) |
| |
| class CompositeBuilder(object): |
| __call__ = Delegate() |
| |
| builder = CompositeBuilder(result, composite) |
| tgts = builder() |
| """) |
| instance = astroid['tgts'] |
| # used to raise "'_Yes' object is not iterable", see |
| # https://bitbucket.org/logilab/astroid/issue/17 |
| self.assertEqual(list(instance.infer()), [util.Uninferable]) |
| |
| def test_slots(self): |
| astroid = builder.parse(""" |
| from collections import deque |
| from textwrap import dedent |
| |
| class First(object): #@ |
| __slots__ = ("a", "b", 1) |
| class Second(object): #@ |
| __slots__ = "a" |
| class Third(object): #@ |
| __slots__ = deque(["a", "b", "c"]) |
| class Fourth(object): #@ |
| __slots__ = {"a": "a", "b": "b"} |
| class Fifth(object): #@ |
| __slots__ = list |
| class Sixth(object): #@ |
| __slots__ = "" |
| class Seventh(object): #@ |
| __slots__ = dedent.__name__ |
| class Eight(object): #@ |
| __slots__ = ("parens") |
| class Ninth(object): #@ |
| pass |
| class Ten(object): #@ |
| __slots__ = dict({"a": "b", "c": "d"}) |
| """) |
| expected = [ |
| ('First', ('a', 'b')), |
| ('Second', ('a', )), |
| ('Third', None), |
| ('Fourth', ('a', 'b')), |
| ('Fifth', None), |
| ('Sixth', None), |
| ('Seventh', ('dedent', )), |
| ('Eight', ('parens', )), |
| ('Ninth', None), |
| ('Ten', ('a', 'c')), |
| ] |
| for cls, expected_value in expected: |
| slots = astroid[cls].slots() |
| if expected_value is None: |
| self.assertIsNone(slots) |
| else: |
| self.assertEqual(list(expected_value), |
| [node.value for node in slots]) |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_slots_py2(self): |
| module = builder.parse(""" |
| class UnicodeSlots(object): |
| __slots__ = (u"a", u"b", "c") |
| """) |
| slots = module['UnicodeSlots'].slots() |
| self.assertEqual(len(slots), 3) |
| self.assertEqual(slots[0].value, "a") |
| self.assertEqual(slots[1].value, "b") |
| self.assertEqual(slots[2].value, "c") |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_slots_py2_not_implemented(self): |
| module = builder.parse(""" |
| class OldStyle: |
| __slots__ = ("a", "b") |
| """) |
| msg = "The concept of slots is undefined for old-style classes." |
| with self.assertRaises(TypeError) as cm: |
| module['OldStyle'].slots() |
| self.assertEqual(str(cm.exception), msg) |
| |
| def test_slots_for_dict_keys(self): |
| module = builder.parse(''' |
| class Issue(object): |
| SlotDefaults = {'id': 0, 'id1':1} |
| __slots__ = SlotDefaults.keys() |
| ''') |
| cls = module['Issue'] |
| slots = cls.slots() |
| self.assertEqual(len(slots), 2) |
| self.assertEqual(slots[0].value, 'id') |
| self.assertEqual(slots[1].value, 'id1') |
| |
| def test_slots_empty_list_of_slots(self): |
| module = builder.parse(""" |
| class Klass(object): |
| __slots__ = () |
| """) |
| cls = module['Klass'] |
| self.assertEqual(cls.slots(), []) |
| |
| def test_slots_taken_from_parents(self): |
| module = builder.parse(''' |
| class FirstParent(object): |
| __slots__ = ('a', 'b', 'c') |
| class SecondParent(FirstParent): |
| __slots__ = ('d', 'e') |
| class Third(SecondParent): |
| __slots__ = ('d', ) |
| ''') |
| cls = module['Third'] |
| slots = cls.slots() |
| self.assertEqual(sorted(set(slot.value for slot in slots)), |
| ['a', 'b', 'c', 'd', 'e']) |
| |
| def test_all_ancestors_need_slots(self): |
| module = builder.parse(''' |
| class A(object): |
| __slots__ = ('a', ) |
| class B(A): pass |
| class C(B): |
| __slots__ = ('a', ) |
| ''') |
| cls = module['C'] |
| self.assertIsNone(cls.slots()) |
| cls = module['B'] |
| self.assertIsNone(cls.slots()) |
| |
| def assertEqualMro(self, klass, expected_mro): |
| self.assertEqual( |
| [member.name for member in klass.mro()], |
| expected_mro) |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_no_mro_for_old_style(self): |
| node = builder.extract_node(""" |
| class Old: pass""") |
| with self.assertRaises(TypeError) as cm: |
| node.mro() |
| self.assertEqual(str(cm.exception), "Could not obtain mro for " |
| "old-style classes.") |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_combined_newstyle_oldstyle_in_mro(self): |
| node = builder.extract_node(''' |
| class Old: |
| pass |
| class New(object): |
| pass |
| class New1(object): |
| pass |
| class New2(New, New1): |
| pass |
| class NewOld(New2, Old): #@ |
| pass |
| ''') |
| self.assertEqualMro(node, ['NewOld', 'New2', 'New', 'New1', 'object', 'Old']) |
| self.assertTrue(node.newstyle) |
| |
| def test_with_metaclass_mro(self): |
| astroid = builder.parse(""" |
| import six |
| |
| class C(object): |
| pass |
| class B(C): |
| pass |
| class A(six.with_metaclass(type, B)): |
| pass |
| """) |
| self.assertEqualMro(astroid['A'], ['A', 'B', 'C', 'object']) |
| |
| def test_mro(self): |
| astroid = builder.parse(""" |
| class C(object): pass |
| class D(dict, C): pass |
| |
| class A1(object): pass |
| class B1(A1): pass |
| class C1(A1): pass |
| class D1(B1, C1): pass |
| class E1(C1, B1): pass |
| class F1(D1, E1): pass |
| class G1(E1, D1): pass |
| |
| class Boat(object): pass |
| class DayBoat(Boat): pass |
| class WheelBoat(Boat): pass |
| class EngineLess(DayBoat): pass |
| class SmallMultihull(DayBoat): pass |
| class PedalWheelBoat(EngineLess, WheelBoat): pass |
| class SmallCatamaran(SmallMultihull): pass |
| class Pedalo(PedalWheelBoat, SmallCatamaran): pass |
| |
| class OuterA(object): |
| class Inner(object): |
| pass |
| class OuterB(OuterA): |
| class Inner(OuterA.Inner): |
| pass |
| class OuterC(OuterA): |
| class Inner(OuterA.Inner): |
| pass |
| class OuterD(OuterC): |
| class Inner(OuterC.Inner, OuterB.Inner): |
| pass |
| class Duplicates(str, str): pass |
| |
| """) |
| self.assertEqualMro(astroid['D'], ['D', 'dict', 'C', 'object']) |
| self.assertEqualMro(astroid['D1'], ['D1', 'B1', 'C1', 'A1', 'object']) |
| self.assertEqualMro(astroid['E1'], ['E1', 'C1', 'B1', 'A1', 'object']) |
| with self.assertRaises(InconsistentMroError) as cm: |
| astroid['F1'].mro() |
| A1 = astroid.getattr('A1')[0] |
| B1 = astroid.getattr('B1')[0] |
| C1 = astroid.getattr('C1')[0] |
| object_ = builder.MANAGER.astroid_cache[BUILTINS].getattr('object')[0] |
| self.assertEqual(cm.exception.mros, [[B1, C1, A1, object_], |
| [C1, B1, A1, object_]]) |
| with self.assertRaises(InconsistentMroError) as cm: |
| astroid['G1'].mro() |
| self.assertEqual(cm.exception.mros, [[C1, B1, A1, object_], |
| [B1, C1, A1, object_]]) |
| self.assertEqualMro( |
| astroid['PedalWheelBoat'], |
| ["PedalWheelBoat", "EngineLess", |
| "DayBoat", "WheelBoat", "Boat", "object"]) |
| |
| self.assertEqualMro( |
| astroid["SmallCatamaran"], |
| ["SmallCatamaran", "SmallMultihull", "DayBoat", "Boat", "object"]) |
| |
| self.assertEqualMro( |
| astroid["Pedalo"], |
| ["Pedalo", "PedalWheelBoat", "EngineLess", "SmallCatamaran", |
| "SmallMultihull", "DayBoat", "WheelBoat", "Boat", "object"]) |
| |
| self.assertEqualMro( |
| astroid['OuterD']['Inner'], |
| ['Inner', 'Inner', 'Inner', 'Inner', 'object']) |
| |
| with self.assertRaises(DuplicateBasesError) as cm: |
| astroid['Duplicates'].mro() |
| Duplicates = astroid.getattr('Duplicates')[0] |
| self.assertEqual(cm.exception.cls, Duplicates) |
| self.assertIsInstance(cm.exception, MroError) |
| self.assertIsInstance(cm.exception, ResolveError) |
| |
| def test_generator_from_infer_call_result_parent(self): |
| func = builder.extract_node(""" |
| import contextlib |
| |
| @contextlib.contextmanager |
| def test(): #@ |
| yield |
| """) |
| result = next(func.infer_call_result(func)) |
| self.assertIsInstance(result, Generator) |
| self.assertEqual(result.parent, func) |
| |
| def test_type_three_arguments(self): |
| classes = builder.extract_node(""" |
| type('A', (object, ), {"a": 1, "b": 2, missing: 3}) #@ |
| """) |
| first = next(classes.infer()) |
| self.assertIsInstance(first, nodes.ClassDef) |
| self.assertEqual(first.name, "A") |
| self.assertEqual(first.basenames, ["object"]) |
| a = next(first['a'].infer()) |
| self.assertIsInstance(a, nodes.Const) |
| self.assertEqual(a.value, 1) |
| b = next(first['b'].infer()) |
| self.assertIsInstance(b, nodes.Const) |
| self.assertEqual(b.value, 2) |
| with self.assertRaises(AttributeInferenceError): |
| first.getattr("missing") |
| |
| def test_implicit_metaclass(self): |
| cls = builder.extract_node(""" |
| class A(object): |
| pass |
| """) |
| type_cls = lookup.builtin_lookup("type")[1][0] |
| self.assertEqual(cls.implicit_metaclass(), type_cls) |
| |
| def test_implicit_metaclass_lookup(self): |
| cls = builder.extract_node(''' |
| class A(object): |
| pass |
| ''') |
| instance = cls.instantiate_class() |
| func = cls.getattr('mro') |
| self.assertEqual(len(func), 1) |
| self.assertRaises(AttributeInferenceError, instance.getattr, 'mro') |
| |
| def test_metaclass_lookup_using_same_class(self): |
| # Check that we don't have recursive attribute access for metaclass |
| cls = builder.extract_node(''' |
| class A(object): pass |
| ''') |
| self.assertEqual(len(cls.getattr('mro')), 1) |
| |
| def test_metaclass_lookup_inferrence_errors(self): |
| module = builder.parse(''' |
| import six |
| |
| class Metaclass(type): |
| foo = lala |
| |
| @six.add_metaclass(Metaclass) |
| class B(object): pass |
| ''') |
| cls = module['B'] |
| self.assertEqual(util.Uninferable, next(cls.igetattr('foo'))) |
| |
| def test_metaclass_lookup(self): |
| module = builder.parse(''' |
| import six |
| |
| class Metaclass(type): |
| foo = 42 |
| @classmethod |
| def class_method(cls): |
| pass |
| def normal_method(cls): |
| pass |
| @property |
| def meta_property(cls): |
| return 42 |
| @staticmethod |
| def static(): |
| pass |
| |
| @six.add_metaclass(Metaclass) |
| class A(object): |
| pass |
| ''') |
| acls = module['A'] |
| normal_attr = next(acls.igetattr('foo')) |
| self.assertIsInstance(normal_attr, nodes.Const) |
| self.assertEqual(normal_attr.value, 42) |
| |
| class_method = next(acls.igetattr('class_method')) |
| self.assertIsInstance(class_method, BoundMethod) |
| self.assertEqual(class_method.bound, module['Metaclass']) |
| |
| normal_method = next(acls.igetattr('normal_method')) |
| self.assertIsInstance(normal_method, BoundMethod) |
| self.assertEqual(normal_method.bound, module['A']) |
| |
| # Attribute access for properties: |
| # from the metaclass is a property object |
| # from the class that uses the metaclass, the value |
| # of the property |
| property_meta = next(module['Metaclass'].igetattr('meta_property')) |
| self.assertIsInstance(property_meta, UnboundMethod if six.PY2 else nodes.FunctionDef) |
| wrapping = scoped_nodes.get_wrapping_class(property_meta) |
| self.assertEqual(wrapping, module['Metaclass']) |
| |
| property_class = next(acls.igetattr('meta_property')) |
| self.assertIsInstance(property_class, nodes.Const) |
| self.assertEqual(property_class.value, 42) |
| |
| static = next(acls.igetattr('static')) |
| self.assertIsInstance(static, scoped_nodes.FunctionDef) |
| |
| @test_utils.require_version(maxver='3.0') |
| def test_implicit_metaclass_is_none(self): |
| cls = builder.extract_node(""" |
| class A: pass |
| """) |
| self.assertIsNone(cls.implicit_metaclass()) |
| |
| def test_local_attr_invalid_mro(self): |
| cls = builder.extract_node(""" |
| # A has an invalid MRO, local_attr should fallback |
| # to using .ancestors. |
| class A(object, object): |
| test = 42 |
| class B(A): #@ |
| pass |
| """) |
| local = cls.local_attr('test')[0] |
| inferred = next(local.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def test_has_dynamic_getattr(self): |
| module = builder.parse(""" |
| class Getattr(object): |
| def __getattr__(self, attrname): |
| pass |
| |
| class Getattribute(object): |
| def __getattribute__(self, attrname): |
| pass |
| |
| class ParentGetattr(Getattr): |
| pass |
| """) |
| self.assertTrue(module['Getattr'].has_dynamic_getattr()) |
| self.assertTrue(module['Getattribute'].has_dynamic_getattr()) |
| self.assertTrue(module['ParentGetattr'].has_dynamic_getattr()) |
| |
| # Test that objects analyzed through the live introspection |
| # aren't considered to have dynamic getattr implemented. |
| import datetime |
| astroid_builder = builder.AstroidBuilder() |
| module = astroid_builder.module_build(datetime) |
| self.assertFalse(module['timedelta'].has_dynamic_getattr()) |
| |
| def test_duplicate_bases_namedtuple(self): |
| module = builder.parse(""" |
| import collections |
| _A = collections.namedtuple('A', 'a') |
| |
| class A(_A): pass |
| |
| class B(A): pass |
| """) |
| self.assertRaises(DuplicateBasesError, module['B'].mro) |
| |
| def test_instance_bound_method_lambdas(self): |
| ast_nodes = builder.extract_node(''' |
| class Test(object): #@ |
| lam = lambda self: self |
| not_method = lambda xargs: xargs |
| Test() #@ |
| ''') |
| cls = next(ast_nodes[0].infer()) |
| self.assertIsInstance(next(cls.igetattr('lam')), scoped_nodes.Lambda) |
| self.assertIsInstance(next(cls.igetattr('not_method')), scoped_nodes.Lambda) |
| |
| instance = next(ast_nodes[1].infer()) |
| lam = next(instance.igetattr('lam')) |
| self.assertIsInstance(lam, BoundMethod) |
| not_method = next(instance.igetattr('not_method')) |
| self.assertIsInstance(not_method, scoped_nodes.Lambda) |
| |
| def test_class_extra_decorators_frame_is_not_class(self): |
| ast_node = builder.extract_node(''' |
| def ala(): |
| def bala(): #@ |
| func = 42 |
| ''') |
| self.assertEqual(ast_node.extra_decorators, []) |
| |
| def test_class_extra_decorators_only_callfunc_are_considered(self): |
| ast_node = builder.extract_node(''' |
| class Ala(object): |
| def func(self): #@ |
| pass |
| func = 42 |
| ''') |
| self.assertEqual(ast_node.extra_decorators, []) |
| |
| def test_class_extra_decorators_only_assignment_names_are_considered(self): |
| ast_node = builder.extract_node(''' |
| class Ala(object): |
| def func(self): #@ |
| pass |
| def __init__(self): |
| self.func = staticmethod(func) |
| |
| ''') |
| self.assertEqual(ast_node.extra_decorators, []) |
| |
| def test_class_extra_decorators_only_same_name_considered(self): |
| ast_node = builder.extract_node(''' |
| class Ala(object): |
| def func(self): #@ |
| pass |
| bala = staticmethod(func) |
| ''') |
| self.assertEqual(ast_node.extra_decorators, []) |
| self.assertEqual(ast_node.type, 'method') |
| |
| def test_class_extra_decorators(self): |
| static_method, clsmethod = builder.extract_node(''' |
| class Ala(object): |
| def static(self): #@ |
| pass |
| def class_method(self): #@ |
| pass |
| class_method = classmethod(class_method) |
| static = staticmethod(static) |
| ''') |
| self.assertEqual(len(clsmethod.extra_decorators), 1) |
| self.assertEqual(clsmethod.type, 'classmethod') |
| self.assertEqual(len(static_method.extra_decorators), 1) |
| self.assertEqual(static_method.type, 'staticmethod') |
| |
| def test_extra_decorators_only_class_level_assignments(self): |
| node = builder.extract_node(''' |
| def _bind(arg): |
| return arg.bind |
| |
| class A(object): |
| @property |
| def bind(self): |
| return 42 |
| def irelevant(self): |
| # This is important, because it used to trigger |
| # a maximum recursion error. |
| bind = _bind(self) |
| return bind |
| A() #@ |
| ''') |
| inferred = next(node.infer()) |
| bind = next(inferred.igetattr('bind')) |
| self.assertIsInstance(bind, nodes.Const) |
| self.assertEqual(bind.value, 42) |
| parent = bind.scope() |
| self.assertEqual(len(parent.extra_decorators), 0) |
| |
| @test_utils.require_version(minver='3.0') |
| def test_class_keywords(self): |
| data = ''' |
| class TestKlass(object, metaclass=TestMetaKlass, |
| foo=42, bar='baz'): |
| pass |
| ''' |
| astroid = builder.parse(data, __name__) |
| cls = astroid['TestKlass'] |
| self.assertEqual(len(cls.keywords), 2) |
| self.assertEqual([x.arg for x in cls.keywords], ['foo', 'bar']) |
| |
| |
| class CallSiteTest(unittest.TestCase): |
| |
| @staticmethod |
| def _call_site_from_call(call, func): |
| if call.keywords: |
| keywords = [(arg.arg, arg.value) for arg in call.keywords] |
| else: |
| keywords = [] |
| return scoped_nodes.CallSite(func, call.args, keywords) |
| |
| def _call_site_from_code(self, code): |
| call = builder.extract_node(code) |
| return self._inferred_call_site_from_call(call) |
| |
| def _inferred_call_site_from_call(self, call): |
| inferred = next(call.func.infer()) |
| return self._call_site_from_call(call, inferred) |
| |
| def _test_call_site_pair(self, code, expected_args, expected_keywords): |
| ast_node = builder.extract_node(code) |
| call_site = self._call_site_from_call(ast_node, ast_node.func) |
| self.assertEqual(len(call_site.positional_arguments), len(expected_args)) |
| self.assertEqual([arg.value for arg in call_site.positional_arguments], |
| expected_args) |
| self.assertEqual(len(call_site.keyword_arguments), len(expected_keywords)) |
| for keyword, value in expected_keywords.items(): |
| self.assertIn(keyword, call_site.keyword_arguments) |
| self.assertEqual(call_site.keyword_arguments[keyword].value, value) |
| |
| def _test_call_site(self, pairs): |
| for pair in pairs: |
| self._test_call_site_pair(*pair) |
| |
| @test_utils.require_version('3.5') |
| def test_call_site_starred_args(self): |
| pairs = [ |
| ( |
| "f(*(1, 2), *(2, 3), *(3, 4), **{'a':1}, **{'b': 2})", |
| [1, 2, 2, 3, 3, 4], |
| {'a': 1, 'b': 2} |
| ), |
| ( |
| "f(1, 2, *(3, 4), 5, *(6, 7), f=24, **{'c':3})", |
| [1, 2, 3, 4, 5, 6, 7], |
| {'f':24, 'c': 3}, |
| ), |
| # Too many fs passed into. |
| ( |
| "f(f=24, **{'f':24})", [], {}, |
| ), |
| ] |
| self._test_call_site(pairs) |
| |
| def test_call_site(self): |
| pairs = [ |
| ( |
| "f(1, 2)", [1, 2], {} |
| ), |
| ( |
| "f(1, 2, *(1, 2))", [1, 2, 1, 2], {} |
| ), |
| ( |
| "f(a=1, b=2, c=3)", [], {'a':1, 'b':2, 'c':3} |
| ) |
| ] |
| self._test_call_site(pairs) |
| |
| def _test_call_site_valid_arguments(self, values, invalid): |
| for value in values: |
| ast_node = builder.extract_node(value) |
| call_site = self._call_site_from_call(ast_node, ast_node.func) |
| self.assertEqual(call_site.has_invalid_arguments(), invalid) |
| |
| def test_call_site_valid_arguments(self): |
| values = [ |
| "f(*lala)", "f(*1)", "f(*object)", |
| ] |
| self._test_call_site_valid_arguments(values, invalid=True) |
| values = [ |
| "f()", "f(*(1, ))", "f(1, 2, *(2, 3))", |
| ] |
| self._test_call_site_valid_arguments(values, invalid=False) |
| |
| def test_duplicated_keyword_arguments(self): |
| ast_node = builder.extract_node('f(f=24, **{"f": 25})') |
| site = self._call_site_from_call(ast_node, ast_node.func) |
| self.assertIn('f', site.duplicated_keywords) |
| |
| def test_duplicate_keywords(self): |
| call_site = self._call_site_from_code(''' |
| def test(a): pass |
| test(a=1, **{'a': 2}) #@ |
| ''') |
| with self.assertRaises(InferenceError): |
| call_site.infer_argument('a', context.InferenceContext()) |
| |
| def test_keyword_argument(self): |
| call_site = self._call_site_from_code(''' |
| def test(a): pass |
| test(a=1) #@ |
| ''') |
| inferred = next(call_site.infer_argument('a', context.InferenceContext())) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 1) |
| |
| def test_too_many_positional_arguments(self): |
| call_site = self._call_site_from_code(''' |
| def test(a): pass |
| test(1, 2) #@ |
| ''') |
| with self.assertRaises(InferenceError): |
| call_site.infer_argument('a', context.InferenceContext()) |
| |
| def test_first_argument_is_instance(self): |
| call_site = self._call_site_from_code(''' |
| class A: |
| def test(self): return self |
| A().test() #@ |
| ''') |
| inferred = next(call_site.infer_argument('self', context.InferenceContext())) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, 'A') |
| |
| def test_first_argument_is_class(self): |
| module = builder.parse(''' |
| class A(type): |
| def something(cls): return cls |
| import six |
| @six.add_metaclass(A) |
| class B(object): pass |
| B.something() #@ |
| ''') |
| bound = module['B'] |
| call_context = context.InferenceContext() |
| call_context.boundnode = bound |
| call = module.body[-1].value |
| call_site = self._inferred_call_site_from_call(call) |
| inferred = next(call_site.infer_argument('cls', call_context)) |
| |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, 'B') |
| |
| def test_first_argument_is_class_for_classmethods(self): |
| call_site = self._call_site_from_code(''' |
| class A: |
| @classmethod |
| def test(cls): cls |
| A.test() #@ |
| ''') |
| inferred = next(call_site.infer_argument('cls', context.InferenceContext())) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, 'A') |
| |
| def test_positional_arguments(self): |
| call_site = self._call_site_from_code(''' |
| def test(a): pass |
| test(2) #@ |
| ''') |
| inferred = next(call_site.infer_argument('a', context.InferenceContext())) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 2) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |