forked from openkylin/astroid
2357 lines
78 KiB
Python
2357 lines
78 KiB
Python
# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
|
|
# Copyright (c) 2011, 2013-2015 Google, Inc.
|
|
# Copyright (c) 2013-2020 Claudiu Popa <pcmanticore@gmail.com>
|
|
# Copyright (c) 2013 Phil Schaf <flying-sheep@web.de>
|
|
# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
|
|
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
|
|
# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
|
|
# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
|
|
# Copyright (c) 2015 Philip Lorenz <philip@bithub.de>
|
|
# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
|
|
# Copyright (c) 2017, 2019 Łukasz Rogalski <rogalski.91@gmail.com>
|
|
# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
|
|
# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
|
|
# Copyright (c) 2018-2019 Ville Skyttä <ville.skytta@iki.fi>
|
|
# Copyright (c) 2018 brendanator <brendan.maginnis@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) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
|
|
# Copyright (c) 2019 Peter de Blanc <peter@standard.ai>
|
|
# Copyright (c) 2020 David Gilman <davidgilman1@gmail.com>
|
|
# Copyright (c) 2020 Tim Martin <tim@asymptotic.co.uk>
|
|
# Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
|
|
# Copyright (c) 2021 Tushar Sadhwani <86737547+tushar-deepsource@users.noreply.github.com>
|
|
# Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
|
|
# Copyright (c) 2021 doranid <ddandd@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
|
|
|
|
"""tests for specific behaviour of astroid scoped nodes (i.e. module, class and
|
|
function)
|
|
"""
|
|
import datetime
|
|
import os
|
|
import sys
|
|
import textwrap
|
|
import unittest
|
|
from functools import partial
|
|
from typing import Any, List, Union
|
|
|
|
import pytest
|
|
|
|
from astroid import MANAGER, builder, nodes, objects, test_utils, util
|
|
from astroid.bases import BoundMethod, Generator, Instance, UnboundMethod
|
|
from astroid.const import PY38_PLUS
|
|
from astroid.exceptions import (
|
|
AttributeInferenceError,
|
|
DuplicateBasesError,
|
|
InconsistentMroError,
|
|
InferenceError,
|
|
MroError,
|
|
NameInferenceError,
|
|
NoDefault,
|
|
ResolveError,
|
|
TooManyLevelsError,
|
|
)
|
|
from astroid.nodes.scoped_nodes.scoped_nodes import _is_metaclass
|
|
|
|
from . import resources
|
|
|
|
try:
|
|
import six # pylint: disable=unused-import
|
|
|
|
HAS_SIX = True
|
|
except ImportError:
|
|
HAS_SIX = False
|
|
|
|
|
|
def _test_dict_interface(
|
|
self: Any,
|
|
node: Union[nodes.ClassDef, nodes.FunctionDef, nodes.Module],
|
|
test_attr: str,
|
|
) -> None:
|
|
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) -> None:
|
|
super().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) -> None:
|
|
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) -> None:
|
|
_test_dict_interface(self, self.module, "YO")
|
|
|
|
def test_getattr(self) -> None:
|
|
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_wildcard_import_names(self) -> None:
|
|
m = resources.build_file("data/all.py", "all")
|
|
self.assertEqual(m.wildcard_import_names(), ["Aaa", "_bla", "name"])
|
|
m = resources.build_file("data/notall.py", "notall")
|
|
res = sorted(m.wildcard_import_names())
|
|
self.assertEqual(res, ["Aaa", "func", "name", "other"])
|
|
|
|
def test_public_names(self) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
# 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) -> None:
|
|
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 "
|
|
f"({level-1}) for module {mod.name!r}"
|
|
)
|
|
self.assertEqual(expected, str(cm.exception))
|
|
|
|
def test_import_1(self) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
data = """irrelevant_variable is irrelevant"""
|
|
astroid = builder.parse(data, "in_memory")
|
|
with astroid.stream() as stream:
|
|
self.assertEqual(stream.read().decode(), data)
|
|
|
|
def test_file_stream_physical(self) -> None:
|
|
path = resources.find("data/all.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_file_stream_api(self) -> None:
|
|
path = resources.find("data/all.py")
|
|
file_build = builder.AstroidBuilder().file_build(path, "all")
|
|
with self.assertRaises(AttributeError):
|
|
# pylint: disable=pointless-statement
|
|
file_build.file_stream
|
|
|
|
def test_stream_api(self) -> None:
|
|
path = resources.find("data/all.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) -> None:
|
|
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) -> None:
|
|
_test_dict_interface(self, self.module["global_access"], "local")
|
|
|
|
def test_default_value(self) -> None:
|
|
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) -> None:
|
|
function = self.module["global_access"]
|
|
self.assertEqual(function.statement(), function)
|
|
self.assertEqual(function.statement(future=True), 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_four_args(self) -> None:
|
|
func = self.module["four_args"]
|
|
local = sorted(func.keys())
|
|
self.assertEqual(local, ["a", "b", "c", "d"])
|
|
self.assertEqual(func.type, "function")
|
|
|
|
def test_format_args(self) -> None:
|
|
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_format_args_keyword_only_args(self) -> None:
|
|
node = (
|
|
builder.parse(
|
|
"""
|
|
def test(a: int, *, b: dict):
|
|
pass
|
|
"""
|
|
)
|
|
.body[-1]
|
|
.args
|
|
)
|
|
formatted = node.format_args()
|
|
self.assertEqual(formatted, "a: int, *, b: dict")
|
|
|
|
def test_is_generator(self) -> None:
|
|
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) -> None:
|
|
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(), "builtins.instancemethod")
|
|
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) -> None:
|
|
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
|
|
"""
|
|
)
|
|
assert len(methods) == 3
|
|
prop, method1, method2 = methods
|
|
assert isinstance(prop, nodes.FunctionDef)
|
|
assert prop.is_abstract(pass_is_abstract=False)
|
|
|
|
assert isinstance(method1, nodes.FunctionDef)
|
|
assert method1.is_abstract(pass_is_abstract=False)
|
|
|
|
assert isinstance(method2, nodes.FunctionDef)
|
|
assert not method2.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) -> None:
|
|
data = """
|
|
def f():
|
|
g = lambda: None
|
|
"""
|
|
astroid = builder.parse(data)
|
|
g = list(astroid["f"].ilookup("g"))[0]
|
|
self.assertEqual(g.pytype(), "builtins.function")
|
|
|
|
def test_lambda_qname(self) -> None:
|
|
astroid = builder.parse("lmbd = lambda: None", __name__)
|
|
self.assertEqual(f"{__name__}.<lambda>", astroid["lmbd"].parent.value.qname())
|
|
|
|
def test_is_method(self) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
"""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_no_returns_is_implicitly_none(self) -> None:
|
|
code = """
|
|
def f():
|
|
print('non-empty, non-pass, no return statements')
|
|
value = f()
|
|
value
|
|
"""
|
|
node = builder.extract_node(code)
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, nodes.Const)
|
|
assert inferred.value is None
|
|
|
|
def test_only_raises_is_not_implicitly_none(self) -> None:
|
|
code = """
|
|
def f():
|
|
raise SystemExit()
|
|
f()
|
|
"""
|
|
node = builder.extract_node(code)
|
|
assert isinstance(node, nodes.Call)
|
|
inferred = next(node.infer())
|
|
assert inferred is util.Uninferable
|
|
|
|
def test_abstract_methods_are_not_implicitly_none(self) -> None:
|
|
code = """
|
|
from abc import ABCMeta, abstractmethod
|
|
|
|
class Abstract(metaclass=ABCMeta):
|
|
@abstractmethod
|
|
def foo(self):
|
|
pass
|
|
def bar(self):
|
|
print('non-empty, non-pass, no return statements')
|
|
Abstract().foo() #@
|
|
Abstract().bar() #@
|
|
|
|
class Concrete(Abstract):
|
|
def foo(self):
|
|
return 123
|
|
Concrete().foo() #@
|
|
Concrete().bar() #@
|
|
"""
|
|
afoo, abar, cfoo, cbar = builder.extract_node(code)
|
|
|
|
assert next(afoo.infer()) is util.Uninferable
|
|
for node, value in ((abar, None), (cfoo, 123), (cbar, None)):
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, nodes.Const)
|
|
assert inferred.value == value
|
|
|
|
def test_func_instance_attr(self) -> None:
|
|
"""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) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
func = builder.extract_node(
|
|
"""
|
|
def test():
|
|
pass
|
|
"""
|
|
)
|
|
assert isinstance(func, nodes.FunctionDef)
|
|
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)
|
|
|
|
def test_return_annotation_is_not_the_last(self) -> None:
|
|
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)
|
|
|
|
def test_method_init_subclass(self) -> None:
|
|
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")
|
|
|
|
def test_dunder_class_local_to_method(self) -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class MyClass:
|
|
def test(self):
|
|
__class__ #@
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
self.assertIsInstance(inferred, nodes.ClassDef)
|
|
self.assertEqual(inferred.name, "MyClass")
|
|
|
|
def test_dunder_class_local_to_function(self) -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
def test(self):
|
|
__class__ #@
|
|
"""
|
|
)
|
|
with self.assertRaises(NameInferenceError):
|
|
next(node.infer())
|
|
|
|
def test_dunder_class_local_to_classmethod(self) -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class MyClass:
|
|
@classmethod
|
|
def test(cls):
|
|
__class__ #@
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
self.assertIsInstance(inferred, nodes.ClassDef)
|
|
self.assertEqual(inferred.name, "MyClass")
|
|
|
|
|
|
class ClassNodeTest(ModuleLoader, unittest.TestCase):
|
|
def test_dict_interface(self) -> None:
|
|
_test_dict_interface(self, self.module["YOUPI"], "method")
|
|
|
|
def test_cls_special_attributes_1(self) -> None:
|
|
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\n haha")
|
|
# YO is an old styled class for Python 2.7
|
|
# May want to stop locals from referencing namespaced variables in the future
|
|
module_attr_num = 4
|
|
self.assertEqual(len(cls.getattr("__module__")), module_attr_num)
|
|
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__")
|
|
for cls in (nodes.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__")), 4)
|
|
self.assertEqual(len(cls.getattr("__dict__")), 1)
|
|
self.assertEqual(len(cls.getattr("__mro__")), 1)
|
|
|
|
def test__mro__attribute(self) -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class A(object): pass
|
|
class B(object): pass
|
|
class C(A, B): pass
|
|
"""
|
|
)
|
|
assert isinstance(node, nodes.ClassDef)
|
|
mro = node.getattr("__mro__")[0]
|
|
self.assertIsInstance(mro, nodes.Tuple)
|
|
self.assertEqual(mro.elts, node.mro())
|
|
|
|
def test__bases__attribute(self) -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class A(object): pass
|
|
class B(object): pass
|
|
class C(A, B): pass
|
|
class D(C): pass
|
|
"""
|
|
)
|
|
assert isinstance(node, nodes.ClassDef)
|
|
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) -> None:
|
|
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__")[1], nodes.Tuple)
|
|
self.assertIsInstance(astroid["A"].getattr("__bases__")[0], nodes.AssignAttr)
|
|
|
|
def test_instance_special_attributes(self) -> None:
|
|
for inst in (Instance(self.module["YO"]), nodes.List(), nodes.Const(1)):
|
|
self.assertRaises(AttributeInferenceError, inst.getattr, "__mro__")
|
|
self.assertRaises(AttributeInferenceError, inst.getattr, "__bases__")
|
|
self.assertRaises(AttributeInferenceError, inst.getattr, "__name__")
|
|
self.assertEqual(len(inst.getattr("__dict__")), 1)
|
|
self.assertEqual(len(inst.getattr("__doc__")), 1)
|
|
|
|
def test_navigation(self) -> None:
|
|
klass = self.module["YO"]
|
|
self.assertEqual(klass.statement(), klass)
|
|
self.assertEqual(klass.statement(future=True), 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) -> None:
|
|
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")
|
|
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) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
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, "nonexistent")
|
|
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)
|
|
|
|
def test_ancestors(self) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
eee = self.nonregr["Ccc"]["Eee"]
|
|
self.assertEqual([n.name for n in eee.ancestors()], ["Ddd", "Aaa", "object"])
|
|
|
|
def test_classmethod_attributes(self) -> None:
|
|
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"]
|
|
assert_keys = [
|
|
"__module__",
|
|
"__qualname__",
|
|
"appli",
|
|
"config",
|
|
"registered",
|
|
"schema",
|
|
]
|
|
self.assertEqual(sorted(cls.locals.keys()), assert_keys)
|
|
|
|
def test_class_getattr(self) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
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)
|
|
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) -> None:
|
|
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) -> None:
|
|
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)
|
|
|
|
def test_metaclass_error(self) -> None:
|
|
astroid = builder.parse(
|
|
"""
|
|
class Test(object):
|
|
__metaclass__ = typ
|
|
"""
|
|
)
|
|
klass = astroid["Test"]
|
|
self.assertFalse(klass.metaclass())
|
|
|
|
def test_metaclass_yes_leak(self) -> None:
|
|
astroid = builder.parse(
|
|
"""
|
|
# notice `ab` instead of `abc`
|
|
from ab import ABCMeta
|
|
|
|
class Meta(object):
|
|
__metaclass__ = ABCMeta
|
|
"""
|
|
)
|
|
klass = astroid["Meta"]
|
|
self.assertIsNone(klass.metaclass())
|
|
|
|
def test_metaclass_type(self) -> None:
|
|
klass = builder.extract_node(
|
|
"""
|
|
def with_metaclass(meta, base=object):
|
|
return meta("NewBase", (base, ), {})
|
|
|
|
class ClassWithMeta(with_metaclass(type)): #@
|
|
pass
|
|
"""
|
|
)
|
|
assert isinstance(klass, nodes.ClassDef)
|
|
self.assertEqual(
|
|
["NewBase", "object"], [base.name for base in klass.ancestors()]
|
|
)
|
|
|
|
def test_no_infinite_metaclass_loop(self) -> None:
|
|
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
|
|
"""
|
|
)
|
|
assert isinstance(klass, nodes.ClassDef)
|
|
self.assertFalse(_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) -> None:
|
|
ast_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 ast_nodes:
|
|
self.assertEqual(None, klass.metaclass())
|
|
|
|
@unittest.skipUnless(HAS_SIX, "These tests require the six library")
|
|
def test_metaclass_generator_hack(self):
|
|
klass = builder.extract_node(
|
|
"""
|
|
import six
|
|
|
|
class WithMeta(six.with_metaclass(type, object)): #@
|
|
pass
|
|
"""
|
|
)
|
|
assert isinstance(klass, nodes.ClassDef)
|
|
self.assertEqual(["object"], [base.name for base in klass.ancestors()])
|
|
self.assertEqual("type", klass.metaclass().name)
|
|
|
|
def test_add_metaclass(self) -> None:
|
|
klass = builder.extract_node(
|
|
"""
|
|
import abc
|
|
|
|
class WithMeta(object, metaclass=abc.ABCMeta):
|
|
pass
|
|
"""
|
|
)
|
|
assert isinstance(klass, nodes.ClassDef)
|
|
inferred = next(klass.infer())
|
|
metaclass = inferred.metaclass()
|
|
self.assertIsInstance(metaclass, nodes.ClassDef)
|
|
self.assertIn(metaclass.qname(), ("abc.ABCMeta", "_py_abc.ABCMeta"))
|
|
|
|
@unittest.skipUnless(HAS_SIX, "These tests require the six library")
|
|
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) -> None:
|
|
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) -> None:
|
|
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])
|
|
|
|
def test_slots_for_dict_keys(self) -> None:
|
|
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) -> None:
|
|
module = builder.parse(
|
|
"""
|
|
class Klass(object):
|
|
__slots__ = ()
|
|
"""
|
|
)
|
|
cls = module["Klass"]
|
|
self.assertEqual(cls.slots(), [])
|
|
|
|
def test_slots_taken_from_parents(self) -> None:
|
|
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({slot.value for slot in slots}), ["a", "b", "c", "d", "e"]
|
|
)
|
|
|
|
def test_all_ancestors_need_slots(self) -> None:
|
|
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 test_slots_added_dynamically_still_inferred(self) -> None:
|
|
code = """
|
|
class NodeBase(object):
|
|
__slots__ = "a", "b"
|
|
|
|
if Options.isFullCompat():
|
|
__slots__ += ("c",)
|
|
|
|
"""
|
|
node = builder.extract_node(code)
|
|
inferred = next(node.infer())
|
|
slots = inferred.slots()
|
|
assert len(slots) == 3, slots
|
|
assert [slot.value for slot in slots] == ["a", "b", "c"]
|
|
|
|
def assertEqualMro(self, klass: nodes.ClassDef, expected_mro: List[str]) -> None:
|
|
self.assertEqual([member.name for member in klass.mro()], expected_mro)
|
|
|
|
def assertEqualMroQName(
|
|
self, klass: nodes.ClassDef, expected_mro: List[str]
|
|
) -> None:
|
|
self.assertEqual([member.qname() for member in klass.mro()], expected_mro)
|
|
|
|
@unittest.skipUnless(HAS_SIX, "These tests require the six library")
|
|
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) -> None:
|
|
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_ = 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_mro_with_factories(self) -> None:
|
|
cls = builder.extract_node(
|
|
"""
|
|
def MixinFactory(cls):
|
|
mixin_name = '{}Mixin'.format(cls.__name__)
|
|
mixin_bases = (object,)
|
|
mixin_attrs = {}
|
|
mixin = type(mixin_name, mixin_bases, mixin_attrs)
|
|
return mixin
|
|
class MixinA(MixinFactory(int)):
|
|
pass
|
|
class MixinB(MixinFactory(str)):
|
|
pass
|
|
class Base(object):
|
|
pass
|
|
class ClassA(MixinA, Base):
|
|
pass
|
|
class ClassB(MixinB, ClassA):
|
|
pass
|
|
class FinalClass(ClassB):
|
|
def __init__(self):
|
|
self.name = 'x'
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMro(
|
|
cls,
|
|
[
|
|
"FinalClass",
|
|
"ClassB",
|
|
"MixinB",
|
|
"",
|
|
"ClassA",
|
|
"MixinA",
|
|
"",
|
|
"Base",
|
|
"object",
|
|
],
|
|
)
|
|
|
|
def test_mro_with_attribute_classes(self) -> None:
|
|
cls = builder.extract_node(
|
|
"""
|
|
class A:
|
|
pass
|
|
class B:
|
|
pass
|
|
class Scope:
|
|
pass
|
|
scope = Scope()
|
|
scope.A = A
|
|
scope.B = B
|
|
class C(scope.A, scope.B):
|
|
pass
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMro(cls, ["C", "A", "B", "object"])
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_1(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
import typing
|
|
T = typing.TypeVar('T')
|
|
class A(typing.Generic[T]): ...
|
|
class B: ...
|
|
class C(A[T], B): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMroQName(
|
|
cls, [".C", ".A", "typing.Generic", ".B", "builtins.object"]
|
|
)
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_2(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic, TypeVar
|
|
T = TypeVar('T')
|
|
class A: ...
|
|
class B(Generic[T]): ...
|
|
class C(Generic[T], A, B[T]): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMroQName(
|
|
cls, [".C", ".A", ".B", "typing.Generic", "builtins.object"]
|
|
)
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_3(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic, TypeVar
|
|
T = TypeVar('T')
|
|
class A: ...
|
|
class B(A, Generic[T]): ...
|
|
class C(Generic[T]): ...
|
|
class D(B[T], C[T], Generic[T]): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMroQName(
|
|
cls, [".D", ".B", ".A", ".C", "typing.Generic", "builtins.object"]
|
|
)
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_4(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic, TypeVar
|
|
T = TypeVar('T')
|
|
class A: ...
|
|
class B(Generic[T]): ...
|
|
class C(A, Generic[T], B[T]): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMroQName(
|
|
cls, [".C", ".A", ".B", "typing.Generic", "builtins.object"]
|
|
)
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_5(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic, TypeVar
|
|
T1 = TypeVar('T1')
|
|
T2 = TypeVar('T2')
|
|
class A(Generic[T1]): ...
|
|
class B(Generic[T2]): ...
|
|
class C(A[T1], B[T2]): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMroQName(
|
|
cls, [".C", ".A", ".B", "typing.Generic", "builtins.object"]
|
|
)
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_6(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic as TGeneric, TypeVar
|
|
T = TypeVar('T')
|
|
class Generic: ...
|
|
class A(Generic): ...
|
|
class B(TGeneric[T]): ...
|
|
class C(A, B[T]): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMroQName(
|
|
cls, [".C", ".A", ".Generic", ".B", "typing.Generic", "builtins.object"]
|
|
)
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_7(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic, TypeVar
|
|
T = TypeVar('T')
|
|
class A(): ...
|
|
class B(Generic[T]): ...
|
|
class C(A, B[T]): ...
|
|
class D: ...
|
|
class E(C[str], D): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqualMroQName(
|
|
cls, [".E", ".C", ".A", ".B", "typing.Generic", ".D", "builtins.object"]
|
|
)
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_error_1(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic, TypeVar
|
|
T1 = TypeVar('T1')
|
|
T2 = TypeVar('T2')
|
|
class A(Generic[T1], Generic[T2]): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
with self.assertRaises(DuplicateBasesError):
|
|
cls.mro()
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_mro_generic_error_2(self):
|
|
cls = builder.extract_node(
|
|
"""
|
|
from typing import Generic, TypeVar
|
|
T = TypeVar('T')
|
|
class A(Generic[T]): ...
|
|
class B(A[T], A[T]): ...
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
with self.assertRaises(DuplicateBasesError):
|
|
cls.mro()
|
|
|
|
def test_generator_from_infer_call_result_parent(self) -> None:
|
|
func = builder.extract_node(
|
|
"""
|
|
import contextlib
|
|
|
|
@contextlib.contextmanager
|
|
def test(): #@
|
|
yield
|
|
"""
|
|
)
|
|
assert isinstance(func, nodes.FunctionDef)
|
|
result = next(func.infer_call_result())
|
|
self.assertIsInstance(result, Generator)
|
|
self.assertEqual(result.parent, func)
|
|
|
|
def test_type_three_arguments(self) -> None:
|
|
classes = builder.extract_node(
|
|
"""
|
|
type('A', (object, ), {"a": 1, "b": 2, missing: 3}) #@
|
|
"""
|
|
)
|
|
assert isinstance(classes, nodes.Call)
|
|
first = next(classes.infer())
|
|
self.assertIsInstance(first, nodes.ClassDef)
|
|
self.assertEqual(first.name, "A")
|
|
self.assertEqual(first.basenames, ["object"])
|
|
self.assertIsInstance(first["a"], nodes.Const)
|
|
self.assertEqual(first["a"].value, 1)
|
|
self.assertIsInstance(first["b"], nodes.Const)
|
|
self.assertEqual(first["b"].value, 2)
|
|
with self.assertRaises(AttributeInferenceError):
|
|
first.getattr("missing")
|
|
|
|
def test_implicit_metaclass(self) -> None:
|
|
cls = builder.extract_node(
|
|
"""
|
|
class A(object):
|
|
pass
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
type_cls = nodes.builtin_lookup("type")[1][0]
|
|
self.assertEqual(cls.implicit_metaclass(), type_cls)
|
|
|
|
def test_implicit_metaclass_lookup(self) -> None:
|
|
cls = builder.extract_node(
|
|
"""
|
|
class A(object):
|
|
pass
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
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) -> None:
|
|
"""Check that we don't have recursive attribute access for metaclass"""
|
|
cls = builder.extract_node(
|
|
"""
|
|
class A(object): pass
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
self.assertEqual(len(cls.getattr("mro")), 1)
|
|
|
|
def test_metaclass_lookup_inference_errors(self) -> None:
|
|
module = builder.parse(
|
|
"""
|
|
class Metaclass(type):
|
|
foo = lala
|
|
|
|
class B(object, metaclass=Metaclass): pass
|
|
"""
|
|
)
|
|
cls = module["B"]
|
|
self.assertEqual(util.Uninferable, next(cls.igetattr("foo")))
|
|
|
|
def test_metaclass_lookup(self) -> None:
|
|
module = builder.parse(
|
|
"""
|
|
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
|
|
|
|
class A(object, metaclass=Metaclass):
|
|
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, objects.Property)
|
|
wrapping = 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, nodes.FunctionDef)
|
|
|
|
def test_local_attr_invalid_mro(self) -> None:
|
|
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
|
|
"""
|
|
)
|
|
assert isinstance(cls, nodes.ClassDef)
|
|
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) -> None:
|
|
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.
|
|
astroid_builder = builder.AstroidBuilder()
|
|
module = astroid_builder.module_build(datetime)
|
|
self.assertFalse(module["timedelta"].has_dynamic_getattr())
|
|
|
|
def test_duplicate_bases_namedtuple(self) -> None:
|
|
module = builder.parse(
|
|
"""
|
|
import collections
|
|
_A = collections.namedtuple('A', 'a')
|
|
|
|
class A(_A): pass
|
|
|
|
class B(A): pass
|
|
"""
|
|
)
|
|
names = ["B", "A", "A", "tuple", "object"]
|
|
mro = module["B"].mro()
|
|
class_names = [i.name for i in mro]
|
|
self.assertEqual(names, class_names)
|
|
|
|
def test_instance_bound_method_lambdas(self) -> None:
|
|
ast_nodes = builder.extract_node(
|
|
"""
|
|
class Test(object): #@
|
|
lam = lambda self: self
|
|
not_method = lambda xargs: xargs
|
|
Test() #@
|
|
"""
|
|
)
|
|
assert isinstance(ast_nodes, list)
|
|
cls = next(ast_nodes[0].infer())
|
|
self.assertIsInstance(next(cls.igetattr("lam")), nodes.Lambda)
|
|
self.assertIsInstance(next(cls.igetattr("not_method")), 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, nodes.Lambda)
|
|
|
|
def test_instance_bound_method_lambdas_2(self) -> None:
|
|
"""
|
|
Test the fact that a method which is a lambda built from
|
|
a factory is well inferred as a bound method (bug pylint 2594)
|
|
"""
|
|
ast_nodes = builder.extract_node(
|
|
"""
|
|
def lambda_factory():
|
|
return lambda self: print("Hello world")
|
|
|
|
class MyClass(object): #@
|
|
f2 = lambda_factory()
|
|
|
|
MyClass() #@
|
|
"""
|
|
)
|
|
assert isinstance(ast_nodes, list)
|
|
cls = next(ast_nodes[0].infer())
|
|
self.assertIsInstance(next(cls.igetattr("f2")), nodes.Lambda)
|
|
|
|
instance = next(ast_nodes[1].infer())
|
|
f2 = next(instance.igetattr("f2"))
|
|
self.assertIsInstance(f2, BoundMethod)
|
|
|
|
def test_class_extra_decorators_frame_is_not_class(self) -> None:
|
|
ast_node = builder.extract_node(
|
|
"""
|
|
def ala():
|
|
def bala(): #@
|
|
func = 42
|
|
"""
|
|
)
|
|
assert isinstance(ast_node, nodes.FunctionDef)
|
|
self.assertEqual(ast_node.extra_decorators, [])
|
|
|
|
def test_class_extra_decorators_only_callfunc_are_considered(self) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
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) -> None:
|
|
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)
|
|
|
|
def test_class_keywords(self) -> None:
|
|
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"])
|
|
children = list(cls.get_children())
|
|
assert len(children) == 4
|
|
assert isinstance(children[1], nodes.Keyword)
|
|
assert isinstance(children[2], nodes.Keyword)
|
|
assert children[1].arg == "foo"
|
|
assert children[2].arg == "bar"
|
|
|
|
def test_kite_graph(self) -> None:
|
|
data = """
|
|
A = type('A', (object,), {})
|
|
|
|
class B1(A): pass
|
|
|
|
class B2(A): pass
|
|
|
|
class C(B1, B2): pass
|
|
|
|
class D(C):
|
|
def update(self):
|
|
self.hello = 'hello'
|
|
"""
|
|
# Should not crash
|
|
builder.parse(data)
|
|
|
|
|
|
def test_issue940_metaclass_subclass_property() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class BaseMeta(type):
|
|
@property
|
|
def __members__(cls):
|
|
return ['a', 'property']
|
|
class Parent(metaclass=BaseMeta):
|
|
pass
|
|
class Derived(Parent):
|
|
pass
|
|
Derived.__members__
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, nodes.List)
|
|
assert [c.value for c in inferred.elts] == ["a", "property"]
|
|
|
|
|
|
def test_issue940_property_grandchild() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class Grandparent:
|
|
@property
|
|
def __members__(self):
|
|
return ['a', 'property']
|
|
class Parent(Grandparent):
|
|
pass
|
|
class Child(Parent):
|
|
pass
|
|
Child().__members__
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, nodes.List)
|
|
assert [c.value for c in inferred.elts] == ["a", "property"]
|
|
|
|
|
|
def test_issue940_metaclass_property() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class BaseMeta(type):
|
|
@property
|
|
def __members__(cls):
|
|
return ['a', 'property']
|
|
class Parent(metaclass=BaseMeta):
|
|
pass
|
|
Parent.__members__
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, nodes.List)
|
|
assert [c.value for c in inferred.elts] == ["a", "property"]
|
|
|
|
|
|
def test_issue940_with_metaclass_class_context_property() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class BaseMeta(type):
|
|
pass
|
|
class Parent(metaclass=BaseMeta):
|
|
@property
|
|
def __members__(self):
|
|
return ['a', 'property']
|
|
class Derived(Parent):
|
|
pass
|
|
Derived.__members__
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
assert not isinstance(inferred, nodes.List)
|
|
assert isinstance(inferred, objects.Property)
|
|
|
|
|
|
def test_issue940_metaclass_values_funcdef() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class BaseMeta(type):
|
|
def __members__(cls):
|
|
return ['a', 'func']
|
|
class Parent(metaclass=BaseMeta):
|
|
pass
|
|
Parent.__members__()
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, nodes.List)
|
|
assert [c.value for c in inferred.elts] == ["a", "func"]
|
|
|
|
|
|
def test_issue940_metaclass_derived_funcdef() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class BaseMeta(type):
|
|
def __members__(cls):
|
|
return ['a', 'func']
|
|
class Parent(metaclass=BaseMeta):
|
|
pass
|
|
class Derived(Parent):
|
|
pass
|
|
Derived.__members__()
|
|
"""
|
|
)
|
|
inferred_result = next(node.infer())
|
|
assert isinstance(inferred_result, nodes.List)
|
|
assert [c.value for c in inferred_result.elts] == ["a", "func"]
|
|
|
|
|
|
def test_issue940_metaclass_funcdef_is_not_datadescriptor() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
class BaseMeta(type):
|
|
def __members__(cls):
|
|
return ['a', 'property']
|
|
class Parent(metaclass=BaseMeta):
|
|
@property
|
|
def __members__(cls):
|
|
return BaseMeta.__members__()
|
|
class Derived(Parent):
|
|
pass
|
|
Derived.__members__
|
|
"""
|
|
)
|
|
# Here the function is defined on the metaclass, but the property
|
|
# is defined on the base class. When loading the attribute in a
|
|
# class context, this should return the property object instead of
|
|
# resolving the data descriptor
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, objects.Property)
|
|
|
|
|
|
def test_issue940_enums_as_a_real_world_usecase() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
from enum import Enum
|
|
class Sounds(Enum):
|
|
bee = "buzz"
|
|
cat = "meow"
|
|
Sounds.__members__
|
|
"""
|
|
)
|
|
inferred_result = next(node.infer())
|
|
assert isinstance(inferred_result, nodes.Dict)
|
|
actual = [k.value for k, _ in inferred_result.items]
|
|
assert sorted(actual) == ["bee", "cat"]
|
|
|
|
|
|
def test_metaclass_cannot_infer_call_yields_an_instance() -> None:
|
|
node = builder.extract_node(
|
|
"""
|
|
from undefined import Undefined
|
|
class Meta(type):
|
|
__call__ = Undefined
|
|
class A(metaclass=Meta):
|
|
pass
|
|
A()
|
|
"""
|
|
)
|
|
inferred = next(node.infer())
|
|
assert isinstance(inferred, Instance)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"func",
|
|
[
|
|
textwrap.dedent(
|
|
"""
|
|
def func(a, b, /, d, e):
|
|
pass
|
|
"""
|
|
),
|
|
textwrap.dedent(
|
|
"""
|
|
def func(a, b=None, /, d=None, e=None):
|
|
pass
|
|
"""
|
|
),
|
|
textwrap.dedent(
|
|
"""
|
|
def func(a, other, other, b=None, /, d=None, e=None):
|
|
pass
|
|
"""
|
|
),
|
|
textwrap.dedent(
|
|
"""
|
|
def func(a, other, other, b=None, /, d=None, e=None, **kwargs):
|
|
pass
|
|
"""
|
|
),
|
|
textwrap.dedent(
|
|
"""
|
|
def name(p1, p2, /, p_or_kw, *, kw):
|
|
pass
|
|
"""
|
|
),
|
|
textwrap.dedent(
|
|
"""
|
|
def __init__(self, other=(), /, **kw):
|
|
pass
|
|
"""
|
|
),
|
|
textwrap.dedent(
|
|
"""
|
|
def __init__(self: int, other: float, /, **kw):
|
|
pass
|
|
"""
|
|
),
|
|
],
|
|
)
|
|
@test_utils.require_version("3.8")
|
|
def test_posonlyargs_python_38(func):
|
|
ast_node = builder.extract_node(func)
|
|
assert ast_node.as_string().strip() == func.strip()
|
|
|
|
|
|
@test_utils.require_version("3.8")
|
|
def test_posonlyargs_default_value() -> None:
|
|
ast_node = builder.extract_node(
|
|
"""
|
|
def func(a, b=1, /, c=2): pass
|
|
"""
|
|
)
|
|
last_param = ast_node.args.default_value("c")
|
|
assert isinstance(last_param, nodes.Const)
|
|
assert last_param.value == 2
|
|
|
|
first_param = ast_node.args.default_value("b")
|
|
assert isinstance(first_param, nodes.Const)
|
|
assert first_param.value == 1
|
|
|
|
|
|
@test_utils.require_version(minver="3.7")
|
|
def test_ancestor_with_generic() -> None:
|
|
# https://github.com/PyCQA/astroid/issues/942
|
|
tree = builder.parse(
|
|
"""
|
|
from typing import TypeVar, Generic
|
|
T = TypeVar("T")
|
|
class A(Generic[T]):
|
|
def a_method(self):
|
|
print("hello")
|
|
class B(A[T]): pass
|
|
class C(B[str]): pass
|
|
"""
|
|
)
|
|
inferred_b = next(tree["B"].infer())
|
|
assert [cdef.name for cdef in inferred_b.ancestors()] == ["A", "Generic", "object"]
|
|
|
|
inferred_c = next(tree["C"].infer())
|
|
assert [cdef.name for cdef in inferred_c.ancestors()] == [
|
|
"B",
|
|
"A",
|
|
"Generic",
|
|
"object",
|
|
]
|
|
|
|
|
|
def test_slots_duplicate_bases_issue_1089() -> None:
|
|
astroid = builder.parse(
|
|
"""
|
|
class First(object, object): #@
|
|
pass
|
|
"""
|
|
)
|
|
with pytest.raises(NotImplementedError):
|
|
astroid["First"].slots()
|
|
|
|
|
|
class TestFrameNodes:
|
|
@staticmethod
|
|
@pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions")
|
|
def test_frame_node():
|
|
"""Test if the frame of FunctionDef, ClassDef and Module is correctly set"""
|
|
module = builder.parse(
|
|
"""
|
|
def func():
|
|
var_1 = x
|
|
return var_1
|
|
|
|
class MyClass:
|
|
|
|
attribute = 1
|
|
|
|
def method():
|
|
pass
|
|
|
|
VAR = lambda y = (named_expr := "walrus"): print(y)
|
|
"""
|
|
)
|
|
function = module.body[0]
|
|
assert function.frame() == function
|
|
assert function.frame(future=True) == function
|
|
assert function.body[0].frame() == function
|
|
assert function.body[0].frame(future=True) == function
|
|
|
|
class_node = module.body[1]
|
|
assert class_node.frame() == class_node
|
|
assert class_node.frame(future=True) == class_node
|
|
assert class_node.body[0].frame() == class_node
|
|
assert class_node.body[0].frame(future=True) == class_node
|
|
assert class_node.body[1].frame() == class_node.body[1]
|
|
assert class_node.body[1].frame(future=True) == class_node.body[1]
|
|
|
|
lambda_assignment = module.body[2].value
|
|
assert lambda_assignment.args.args[0].frame() == lambda_assignment
|
|
assert lambda_assignment.args.args[0].frame(future=True) == lambda_assignment
|
|
|
|
assert module.frame() == module
|
|
assert module.frame(future=True) == module
|
|
|
|
@staticmethod
|
|
def test_non_frame_node():
|
|
"""Test if the frame of non frame nodes is set correctly"""
|
|
module = builder.parse(
|
|
"""
|
|
VAR_ONE = 1
|
|
|
|
VAR_TWO = [x for x in range(1)]
|
|
"""
|
|
)
|
|
assert module.body[0].frame() == module
|
|
assert module.body[0].frame(future=True) == module
|
|
|
|
assert module.body[1].value.locals["x"][0].frame() == module
|
|
assert module.body[1].value.locals["x"][0].frame(future=True) == module
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|