blob: 44b88130bcdb3db36cb0528eeac4f284cd87c6a2 [file] [log] [blame]
# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2020 David Gilman <davidgilman1@gmail.com>
# Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
# Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
# Copyright (c) 2021 hippo91 <guillaume.peillex@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/main/LICENSE
import builtins
import unittest
from astroid import builder, helpers, manager, nodes, raw_building, util
from astroid.exceptions import _NonDeducibleTypeHierarchy
from astroid.nodes.scoped_nodes import ClassDef
class TestHelpers(unittest.TestCase):
def setUp(self) -> None:
builtins_name = builtins.__name__
astroid_manager = manager.AstroidManager()
self.builtins = astroid_manager.astroid_cache[builtins_name]
self.manager = manager.AstroidManager()
def _extract(self, obj_name: str) -> ClassDef:
return self.builtins.getattr(obj_name)[0]
def _build_custom_builtin(self, obj_name: str) -> ClassDef:
proxy = raw_building.build_class(obj_name)
proxy.parent = self.builtins
return proxy
def assert_classes_equal(self, cls: ClassDef, other: ClassDef) -> None:
self.assertEqual(cls.name, other.name)
self.assertEqual(cls.parent, other.parent)
self.assertEqual(cls.qname(), other.qname())
def test_object_type(self) -> None:
pairs = [
("1", self._extract("int")),
("[]", self._extract("list")),
("{1, 2, 3}", self._extract("set")),
("{1:2, 4:3}", self._extract("dict")),
("type", self._extract("type")),
("object", self._extract("type")),
("object()", self._extract("object")),
("lambda: None", self._build_custom_builtin("function")),
("len", self._build_custom_builtin("builtin_function_or_method")),
("None", self._build_custom_builtin("NoneType")),
("import sys\nsys#@", self._build_custom_builtin("module")),
]
for code, expected in pairs:
node = builder.extract_node(code)
objtype = helpers.object_type(node)
self.assert_classes_equal(objtype, expected)
def test_object_type_classes_and_functions(self) -> None:
ast_nodes = builder.extract_node(
"""
def generator():
yield
class A(object):
def test(self):
self #@
@classmethod
def cls_method(cls): pass
@staticmethod
def static_method(): pass
A #@
A() #@
A.test #@
A().test #@
A.cls_method #@
A().cls_method #@
A.static_method #@
A().static_method #@
generator() #@
"""
)
assert isinstance(ast_nodes, list)
from_self = helpers.object_type(ast_nodes[0])
cls = next(ast_nodes[1].infer())
self.assert_classes_equal(from_self, cls)
cls_type = helpers.object_type(ast_nodes[1])
self.assert_classes_equal(cls_type, self._extract("type"))
instance_type = helpers.object_type(ast_nodes[2])
cls = next(ast_nodes[2].infer())._proxied
self.assert_classes_equal(instance_type, cls)
expected_method_types = [
(ast_nodes[3], "function"),
(ast_nodes[4], "method"),
(ast_nodes[5], "method"),
(ast_nodes[6], "method"),
(ast_nodes[7], "function"),
(ast_nodes[8], "function"),
(ast_nodes[9], "generator"),
]
for node, expected in expected_method_types:
node_type = helpers.object_type(node)
expected_type = self._build_custom_builtin(expected)
self.assert_classes_equal(node_type, expected_type)
def test_object_type_metaclasses(self) -> None:
module = builder.parse(
"""
import abc
class Meta(metaclass=abc.ABCMeta):
pass
meta_instance = Meta()
"""
)
meta_type = helpers.object_type(module["Meta"])
self.assert_classes_equal(meta_type, module["Meta"].metaclass())
meta_instance = next(module["meta_instance"].infer())
instance_type = helpers.object_type(meta_instance)
self.assert_classes_equal(instance_type, module["Meta"])
def test_object_type_most_derived(self) -> None:
node = builder.extract_node(
"""
class A(type):
def __new__(*args, **kwargs):
return type.__new__(*args, **kwargs)
class B(object): pass
class C(object, metaclass=A): pass
# The most derived metaclass of D is A rather than type.
class D(B , C): #@
pass
"""
)
assert isinstance(node, nodes.NodeNG)
metaclass = node.metaclass()
self.assertEqual(metaclass.name, "A")
obj_type = helpers.object_type(node)
self.assertEqual(metaclass, obj_type)
def test_inference_errors(self) -> None:
node = builder.extract_node(
"""
from unknown import Unknown
u = Unknown #@
"""
)
self.assertEqual(helpers.object_type(node), util.Uninferable)
def test_object_type_too_many_types(self) -> None:
node = builder.extract_node(
"""
from unknown import Unknown
def test(x):
if x:
return lambda: None
else:
return 1
test(Unknown) #@
"""
)
self.assertEqual(helpers.object_type(node), util.Uninferable)
def test_is_subtype(self) -> None:
ast_nodes = builder.extract_node(
"""
class int_subclass(int):
pass
class A(object): pass #@
class B(A): pass #@
class C(A): pass #@
int_subclass() #@
"""
)
assert isinstance(ast_nodes, list)
cls_a = ast_nodes[0]
cls_b = ast_nodes[1]
cls_c = ast_nodes[2]
int_subclass = ast_nodes[3]
int_subclass = helpers.object_type(next(int_subclass.infer()))
base_int = self._extract("int")
self.assertTrue(helpers.is_subtype(int_subclass, base_int))
self.assertTrue(helpers.is_supertype(base_int, int_subclass))
self.assertTrue(helpers.is_supertype(cls_a, cls_b))
self.assertTrue(helpers.is_supertype(cls_a, cls_c))
self.assertTrue(helpers.is_subtype(cls_b, cls_a))
self.assertTrue(helpers.is_subtype(cls_c, cls_a))
self.assertFalse(helpers.is_subtype(cls_a, cls_b))
self.assertFalse(helpers.is_subtype(cls_a, cls_b))
def test_is_subtype_supertype_mro_error(self) -> None:
cls_e, cls_f = builder.extract_node(
"""
class A(object): pass
class B(A): pass
class C(A): pass
class D(B, C): pass
class E(C, B): pass #@
class F(D, E): pass #@
"""
)
self.assertFalse(helpers.is_subtype(cls_e, cls_f))
self.assertFalse(helpers.is_subtype(cls_e, cls_f))
with self.assertRaises(_NonDeducibleTypeHierarchy):
helpers.is_subtype(cls_f, cls_e)
self.assertFalse(helpers.is_supertype(cls_f, cls_e))
def test_is_subtype_supertype_unknown_bases(self) -> None:
cls_a, cls_b = builder.extract_node(
"""
from unknown import Unknown
class A(Unknown): pass #@
class B(A): pass #@
"""
)
with self.assertRaises(_NonDeducibleTypeHierarchy):
helpers.is_subtype(cls_a, cls_b)
with self.assertRaises(_NonDeducibleTypeHierarchy):
helpers.is_supertype(cls_a, cls_b)
def test_is_subtype_supertype_unrelated_classes(self) -> None:
cls_a, cls_b = builder.extract_node(
"""
class A(object): pass #@
class B(object): pass #@
"""
)
self.assertFalse(helpers.is_subtype(cls_a, cls_b))
self.assertFalse(helpers.is_subtype(cls_b, cls_a))
self.assertFalse(helpers.is_supertype(cls_a, cls_b))
self.assertFalse(helpers.is_supertype(cls_b, cls_a))
def test_is_subtype_supertype_classes_no_type_ancestor(self) -> None:
cls_a = builder.extract_node(
"""
class A(object): #@
pass
"""
)
builtin_type = self._extract("type")
self.assertFalse(helpers.is_supertype(builtin_type, cls_a))
self.assertFalse(helpers.is_subtype(cls_a, builtin_type))
def test_is_subtype_supertype_classes_metaclasses(self) -> None:
cls_a = builder.extract_node(
"""
class A(type): #@
pass
"""
)
builtin_type = self._extract("type")
self.assertTrue(helpers.is_supertype(builtin_type, cls_a))
self.assertTrue(helpers.is_subtype(cls_a, builtin_type))
if __name__ == "__main__":
unittest.main()