blob: e6ba9ec5de89d289fb04d459d76bf6f33edd8585 [file] [log] [blame]
# 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()