diff options
author | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2023-02-09 21:54:24 +0100 |
---|---|---|
committer | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2023-02-09 23:23:31 +0100 |
commit | d4852be40cdf88d61d2c13fce22f1ec08fa23f3f (patch) | |
tree | eeafa70f68063de0d87119e871807bd4f0af6c62 /tests/test_object_model.py | |
parent | dc98a63a9fddf774e29ad340ff40cac3718faac3 (diff) | |
download | astroid-git-d4852be40cdf88d61d2c13fce22f1ec08fa23f3f.tar.gz |
[brain tests] Rename 'unittest' prefix to 'test'
Diffstat (limited to 'tests/test_object_model.py')
-rw-r--r-- | tests/test_object_model.py | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/tests/test_object_model.py b/tests/test_object_model.py new file mode 100644 index 00000000..8f41eda5 --- /dev/null +++ b/tests/test_object_model.py @@ -0,0 +1,834 @@ +# 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 +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +import unittest +import xml + +import pytest + +import astroid +from astroid import bases, builder, nodes, objects, test_utils, util +from astroid.const import PY311_PLUS +from astroid.exceptions import InferenceError + +try: + import six # pylint: disable=unused-import + + HAS_SIX = True +except ImportError: + HAS_SIX = False + + +class InstanceModelTest(unittest.TestCase): + def test_instance_special_model(self) -> None: + ast_nodes = builder.extract_node( + """ + class A: + "test" + def __init__(self): + self.a = 42 + a = A() + a.__class__ #@ + a.__module__ #@ + a.__doc__ #@ + a.__dict__ #@ + """, + module_name="fake_module", + ) + assert isinstance(ast_nodes, list) + cls = next(ast_nodes[0].infer()) + self.assertIsInstance(cls, astroid.ClassDef) + self.assertEqual(cls.name, "A") + + module = next(ast_nodes[1].infer()) + self.assertIsInstance(module, astroid.Const) + self.assertEqual(module.value, "fake_module") + + doc = next(ast_nodes[2].infer()) + self.assertIsInstance(doc, astroid.Const) + self.assertEqual(doc.value, "test") + + dunder_dict = next(ast_nodes[3].infer()) + self.assertIsInstance(dunder_dict, astroid.Dict) + attr = next(dunder_dict.getitem(astroid.Const("a")).infer()) + self.assertIsInstance(attr, astroid.Const) + self.assertEqual(attr.value, 42) + + @pytest.mark.xfail(reason="Instance lookup cannot override object model") + def test_instance_local_attributes_overrides_object_model(self): + # The instance lookup needs to be changed in order for this to work. + ast_node = builder.extract_node( + """ + class A: + @property + def __dict__(self): + return [] + A().__dict__ + """ + ) + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, astroid.List) + self.assertEqual(inferred.elts, []) + + +class BoundMethodModelTest(unittest.TestCase): + def test_bound_method_model(self) -> None: + ast_nodes = builder.extract_node( + """ + class A: + def test(self): pass + a = A() + a.test.__func__ #@ + a.test.__self__ #@ + """ + ) + assert isinstance(ast_nodes, list) + func = next(ast_nodes[0].infer()) + self.assertIsInstance(func, astroid.FunctionDef) + self.assertEqual(func.name, "test") + + self_ = next(ast_nodes[1].infer()) + self.assertIsInstance(self_, astroid.Instance) + self.assertEqual(self_.name, "A") + + +class UnboundMethodModelTest(unittest.TestCase): + def test_unbound_method_model(self) -> None: + ast_nodes = builder.extract_node( + """ + class A: + def test(self): pass + t = A.test + t.__class__ #@ + t.__func__ #@ + t.__self__ #@ + t.im_class #@ + t.im_func #@ + t.im_self #@ + """ + ) + assert isinstance(ast_nodes, list) + cls = next(ast_nodes[0].infer()) + self.assertIsInstance(cls, astroid.ClassDef) + unbound_name = "function" + + self.assertEqual(cls.name, unbound_name) + + func = next(ast_nodes[1].infer()) + self.assertIsInstance(func, astroid.FunctionDef) + self.assertEqual(func.name, "test") + + self_ = next(ast_nodes[2].infer()) + self.assertIsInstance(self_, astroid.Const) + self.assertIsNone(self_.value) + + self.assertEqual(cls.name, next(ast_nodes[3].infer()).name) + self.assertEqual(func, next(ast_nodes[4].infer())) + self.assertIsNone(next(ast_nodes[5].infer()).value) + + +class ClassModelTest(unittest.TestCase): + def test_priority_to_local_defined_values(self) -> None: + ast_node = builder.extract_node( + """ + class A: + __doc__ = "first" + A.__doc__ #@ + """ + ) + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, astroid.Const) + self.assertEqual(inferred.value, "first") + + def test_class_model_correct_mro_subclasses_proxied(self) -> None: + ast_nodes = builder.extract_node( + """ + class A(object): + pass + A.mro #@ + A.__subclasses__ #@ + """ + ) + for node in ast_nodes: + inferred = next(node.infer()) + self.assertIsInstance(inferred, astroid.BoundMethod) + self.assertIsInstance(inferred._proxied, astroid.FunctionDef) + self.assertIsInstance(inferred.bound, astroid.ClassDef) + self.assertEqual(inferred.bound.name, "type") + + def test_class_model(self) -> None: + ast_nodes = builder.extract_node( + """ + class A(object): + "test" + + class B(A): pass + class C(A): pass + + A.__module__ #@ + A.__name__ #@ + A.__qualname__ #@ + A.__doc__ #@ + A.__mro__ #@ + A.mro() #@ + A.__bases__ #@ + A.__class__ #@ + A.__dict__ #@ + A.__subclasses__() #@ + """, + module_name="fake_module", + ) + assert isinstance(ast_nodes, list) + module = next(ast_nodes[0].infer()) + self.assertIsInstance(module, astroid.Const) + self.assertEqual(module.value, "fake_module") + + name = next(ast_nodes[1].infer()) + self.assertIsInstance(name, astroid.Const) + self.assertEqual(name.value, "A") + + qualname = next(ast_nodes[2].infer()) + self.assertIsInstance(qualname, astroid.Const) + self.assertEqual(qualname.value, "fake_module.A") + + doc = next(ast_nodes[3].infer()) + self.assertIsInstance(doc, astroid.Const) + self.assertEqual(doc.value, "test") + + mro = next(ast_nodes[4].infer()) + self.assertIsInstance(mro, astroid.Tuple) + self.assertEqual([cls.name for cls in mro.elts], ["A", "object"]) + + called_mro = next(ast_nodes[5].infer()) + self.assertEqual(called_mro.elts, mro.elts) + + base_nodes = next(ast_nodes[6].infer()) + self.assertIsInstance(base_nodes, astroid.Tuple) + self.assertEqual([cls.name for cls in base_nodes.elts], ["object"]) + + cls = next(ast_nodes[7].infer()) + self.assertIsInstance(cls, astroid.ClassDef) + self.assertEqual(cls.name, "type") + + cls_dict = next(ast_nodes[8].infer()) + self.assertIsInstance(cls_dict, astroid.Dict) + + subclasses = next(ast_nodes[9].infer()) + self.assertIsInstance(subclasses, astroid.List) + self.assertEqual([cls.name for cls in subclasses.elts], ["B", "C"]) + + +class ModuleModelTest(unittest.TestCase): + def test_priority_to_local_defined_values(self) -> None: + ast_node = astroid.parse( + """ + __file__ = "mine" + """ + ) + file_value = next(ast_node.igetattr("__file__")) + self.assertIsInstance(file_value, astroid.Const) + self.assertEqual(file_value.value, "mine") + + def test__path__not_a_package(self) -> None: + ast_node = builder.extract_node( + """ + import sys + sys.__path__ #@ + """ + ) + with self.assertRaises(InferenceError): + next(ast_node.infer()) + + def test_module_model(self) -> None: + ast_nodes = builder.extract_node( + """ + import xml + xml.__path__ #@ + xml.__name__ #@ + xml.__doc__ #@ + xml.__file__ #@ + xml.__spec__ #@ + xml.__loader__ #@ + xml.__cached__ #@ + xml.__package__ #@ + xml.__dict__ #@ + xml.__init__ #@ + xml.__new__ #@ + + xml.__subclasshook__ #@ + xml.__str__ #@ + xml.__sizeof__ #@ + xml.__repr__ #@ + xml.__reduce__ #@ + + xml.__setattr__ #@ + xml.__reduce_ex__ #@ + xml.__lt__ #@ + xml.__eq__ #@ + xml.__gt__ #@ + xml.__format__ #@ + xml.__delattr___ #@ + xml.__getattribute__ #@ + xml.__hash__ #@ + xml.__dir__ #@ + xml.__call__ #@ + xml.__closure__ #@ + """ + ) + assert isinstance(ast_nodes, list) + path = next(ast_nodes[0].infer()) + self.assertIsInstance(path, astroid.List) + self.assertIsInstance(path.elts[0], astroid.Const) + self.assertEqual(path.elts[0].value, xml.__path__[0]) + + name = next(ast_nodes[1].infer()) + self.assertIsInstance(name, astroid.Const) + self.assertEqual(name.value, "xml") + + doc = next(ast_nodes[2].infer()) + self.assertIsInstance(doc, astroid.Const) + self.assertEqual(doc.value, xml.__doc__) + + file_ = next(ast_nodes[3].infer()) + self.assertIsInstance(file_, astroid.Const) + self.assertEqual(file_.value, xml.__file__.replace(".pyc", ".py")) + + for ast_node in ast_nodes[4:7]: + inferred = next(ast_node.infer()) + self.assertIs(inferred, astroid.Uninferable) + + package = next(ast_nodes[7].infer()) + self.assertIsInstance(package, astroid.Const) + self.assertEqual(package.value, "xml") + + dict_ = next(ast_nodes[8].infer()) + self.assertIsInstance(dict_, astroid.Dict) + + init_ = next(ast_nodes[9].infer()) + assert isinstance(init_, bases.BoundMethod) + init_result = next(init_.infer_call_result(nodes.Call())) + assert isinstance(init_result, nodes.Const) + assert init_result.value is None + + new_ = next(ast_nodes[10].infer()) + assert isinstance(new_, bases.BoundMethod) + + # The following nodes are just here for theoretical completeness, + # and they either return Uninferable or raise InferenceError. + for ast_node in ast_nodes[11:28]: + with pytest.raises(InferenceError): + next(ast_node.infer()) + + +class FunctionModelTest(unittest.TestCase): + def test_partial_descriptor_support(self) -> None: + bound, result = builder.extract_node( + """ + class A(object): pass + def test(self): return 42 + f = test.__get__(A(), A) + f #@ + f() #@ + """ + ) + bound = next(bound.infer()) + self.assertIsInstance(bound, astroid.BoundMethod) + self.assertEqual(bound._proxied._proxied.name, "test") + result = next(result.infer()) + self.assertIsInstance(result, astroid.Const) + self.assertEqual(result.value, 42) + + def test___get__has_extra_params_defined(self) -> None: + node = builder.extract_node( + """ + def test(self): return 42 + test.__get__ + """ + ) + inferred = next(node.infer()) + self.assertIsInstance(inferred, astroid.BoundMethod) + args = inferred.args.args + self.assertEqual(len(args), 2) + self.assertEqual([arg.name for arg in args], ["self", "type"]) + + @test_utils.require_version(minver="3.8") + def test__get__and_positional_only_args(self): + node = builder.extract_node( + """ + def test(self, a, b, /, c): return a + b + c + test.__get__(test)(1, 2, 3) + """ + ) + inferred = next(node.infer()) + assert inferred is util.Uninferable + + @pytest.mark.xfail(reason="Descriptors cannot infer what self is") + def test_descriptor_not_inferrring_self(self): + # We can't infer __get__(X, Y)() when the bounded function + # uses self, because of the tree's parent not being propagating good enough. + result = builder.extract_node( + """ + class A(object): + x = 42 + def test(self): return self.x + f = test.__get__(A(), A) + f() #@ + """ + ) + result = next(result.infer()) + self.assertIsInstance(result, astroid.Const) + self.assertEqual(result.value, 42) + + def test_descriptors_binding_invalid(self) -> None: + ast_nodes = builder.extract_node( + """ + class A: pass + def test(self): return 42 + test.__get__()() #@ + test.__get__(2, 3, 4) #@ + """ + ) + for node in ast_nodes: + with self.assertRaises(InferenceError): + next(node.infer()) + + def test_descriptor_error_regression(self) -> None: + """Make sure the following code does node cause an exception.""" + node = builder.extract_node( + """ + class MyClass: + text = "MyText" + + def mymethod1(self): + return self.text + + def mymethod2(self): + return self.mymethod1.__get__(self, MyClass) + + + cl = MyClass().mymethod2()() + cl #@ + """ + ) + assert isinstance(node, nodes.NodeNG) + [const] = node.inferred() + assert const.value == "MyText" + + def test_function_model(self) -> None: + ast_nodes = builder.extract_node( + ''' + def func(a=1, b=2): + """test""" + func.__name__ #@ + func.__doc__ #@ + func.__qualname__ #@ + func.__module__ #@ + func.__defaults__ #@ + func.__dict__ #@ + func.__globals__ #@ + func.__code__ #@ + func.__closure__ #@ + func.__init__ #@ + func.__new__ #@ + + func.__subclasshook__ #@ + func.__str__ #@ + func.__sizeof__ #@ + func.__repr__ #@ + func.__reduce__ #@ + + func.__reduce_ex__ #@ + func.__lt__ #@ + func.__eq__ #@ + func.__gt__ #@ + func.__format__ #@ + func.__delattr___ #@ + func.__getattribute__ #@ + func.__hash__ #@ + func.__dir__ #@ + func.__class__ #@ + + func.__setattr__ #@ + ''', + module_name="fake_module", + ) + assert isinstance(ast_nodes, list) + name = next(ast_nodes[0].infer()) + self.assertIsInstance(name, astroid.Const) + self.assertEqual(name.value, "func") + + doc = next(ast_nodes[1].infer()) + self.assertIsInstance(doc, astroid.Const) + self.assertEqual(doc.value, "test") + + qualname = next(ast_nodes[2].infer()) + self.assertIsInstance(qualname, astroid.Const) + self.assertEqual(qualname.value, "fake_module.func") + + module = next(ast_nodes[3].infer()) + self.assertIsInstance(module, astroid.Const) + self.assertEqual(module.value, "fake_module") + + defaults = next(ast_nodes[4].infer()) + self.assertIsInstance(defaults, astroid.Tuple) + self.assertEqual([default.value for default in defaults.elts], [1, 2]) + + dict_ = next(ast_nodes[5].infer()) + self.assertIsInstance(dict_, astroid.Dict) + + globals_ = next(ast_nodes[6].infer()) + self.assertIsInstance(globals_, astroid.Dict) + + for ast_node in ast_nodes[7:9]: + self.assertIs(next(ast_node.infer()), astroid.Uninferable) + + init_ = next(ast_nodes[9].infer()) + assert isinstance(init_, bases.BoundMethod) + init_result = next(init_.infer_call_result(nodes.Call())) + assert isinstance(init_result, nodes.Const) + assert init_result.value is None + + new_ = next(ast_nodes[10].infer()) + assert isinstance(new_, bases.BoundMethod) + + # The following nodes are just here for theoretical completeness, + # and they either return Uninferable or raise InferenceError. + for ast_node in ast_nodes[11:26]: + inferred = next(ast_node.infer()) + assert inferred is util.Uninferable + + for ast_node in ast_nodes[26:27]: + with pytest.raises(InferenceError): + inferred = next(ast_node.infer()) + + def test_empty_return_annotation(self) -> None: + ast_node = builder.extract_node( + """ + def test(): pass + test.__annotations__ + """ + ) + annotations = next(ast_node.infer()) + self.assertIsInstance(annotations, astroid.Dict) + self.assertEqual(len(annotations.items), 0) + + def test_builtin_dunder_init_does_not_crash_when_accessing_annotations( + self, + ) -> None: + ast_node = builder.extract_node( + """ + class Class: + @classmethod + def class_method(cls): + cls.__init__.__annotations__ #@ + """ + ) + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, astroid.Dict) + self.assertEqual(len(inferred.items), 0) + + def test_annotations_kwdefaults(self) -> None: + ast_node = builder.extract_node( + """ + def test(a: 1, *args: 2, f:4='lala', **kwarg:3)->2: pass + test.__annotations__ #@ + test.__kwdefaults__ #@ + """ + ) + annotations = next(ast_node[0].infer()) + self.assertIsInstance(annotations, astroid.Dict) + self.assertIsInstance( + annotations.getitem(astroid.Const("return")), astroid.Const + ) + self.assertEqual(annotations.getitem(astroid.Const("return")).value, 2) + self.assertIsInstance(annotations.getitem(astroid.Const("a")), astroid.Const) + self.assertEqual(annotations.getitem(astroid.Const("a")).value, 1) + self.assertEqual(annotations.getitem(astroid.Const("args")).value, 2) + self.assertEqual(annotations.getitem(astroid.Const("kwarg")).value, 3) + + self.assertEqual(annotations.getitem(astroid.Const("f")).value, 4) + + kwdefaults = next(ast_node[1].infer()) + self.assertIsInstance(kwdefaults, astroid.Dict) + # self.assertEqual(kwdefaults.getitem('f').value, 'lala') + + @test_utils.require_version(minver="3.8") + def test_annotation_positional_only(self): + ast_node = builder.extract_node( + """ + def test(a: 1, b: 2, /, c: 3): pass + test.__annotations__ #@ + """ + ) + annotations = next(ast_node.infer()) + self.assertIsInstance(annotations, astroid.Dict) + + self.assertIsInstance(annotations.getitem(astroid.Const("a")), astroid.Const) + self.assertEqual(annotations.getitem(astroid.Const("a")).value, 1) + self.assertEqual(annotations.getitem(astroid.Const("b")).value, 2) + self.assertEqual(annotations.getitem(astroid.Const("c")).value, 3) + + +class TestContextManagerModel: + def test_model(self) -> None: + """We use a generator to test this model.""" + ast_nodes = builder.extract_node( + """ + def test(): + "a" + yield + + gen = test() + gen.__enter__ #@ + gen.__exit__ #@ + """ + ) + assert isinstance(ast_nodes, list) + + enter = next(ast_nodes[0].infer()) + assert isinstance(enter, astroid.BoundMethod) + # Test that the method is correctly bound + assert isinstance(enter.bound, bases.Generator) + assert enter.bound._proxied.qname() == "builtins.generator" + # Test that thet FunctionDef accepts no arguments except self + # NOTE: This probably shouldn't be double proxied, but this is a + # quirck of the current model implementations. + assert isinstance(enter._proxied._proxied, nodes.FunctionDef) + assert len(enter._proxied._proxied.args.args) == 1 + assert enter._proxied._proxied.args.args[0].name == "self" + + exit_node = next(ast_nodes[1].infer()) + assert isinstance(exit_node, astroid.BoundMethod) + # Test that the FunctionDef accepts the arguments as defiend in the ObjectModel + assert isinstance(exit_node._proxied._proxied, nodes.FunctionDef) + assert len(exit_node._proxied._proxied.args.args) == 4 + assert exit_node._proxied._proxied.args.args[0].name == "self" + assert exit_node._proxied._proxied.args.args[1].name == "exc_type" + assert exit_node._proxied._proxied.args.args[2].name == "exc_value" + assert exit_node._proxied._proxied.args.args[3].name == "traceback" + + +class GeneratorModelTest(unittest.TestCase): + def test_model(self) -> None: + ast_nodes = builder.extract_node( + """ + def test(): + "a" + yield + + gen = test() + gen.__name__ #@ + gen.__doc__ #@ + gen.gi_code #@ + gen.gi_frame #@ + gen.send #@ + gen.__enter__ #@ + gen.__exit__ #@ + """ + ) + assert isinstance(ast_nodes, list) + name = next(ast_nodes[0].infer()) + self.assertEqual(name.value, "test") + + doc = next(ast_nodes[1].infer()) + self.assertEqual(doc.value, "a") + + gi_code = next(ast_nodes[2].infer()) + self.assertIsInstance(gi_code, astroid.ClassDef) + self.assertEqual(gi_code.name, "gi_code") + + gi_frame = next(ast_nodes[3].infer()) + self.assertIsInstance(gi_frame, astroid.ClassDef) + self.assertEqual(gi_frame.name, "gi_frame") + + send = next(ast_nodes[4].infer()) + self.assertIsInstance(send, astroid.BoundMethod) + + enter = next(ast_nodes[5].infer()) + assert isinstance(enter, astroid.BoundMethod) + + exit_node = next(ast_nodes[6].infer()) + assert isinstance(exit_node, astroid.BoundMethod) + + +class ExceptionModelTest(unittest.TestCase): + @staticmethod + def test_valueerror_py3() -> None: + ast_nodes = builder.extract_node( + """ + try: + x[42] + except ValueError as err: + err.args #@ + err.__traceback__ #@ + + err.message #@ + """ + ) + assert isinstance(ast_nodes, list) + args = next(ast_nodes[0].infer()) + assert isinstance(args, astroid.Tuple) + tb = next(ast_nodes[1].infer()) + # Python 3.11: If 'contextlib' is loaded, '__traceback__' + # could be set inside '__exit__' method in + # which case 'err.__traceback__' will be 'Uninferable' + try: + assert isinstance(tb, astroid.Instance) + assert tb.name == "traceback" + except AssertionError: + if PY311_PLUS: + assert tb == util.Uninferable + else: + raise + + with pytest.raises(InferenceError): + next(ast_nodes[2].infer()) + + def test_syntax_error(self) -> None: + ast_node = builder.extract_node( + """ + try: + x[42] + except SyntaxError as err: + err.text #@ + """ + ) + inferred = next(ast_node.infer()) + assert isinstance(inferred, astroid.Const) + + @unittest.skipIf(HAS_SIX, "This test fails if the six library is installed") + def test_oserror(self) -> None: + ast_nodes = builder.extract_node( + """ + try: + raise OSError("a") + except OSError as err: + err.filename #@ + err.filename2 #@ + err.errno #@ + """ + ) + expected_values = ["", "", 0] + for node, value in zip(ast_nodes, expected_values): + inferred = next(node.infer()) + assert isinstance(inferred, astroid.Const) + assert inferred.value == value + + def test_unicodedecodeerror(self) -> None: + code = """ + try: + raise UnicodeDecodeError("utf-8", "blob", 0, 1, "reason") + except UnicodeDecodeError as error: + error.object[:1] #@ + """ + node = builder.extract_node(code) + inferred = next(node.infer()) + assert isinstance(inferred, astroid.Const) + + def test_import_error(self) -> None: + ast_nodes = builder.extract_node( + """ + try: + raise ImportError("a") + except ImportError as err: + err.name #@ + err.path #@ + """ + ) + for node in ast_nodes: + inferred = next(node.infer()) + assert isinstance(inferred, astroid.Const) + assert inferred.value == "" + + def test_exception_instance_correctly_instantiated(self) -> None: + ast_node = builder.extract_node( + """ + try: + raise ImportError("a") + except ImportError as err: + err #@ + """ + ) + inferred = next(ast_node.infer()) + assert isinstance(inferred, astroid.Instance) + cls = next(inferred.igetattr("__class__")) + assert isinstance(cls, astroid.ClassDef) + + +class DictObjectModelTest(unittest.TestCase): + def test__class__(self) -> None: + ast_node = builder.extract_node("{}.__class__") + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, astroid.ClassDef) + self.assertEqual(inferred.name, "dict") + + def test_attributes_inferred_as_methods(self) -> None: + ast_nodes = builder.extract_node( + """ + {}.values #@ + {}.items #@ + {}.keys #@ + """ + ) + for node in ast_nodes: + inferred = next(node.infer()) + self.assertIsInstance(inferred, astroid.BoundMethod) + + def test_wrapper_objects_for_dict_methods_python3(self) -> None: + ast_nodes = builder.extract_node( + """ + {1:1, 2:3}.values() #@ + {1:1, 2:3}.keys() #@ + {1:1, 2:3}.items() #@ + """ + ) + assert isinstance(ast_nodes, list) + values = next(ast_nodes[0].infer()) + self.assertIsInstance(values, objects.DictValues) + self.assertEqual([elt.value for elt in values.elts], [1, 3]) + keys = next(ast_nodes[1].infer()) + self.assertIsInstance(keys, objects.DictKeys) + self.assertEqual([elt.value for elt in keys.elts], [1, 2]) + items = next(ast_nodes[2].infer()) + self.assertIsInstance(items, objects.DictItems) + + +class TestExceptionInstanceModel: + """Tests for ExceptionInstanceModel.""" + + def test_str_argument_not_required(self) -> None: + """Test that the first argument to an exception does not need to be a str.""" + ast_node = builder.extract_node( + """ + BaseException() #@ + """ + ) + args: nodes.Tuple = next(ast_node.infer()).getattr("args")[0] + # BaseException doesn't have any required args, not even a string + assert not args.elts + + +class LruCacheModelTest(unittest.TestCase): + def test_lru_cache(self) -> None: + ast_nodes = builder.extract_node( + """ + import functools + class Foo(object): + @functools.lru_cache() + def foo(): + pass + f = Foo() + f.foo.cache_clear #@ + f.foo.__wrapped__ #@ + f.foo.cache_info() #@ + """ + ) + assert isinstance(ast_nodes, list) + cache_clear = next(ast_nodes[0].infer()) + self.assertIsInstance(cache_clear, astroid.BoundMethod) + wrapped = next(ast_nodes[1].infer()) + self.assertIsInstance(wrapped, astroid.FunctionDef) + self.assertEqual(wrapped.name, "foo") + cache_info = next(ast_nodes[2].infer()) + self.assertIsInstance(cache_info, astroid.Instance) |