summaryrefslogtreecommitdiff
path: root/tests/test_regrtest.py
diff options
context:
space:
mode:
authorPierre Sassoulas <pierre.sassoulas@gmail.com>2023-02-09 21:54:24 +0100
committerPierre Sassoulas <pierre.sassoulas@gmail.com>2023-02-09 23:23:31 +0100
commitd4852be40cdf88d61d2c13fce22f1ec08fa23f3f (patch)
treeeeafa70f68063de0d87119e871807bd4f0af6c62 /tests/test_regrtest.py
parentdc98a63a9fddf774e29ad340ff40cac3718faac3 (diff)
downloadastroid-git-d4852be40cdf88d61d2c13fce22f1ec08fa23f3f.tar.gz
[brain tests] Rename 'unittest' prefix to 'test'
Diffstat (limited to 'tests/test_regrtest.py')
-rw-r--r--tests/test_regrtest.py441
1 files changed, 441 insertions, 0 deletions
diff --git a/tests/test_regrtest.py b/tests/test_regrtest.py
new file mode 100644
index 00000000..783f1cc1
--- /dev/null
+++ b/tests/test_regrtest.py
@@ -0,0 +1,441 @@
+# 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 sys
+import textwrap
+import unittest
+from unittest import mock
+
+import pytest
+
+from astroid import MANAGER, Instance, bases, nodes, parse, test_utils
+from astroid.builder import AstroidBuilder, _extract_single_node, extract_node
+from astroid.const import PY38_PLUS
+from astroid.context import InferenceContext
+from astroid.exceptions import InferenceError
+from astroid.raw_building import build_module
+from astroid.util import Uninferable
+
+from . import resources
+
+try:
+ import numpy # pylint: disable=unused-import
+except ImportError:
+ HAS_NUMPY = False
+else:
+ HAS_NUMPY = True
+
+
+class NonRegressionTests(resources.AstroidCacheSetupMixin, unittest.TestCase):
+ def setUp(self) -> None:
+ sys.path.insert(0, resources.find("data"))
+ MANAGER.always_load_extensions = True
+
+ def tearDown(self) -> None:
+ MANAGER.always_load_extensions = False
+ sys.path.pop(0)
+ sys.path_importer_cache.pop(resources.find("data"), None)
+
+ def test_module_path(self) -> None:
+ man = test_utils.brainless_manager()
+ mod = man.ast_from_module_name("package.import_package_subpackage_module")
+ package = next(mod.igetattr("package"))
+ self.assertEqual(package.name, "package")
+ subpackage = next(package.igetattr("subpackage"))
+ self.assertIsInstance(subpackage, nodes.Module)
+ self.assertTrue(subpackage.package)
+ self.assertEqual(subpackage.name, "package.subpackage")
+ module = next(subpackage.igetattr("module"))
+ self.assertEqual(module.name, "package.subpackage.module")
+
+ def test_package_sidepackage(self) -> None:
+ manager = test_utils.brainless_manager()
+ assert "package.sidepackage" not in MANAGER.astroid_cache
+ package = manager.ast_from_module_name("absimp")
+ self.assertIsInstance(package, nodes.Module)
+ self.assertTrue(package.package)
+ subpackage = next(package.getattr("sidepackage")[0].infer())
+ self.assertIsInstance(subpackage, nodes.Module)
+ self.assertTrue(subpackage.package)
+ self.assertEqual(subpackage.name, "absimp.sidepackage")
+
+ def test_living_property(self) -> None:
+ builder = AstroidBuilder()
+ builder._done = {}
+ builder._module = sys.modules[__name__]
+ builder.object_build(build_module("module_name", ""), Whatever)
+
+ @unittest.skipIf(not HAS_NUMPY, "Needs numpy")
+ def test_numpy_crash(self):
+ """Test don't crash on numpy."""
+ # a crash occurred somewhere in the past, and an
+ # InferenceError instead of a crash was better, but now we even infer!
+ builder = AstroidBuilder()
+ data = """
+from numpy import multiply
+
+multiply([1, 2], [3, 4])
+"""
+ astroid = builder.string_build(data, __name__, __file__)
+ callfunc = astroid.body[1].value.func
+ inferred = callfunc.inferred()
+ self.assertEqual(len(inferred), 1)
+
+ @unittest.skipUnless(HAS_NUMPY, "Needs numpy")
+ def test_numpy_distutils(self):
+ """Special handling of virtualenv's patching of distutils shouldn't interfere
+ with numpy.distutils.
+
+ PY312_PLUS -- This test will likely become unnecessary when Python 3.12 is
+ numpy's minimum version. (numpy.distutils will be removed then.)
+ """
+ node = extract_node(
+ """
+from numpy.distutils.misc_util import is_sequence
+is_sequence("ABC") #@
+"""
+ )
+ inferred = node.inferred()
+ self.assertIsInstance(inferred[0], nodes.Const)
+
+ def test_nameconstant(self) -> None:
+ # used to fail for Python 3.4
+ builder = AstroidBuilder()
+ astroid = builder.string_build("def test(x=True): pass")
+ default = astroid.body[0].args.args[0]
+ self.assertEqual(default.name, "x")
+ self.assertEqual(next(default.infer()).value, True)
+
+ def test_recursion_regression_issue25(self) -> None:
+ builder = AstroidBuilder()
+ data = """
+import recursion as base
+
+_real_Base = base.Base
+
+class Derived(_real_Base):
+ pass
+
+def run():
+ base.Base = Derived
+"""
+ astroid = builder.string_build(data, __name__, __file__)
+ # Used to crash in _is_metaclass, due to wrong
+ # ancestors chain
+ classes = astroid.nodes_of_class(nodes.ClassDef)
+ for klass in classes:
+ # triggers the _is_metaclass call
+ klass.type # pylint: disable=pointless-statement
+
+ def test_decorator_callchain_issue42(self) -> None:
+ builder = AstroidBuilder()
+ data = """
+
+def test():
+ def factory(func):
+ def newfunc():
+ func()
+ return newfunc
+ return factory
+
+@test()
+def crash():
+ pass
+"""
+ astroid = builder.string_build(data, __name__, __file__)
+ self.assertEqual(astroid["crash"].type, "function")
+
+ def test_filter_stmts_scoping(self) -> None:
+ builder = AstroidBuilder()
+ data = """
+def test():
+ compiler = int()
+ class B(compiler.__class__):
+ pass
+ compiler = B()
+ return compiler
+"""
+ astroid = builder.string_build(data, __name__, __file__)
+ test = astroid["test"]
+ result = next(test.infer_call_result(astroid))
+ self.assertIsInstance(result, Instance)
+ base = next(result._proxied.bases[0].infer())
+ self.assertEqual(base.name, "int")
+
+ @pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions")
+ def test_filter_stmts_nested_if(self) -> None:
+ builder = AstroidBuilder()
+ data = """
+def test(val):
+ variable = None
+
+ if val == 1:
+ variable = "value"
+ if variable := "value":
+ pass
+
+ elif val == 2:
+ variable = "value_two"
+ variable = "value_two"
+
+ return variable
+"""
+ module = builder.string_build(data, __name__, __file__)
+ test_func = module["test"]
+ result = list(test_func.infer_call_result(module))
+ assert len(result) == 3
+ assert isinstance(result[0], nodes.Const)
+ assert result[0].value is None
+ assert result[0].lineno == 3
+ assert isinstance(result[1], nodes.Const)
+ assert result[1].value == "value"
+ assert result[1].lineno == 7
+ assert isinstance(result[1], nodes.Const)
+ assert result[2].value == "value_two"
+ assert result[2].lineno == 12
+
+ def test_ancestors_patching_class_recursion(self) -> None:
+ node = AstroidBuilder().string_build(
+ textwrap.dedent(
+ """
+ import string
+ Template = string.Template
+
+ class A(Template):
+ pass
+
+ class B(A):
+ pass
+
+ def test(x=False):
+ if x:
+ string.Template = A
+ else:
+ string.Template = B
+ """
+ )
+ )
+ klass = node["A"]
+ ancestors = list(klass.ancestors())
+ self.assertEqual(ancestors[0].qname(), "string.Template")
+
+ def test_ancestors_yes_in_bases(self) -> None:
+ # Test for issue https://bitbucket.org/logilab/astroid/issue/84
+ # This used to crash astroid with a TypeError, because an Uninferable
+ # node was present in the bases
+ node = extract_node(
+ """
+ def with_metaclass(meta, *bases):
+ class metaclass(meta):
+ def __new__(cls, name, this_bases, d):
+ return meta(name, bases, d)
+ return type.__new__(metaclass, 'temporary_class', (), {})
+
+ import lala
+
+ class A(with_metaclass(object, lala.lala)): #@
+ pass
+ """
+ )
+ ancestors = list(node.ancestors())
+ self.assertEqual(len(ancestors), 1)
+ self.assertEqual(ancestors[0].qname(), "builtins.object")
+
+ def test_ancestors_missing_from_function(self) -> None:
+ # Test for https://www.logilab.org/ticket/122793
+ node = extract_node(
+ """
+ def gen(): yield
+ GEN = gen()
+ next(GEN)
+ """
+ )
+ self.assertRaises(InferenceError, next, node.infer())
+
+ def test_unicode_in_docstring(self) -> None:
+ # Crashed for astroid==1.4.1
+ # Test for https://bitbucket.org/logilab/astroid/issues/273/
+
+ # In a regular file, "coding: utf-8" would have been used.
+ node = extract_node(
+ f"""
+ from __future__ import unicode_literals
+
+ class MyClass(object):
+ def method(self):
+ "With unicode : {'’'} "
+
+ instance = MyClass()
+ """
+ )
+
+ next(node.value.infer()).as_string()
+
+ def test_binop_generates_nodes_with_parents(self) -> None:
+ node = extract_node(
+ """
+ def no_op(*args):
+ pass
+ def foo(*args):
+ def inner(*more_args):
+ args + more_args #@
+ return inner
+ """
+ )
+ inferred = next(node.infer())
+ self.assertIsInstance(inferred, nodes.Tuple)
+ self.assertIsNotNone(inferred.parent)
+ self.assertIsInstance(inferred.parent, nodes.BinOp)
+
+ def test_decorator_names_inference_error_leaking(self) -> None:
+ node = extract_node(
+ """
+ class Parent(object):
+ @property
+ def foo(self):
+ pass
+
+ class Child(Parent):
+ @Parent.foo.getter
+ def foo(self): #@
+ return super(Child, self).foo + ['oink']
+ """
+ )
+ inferred = next(node.infer())
+ self.assertEqual(inferred.decoratornames(), {".Parent.foo.getter"})
+
+ def test_recursive_property_method(self) -> None:
+ node = extract_node(
+ """
+ class APropert():
+ @property
+ def property(self):
+ return self
+ APropert().property
+ """
+ )
+ next(node.infer())
+
+ def test_uninferable_string_argument_of_namedtuple(self) -> None:
+ node = extract_node(
+ """
+ import collections
+ collections.namedtuple('{}'.format("a"), '')()
+ """
+ )
+ next(node.infer())
+
+ def test_regression_inference_of_self_in_lambda(self) -> None:
+ code = """
+ class A:
+ @b(lambda self: __(self))
+ def d(self):
+ pass
+ """
+ node = extract_node(code)
+ inferred = next(node.infer())
+ assert isinstance(inferred, Instance)
+ assert inferred.qname() == ".A"
+
+
+class Whatever:
+ a = property(lambda x: x, lambda x: x) # type: ignore[misc]
+
+
+def test_ancestor_looking_up_redefined_function() -> None:
+ code = """
+ class Foo:
+ def _format(self):
+ pass
+
+ def format(self):
+ self.format = self._format
+ self.format()
+ Foo
+ """
+ node = extract_node(code)
+ inferred = next(node.infer())
+ ancestor = next(inferred.ancestors())
+ _, found = ancestor.lookup("format")
+ assert len(found) == 1
+ assert isinstance(found[0], nodes.FunctionDef)
+
+
+def test_crash_in_dunder_inference_prevented() -> None:
+ code = """
+ class MyClass():
+ def fu(self, objects):
+ delitem = dict.__delitem__.__get__(self, dict)
+ delitem #@
+ """
+ inferred = next(extract_node(code).infer())
+ assert inferred.qname() == "builtins.dict.__delitem__"
+
+
+def test_regression_crash_classmethod() -> None:
+ """Regression test for a crash reported in
+ https://github.com/PyCQA/pylint/issues/4982.
+ """
+ code = """
+ class Base:
+ @classmethod
+ def get_first_subclass(cls):
+ for subclass in cls.__subclasses__():
+ return subclass
+ return object
+
+
+ subclass = Base.get_first_subclass()
+
+
+ class Another(subclass):
+ pass
+ """
+ parse(code)
+
+
+def test_max_inferred_for_complicated_class_hierarchy() -> None:
+ """Regression test for a crash reported in
+ https://github.com/PyCQA/pylint/issues/5679.
+
+ The class hierarchy of 'sqlalchemy' is so intricate that it becomes uninferable with
+ the standard max_inferred of 100. We used to crash when this happened.
+ """
+ # Create module and get relevant nodes
+ module = resources.build_file(
+ str(resources.RESOURCE_PATH / "max_inferable_limit_for_classes" / "main.py")
+ )
+ init_attr_node = module.body[-1].body[0].body[0].value.func
+ init_object_node = module.body[-1].mro()[-1]["__init__"]
+ super_node = next(init_attr_node.expr.infer())
+
+ # Arbitrarily limit the max number of infered nodes per context
+ InferenceContext.max_inferred = -1
+ context = InferenceContext()
+
+ # Try to infer 'object.__init__' > because of limit is impossible
+ for inferred in bases._infer_stmts([init_object_node], context, frame=super):
+ assert inferred == Uninferable
+
+ # Reset inference limit
+ InferenceContext.max_inferred = 100
+ # Check that we don't crash on a previously uninferable node
+ assert super_node.getattr("__init__", context=context)[0] == Uninferable
+
+
+@mock.patch(
+ "astroid.nodes.ImportFrom._infer",
+ side_effect=RecursionError,
+)
+def test_recursion_during_inference(mocked) -> None:
+ """Check that we don't crash if we hit the recursion limit during inference."""
+ node: nodes.Call = _extract_single_node(
+ """
+ from module import something
+ something()
+ """
+ )
+ with pytest.raises(InferenceError) as error:
+ next(node.infer())
+ assert error.value.message.startswith("RecursionError raised")