astroid/tests/unittest_regrtest.py

404 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) 2006-2008, 2010-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2007 Marien Zwart <marienz@gentoo.org>
# Copyright (c) 2013-2014 Google, Inc.
# Copyright (c) 2014-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
# Copyright (c) 2019, 2021 hippo91 <guillaume.peillex@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2020 David Gilman <davidgilman1@gmail.com>
# Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
# Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
# Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
# Copyright (c) 2021 Andrew Haigh <hello@nelf.in>
# 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 sys
import textwrap
import unittest
import pytest
from astroid import MANAGER, Instance, nodes, parse, test_utils
from astroid.builder import AstroidBuilder, extract_node
from astroid.const import PY38_PLUS
from astroid.exceptions import InferenceError
from astroid.raw_building import build_module
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)
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_ssl_protocol(self) -> None:
node = extract_node(
"""
import ssl
ssl.PROTOCOL_TLSv1
"""
)
inferred = next(node.infer())
self.assertIsInstance(inferred, nodes.Const)
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)
if __name__ == "__main__":
unittest.main()