| # 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 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> |
| # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.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 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) |
| 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) |
| 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: |
| @pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions") |
| @staticmethod |
| 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.body[0].frame() == function |
| |
| class_node = module.body[1] |
| assert class_node.frame() == class_node |
| assert class_node.body[0].frame() == class_node |
| assert class_node.body[1].frame() == class_node.body[1] |
| |
| lambda_assignment = module.body[2].value |
| assert lambda_assignment.args.args[0].frame() == lambda_assignment |
| |
| assert module.frame() == 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[1].value.locals["x"][0].frame() == module |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |