| # Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> |
| # Copyright (c) 2007 Marien Zwart <marienz@gentoo.org> |
| # Copyright (c) 2013-2014 Google, Inc. |
| # Copyright (c) 2014-2021 Claudiu Popa <pcmanticore@gmail.com> |
| # Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com> |
| # Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com> |
| # Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru> |
| # Copyright (c) 2015 Rene Zhang <rz99@cornell.edu> |
| # Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net> |
| # Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com> |
| # Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com> |
| # Copyright (c) 2017 Calen Pennington <cale@edx.org> |
| # Copyright (c) 2017 Calen Pennington <calen.pennington@gmail.com> |
| # Copyright (c) 2017 David Euresti <david@dropbox.com> |
| # Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com> |
| # Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com> |
| # Copyright (c) 2018 Daniel Martin <daniel.martin@crowdstrike.com> |
| # Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi> |
| # Copyright (c) 2018 Anthony Sottile <asottile@umich.edu> |
| # Copyright (c) 2019, 2021 David Liu <david@cs.toronto.edu> |
| # Copyright (c) 2019-2021 hippo91 <guillaume.peillex@gmail.com> |
| # Copyright (c) 2019 Stanislav Levin <slev@altlinux.org> |
| # Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk> |
| # Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com> |
| # Copyright (c) 2020 David Gilman <davidgilman1@gmail.com> |
| # Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com> |
| # Copyright (c) 2020 Karthikeyan Singaravelan <tir.karthi@gmail.com> |
| # Copyright (c) 2020 Bryce Guinta <bryce.guinta@protonmail.com> |
| # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com> |
| # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com> |
| # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> |
| # Copyright (c) 2021 Andrew Haigh <hello@nelf.in> |
| # Copyright (c) 2021 doranid <ddandd@gmail.com> |
| # Copyright (c) 2021 Francis Charette Migneault <francis.charette.migneault@gmail.com> |
| |
| # 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 the astroid inference capabilities""" |
| |
| import platform |
| import textwrap |
| import unittest |
| from abc import ABCMeta |
| from functools import partial |
| from typing import Any, Callable, Dict, List, Tuple, Union |
| from unittest.mock import patch |
| |
| import pytest |
| |
| from astroid import Slice, arguments |
| from astroid import decorators as decoratorsmod |
| from astroid import helpers, nodes, objects, test_utils, util |
| from astroid.arguments import CallSite |
| from astroid.bases import BoundMethod, Instance, UnboundMethod |
| from astroid.builder import AstroidBuilder, extract_node, parse |
| from astroid.const import PY38_PLUS, PY39_PLUS |
| from astroid.context import InferenceContext |
| from astroid.exceptions import ( |
| AstroidTypeError, |
| AttributeInferenceError, |
| InferenceError, |
| NotFoundError, |
| ) |
| from astroid.inference import infer_end as inference_infer_end |
| from astroid.objects import ExceptionInstance |
| |
| from . import resources |
| |
| try: |
| import six # pylint: disable=unused-import |
| |
| HAS_SIX = True |
| except ImportError: |
| HAS_SIX = False |
| |
| |
| def get_node_of_class(start_from: nodes.FunctionDef, klass: type) -> nodes.Attribute: |
| return next(start_from.nodes_of_class(klass)) |
| |
| |
| builder = AstroidBuilder() |
| |
| EXC_MODULE = "builtins" |
| BOOL_SPECIAL_METHOD = "__bool__" |
| |
| |
| class InferenceUtilsTest(unittest.TestCase): |
| def test_path_wrapper(self) -> None: |
| def infer_default(self: Any, *args: InferenceContext) -> None: |
| raise InferenceError |
| |
| infer_default = decoratorsmod.path_wrapper(infer_default) |
| infer_end = decoratorsmod.path_wrapper(inference_infer_end) |
| with self.assertRaises(InferenceError): |
| next(infer_default(1)) |
| self.assertEqual(next(infer_end(1)), 1) |
| |
| |
| def _assertInferElts( |
| node_type: ABCMeta, |
| self: "InferenceTest", |
| node: Any, |
| elts: Union[List[int], List[str]], |
| ) -> None: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, node_type) |
| self.assertEqual(sorted(elt.value for elt in inferred.elts), elts) |
| |
| |
| def partialmethod(func, arg): |
| """similar to functools.partial but return a lambda instead of a class so returned value may be |
| turned into a method. |
| """ |
| return lambda *args, **kwargs: func(arg, *args, **kwargs) |
| |
| |
| class InferenceTest(resources.SysPathSetup, unittest.TestCase): |
| |
| # additional assertInfer* method for builtin types |
| |
| def assertInferConst(self, node: nodes.Call, expected: str) -> None: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, expected) |
| |
| def assertInferDict( |
| self, node: Union[nodes.Call, nodes.Dict, nodes.NodeNG], expected: Any |
| ) -> None: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Dict) |
| |
| elts = {(key.value, value.value) for (key, value) in inferred.items} |
| self.assertEqual(sorted(elts), sorted(expected.items())) |
| |
| assertInferTuple = partialmethod(_assertInferElts, nodes.Tuple) |
| assertInferList = partialmethod(_assertInferElts, nodes.List) |
| assertInferSet = partialmethod(_assertInferElts, nodes.Set) |
| assertInferFrozenSet = partialmethod(_assertInferElts, objects.FrozenSet) |
| |
| CODE = """ |
| class C(object): |
| "new style" |
| attr = 4 |
| |
| def meth1(self, arg1, optarg=0): |
| var = object() |
| print ("yo", arg1, optarg) |
| self.iattr = "hop" |
| return var |
| |
| def meth2(self): |
| self.meth1(*self.meth3) |
| |
| def meth3(self, d=attr): |
| b = self.attr |
| c = self.iattr |
| return b, c |
| |
| ex = Exception("msg") |
| v = C().meth1(1) |
| m_unbound = C.meth1 |
| m_bound = C().meth1 |
| a, b, c = ex, 1, "bonjour" |
| [d, e, f] = [ex, 1.0, ("bonjour", v)] |
| g, h = f |
| i, (j, k) = "glup", f |
| |
| a, b= b, a # Gasp ! |
| """ |
| |
| ast = parse(CODE, __name__) |
| |
| def test_infer_abstract_property_return_values(self) -> None: |
| module = parse( |
| """ |
| import abc |
| |
| class A(object): |
| @abc.abstractproperty |
| def test(self): |
| return 42 |
| |
| a = A() |
| x = a.test |
| """ |
| ) |
| inferred = next(module["x"].infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def test_module_inference(self) -> None: |
| inferred = self.ast.infer() |
| obj = next(inferred) |
| self.assertEqual(obj.name, __name__) |
| self.assertEqual(obj.root().name, __name__) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_class_inference(self) -> None: |
| inferred = self.ast["C"].infer() |
| obj = next(inferred) |
| self.assertEqual(obj.name, "C") |
| self.assertEqual(obj.root().name, __name__) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_function_inference(self) -> None: |
| inferred = self.ast["C"]["meth1"].infer() |
| obj = next(inferred) |
| self.assertEqual(obj.name, "meth1") |
| self.assertEqual(obj.root().name, __name__) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_builtin_name_inference(self) -> None: |
| inferred = self.ast["C"]["meth1"]["var"].infer() |
| var = next(inferred) |
| self.assertEqual(var.name, "object") |
| self.assertEqual(var.root().name, "builtins") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_tupleassign_name_inference(self) -> None: |
| inferred = self.ast["a"].infer() |
| exc = next(inferred) |
| self.assertIsInstance(exc, Instance) |
| self.assertEqual(exc.name, "Exception") |
| self.assertEqual(exc.root().name, EXC_MODULE) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast["b"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, 1) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast["c"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, "bonjour") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_listassign_name_inference(self) -> None: |
| inferred = self.ast["d"].infer() |
| exc = next(inferred) |
| self.assertIsInstance(exc, Instance) |
| self.assertEqual(exc.name, "Exception") |
| self.assertEqual(exc.root().name, EXC_MODULE) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast["e"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, 1.0) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast["f"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Tuple) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_advanced_tupleassign_name_inference1(self) -> None: |
| inferred = self.ast["g"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, "bonjour") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast["h"].infer() |
| var = next(inferred) |
| self.assertEqual(var.name, "object") |
| self.assertEqual(var.root().name, "builtins") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_advanced_tupleassign_name_inference2(self) -> None: |
| inferred = self.ast["i"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, "glup") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast["j"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, "bonjour") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast["k"].infer() |
| var = next(inferred) |
| self.assertEqual(var.name, "object") |
| self.assertEqual(var.root().name, "builtins") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_swap_assign_inference(self) -> None: |
| inferred = self.ast.locals["a"][1].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, 1) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| inferred = self.ast.locals["b"][1].infer() |
| exc = next(inferred) |
| self.assertIsInstance(exc, Instance) |
| self.assertEqual(exc.name, "Exception") |
| self.assertEqual(exc.root().name, EXC_MODULE) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_getattr_inference1(self) -> None: |
| inferred = self.ast["ex"].infer() |
| exc = next(inferred) |
| self.assertIsInstance(exc, Instance) |
| self.assertEqual(exc.name, "Exception") |
| self.assertEqual(exc.root().name, EXC_MODULE) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_getattr_inference2(self) -> None: |
| inferred = get_node_of_class(self.ast["C"]["meth2"], nodes.Attribute).infer() |
| meth1 = next(inferred) |
| self.assertEqual(meth1.name, "meth1") |
| self.assertEqual(meth1.root().name, __name__) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_getattr_inference3(self) -> None: |
| inferred = self.ast["C"]["meth3"]["b"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, 4) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_getattr_inference4(self) -> None: |
| inferred = self.ast["C"]["meth3"]["c"].infer() |
| const = next(inferred) |
| self.assertIsInstance(const, nodes.Const) |
| self.assertEqual(const.value, "hop") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_callfunc_inference(self) -> None: |
| inferred = self.ast["v"].infer() |
| meth1 = next(inferred) |
| self.assertIsInstance(meth1, Instance) |
| self.assertEqual(meth1.name, "object") |
| self.assertEqual(meth1.root().name, "builtins") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_unbound_method_inference(self) -> None: |
| inferred = self.ast["m_unbound"].infer() |
| meth1 = next(inferred) |
| self.assertIsInstance(meth1, UnboundMethod) |
| self.assertEqual(meth1.name, "meth1") |
| self.assertEqual(meth1.parent.frame().name, "C") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_bound_method_inference(self) -> None: |
| inferred = self.ast["m_bound"].infer() |
| meth1 = next(inferred) |
| self.assertIsInstance(meth1, BoundMethod) |
| self.assertEqual(meth1.name, "meth1") |
| self.assertEqual(meth1.parent.frame().name, "C") |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_args_default_inference1(self) -> None: |
| optarg = test_utils.get_name_node(self.ast["C"]["meth1"], "optarg") |
| inferred = optarg.infer() |
| obj1 = next(inferred) |
| self.assertIsInstance(obj1, nodes.Const) |
| self.assertEqual(obj1.value, 0) |
| obj1 = next(inferred) |
| self.assertIs(obj1, util.Uninferable, obj1) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_args_default_inference2(self) -> None: |
| inferred = self.ast["C"]["meth3"].ilookup("d") |
| obj1 = next(inferred) |
| self.assertIsInstance(obj1, nodes.Const) |
| self.assertEqual(obj1.value, 4) |
| obj1 = next(inferred) |
| self.assertIs(obj1, util.Uninferable, obj1) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_inference_restrictions(self) -> None: |
| inferred = test_utils.get_name_node(self.ast["C"]["meth1"], "arg1").infer() |
| obj1 = next(inferred) |
| self.assertIs(obj1, util.Uninferable, obj1) |
| self.assertRaises(StopIteration, partial(next, inferred)) |
| |
| def test_ancestors_inference(self) -> None: |
| code = """ |
| class A(object): #@ |
| pass |
| |
| class A(A): #@ |
| pass |
| """ |
| a1, a2 = extract_node(code, __name__) |
| a2_ancestors = list(a2.ancestors()) |
| self.assertEqual(len(a2_ancestors), 2) |
| self.assertIs(a2_ancestors[0], a1) |
| |
| def test_ancestors_inference2(self) -> None: |
| code = """ |
| class A(object): #@ |
| pass |
| |
| class B(A): #@ |
| pass |
| |
| class A(B): #@ |
| pass |
| """ |
| a1, b, a2 = extract_node(code, __name__) |
| a2_ancestors = list(a2.ancestors()) |
| self.assertEqual(len(a2_ancestors), 3) |
| self.assertIs(a2_ancestors[0], b) |
| self.assertIs(a2_ancestors[1], a1) |
| |
| def test_f_arg_f(self) -> None: |
| code = """ |
| def f(f=1): |
| return f |
| |
| a = f() |
| """ |
| ast = parse(code, __name__) |
| a = ast["a"] |
| a_inferred = a.inferred() |
| self.assertEqual(a_inferred[0].value, 1) |
| self.assertEqual(len(a_inferred), 1) |
| |
| def test_exc_ancestors(self) -> None: |
| code = """ |
| def f(): |
| raise __(NotImplementedError) |
| """ |
| error = extract_node(code, __name__) |
| nie = error.inferred()[0] |
| self.assertIsInstance(nie, nodes.ClassDef) |
| nie_ancestors = [c.name for c in nie.ancestors()] |
| expected = ["RuntimeError", "Exception", "BaseException", "object"] |
| self.assertEqual(nie_ancestors, expected) |
| |
| def test_except_inference(self) -> None: |
| code = """ |
| try: |
| print (hop) |
| except NameError as ex: |
| ex1 = ex |
| except Exception as ex: |
| ex2 = ex |
| raise |
| """ |
| ast = parse(code, __name__) |
| ex1 = ast["ex1"] |
| ex1_infer = ex1.infer() |
| ex1 = next(ex1_infer) |
| self.assertIsInstance(ex1, Instance) |
| self.assertEqual(ex1.name, "NameError") |
| self.assertRaises(StopIteration, partial(next, ex1_infer)) |
| ex2 = ast["ex2"] |
| ex2_infer = ex2.infer() |
| ex2 = next(ex2_infer) |
| self.assertIsInstance(ex2, Instance) |
| self.assertEqual(ex2.name, "Exception") |
| self.assertRaises(StopIteration, partial(next, ex2_infer)) |
| |
| def test_del1(self) -> None: |
| code = """ |
| del undefined_attr |
| """ |
| delete = extract_node(code, __name__) |
| self.assertRaises(InferenceError, next, delete.infer()) |
| |
| def test_del2(self) -> None: |
| code = """ |
| a = 1 |
| b = a |
| del a |
| c = a |
| a = 2 |
| d = a |
| """ |
| ast = parse(code, __name__) |
| n = ast["b"] |
| n_infer = n.infer() |
| inferred = next(n_infer) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 1) |
| self.assertRaises(StopIteration, partial(next, n_infer)) |
| n = ast["c"] |
| n_infer = n.infer() |
| self.assertRaises(InferenceError, partial(next, n_infer)) |
| n = ast["d"] |
| n_infer = n.infer() |
| inferred = next(n_infer) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 2) |
| self.assertRaises(StopIteration, partial(next, n_infer)) |
| |
| def test_builtin_types(self) -> None: |
| code = """ |
| l = [1] |
| t = (2,) |
| d = {} |
| s = '' |
| s2 = '_' |
| """ |
| ast = parse(code, __name__) |
| n = ast["l"] |
| inferred = next(n.infer()) |
| self.assertIsInstance(inferred, nodes.List) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.getitem(nodes.Const(0)).value, 1) |
| self.assertIsInstance(inferred._proxied, nodes.ClassDef) |
| self.assertEqual(inferred._proxied.name, "list") |
| self.assertIn("append", inferred._proxied.locals) |
| n = ast["t"] |
| inferred = next(n.infer()) |
| self.assertIsInstance(inferred, nodes.Tuple) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.getitem(nodes.Const(0)).value, 2) |
| self.assertIsInstance(inferred._proxied, nodes.ClassDef) |
| self.assertEqual(inferred._proxied.name, "tuple") |
| n = ast["d"] |
| inferred = next(n.infer()) |
| self.assertIsInstance(inferred, nodes.Dict) |
| self.assertIsInstance(inferred, Instance) |
| self.assertIsInstance(inferred._proxied, nodes.ClassDef) |
| self.assertEqual(inferred._proxied.name, "dict") |
| self.assertIn("get", inferred._proxied.locals) |
| n = ast["s"] |
| inferred = next(n.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "str") |
| self.assertIn("lower", inferred._proxied.locals) |
| n = ast["s2"] |
| inferred = next(n.infer()) |
| self.assertEqual(inferred.getitem(nodes.Const(0)).value, "_") |
| |
| code = "s = {1}" |
| ast = parse(code, __name__) |
| n = ast["s"] |
| inferred = next(n.infer()) |
| self.assertIsInstance(inferred, nodes.Set) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "set") |
| self.assertIn("remove", inferred._proxied.locals) |
| |
| @pytest.mark.xfail(reason="Descriptors are not properly inferred as callable") |
| def test_descriptor_are_callable(self): |
| code = """ |
| class A: |
| statm = staticmethod(open) |
| clsm = classmethod('whatever') |
| """ |
| ast = parse(code, __name__) |
| statm = next(ast["A"].igetattr("statm")) |
| self.assertTrue(statm.callable()) |
| clsm = next(ast["A"].igetattr("clsm")) |
| self.assertFalse(clsm.callable()) |
| |
| def test_bt_ancestor_crash(self) -> None: |
| code = """ |
| class Warning(Warning): |
| pass |
| """ |
| ast = parse(code, __name__) |
| w = ast["Warning"] |
| ancestors = w.ancestors() |
| ancestor = next(ancestors) |
| self.assertEqual(ancestor.name, "Warning") |
| self.assertEqual(ancestor.root().name, EXC_MODULE) |
| ancestor = next(ancestors) |
| self.assertEqual(ancestor.name, "Exception") |
| self.assertEqual(ancestor.root().name, EXC_MODULE) |
| ancestor = next(ancestors) |
| self.assertEqual(ancestor.name, "BaseException") |
| self.assertEqual(ancestor.root().name, EXC_MODULE) |
| ancestor = next(ancestors) |
| self.assertEqual(ancestor.name, "object") |
| self.assertEqual(ancestor.root().name, "builtins") |
| self.assertRaises(StopIteration, partial(next, ancestors)) |
| |
| def test_method_argument(self) -> None: |
| code = ''' |
| class ErudiEntitySchema: |
| """an entity has a type, a set of subject and or object relations""" |
| def __init__(self, e_type, **kwargs): |
| kwargs['e_type'] = e_type.capitalize().encode() |
| |
| def meth(self, e_type, *args, **kwargs): |
| kwargs['e_type'] = e_type.capitalize().encode() |
| print(args) |
| ''' |
| ast = parse(code, __name__) |
| arg = test_utils.get_name_node(ast["ErudiEntitySchema"]["__init__"], "e_type") |
| self.assertEqual( |
| [n.__class__ for n in arg.infer()], [util.Uninferable.__class__] |
| ) |
| arg = test_utils.get_name_node(ast["ErudiEntitySchema"]["__init__"], "kwargs") |
| self.assertEqual([n.__class__ for n in arg.infer()], [nodes.Dict]) |
| arg = test_utils.get_name_node(ast["ErudiEntitySchema"]["meth"], "e_type") |
| self.assertEqual( |
| [n.__class__ for n in arg.infer()], [util.Uninferable.__class__] |
| ) |
| arg = test_utils.get_name_node(ast["ErudiEntitySchema"]["meth"], "args") |
| self.assertEqual([n.__class__ for n in arg.infer()], [nodes.Tuple]) |
| arg = test_utils.get_name_node(ast["ErudiEntitySchema"]["meth"], "kwargs") |
| self.assertEqual([n.__class__ for n in arg.infer()], [nodes.Dict]) |
| |
| def test_tuple_then_list(self) -> None: |
| code = """ |
| def test_view(rql, vid, tags=()): |
| tags = list(tags) |
| __(tags).append(vid) |
| """ |
| name = extract_node(code, __name__) |
| it = name.infer() |
| tags = next(it) |
| self.assertIsInstance(tags, nodes.List) |
| self.assertEqual(tags.elts, []) |
| with self.assertRaises(StopIteration): |
| next(it) |
| |
| def test_mulassign_inference(self) -> None: |
| code = ''' |
| def first_word(line): |
| """Return the first word of a line""" |
| |
| return line.split()[0] |
| |
| def last_word(line): |
| """Return last word of a line""" |
| |
| return line.split()[-1] |
| |
| def process_line(word_pos): |
| """Silly function: returns (ok, callable) based on argument. |
| |
| For test purpose only. |
| """ |
| |
| if word_pos > 0: |
| return (True, first_word) |
| elif word_pos < 0: |
| return (True, last_word) |
| else: |
| return (False, None) |
| |
| if __name__ == '__main__': |
| |
| line_number = 0 |
| for a_line in file('test_callable.py'): |
| tupletest = process_line(line_number) |
| (ok, fct) = process_line(line_number) |
| if ok: |
| fct(a_line) |
| ''' |
| ast = parse(code, __name__) |
| self.assertEqual(len(list(ast["process_line"].infer_call_result(None))), 3) |
| self.assertEqual(len(list(ast["tupletest"].infer())), 3) |
| values = [ |
| "<FunctionDef.first_word", |
| "<FunctionDef.last_word", |
| "<Const.NoneType", |
| ] |
| self.assertTrue( |
| all( |
| repr(inferred).startswith(value) |
| for inferred, value in zip(ast["fct"].infer(), values) |
| ) |
| ) |
| |
| def test_float_complex_ambiguity(self) -> None: |
| code = ''' |
| def no_conjugate_member(magic_flag): #@ |
| """should not raise E1101 on something.conjugate""" |
| if magic_flag: |
| something = 1.0 |
| else: |
| something = 1.0j |
| if isinstance(something, float): |
| return something |
| return __(something).conjugate() |
| ''' |
| func, retval = extract_node(code, __name__) |
| self.assertEqual([i.value for i in func.ilookup("something")], [1.0, 1.0j]) |
| self.assertEqual([i.value for i in retval.infer()], [1.0, 1.0j]) |
| |
| def test_lookup_cond_branches(self) -> None: |
| code = ''' |
| def no_conjugate_member(magic_flag): |
| """should not raise E1101 on something.conjugate""" |
| something = 1.0 |
| if magic_flag: |
| something = 1.0j |
| return something.conjugate() |
| ''' |
| ast = parse(code, __name__) |
| values = [ |
| i.value for i in test_utils.get_name_node(ast, "something", -1).infer() |
| ] |
| self.assertEqual(values, [1.0, 1.0j]) |
| |
| def test_simple_subscript(self) -> None: |
| code = """ |
| class A(object): |
| def __getitem__(self, index): |
| return index + 42 |
| [1, 2, 3][0] #@ |
| (1, 2, 3)[1] #@ |
| (1, 2, 3)[-1] #@ |
| [1, 2, 3][0] + (2, )[0] + (3, )[-1] #@ |
| e = {'key': 'value'} |
| e['key'] #@ |
| "first"[0] #@ |
| list([1, 2, 3])[-1] #@ |
| tuple((4, 5, 6))[2] #@ |
| A()[0] #@ |
| A()[-1] #@ |
| """ |
| ast_nodes = extract_node(code, __name__) |
| expected = [1, 2, 3, 6, "value", "f", 3, 6, 42, 41] |
| for node, expected_value in zip(ast_nodes, expected): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_invalid_subscripts(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class NoGetitem(object): |
| pass |
| class InvalidGetitem(object): |
| def __getitem__(self): pass |
| class InvalidGetitem2(object): |
| __getitem__ = 42 |
| NoGetitem()[4] #@ |
| InvalidGetitem()[5] #@ |
| InvalidGetitem2()[10] #@ |
| [1, 2, 3][None] #@ |
| 'lala'['bala'] #@ |
| """ |
| ) |
| for node in ast_nodes: |
| self.assertRaises(InferenceError, next, node.infer()) |
| |
| def test_bytes_subscript(self) -> None: |
| node = extract_node("""b'a'[0]""") |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 97) |
| |
| def test_subscript_multi_value(self) -> None: |
| code = """ |
| def do_thing_with_subscript(magic_flag): |
| src = [3, 2, 1] |
| if magic_flag: |
| src = [1, 2, 3] |
| something = src[0] |
| return something |
| """ |
| ast = parse(code, __name__) |
| values = [ |
| i.value for i in test_utils.get_name_node(ast, "something", -1).infer() |
| ] |
| self.assertEqual(list(sorted(values)), [1, 3]) |
| |
| def test_subscript_multi_slice(self) -> None: |
| code = """ |
| def zero_or_one(magic_flag): |
| if magic_flag: |
| return 1 |
| return 0 |
| |
| def do_thing_with_subscript(magic_flag): |
| src = [3, 2, 1] |
| index = zero_or_one(magic_flag) |
| something = src[index] |
| return something |
| """ |
| ast = parse(code, __name__) |
| values = [ |
| i.value for i in test_utils.get_name_node(ast, "something", -1).infer() |
| ] |
| self.assertEqual(list(sorted(values)), [2, 3]) |
| |
| def test_simple_tuple(self) -> None: |
| module = parse( |
| """ |
| a = (1,) |
| b = (22,) |
| some = a + b #@ |
| """ |
| ) |
| ast = next(module["some"].infer()) |
| self.assertIsInstance(ast, nodes.Tuple) |
| self.assertEqual(len(ast.elts), 2) |
| self.assertEqual(ast.elts[0].value, 1) |
| self.assertEqual(ast.elts[1].value, 22) |
| |
| def test_simple_for(self) -> None: |
| code = """ |
| for a in [1, 2, 3]: |
| print (a) |
| for b,c in [(1,2), (3,4)]: |
| print (b) |
| print (c) |
| |
| print ([(d,e) for e,d in ([1,2], [3,4])]) |
| """ |
| ast = parse(code, __name__) |
| self.assertEqual( |
| [i.value for i in test_utils.get_name_node(ast, "a", -1).infer()], [1, 2, 3] |
| ) |
| self.assertEqual( |
| [i.value for i in test_utils.get_name_node(ast, "b", -1).infer()], [1, 3] |
| ) |
| self.assertEqual( |
| [i.value for i in test_utils.get_name_node(ast, "c", -1).infer()], [2, 4] |
| ) |
| self.assertEqual( |
| [i.value for i in test_utils.get_name_node(ast, "d", -1).infer()], [2, 4] |
| ) |
| self.assertEqual( |
| [i.value for i in test_utils.get_name_node(ast, "e", -1).infer()], [1, 3] |
| ) |
| |
| def test_simple_for_genexpr(self) -> None: |
| code = """ |
| print ((d,e) for e,d in ([1,2], [3,4])) |
| """ |
| ast = parse(code, __name__) |
| self.assertEqual( |
| [i.value for i in test_utils.get_name_node(ast, "d", -1).infer()], [2, 4] |
| ) |
| self.assertEqual( |
| [i.value for i in test_utils.get_name_node(ast, "e", -1).infer()], [1, 3] |
| ) |
| |
| def test_builtin_help(self) -> None: |
| code = """ |
| help() |
| """ |
| # XXX failing since __builtin__.help assignment has |
| # been moved into a function... |
| node = extract_node(code, __name__) |
| inferred = list(node.func.infer()) |
| self.assertEqual(len(inferred), 1, inferred) |
| self.assertIsInstance(inferred[0], Instance) |
| self.assertEqual(inferred[0].name, "_Helper") |
| |
| def test_builtin_open(self) -> None: |
| code = """ |
| open("toto.txt") |
| """ |
| node = extract_node(code, __name__).func |
| inferred = list(node.infer()) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.FunctionDef) |
| self.assertEqual(inferred[0].name, "open") |
| |
| if platform.python_implementation() == "PyPy": |
| test_builtin_open = unittest.expectedFailure(test_builtin_open) |
| |
| def test_callfunc_context_func(self) -> None: |
| code = """ |
| def mirror(arg=None): |
| return arg |
| |
| un = mirror(1) |
| """ |
| ast = parse(code, __name__) |
| inferred = list(ast.igetattr("un")) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Const) |
| self.assertEqual(inferred[0].value, 1) |
| |
| def test_callfunc_context_lambda(self) -> None: |
| code = """ |
| mirror = lambda x=None: x |
| |
| un = mirror(1) |
| """ |
| ast = parse(code, __name__) |
| inferred = list(ast.igetattr("mirror")) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Lambda) |
| inferred = list(ast.igetattr("un")) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Const) |
| self.assertEqual(inferred[0].value, 1) |
| |
| def test_factory_method(self) -> None: |
| code = """ |
| class Super(object): |
| @classmethod |
| def instance(cls): |
| return cls() |
| |
| class Sub(Super): |
| def method(self): |
| print ('method called') |
| |
| sub = Sub.instance() |
| """ |
| ast = parse(code, __name__) |
| inferred = list(ast.igetattr("sub")) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], Instance) |
| self.assertEqual(inferred[0]._proxied.name, "Sub") |
| |
| def test_factory_methods_cls_call(self) -> None: |
| ast = extract_node( |
| """ |
| class C: |
| @classmethod |
| def factory(cls): |
| return cls() |
| |
| class D(C): |
| pass |
| |
| C.factory() #@ |
| D.factory() #@ |
| """, |
| "module", |
| ) |
| should_be_c = list(ast[0].infer()) |
| should_be_d = list(ast[1].infer()) |
| self.assertEqual(1, len(should_be_c)) |
| self.assertEqual(1, len(should_be_d)) |
| self.assertEqual("module.C", should_be_c[0].qname()) |
| self.assertEqual("module.D", should_be_d[0].qname()) |
| |
| def test_factory_methods_object_new_call(self) -> None: |
| ast = extract_node( |
| """ |
| class C: |
| @classmethod |
| def factory(cls): |
| return object.__new__(cls) |
| |
| class D(C): |
| pass |
| |
| C.factory() #@ |
| D.factory() #@ |
| """, |
| "module", |
| ) |
| should_be_c = list(ast[0].infer()) |
| should_be_d = list(ast[1].infer()) |
| self.assertEqual(1, len(should_be_c)) |
| self.assertEqual(1, len(should_be_d)) |
| self.assertEqual("module.C", should_be_c[0].qname()) |
| self.assertEqual("module.D", should_be_d[0].qname()) |
| |
| @pytest.mark.skipif( |
| PY38_PLUS, |
| reason="pathlib.Path cannot be inferred on Python 3.8", |
| ) |
| def test_factory_methods_inside_binary_operation(self): |
| node = extract_node( |
| """ |
| from pathlib import Path |
| h = Path("/home") |
| u = h / "user" |
| u #@ |
| """ |
| ) |
| assert next(node.infer()).qname() == "pathlib.Path" |
| |
| def test_import_as(self) -> None: |
| code = """ |
| import os.path as osp |
| print (osp.dirname(__file__)) |
| |
| from os.path import exists as e |
| assert e(__file__) |
| """ |
| ast = parse(code, __name__) |
| inferred = list(ast.igetattr("osp")) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Module) |
| self.assertEqual(inferred[0].name, "os.path") |
| inferred = list(ast.igetattr("e")) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.FunctionDef) |
| self.assertEqual(inferred[0].name, "exists") |
| |
| def _test_const_inferred( |
| self, node: nodes.AssignName, value: Union[float, str] |
| ) -> None: |
| inferred = list(node.infer()) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Const) |
| self.assertEqual(inferred[0].value, value) |
| |
| def test_unary_not(self) -> None: |
| for code in ( |
| "a = not (1,); b = not ()", |
| "a = not {1:2}; b = not {}", |
| "a = not [1, 2]; b = not []", |
| "a = not {1, 2}; b = not set()", |
| "a = not 1; b = not 0", |
| 'a = not "a"; b = not ""', |
| 'a = not b"a"; b = not b""', |
| ): |
| ast = builder.string_build(code, __name__, __file__) |
| self._test_const_inferred(ast["a"], False) |
| self._test_const_inferred(ast["b"], True) |
| |
| def test_unary_op_numbers(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| +1 #@ |
| -1 #@ |
| ~1 #@ |
| +2.0 #@ |
| -2.0 #@ |
| """ |
| ) |
| expected = [1, -1, -2, 2.0, -2.0] |
| for node, expected_value in zip(ast_nodes, expected): |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_matmul(self) -> None: |
| node = extract_node( |
| """ |
| class Array: |
| def __matmul__(self, other): |
| return 42 |
| Array() @ Array() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def test_binary_op_int_add(self) -> None: |
| ast = builder.string_build("a = 1 + 2", __name__, __file__) |
| self._test_const_inferred(ast["a"], 3) |
| |
| def test_binary_op_int_sub(self) -> None: |
| ast = builder.string_build("a = 1 - 2", __name__, __file__) |
| self._test_const_inferred(ast["a"], -1) |
| |
| def test_binary_op_float_div(self) -> None: |
| ast = builder.string_build("a = 1 / 2.", __name__, __file__) |
| self._test_const_inferred(ast["a"], 1 / 2.0) |
| |
| def test_binary_op_str_mul(self) -> None: |
| ast = builder.string_build('a = "*" * 40', __name__, __file__) |
| self._test_const_inferred(ast["a"], "*" * 40) |
| |
| def test_binary_op_int_bitand(self) -> None: |
| ast = builder.string_build("a = 23&20", __name__, __file__) |
| self._test_const_inferred(ast["a"], 23 & 20) |
| |
| def test_binary_op_int_bitor(self) -> None: |
| ast = builder.string_build("a = 23|8", __name__, __file__) |
| self._test_const_inferred(ast["a"], 23 | 8) |
| |
| def test_binary_op_int_bitxor(self) -> None: |
| ast = builder.string_build("a = 23^9", __name__, __file__) |
| self._test_const_inferred(ast["a"], 23 ^ 9) |
| |
| def test_binary_op_int_shiftright(self) -> None: |
| ast = builder.string_build("a = 23 >>1", __name__, __file__) |
| self._test_const_inferred(ast["a"], 23 >> 1) |
| |
| def test_binary_op_int_shiftleft(self) -> None: |
| ast = builder.string_build("a = 23 <<1", __name__, __file__) |
| self._test_const_inferred(ast["a"], 23 << 1) |
| |
| def test_binary_op_other_type(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A: |
| def __add__(self, other): |
| return other + 42 |
| A() + 1 #@ |
| 1 + A() #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertIsInstance(first, nodes.Const) |
| self.assertEqual(first.value, 43) |
| |
| second = next(ast_nodes[1].infer()) |
| self.assertEqual(second, util.Uninferable) |
| |
| def test_binary_op_other_type_using_reflected_operands(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(object): |
| def __radd__(self, other): |
| return other + 42 |
| A() + 1 #@ |
| 1 + A() #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertEqual(first, util.Uninferable) |
| |
| second = next(ast_nodes[1].infer()) |
| self.assertIsInstance(second, nodes.Const) |
| self.assertEqual(second.value, 43) |
| |
| def test_binary_op_reflected_and_not_implemented_is_type_error(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __radd__(self, other): return NotImplemented |
| |
| 1 + A() #@ |
| """ |
| ) |
| first = next(ast_node.infer()) |
| self.assertEqual(first, util.Uninferable) |
| |
| def test_binary_op_list_mul(self) -> None: |
| for code in ("a = [[]] * 2", "a = 2 * [[]]"): |
| ast = builder.string_build(code, __name__, __file__) |
| inferred = list(ast["a"].infer()) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.List) |
| self.assertEqual(len(inferred[0].elts), 2) |
| self.assertIsInstance(inferred[0].elts[0], nodes.List) |
| self.assertIsInstance(inferred[0].elts[1], nodes.List) |
| |
| def test_binary_op_list_mul_none(self) -> None: |
| "test correct handling on list multiplied by None" |
| ast = builder.string_build('a = [1] * None\nb = [1] * "r"') |
| inferred = ast["a"].inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertEqual(inferred[0], util.Uninferable) |
| inferred = ast["b"].inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertEqual(inferred[0], util.Uninferable) |
| |
| def test_binary_op_list_mul_int(self) -> None: |
| "test correct handling on list multiplied by int when there are more than one" |
| code = """ |
| from ctypes import c_int |
| seq = [c_int()] * 4 |
| """ |
| ast = parse(code, __name__) |
| inferred = ast["seq"].inferred() |
| self.assertEqual(len(inferred), 1) |
| listval = inferred[0] |
| self.assertIsInstance(listval, nodes.List) |
| self.assertEqual(len(listval.itered()), 4) |
| |
| def test_binary_op_on_self(self) -> None: |
| "test correct handling of applying binary operator to self" |
| code = """ |
| import sys |
| sys.path = ['foo'] + sys.path |
| sys.path.insert(0, 'bar') |
| path = sys.path |
| """ |
| ast = parse(code, __name__) |
| inferred = ast["path"].inferred() |
| self.assertIsInstance(inferred[0], nodes.List) |
| |
| def test_binary_op_tuple_add(self) -> None: |
| ast = builder.string_build("a = (1,) + (2,)", __name__, __file__) |
| inferred = list(ast["a"].infer()) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Tuple) |
| self.assertEqual(len(inferred[0].elts), 2) |
| self.assertEqual(inferred[0].elts[0].value, 1) |
| self.assertEqual(inferred[0].elts[1].value, 2) |
| |
| def test_binary_op_custom_class(self) -> None: |
| code = """ |
| class myarray: |
| def __init__(self, array): |
| self.array = array |
| def __mul__(self, x): |
| return myarray([2,4,6]) |
| def astype(self): |
| return "ASTYPE" |
| |
| def randint(maximum): |
| if maximum is not None: |
| return myarray([1,2,3]) * 2 |
| else: |
| return int(5) |
| |
| x = randint(1) |
| """ |
| ast = parse(code, __name__) |
| inferred = list(ast.igetattr("x")) |
| self.assertEqual(len(inferred), 2) |
| value = [str(v) for v in inferred] |
| # The __name__ trick here makes it work when invoked directly |
| # (__name__ == '__main__') and through pytest (__name__ == |
| # 'unittest_inference') |
| self.assertEqual( |
| value, |
| [ |
| f"Instance of {__name__}.myarray", |
| "Const.int(value=5,\n kind=None)", |
| ], |
| ) |
| |
| def test_nonregr_lambda_arg(self) -> None: |
| code = """ |
| def f(g = lambda: None): |
| __(g()).x |
| """ |
| callfuncnode = extract_node(code) |
| inferred = list(callfuncnode.infer()) |
| self.assertEqual(len(inferred), 2, inferred) |
| inferred.remove(util.Uninferable) |
| self.assertIsInstance(inferred[0], nodes.Const) |
| self.assertIsNone(inferred[0].value) |
| |
| def test_nonregr_getitem_empty_tuple(self) -> None: |
| code = """ |
| def f(x): |
| a = ()[x] |
| """ |
| ast = parse(code, __name__) |
| inferred = list(ast["f"].ilookup("a")) |
| self.assertEqual(len(inferred), 1) |
| self.assertEqual(inferred[0], util.Uninferable) |
| |
| def test_nonregr_instance_attrs(self) -> None: |
| """non regression for instance_attrs infinite loop : pylint / #4""" |
| |
| code = """ |
| class Foo(object): |
| |
| def set_42(self): |
| self.attr = 42 |
| |
| class Bar(Foo): |
| |
| def __init__(self): |
| self.attr = 41 |
| """ |
| ast = parse(code, __name__) |
| foo_class = ast["Foo"] |
| bar_class = ast["Bar"] |
| bar_self = ast["Bar"]["__init__"]["self"] |
| assattr = bar_class.instance_attrs["attr"][0] |
| self.assertEqual(len(foo_class.instance_attrs["attr"]), 1) |
| self.assertEqual(len(bar_class.instance_attrs["attr"]), 1) |
| self.assertEqual(bar_class.instance_attrs, {"attr": [assattr]}) |
| # call 'instance_attr' via 'Instance.getattr' to trigger the bug: |
| instance = bar_self.inferred()[0] |
| instance.getattr("attr") |
| self.assertEqual(len(bar_class.instance_attrs["attr"]), 1) |
| self.assertEqual(len(foo_class.instance_attrs["attr"]), 1) |
| self.assertEqual(bar_class.instance_attrs, {"attr": [assattr]}) |
| |
| def test_nonregr_multi_referential_addition(self) -> None: |
| """Regression test for https://github.com/PyCQA/astroid/issues/483 |
| Make sure issue where referring to the same variable |
| in the same inferred expression caused an uninferable result. |
| """ |
| code = """ |
| b = 1 |
| a = b + b |
| a #@ |
| """ |
| variable_a = extract_node(code) |
| self.assertEqual(variable_a.inferred()[0].value, 2) |
| |
| def test_nonregr_layed_dictunpack(self) -> None: |
| """Regression test for https://github.com/PyCQA/astroid/issues/483 |
| Make sure multiple dictunpack references are inferable |
| """ |
| code = """ |
| base = {'data': 0} |
| new = {**base, 'data': 1} |
| new3 = {**base, **new} |
| new3 #@ |
| """ |
| ass = extract_node(code) |
| self.assertIsInstance(ass.inferred()[0], nodes.Dict) |
| |
| def test_nonregr_inference_modifying_col_offset(self) -> None: |
| """Make sure inference doesn't improperly modify col_offset |
| |
| Regression test for https://github.com/PyCQA/pylint/issues/1839 |
| """ |
| |
| code = """ |
| class F: |
| def _(self): |
| return type(self).f |
| """ |
| mod = parse(code) |
| cdef = mod.body[0] |
| call = cdef.body[0].body[0].value.expr |
| orig_offset = cdef.col_offset |
| call.inferred() |
| self.assertEqual(cdef.col_offset, orig_offset) |
| |
| def test_no_runtime_error_in_repeat_inference(self) -> None: |
| """Stop repeat inference attempt causing a RuntimeError in Python3.7 |
| |
| See https://github.com/PyCQA/pylint/issues/2317 |
| """ |
| code = """ |
| |
| class ContextMixin: |
| def get_context_data(self, **kwargs): |
| return kwargs |
| |
| class DVM(ContextMixin): |
| def get_context_data(self, **kwargs): |
| ctx = super().get_context_data(**kwargs) |
| return ctx |
| |
| |
| class IFDVM(DVM): |
| def get_context_data(self, **kwargs): |
| ctx = super().get_context_data(**kwargs) |
| ctx['bar'] = 'foo' |
| ctx #@ |
| return ctx |
| """ |
| node = extract_node(code) |
| assert isinstance(node, nodes.NodeNG) |
| result = node.inferred() |
| assert len(result) == 2 |
| assert isinstance(result[0], nodes.Dict) |
| assert result[1] is util.Uninferable |
| |
| def test_python25_no_relative_import(self) -> None: |
| ast = resources.build_file("data/package/absimport.py") |
| self.assertTrue(ast.absolute_import_activated(), True) |
| inferred = next( |
| test_utils.get_name_node(ast, "import_package_subpackage_module").infer() |
| ) |
| # failed to import since absolute_import is activated |
| self.assertIs(inferred, util.Uninferable) |
| |
| def test_nonregr_absolute_import(self) -> None: |
| ast = resources.build_file("data/absimp/string.py", "data.absimp.string") |
| self.assertTrue(ast.absolute_import_activated(), True) |
| inferred = next(test_utils.get_name_node(ast, "string").infer()) |
| self.assertIsInstance(inferred, nodes.Module) |
| self.assertEqual(inferred.name, "string") |
| self.assertIn("ascii_letters", inferred.locals) |
| |
| def test_property(self) -> None: |
| code = """ |
| from smtplib import SMTP |
| class SendMailController(object): |
| |
| @property |
| def smtp(self): |
| return SMTP(mailhost, port) |
| |
| @property |
| def me(self): |
| return self |
| |
| my_smtp = SendMailController().smtp |
| my_me = SendMailController().me |
| """ |
| decorators = {"builtins.property"} |
| ast = parse(code, __name__) |
| self.assertEqual(ast["SendMailController"]["smtp"].decoratornames(), decorators) |
| propinferred = list(ast.body[2].value.infer()) |
| self.assertEqual(len(propinferred), 1) |
| propinferred = propinferred[0] |
| self.assertIsInstance(propinferred, Instance) |
| self.assertEqual(propinferred.name, "SMTP") |
| self.assertEqual(propinferred.root().name, "smtplib") |
| self.assertEqual(ast["SendMailController"]["me"].decoratornames(), decorators) |
| propinferred = list(ast.body[3].value.infer()) |
| self.assertEqual(len(propinferred), 1) |
| propinferred = propinferred[0] |
| self.assertIsInstance(propinferred, Instance) |
| self.assertEqual(propinferred.name, "SendMailController") |
| self.assertEqual(propinferred.root().name, __name__) |
| |
| def test_im_func_unwrap(self) -> None: |
| code = """ |
| class EnvBasedTC: |
| def pactions(self): |
| pass |
| pactions = EnvBasedTC.pactions.im_func |
| print (pactions) |
| |
| class EnvBasedTC2: |
| pactions = EnvBasedTC.pactions.im_func |
| print (pactions) |
| """ |
| ast = parse(code, __name__) |
| pactions = test_utils.get_name_node(ast, "pactions") |
| inferred = list(pactions.infer()) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.FunctionDef) |
| pactions = test_utils.get_name_node(ast["EnvBasedTC2"], "pactions") |
| inferred = list(pactions.infer()) |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.FunctionDef) |
| |
| def test_augassign(self) -> None: |
| code = """ |
| a = 1 |
| a += 2 |
| print (a) |
| """ |
| ast = parse(code, __name__) |
| inferred = list(test_utils.get_name_node(ast, "a").infer()) |
| |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Const) |
| self.assertEqual(inferred[0].value, 3) |
| |
| def test_nonregr_func_arg(self) -> None: |
| code = """ |
| def foo(self, bar): |
| def baz(): |
| pass |
| def qux(): |
| return baz |
| spam = bar(None, qux) |
| print (spam) |
| """ |
| ast = parse(code, __name__) |
| inferred = list(test_utils.get_name_node(ast["foo"], "spam").infer()) |
| self.assertEqual(len(inferred), 1) |
| self.assertIs(inferred[0], util.Uninferable) |
| |
| def test_nonregr_func_global(self) -> None: |
| code = """ |
| active_application = None |
| |
| def get_active_application(): |
| global active_application |
| return active_application |
| |
| class Application(object): |
| def __init__(self): |
| global active_application |
| active_application = self |
| |
| class DataManager(object): |
| def __init__(self, app=None): |
| self.app = get_active_application() |
| def test(self): |
| p = self.app |
| print (p) |
| """ |
| ast = parse(code, __name__) |
| inferred = list(Instance(ast["DataManager"]).igetattr("app")) |
| self.assertEqual(len(inferred), 2, inferred) # None / Instance(Application) |
| inferred = list( |
| test_utils.get_name_node(ast["DataManager"]["test"], "p").infer() |
| ) |
| self.assertEqual(len(inferred), 2, inferred) |
| for node in inferred: |
| if isinstance(node, Instance) and node.name == "Application": |
| break |
| else: |
| self.fail(f"expected to find an instance of Application in {inferred}") |
| |
| def test_list_inference(self) -> None: |
| """#20464""" |
| code = """ |
| from unknown import Unknown |
| A = [] |
| B = [] |
| |
| def test(): |
| xyz = [ |
| Unknown |
| ] + A + B |
| return xyz |
| |
| Z = test() |
| """ |
| ast = parse(code, __name__) |
| inferred = next(ast["Z"].infer()) |
| self.assertIsInstance(inferred, nodes.List) |
| self.assertEqual(len(inferred.elts), 1) |
| self.assertIsInstance(inferred.elts[0], nodes.Unknown) |
| |
| def test__new__(self) -> None: |
| code = """ |
| class NewTest(object): |
| "doc" |
| def __new__(cls, arg): |
| self = object.__new__(cls) |
| self.arg = arg |
| return self |
| |
| n = NewTest() |
| """ |
| ast = parse(code, __name__) |
| self.assertRaises(InferenceError, list, ast["NewTest"].igetattr("arg")) |
| n = next(ast["n"].infer()) |
| inferred = list(n.igetattr("arg")) |
| self.assertEqual(len(inferred), 1, inferred) |
| |
| def test__new__bound_methods(self) -> None: |
| node = extract_node( |
| """ |
| class cls(object): pass |
| cls().__new__(cls) #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred._proxied, node.root()["cls"]) |
| |
| def test_two_parents_from_same_module(self) -> None: |
| code = """ |
| from data import nonregr |
| class Xxx(nonregr.Aaa, nonregr.Ccc): |
| "doc" |
| """ |
| ast = parse(code, __name__) |
| parents = list(ast["Xxx"].ancestors()) |
| self.assertEqual(len(parents), 3, parents) # Aaa, Ccc, object |
| |
| def test_pluggable_inference(self) -> None: |
| code = """ |
| from collections import namedtuple |
| A = namedtuple('A', ['a', 'b']) |
| B = namedtuple('B', 'a b') |
| """ |
| ast = parse(code, __name__) |
| aclass = ast["A"].inferred()[0] |
| self.assertIsInstance(aclass, nodes.ClassDef) |
| self.assertIn("a", aclass.instance_attrs) |
| self.assertIn("b", aclass.instance_attrs) |
| bclass = ast["B"].inferred()[0] |
| self.assertIsInstance(bclass, nodes.ClassDef) |
| self.assertIn("a", bclass.instance_attrs) |
| self.assertIn("b", bclass.instance_attrs) |
| |
| def test_infer_arguments(self) -> None: |
| code = """ |
| class A(object): |
| def first(self, arg1, arg2): |
| return arg1 |
| @classmethod |
| def method(cls, arg1, arg2): |
| return arg2 |
| @classmethod |
| def empty(cls): |
| return 2 |
| @staticmethod |
| def static(arg1, arg2): |
| return arg1 |
| def empty_method(self): |
| return [] |
| x = A().first(1, []) |
| y = A.method(1, []) |
| z = A.static(1, []) |
| empty = A.empty() |
| empty_list = A().empty_method() |
| """ |
| ast = parse(code, __name__) |
| int_node = ast["x"].inferred()[0] |
| self.assertIsInstance(int_node, nodes.Const) |
| self.assertEqual(int_node.value, 1) |
| list_node = ast["y"].inferred()[0] |
| self.assertIsInstance(list_node, nodes.List) |
| int_node = ast["z"].inferred()[0] |
| self.assertIsInstance(int_node, nodes.Const) |
| self.assertEqual(int_node.value, 1) |
| empty = ast["empty"].inferred()[0] |
| self.assertIsInstance(empty, nodes.Const) |
| self.assertEqual(empty.value, 2) |
| empty_list = ast["empty_list"].inferred()[0] |
| self.assertIsInstance(empty_list, nodes.List) |
| |
| def test_infer_variable_arguments(self) -> None: |
| code = """ |
| def test(*args, **kwargs): |
| vararg = args |
| kwarg = kwargs |
| """ |
| ast = parse(code, __name__) |
| func = ast["test"] |
| vararg = func.body[0].value |
| kwarg = func.body[1].value |
| |
| kwarg_inferred = kwarg.inferred()[0] |
| self.assertIsInstance(kwarg_inferred, nodes.Dict) |
| self.assertIs(kwarg_inferred.parent, func.args) |
| |
| vararg_inferred = vararg.inferred()[0] |
| self.assertIsInstance(vararg_inferred, nodes.Tuple) |
| self.assertIs(vararg_inferred.parent, func.args) |
| |
| def test_infer_nested(self) -> None: |
| code = """ |
| def nested(): |
| from threading import Thread |
| |
| class NestedThread(Thread): |
| def __init__(self): |
| Thread.__init__(self) |
| """ |
| # Test that inferring Thread.__init__ looks up in |
| # the nested scope. |
| ast = parse(code, __name__) |
| callfunc = next(ast.nodes_of_class(nodes.Call)) |
| func = callfunc.func |
| inferred = func.inferred()[0] |
| self.assertIsInstance(inferred, UnboundMethod) |
| |
| def test_instance_binary_operations(self) -> None: |
| code = """ |
| class A(object): |
| def __mul__(self, other): |
| return 42 |
| a = A() |
| b = A() |
| sub = a - b |
| mul = a * b |
| """ |
| ast = parse(code, __name__) |
| sub = ast["sub"].inferred()[0] |
| mul = ast["mul"].inferred()[0] |
| self.assertIs(sub, util.Uninferable) |
| self.assertIsInstance(mul, nodes.Const) |
| self.assertEqual(mul.value, 42) |
| |
| def test_instance_binary_operations_parent(self) -> None: |
| code = """ |
| class A(object): |
| def __mul__(self, other): |
| return 42 |
| class B(A): |
| pass |
| a = B() |
| b = B() |
| sub = a - b |
| mul = a * b |
| """ |
| ast = parse(code, __name__) |
| sub = ast["sub"].inferred()[0] |
| mul = ast["mul"].inferred()[0] |
| self.assertIs(sub, util.Uninferable) |
| self.assertIsInstance(mul, nodes.Const) |
| self.assertEqual(mul.value, 42) |
| |
| def test_instance_binary_operations_multiple_methods(self) -> None: |
| code = """ |
| class A(object): |
| def __mul__(self, other): |
| return 42 |
| class B(A): |
| def __mul__(self, other): |
| return [42] |
| a = B() |
| b = B() |
| sub = a - b |
| mul = a * b |
| """ |
| ast = parse(code, __name__) |
| sub = ast["sub"].inferred()[0] |
| mul = ast["mul"].inferred()[0] |
| self.assertIs(sub, util.Uninferable) |
| self.assertIsInstance(mul, nodes.List) |
| self.assertIsInstance(mul.elts[0], nodes.Const) |
| self.assertEqual(mul.elts[0].value, 42) |
| |
| def test_infer_call_result_crash(self) -> None: |
| code = """ |
| class A(object): |
| def __mul__(self, other): |
| return type.__new__() |
| |
| a = A() |
| b = A() |
| c = a * b |
| """ |
| ast = parse(code, __name__) |
| node = ast["c"] |
| assert isinstance(node, nodes.NodeNG) |
| self.assertEqual(node.inferred(), [util.Uninferable]) |
| |
| def test_infer_empty_nodes(self) -> None: |
| # Should not crash when trying to infer EmptyNodes. |
| node = nodes.EmptyNode() |
| assert isinstance(node, nodes.NodeNG) |
| self.assertEqual(node.inferred(), [util.Uninferable]) |
| |
| def test_infinite_loop_for_decorators(self) -> None: |
| # Issue https://bitbucket.org/logilab/astroid/issue/50 |
| # A decorator that returns itself leads to an infinite loop. |
| code = """ |
| def decorator(): |
| def wrapper(): |
| return decorator() |
| return wrapper |
| |
| @decorator() |
| def do_a_thing(): |
| pass |
| """ |
| ast = parse(code, __name__) |
| node = ast["do_a_thing"] |
| self.assertEqual(node.type, "function") |
| |
| def test_no_infinite_ancestor_loop(self) -> None: |
| klass = extract_node( |
| """ |
| import datetime |
| |
| def method(self): |
| datetime.datetime = something() |
| |
| class something(datetime.datetime): #@ |
| pass |
| """ |
| ) |
| ancestors = [base.name for base in klass.ancestors()] |
| expected_subset = ["datetime", "date"] |
| self.assertEqual(expected_subset, ancestors[:2]) |
| |
| def test_stop_iteration_leak(self) -> None: |
| code = """ |
| class Test: |
| def __init__(self): |
| self.config = {0: self.config[0]} |
| self.config[0].test() #@ |
| """ |
| ast = extract_node(code, __name__) |
| expr = ast.func.expr |
| with pytest.raises(InferenceError): |
| next(expr.infer()) |
| |
| def test_tuple_builtin_inference(self) -> None: |
| code = """ |
| var = (1, 2) |
| tuple() #@ |
| tuple([1]) #@ |
| tuple({2}) #@ |
| tuple("abc") #@ |
| tuple({1: 2}) #@ |
| tuple(var) #@ |
| tuple(tuple([1])) #@ |
| tuple(frozenset((1, 2))) #@ |
| |
| tuple(None) #@ |
| tuple(1) #@ |
| tuple(1, 2) #@ |
| """ |
| ast = extract_node(code, __name__) |
| |
| self.assertInferTuple(ast[0], []) |
| self.assertInferTuple(ast[1], [1]) |
| self.assertInferTuple(ast[2], [2]) |
| self.assertInferTuple(ast[3], ["a", "b", "c"]) |
| self.assertInferTuple(ast[4], [1]) |
| self.assertInferTuple(ast[5], [1, 2]) |
| self.assertInferTuple(ast[6], [1]) |
| self.assertInferTuple(ast[7], [1, 2]) |
| |
| for node in ast[8:]: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.qname(), "builtins.tuple") |
| |
| def test_starred_in_tuple_literal(self) -> None: |
| code = """ |
| var = (1, 2, 3) |
| bar = (5, 6, 7) |
| foo = [999, 1000, 1001] |
| (0, *var) #@ |
| (0, *var, 4) #@ |
| (0, *var, 4, *bar) #@ |
| (0, *var, 4, *(*bar, 8)) #@ |
| (0, *var, 4, *(*bar, *foo)) #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferTuple(ast[0], [0, 1, 2, 3]) |
| self.assertInferTuple(ast[1], [0, 1, 2, 3, 4]) |
| self.assertInferTuple(ast[2], [0, 1, 2, 3, 4, 5, 6, 7]) |
| self.assertInferTuple(ast[3], [0, 1, 2, 3, 4, 5, 6, 7, 8]) |
| self.assertInferTuple(ast[4], [0, 1, 2, 3, 4, 5, 6, 7, 999, 1000, 1001]) |
| |
| def test_starred_in_list_literal(self) -> None: |
| code = """ |
| var = (1, 2, 3) |
| bar = (5, 6, 7) |
| foo = [999, 1000, 1001] |
| [0, *var] #@ |
| [0, *var, 4] #@ |
| [0, *var, 4, *bar] #@ |
| [0, *var, 4, *[*bar, 8]] #@ |
| [0, *var, 4, *[*bar, *foo]] #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferList(ast[0], [0, 1, 2, 3]) |
| self.assertInferList(ast[1], [0, 1, 2, 3, 4]) |
| self.assertInferList(ast[2], [0, 1, 2, 3, 4, 5, 6, 7]) |
| self.assertInferList(ast[3], [0, 1, 2, 3, 4, 5, 6, 7, 8]) |
| self.assertInferList(ast[4], [0, 1, 2, 3, 4, 5, 6, 7, 999, 1000, 1001]) |
| |
| def test_starred_in_set_literal(self) -> None: |
| code = """ |
| var = (1, 2, 3) |
| bar = (5, 6, 7) |
| foo = [999, 1000, 1001] |
| {0, *var} #@ |
| {0, *var, 4} #@ |
| {0, *var, 4, *bar} #@ |
| {0, *var, 4, *{*bar, 8}} #@ |
| {0, *var, 4, *{*bar, *foo}} #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferSet(ast[0], [0, 1, 2, 3]) |
| self.assertInferSet(ast[1], [0, 1, 2, 3, 4]) |
| self.assertInferSet(ast[2], [0, 1, 2, 3, 4, 5, 6, 7]) |
| self.assertInferSet(ast[3], [0, 1, 2, 3, 4, 5, 6, 7, 8]) |
| self.assertInferSet(ast[4], [0, 1, 2, 3, 4, 5, 6, 7, 999, 1000, 1001]) |
| |
| def test_starred_in_literals_inference_issues(self) -> None: |
| code = """ |
| {0, *var} #@ |
| {0, *var, 4} #@ |
| {0, *var, 4, *bar} #@ |
| {0, *var, 4, *{*bar, 8}} #@ |
| {0, *var, 4, *{*bar, *foo}} #@ |
| """ |
| ast = extract_node(code, __name__) |
| for node in ast: |
| with self.assertRaises(InferenceError): |
| next(node.infer()) |
| |
| def test_starred_in_mapping_literal(self) -> None: |
| code = """ |
| var = {1: 'b', 2: 'c'} |
| bar = {4: 'e', 5: 'f'} |
| {0: 'a', **var} #@ |
| {0: 'a', **var, 3: 'd'} #@ |
| {0: 'a', **var, 3: 'd', **{**bar, 6: 'g'}} #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferDict(ast[0], {0: "a", 1: "b", 2: "c"}) |
| self.assertInferDict(ast[1], {0: "a", 1: "b", 2: "c", 3: "d"}) |
| self.assertInferDict( |
| ast[2], {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g"} |
| ) |
| |
| def test_starred_in_mapping_literal_no_inference_possible(self) -> None: |
| node = extract_node( |
| """ |
| from unknown import unknown |
| |
| def test(a): |
| return a + 1 |
| |
| def func(): |
| a = {unknown: 'a'} |
| return {0: 1, **a} |
| |
| test(**func()) |
| """ |
| ) |
| self.assertEqual(next(node.infer()), util.Uninferable) |
| |
| def test_starred_in_mapping_inference_issues(self) -> None: |
| code = """ |
| {0: 'a', **var} #@ |
| {0: 'a', **var, 3: 'd'} #@ |
| {0: 'a', **var, 3: 'd', **{**bar, 6: 'g'}} #@ |
| """ |
| ast = extract_node(code, __name__) |
| for node in ast: |
| with self.assertRaises(InferenceError): |
| next(node.infer()) |
| |
| def test_starred_in_mapping_literal_non_const_keys_values(self) -> None: |
| code = """ |
| a, b, c, d, e, f, g, h, i, j = "ABCDEFGHIJ" |
| var = {c: d, e: f} |
| bar = {i: j} |
| {a: b, **var} #@ |
| {a: b, **var, **{g: h, **bar}} #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferDict(ast[0], {"A": "B", "C": "D", "E": "F"}) |
| self.assertInferDict(ast[1], {"A": "B", "C": "D", "E": "F", "G": "H", "I": "J"}) |
| |
| def test_frozenset_builtin_inference(self) -> None: |
| code = """ |
| var = (1, 2) |
| frozenset() #@ |
| frozenset([1, 2, 1]) #@ |
| frozenset({2, 3, 1}) #@ |
| frozenset("abcab") #@ |
| frozenset({1: 2}) #@ |
| frozenset(var) #@ |
| frozenset(tuple([1])) #@ |
| |
| frozenset(set(tuple([4, 5, set([2])]))) #@ |
| frozenset(None) #@ |
| frozenset(1) #@ |
| frozenset(1, 2) #@ |
| """ |
| ast = extract_node(code, __name__) |
| |
| self.assertInferFrozenSet(ast[0], []) |
| self.assertInferFrozenSet(ast[1], [1, 2]) |
| self.assertInferFrozenSet(ast[2], [1, 2, 3]) |
| self.assertInferFrozenSet(ast[3], ["a", "b", "c"]) |
| self.assertInferFrozenSet(ast[4], [1]) |
| self.assertInferFrozenSet(ast[5], [1, 2]) |
| self.assertInferFrozenSet(ast[6], [1]) |
| |
| for node in ast[7:]: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.qname(), "builtins.frozenset") |
| |
| def test_set_builtin_inference(self) -> None: |
| code = """ |
| var = (1, 2) |
| set() #@ |
| set([1, 2, 1]) #@ |
| set({2, 3, 1}) #@ |
| set("abcab") #@ |
| set({1: 2}) #@ |
| set(var) #@ |
| set(tuple([1])) #@ |
| |
| set(set(tuple([4, 5, set([2])]))) #@ |
| set(None) #@ |
| set(1) #@ |
| set(1, 2) #@ |
| """ |
| ast = extract_node(code, __name__) |
| |
| self.assertInferSet(ast[0], []) |
| self.assertInferSet(ast[1], [1, 2]) |
| self.assertInferSet(ast[2], [1, 2, 3]) |
| self.assertInferSet(ast[3], ["a", "b", "c"]) |
| self.assertInferSet(ast[4], [1]) |
| self.assertInferSet(ast[5], [1, 2]) |
| self.assertInferSet(ast[6], [1]) |
| |
| for node in ast[7:]: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.qname(), "builtins.set") |
| |
| def test_list_builtin_inference(self) -> None: |
| code = """ |
| var = (1, 2) |
| list() #@ |
| list([1, 2, 1]) #@ |
| list({2, 3, 1}) #@ |
| list("abcab") #@ |
| list({1: 2}) #@ |
| list(var) #@ |
| list(tuple([1])) #@ |
| |
| list(list(tuple([4, 5, list([2])]))) #@ |
| list(None) #@ |
| list(1) #@ |
| list(1, 2) #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferList(ast[0], []) |
| self.assertInferList(ast[1], [1, 1, 2]) |
| self.assertInferList(ast[2], [1, 2, 3]) |
| self.assertInferList(ast[3], ["a", "a", "b", "b", "c"]) |
| self.assertInferList(ast[4], [1]) |
| self.assertInferList(ast[5], [1, 2]) |
| self.assertInferList(ast[6], [1]) |
| |
| for node in ast[7:]: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.qname(), "builtins.list") |
| |
| def test_conversion_of_dict_methods(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| list({1:2, 2:3}.values()) #@ |
| list({1:2, 2:3}.keys()) #@ |
| tuple({1:2, 2:3}.values()) #@ |
| tuple({1:2, 3:4}.keys()) #@ |
| set({1:2, 2:4}.keys()) #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| self.assertInferList(ast_nodes[0], [2, 3]) |
| self.assertInferList(ast_nodes[1], [1, 2]) |
| self.assertInferTuple(ast_nodes[2], [2, 3]) |
| self.assertInferTuple(ast_nodes[3], [1, 3]) |
| self.assertInferSet(ast_nodes[4], [1, 2]) |
| |
| def test_builtin_inference_py3k(self) -> None: |
| code = """ |
| list(b"abc") #@ |
| tuple(b"abc") #@ |
| set(b"abc") #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferList(ast[0], [97, 98, 99]) |
| self.assertInferTuple(ast[1], [97, 98, 99]) |
| self.assertInferSet(ast[2], [97, 98, 99]) |
| |
| def test_dict_inference(self) -> None: |
| code = """ |
| dict() #@ |
| dict(a=1, b=2, c=3) #@ |
| dict([(1, 2), (2, 3)]) #@ |
| dict([[1, 2], [2, 3]]) #@ |
| dict([(1, 2), [2, 3]]) #@ |
| dict([('a', 2)], b=2, c=3) #@ |
| dict({1: 2}) #@ |
| dict({'c': 2}, a=4, b=5) #@ |
| def func(): |
| return dict(a=1, b=2) |
| func() #@ |
| var = {'x': 2, 'y': 3} |
| dict(var, a=1, b=2) #@ |
| |
| dict([1, 2, 3]) #@ |
| dict([(1, 2), (1, 2, 3)]) #@ |
| dict({1: 2}, {1: 2}) #@ |
| dict({1: 2}, (1, 2)) #@ |
| dict({1: 2}, (1, 2), a=4) #@ |
| dict([(1, 2), ([4, 5], 2)]) #@ |
| dict([None, None]) #@ |
| |
| def using_unknown_kwargs(**kwargs): |
| return dict(**kwargs) |
| using_unknown_kwargs(a=1, b=2) #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferDict(ast[0], {}) |
| self.assertInferDict(ast[1], {"a": 1, "b": 2, "c": 3}) |
| for i in range(2, 5): |
| self.assertInferDict(ast[i], {1: 2, 2: 3}) |
| self.assertInferDict(ast[5], {"a": 2, "b": 2, "c": 3}) |
| self.assertInferDict(ast[6], {1: 2}) |
| self.assertInferDict(ast[7], {"c": 2, "a": 4, "b": 5}) |
| self.assertInferDict(ast[8], {"a": 1, "b": 2}) |
| self.assertInferDict(ast[9], {"x": 2, "y": 3, "a": 1, "b": 2}) |
| |
| for node in ast[10:]: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.qname(), "builtins.dict") |
| |
| def test_dict_inference_kwargs(self) -> None: |
| ast_node = extract_node("""dict(a=1, b=2, **{'c': 3})""") |
| self.assertInferDict(ast_node, {"a": 1, "b": 2, "c": 3}) |
| |
| def test_dict_inference_for_multiple_starred(self) -> None: |
| pairs = [ |
| ('dict(a=1, **{"b": 2}, **{"c":3})', {"a": 1, "b": 2, "c": 3}), |
| ('dict(a=1, **{"b": 2}, d=4, **{"c":3})', {"a": 1, "b": 2, "c": 3, "d": 4}), |
| ('dict({"a":1}, b=2, **{"c":3})', {"a": 1, "b": 2, "c": 3}), |
| ] |
| for code, expected_value in pairs: |
| node = extract_node(code) |
| self.assertInferDict(node, expected_value) |
| |
| def test_dict_inference_unpack_repeated_key(self) -> None: |
| """Make sure astroid does not infer repeated keys in a dictionary |
| |
| Regression test for https://github.com/PyCQA/pylint/issues/1843 |
| """ |
| code = """ |
| base = {'data': 0} |
| new = {**base, 'data': 1} #@ |
| new2 = {'data': 1, **base} #@ # Make sure overwrite works |
| a = 'd' + 'ata' |
| b3 = {**base, a: 3} #@ Make sure keys are properly inferred |
| b4 = {a: 3, **base} #@ |
| """ |
| ast = extract_node(code) |
| final_values = ("{'data': 1}", "{'data': 0}", "{'data': 3}", "{'data': 0}") |
| for node, final_value in zip(ast, final_values): |
| assert node.targets[0].inferred()[0].as_string() == final_value |
| |
| def test_dict_invalid_args(self) -> None: |
| invalid_values = ["dict(*1)", "dict(**lala)", "dict(**[])"] |
| for invalid in invalid_values: |
| ast_node = extract_node(invalid) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.qname(), "builtins.dict") |
| |
| def test_str_methods(self) -> None: |
| code = """ |
| ' '.decode() #@ |
| ' '.join('abcd') #@ |
| ' '.replace('a', 'b') #@ |
| ' '.format('a') #@ |
| ' '.capitalize() #@ |
| ' '.title() #@ |
| ' '.lower() #@ |
| ' '.upper() #@ |
| ' '.swapcase() #@ |
| ' '.strip() #@ |
| ' '.rstrip() #@ |
| ' '.lstrip() #@ |
| ' '.rjust() #@ |
| ' '.ljust() #@ |
| ' '.center() #@ |
| |
| ' '.index() #@ |
| ' '.find() #@ |
| ' '.count() #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferConst(ast[0], "") |
| for i in range(1, 15): |
| self.assertInferConst(ast[i], "") |
| for i in range(15, 18): |
| self.assertInferConst(ast[i], 0) |
| |
| def test_unicode_methods(self) -> None: |
| code = """ |
| u' '.decode() #@ |
| u' '.join('abcd') #@ |
| u' '.replace('a', 'b') #@ |
| u' '.format('a') #@ |
| u' '.capitalize() #@ |
| u' '.title() #@ |
| u' '.lower() #@ |
| u' '.upper() #@ |
| u' '.swapcase() #@ |
| u' '.strip() #@ |
| u' '.rstrip() #@ |
| u' '.lstrip() #@ |
| u' '.rjust() #@ |
| u' '.ljust() #@ |
| u' '.center() #@ |
| |
| u' '.index() #@ |
| u' '.find() #@ |
| u' '.count() #@ |
| """ |
| ast = extract_node(code, __name__) |
| self.assertInferConst(ast[0], "") |
| for i in range(1, 15): |
| self.assertInferConst(ast[i], "") |
| for i in range(15, 18): |
| self.assertInferConst(ast[i], 0) |
| |
| def test_scope_lookup_same_attributes(self) -> None: |
| code = """ |
| import collections |
| class Second(collections.Counter): |
| def collections(self): |
| return "second" |
| |
| """ |
| ast = parse(code, __name__) |
| bases = ast["Second"].bases[0] |
| inferred = next(bases.infer()) |
| self.assertTrue(inferred) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.qname(), "collections.Counter") |
| |
| def test_inferring_with_statement_failures(self) -> None: |
| module = parse( |
| """ |
| class NoEnter(object): |
| pass |
| class NoMethod(object): |
| __enter__ = None |
| class NoElts(object): |
| def __enter__(self): |
| return 42 |
| |
| with NoEnter() as no_enter: |
| pass |
| with NoMethod() as no_method: |
| pass |
| with NoElts() as (no_elts, no_elts1): |
| pass |
| """ |
| ) |
| self.assertRaises(InferenceError, next, module["no_enter"].infer()) |
| self.assertRaises(InferenceError, next, module["no_method"].infer()) |
| self.assertRaises(InferenceError, next, module["no_elts"].infer()) |
| |
| def test_inferring_with_statement(self) -> None: |
| module = parse( |
| """ |
| class SelfContext(object): |
| def __enter__(self): |
| return self |
| |
| class OtherContext(object): |
| def __enter__(self): |
| return SelfContext() |
| |
| class MultipleReturns(object): |
| def __enter__(self): |
| return SelfContext(), OtherContext() |
| |
| class MultipleReturns2(object): |
| def __enter__(self): |
| return [1, [2, 3]] |
| |
| with SelfContext() as self_context: |
| pass |
| with OtherContext() as other_context: |
| pass |
| with MultipleReturns(), OtherContext() as multiple_with: |
| pass |
| with MultipleReturns2() as (stdout, (stderr, stdin)): |
| pass |
| """ |
| ) |
| self_context = module["self_context"] |
| inferred = next(self_context.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "SelfContext") |
| |
| other_context = module["other_context"] |
| inferred = next(other_context.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "SelfContext") |
| |
| multiple_with = module["multiple_with"] |
| inferred = next(multiple_with.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "SelfContext") |
| |
| stdout = module["stdout"] |
| inferred = next(stdout.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 1) |
| stderr = module["stderr"] |
| inferred = next(stderr.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 2) |
| |
| def test_inferring_with_contextlib_contextmanager(self) -> None: |
| module = parse( |
| """ |
| import contextlib |
| from contextlib import contextmanager |
| |
| @contextlib.contextmanager |
| def manager_none(): |
| try: |
| yield |
| finally: |
| pass |
| |
| @contextlib.contextmanager |
| def manager_something(): |
| try: |
| yield 42 |
| yield 24 # This should be ignored. |
| finally: |
| pass |
| |
| @contextmanager |
| def manager_multiple(): |
| with manager_none() as foo: |
| with manager_something() as bar: |
| yield foo, bar |
| |
| with manager_none() as none: |
| pass |
| with manager_something() as something: |
| pass |
| with manager_multiple() as (first, second): |
| pass |
| """ |
| ) |
| none = module["none"] |
| inferred = next(none.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertIsNone(inferred.value) |
| |
| something = module["something"] |
| inferred = something.inferred() |
| self.assertEqual(len(inferred), 1) |
| inferred = inferred[0] |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| first, second = module["first"], module["second"] |
| first = next(first.infer()) |
| second = next(second.infer()) |
| self.assertIsInstance(first, nodes.Const) |
| self.assertIsNone(first.value) |
| self.assertIsInstance(second, nodes.Const) |
| self.assertEqual(second.value, 42) |
| |
| def test_inferring_context_manager_skip_index_error(self) -> None: |
| # Raise an InferenceError when having multiple 'as' bindings |
| # from a context manager, but its result doesn't have those |
| # indices. This is the case of contextlib.nested, where the |
| # result is a list, which is mutated later on, so it's |
| # undetected by astroid. |
| module = parse( |
| """ |
| class Manager(object): |
| def __enter__(self): |
| return [] |
| with Manager() as (a, b, c): |
| pass |
| """ |
| ) |
| self.assertRaises(InferenceError, next, module["a"].infer()) |
| |
| def test_inferring_context_manager_unpacking_inference_error(self) -> None: |
| # https://github.com/PyCQA/pylint/issues/1463 |
| module = parse( |
| """ |
| import contextlib |
| |
| @contextlib.contextmanager |
| def _select_source(a=None): |
| with _select_source() as result: |
| yield result |
| |
| result = _select_source() |
| with result as (a, b, c): |
| pass |
| """ |
| ) |
| self.assertRaises(InferenceError, next, module["a"].infer()) |
| |
| def test_inferring_with_contextlib_contextmanager_failures(self) -> None: |
| module = parse( |
| """ |
| from contextlib import contextmanager |
| |
| def no_decorators_mgr(): |
| yield |
| @no_decorators_mgr |
| def other_decorators_mgr(): |
| yield |
| @contextmanager |
| def no_yield_mgr(): |
| pass |
| |
| with no_decorators_mgr() as no_decorators: |
| pass |
| with other_decorators_mgr() as other_decorators: |
| pass |
| with no_yield_mgr() as no_yield: |
| pass |
| """ |
| ) |
| self.assertRaises(InferenceError, next, module["no_decorators"].infer()) |
| self.assertRaises(InferenceError, next, module["other_decorators"].infer()) |
| self.assertRaises(InferenceError, next, module["no_yield"].infer()) |
| |
| def test_nested_contextmanager(self) -> None: |
| """Make sure contextmanager works with nested functions |
| |
| Previously contextmanager would retrieve |
| the first yield instead of the yield in the |
| proper scope |
| |
| Fixes https://github.com/PyCQA/pylint/issues/1746 |
| """ |
| code = """ |
| from contextlib import contextmanager |
| |
| @contextmanager |
| def outer(): |
| @contextmanager |
| def inner(): |
| yield 2 |
| yield inner |
| |
| with outer() as ctx: |
| ctx #@ |
| with ctx() as val: |
| val #@ |
| """ |
| context_node, value_node = extract_node(code) |
| value = next(value_node.infer()) |
| context = next(context_node.infer()) |
| assert isinstance(context, nodes.FunctionDef) |
| assert isinstance(value, nodes.Const) |
| |
| def test_unary_op_leaks_stop_iteration(self) -> None: |
| node = extract_node("+[] #@") |
| self.assertEqual(util.Uninferable, next(node.infer())) |
| |
| def test_unary_operands(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| import os |
| def func(): pass |
| from missing import missing |
| class GoodInstance(object): |
| def __pos__(self): |
| return 42 |
| def __neg__(self): |
| return +self - 41 |
| def __invert__(self): |
| return 42 |
| class BadInstance(object): |
| def __pos__(self): |
| return lala |
| def __neg__(self): |
| return missing |
| class LambdaInstance(object): |
| __pos__ = lambda self: self.lala |
| __neg__ = lambda self: self.lala + 1 |
| @property |
| def lala(self): return 24 |
| class InstanceWithAttr(object): |
| def __init__(self): |
| self.x = 42 |
| def __pos__(self): |
| return self.x |
| def __neg__(self): |
| return +self - 41 |
| def __invert__(self): |
| return self.x + 1 |
| instance = GoodInstance() |
| lambda_instance = LambdaInstance() |
| instance_with_attr = InstanceWithAttr() |
| +instance #@ |
| -instance #@ |
| ~instance #@ |
| --instance #@ |
| +lambda_instance #@ |
| -lambda_instance #@ |
| +instance_with_attr #@ |
| -instance_with_attr #@ |
| ~instance_with_attr #@ |
| |
| bad_instance = BadInstance() |
| +bad_instance #@ |
| -bad_instance #@ |
| ~bad_instance #@ |
| |
| # These should be TypeErrors. |
| ~BadInstance #@ |
| ~os #@ |
| -func #@ |
| +BadInstance #@ |
| """ |
| ) |
| expected = [42, 1, 42, -1, 24, 25, 42, 1, 43] |
| for node, value in zip(ast_nodes[:9], expected): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, value) |
| |
| for bad_node in ast_nodes[9:]: |
| inferred = next(bad_node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_unary_op_instance_method_not_callable(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A: |
| __pos__ = (i for i in range(10)) |
| +A() #@ |
| """ |
| ) |
| self.assertRaises(InferenceError, next, ast_node.infer()) |
| |
| def test_binary_op_type_errors(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| import collections |
| 1 + "a" #@ |
| 1 - [] #@ |
| 1 * {} #@ |
| 1 / collections #@ |
| 1 ** (lambda x: x) #@ |
| {} * {} #@ |
| {} - {} #@ |
| {} >> {} #@ |
| [] + () #@ |
| () + [] #@ |
| [] * 2.0 #@ |
| () * 2.0 #@ |
| 2.0 >> 2.0 #@ |
| class A(object): pass |
| class B(object): pass |
| A() + B() #@ |
| class A1(object): |
| def __add__(self, other): return NotImplemented |
| A1() + A1() #@ |
| class A(object): |
| def __add__(self, other): return NotImplemented |
| class B(object): |
| def __radd__(self, other): return NotImplemented |
| A() + B() #@ |
| class Parent(object): |
| pass |
| class Child(Parent): |
| def __add__(self, other): return NotImplemented |
| Child() + Parent() #@ |
| class A(object): |
| def __add__(self, other): return NotImplemented |
| class B(A): |
| def __radd__(self, other): |
| return NotImplemented |
| A() + B() #@ |
| # Augmented |
| f = 1 |
| f+=A() #@ |
| x = 1 |
| x+=[] #@ |
| """ |
| ) |
| msg = "unsupported operand type(s) for {op}: {lhs!r} and {rhs!r}" |
| expected = [ |
| msg.format(op="+", lhs="int", rhs="str"), |
| msg.format(op="-", lhs="int", rhs="list"), |
| msg.format(op="*", lhs="int", rhs="dict"), |
| msg.format(op="/", lhs="int", rhs="module"), |
| msg.format(op="**", lhs="int", rhs="function"), |
| msg.format(op="*", lhs="dict", rhs="dict"), |
| msg.format(op="-", lhs="dict", rhs="dict"), |
| msg.format(op=">>", lhs="dict", rhs="dict"), |
| msg.format(op="+", lhs="list", rhs="tuple"), |
| msg.format(op="+", lhs="tuple", rhs="list"), |
| msg.format(op="*", lhs="list", rhs="float"), |
| msg.format(op="*", lhs="tuple", rhs="float"), |
| msg.format(op=">>", lhs="float", rhs="float"), |
| msg.format(op="+", lhs="A", rhs="B"), |
| msg.format(op="+", lhs="A1", rhs="A1"), |
| msg.format(op="+", lhs="A", rhs="B"), |
| msg.format(op="+", lhs="Child", rhs="Parent"), |
| msg.format(op="+", lhs="A", rhs="B"), |
| msg.format(op="+=", lhs="int", rhs="A"), |
| msg.format(op="+=", lhs="int", rhs="list"), |
| ] |
| |
| # PEP-584 supports | for dictionary union |
| if not PY39_PLUS: |
| ast_nodes.append(extract_node("{} | {} #@")) |
| expected.append(msg.format(op="|", lhs="dict", rhs="dict")) |
| |
| for node, expected_value in zip(ast_nodes, expected): |
| errors = node.type_errors() |
| self.assertEqual(len(errors), 1) |
| error = errors[0] |
| self.assertEqual(str(error), expected_value) |
| |
| def test_unary_type_errors(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| import collections |
| ~[] #@ |
| ~() #@ |
| ~dict() #@ |
| ~{} #@ |
| ~set() #@ |
| -set() #@ |
| -"" #@ |
| ~"" #@ |
| +"" #@ |
| class A(object): pass |
| ~(lambda: None) #@ |
| ~A #@ |
| ~A() #@ |
| ~collections #@ |
| ~2.0 #@ |
| """ |
| ) |
| msg = "bad operand type for unary {op}: {type}" |
| expected = [ |
| msg.format(op="~", type="list"), |
| msg.format(op="~", type="tuple"), |
| msg.format(op="~", type="dict"), |
| msg.format(op="~", type="dict"), |
| msg.format(op="~", type="set"), |
| msg.format(op="-", type="set"), |
| msg.format(op="-", type="str"), |
| msg.format(op="~", type="str"), |
| msg.format(op="+", type="str"), |
| msg.format(op="~", type="<lambda>"), |
| msg.format(op="~", type="A"), |
| msg.format(op="~", type="A"), |
| msg.format(op="~", type="collections"), |
| msg.format(op="~", type="float"), |
| ] |
| for node, expected_value in zip(ast_nodes, expected): |
| errors = node.type_errors() |
| self.assertEqual(len(errors), 1) |
| error = errors[0] |
| self.assertEqual(str(error), expected_value) |
| |
| def test_unary_empty_type_errors(self) -> None: |
| # These aren't supported right now |
| ast_nodes = extract_node( |
| """ |
| ~(2 and []) #@ |
| -(0 or {}) #@ |
| """ |
| ) |
| expected = [ |
| "bad operand type for unary ~: list", |
| "bad operand type for unary -: dict", |
| ] |
| for node, expected_value in zip(ast_nodes, expected): |
| errors = node.type_errors() |
| self.assertEqual(len(errors), 1, (expected, node)) |
| self.assertEqual(str(errors[0]), expected_value) |
| |
| def test_unary_type_errors_for_non_instance_objects(self) -> None: |
| node = extract_node("~slice(1, 2, 3)") |
| errors = node.type_errors() |
| self.assertEqual(len(errors), 1) |
| self.assertEqual(str(errors[0]), "bad operand type for unary ~: slice") |
| |
| def test_bool_value_recursive(self) -> None: |
| pairs = [ |
| ("{}", False), |
| ("{1:2}", True), |
| ("()", False), |
| ("(1, 2)", True), |
| ("[]", False), |
| ("[1,2]", True), |
| ("frozenset()", False), |
| ("frozenset((1, 2))", True), |
| ] |
| for code, expected in pairs: |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.bool_value(), expected) |
| |
| def test_genexpr_bool_value(self) -> None: |
| node = extract_node("""(x for x in range(10))""") |
| self.assertTrue(node.bool_value()) |
| |
| def test_name_bool_value(self) -> None: |
| node = extract_node( |
| """ |
| x = 42 |
| y = x |
| y |
| """ |
| ) |
| self.assertIs(node.bool_value(), util.Uninferable) |
| |
| def test_bool_value(self) -> None: |
| # Verify the truth value of nodes. |
| module = parse( |
| """ |
| import collections |
| collections_module = collections |
| def function(): pass |
| class Class(object): |
| def method(self): pass |
| dict_comp = {x:y for (x, y) in ((1, 2), (2, 3))} |
| set_comp = {x for x in range(10)} |
| list_comp = [x for x in range(10)] |
| lambda_func = lambda: None |
| unbound_method = Class.method |
| instance = Class() |
| bound_method = instance.method |
| def generator_func(): |
| yield |
| def true_value(): |
| return True |
| generator = generator_func() |
| bin_op = 1 + 2 |
| bool_op = x and y |
| callfunc = test() |
| good_callfunc = true_value() |
| compare = 2 < 3 |
| const_str_true = 'testconst' |
| const_str_false = '' |
| """ |
| ) |
| collections_module = next(module["collections_module"].infer()) |
| self.assertTrue(collections_module.bool_value()) |
| function = module["function"] |
| self.assertTrue(function.bool_value()) |
| klass = module["Class"] |
| self.assertTrue(klass.bool_value()) |
| dict_comp = next(module["dict_comp"].infer()) |
| self.assertEqual(dict_comp, util.Uninferable) |
| set_comp = next(module["set_comp"].infer()) |
| self.assertEqual(set_comp, util.Uninferable) |
| list_comp = next(module["list_comp"].infer()) |
| self.assertEqual(list_comp, util.Uninferable) |
| lambda_func = next(module["lambda_func"].infer()) |
| self.assertTrue(lambda_func) |
| unbound_method = next(module["unbound_method"].infer()) |
| self.assertTrue(unbound_method) |
| bound_method = next(module["bound_method"].infer()) |
| self.assertTrue(bound_method) |
| generator = next(module["generator"].infer()) |
| self.assertTrue(generator) |
| bin_op = module["bin_op"].parent.value |
| self.assertIs(bin_op.bool_value(), util.Uninferable) |
| bool_op = module["bool_op"].parent.value |
| self.assertEqual(bool_op.bool_value(), util.Uninferable) |
| callfunc = module["callfunc"].parent.value |
| self.assertEqual(callfunc.bool_value(), util.Uninferable) |
| good_callfunc = next(module["good_callfunc"].infer()) |
| self.assertTrue(good_callfunc.bool_value()) |
| compare = module["compare"].parent.value |
| self.assertEqual(compare.bool_value(), util.Uninferable) |
| |
| def test_bool_value_instances(self) -> None: |
| instances = extract_node( |
| f""" |
| class FalseBoolInstance(object): |
| def {BOOL_SPECIAL_METHOD}(self): |
| return False |
| class TrueBoolInstance(object): |
| def {BOOL_SPECIAL_METHOD}(self): |
| return True |
| class FalseLenInstance(object): |
| def __len__(self): |
| return 0 |
| class TrueLenInstance(object): |
| def __len__(self): |
| return 14 |
| class AlwaysTrueInstance(object): |
| pass |
| class ErrorInstance(object): |
| def __bool__(self): |
| return lala |
| def __len__(self): |
| return lala |
| class NonMethods(object): |
| __bool__ = 1 |
| __len__ = 2 |
| FalseBoolInstance() #@ |
| TrueBoolInstance() #@ |
| FalseLenInstance() #@ |
| TrueLenInstance() #@ |
| AlwaysTrueInstance() #@ |
| ErrorInstance() #@ |
| """ |
| ) |
| expected = (False, True, False, True, True, util.Uninferable, util.Uninferable) |
| for node, expected_value in zip(instances, expected): |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.bool_value(), expected_value) |
| |
| def test_bool_value_variable(self) -> None: |
| instance = extract_node( |
| f""" |
| class VariableBoolInstance(object): |
| def __init__(self, value): |
| self.value = value |
| def {BOOL_SPECIAL_METHOD}(self): |
| return self.value |
| |
| not VariableBoolInstance(True) |
| """ |
| ) |
| inferred = next(instance.infer()) |
| self.assertIs(inferred.bool_value(), util.Uninferable) |
| |
| def test_infer_coercion_rules_for_floats_complex(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| 1 + 1.0 #@ |
| 1 * 1.0 #@ |
| 2 - 1.0 #@ |
| 2 / 2.0 #@ |
| 1 + 1j #@ |
| 2 * 1j #@ |
| 2 - 1j #@ |
| 3 / 1j #@ |
| """ |
| ) |
| expected_values = [2.0, 1.0, 1.0, 1.0, 1 + 1j, 2j, 2 - 1j, -3j] |
| for node, expected in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.value, expected) |
| |
| def test_binop_list_with_elts(self) -> None: |
| ast_node = extract_node( |
| """ |
| x = [A] * 1 |
| [1] + x |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.List) |
| self.assertEqual(len(inferred.elts), 2) |
| self.assertIsInstance(inferred.elts[0], nodes.Const) |
| self.assertIsInstance(inferred.elts[1], nodes.Unknown) |
| |
| def test_binop_same_types(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(object): |
| def __add__(self, other): |
| return 42 |
| 1 + 1 #@ |
| 1 - 1 #@ |
| "a" + "b" #@ |
| A() + A() #@ |
| """ |
| ) |
| expected_values = [2, 0, "ab", 42] |
| for node, expected in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, expected) |
| |
| def test_binop_different_types_reflected_only(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| pass |
| class B(object): |
| def __radd__(self, other): |
| return other |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_binop_different_types_unknown_bases(self) -> None: |
| node = extract_node( |
| """ |
| from foo import bar |
| |
| class A(bar): |
| pass |
| class B(object): |
| def __radd__(self, other): |
| return other |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(inferred, util.Uninferable) |
| |
| def test_binop_different_types_normal_not_implemented_and_reflected(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| def __add__(self, other): |
| return NotImplemented |
| class B(object): |
| def __radd__(self, other): |
| return other |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_binop_different_types_no_method_implemented(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| pass |
| class B(object): pass |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_binop_different_types_reflected_and_normal_not_implemented(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| def __add__(self, other): return NotImplemented |
| class B(object): |
| def __radd__(self, other): return NotImplemented |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_binop_subtype(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): pass |
| class B(A): |
| def __add__(self, other): return other |
| B() + A() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_binop_subtype_implemented_in_parent(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| def __add__(self, other): return other |
| class B(A): pass |
| B() + A() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_binop_subtype_not_implemented(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| pass |
| class B(A): |
| def __add__(self, other): return NotImplemented |
| B() + A() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_binop_supertype(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| pass |
| class B(A): |
| def __radd__(self, other): |
| return other |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_binop_supertype_rop_not_implemented(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| def __add__(self, other): |
| return other |
| class B(A): |
| def __radd__(self, other): |
| return NotImplemented |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "B") |
| |
| def test_binop_supertype_both_not_implemented(self) -> None: |
| node = extract_node( |
| """ |
| class A(object): |
| def __add__(self): return NotImplemented |
| class B(A): |
| def __radd__(self, other): |
| return NotImplemented |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_binop_inference_errors(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| from unknown import Unknown |
| class A(object): |
| def __add__(self, other): return NotImplemented |
| class B(object): |
| def __add__(self, other): return Unknown |
| A() + Unknown #@ |
| Unknown + A() #@ |
| B() + A() #@ |
| A() + B() #@ |
| """ |
| ) |
| for node in ast_nodes: |
| self.assertEqual(next(node.infer()), util.Uninferable) |
| |
| def test_binop_ambiguity(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(object): |
| def __add__(self, other): |
| if isinstance(other, B): |
| return NotImplemented |
| if type(other) is type(self): |
| return 42 |
| return NotImplemented |
| class B(A): pass |
| class C(object): |
| def __radd__(self, other): |
| if isinstance(other, B): |
| return 42 |
| return NotImplemented |
| A() + B() #@ |
| B() + A() #@ |
| A() + C() #@ |
| C() + A() #@ |
| """ |
| ) |
| for node in ast_nodes: |
| self.assertEqual(next(node.infer()), util.Uninferable) |
| |
| def test_metaclass__getitem__(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Meta(type): |
| def __getitem__(cls, arg): |
| return 24 |
| class A(object, metaclass=Meta): |
| pass |
| |
| A['Awesome'] #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 24) |
| |
| @unittest.skipUnless(HAS_SIX, "These tests require the six library") |
| def test_with_metaclass__getitem__(self): |
| ast_node = extract_node( |
| """ |
| class Meta(type): |
| def __getitem__(cls, arg): |
| return 24 |
| import six |
| class A(six.with_metaclass(Meta)): |
| pass |
| |
| A['Awesome'] #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 24) |
| |
| def test_bin_op_classes(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Meta(type): |
| def __or__(self, other): |
| return 24 |
| class A(object, metaclass=Meta): |
| pass |
| |
| A | A |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 24) |
| |
| @unittest.skipUnless(HAS_SIX, "These tests require the six library") |
| def test_bin_op_classes_with_metaclass(self): |
| ast_node = extract_node( |
| """ |
| class Meta(type): |
| def __or__(self, other): |
| return 24 |
| import six |
| class A(six.with_metaclass(Meta)): |
| pass |
| |
| A | A |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 24) |
| |
| def test_bin_op_supertype_more_complicated_example(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __init__(self): |
| self.foo = 42 |
| def __add__(self, other): |
| return other.bar + self.foo / 2 |
| |
| class B(A): |
| def __init__(self): |
| self.bar = 24 |
| def __radd__(self, other): |
| return NotImplemented |
| |
| A() + B() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(int(inferred.value), 45) |
| |
| def test_aug_op_same_type_not_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| def __add__(self, other): return NotImplemented |
| A() + A() #@ |
| """ |
| ) |
| self.assertEqual(next(ast_node.infer()), util.Uninferable) |
| |
| def test_aug_op_same_type_aug_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return other |
| f = A() |
| f += A() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_aug_op_same_type_aug_not_implemented_normal_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| def __add__(self, other): return 42 |
| f = A() |
| f += A() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def test_aug_op_subtype_both_not_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| def __add__(self, other): return NotImplemented |
| class B(A): |
| pass |
| b = B() |
| b+=A() #@ |
| """ |
| ) |
| self.assertEqual(next(ast_node.infer()), util.Uninferable) |
| |
| def test_aug_op_subtype_aug_op_is_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return 42 |
| class B(A): |
| pass |
| b = B() |
| b+=A() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def test_aug_op_subtype_normal_op_is_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __add__(self, other): return 42 |
| class B(A): |
| pass |
| b = B() |
| b+=A() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def test_aug_different_types_no_method_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): pass |
| class B(object): pass |
| f = A() |
| f += B() #@ |
| """ |
| ) |
| self.assertEqual(next(ast_node.infer()), util.Uninferable) |
| |
| def test_aug_different_types_augop_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return other |
| class B(object): pass |
| f = A() |
| f += B() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "B") |
| |
| def test_aug_different_types_aug_not_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| def __add__(self, other): return other |
| class B(object): pass |
| f = A() |
| f += B() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "B") |
| |
| def test_aug_different_types_aug_not_implemented_rop_fallback(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| def __add__(self, other): return NotImplemented |
| class B(object): |
| def __radd__(self, other): return other |
| f = A() |
| f += B() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_augop_supertypes_none_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): pass |
| class B(object): pass |
| a = A() |
| a += B() #@ |
| """ |
| ) |
| self.assertEqual(next(ast_node.infer()), util.Uninferable) |
| |
| def test_augop_supertypes_not_implemented_returned_for_all(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| def __add__(self, other): return NotImplemented |
| class B(object): |
| def __add__(self, other): return NotImplemented |
| a = A() |
| a += B() #@ |
| """ |
| ) |
| self.assertEqual(next(ast_node.infer()), util.Uninferable) |
| |
| def test_augop_supertypes_augop_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return other |
| class B(A): pass |
| a = A() |
| a += B() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "B") |
| |
| def test_augop_supertypes_reflected_binop_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| class B(A): |
| def __radd__(self, other): return other |
| a = A() |
| a += B() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_augop_supertypes_normal_binop_implemented(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __iadd__(self, other): return NotImplemented |
| def __add__(self, other): return other |
| class B(A): |
| def __radd__(self, other): return NotImplemented |
| |
| a = A() |
| a += B() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "B") |
| |
| @pytest.mark.xfail(reason="String interpolation is incorrect for modulo formatting") |
| def test_string_interpolation(self): |
| ast_nodes = extract_node( |
| """ |
| "a%d%d" % (1, 2) #@ |
| "a%(x)s" % {"x": 42} #@ |
| """ |
| ) |
| expected = ["a12", "a42"] |
| for node, expected_value in zip(ast_nodes, expected): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_mul_list_supports__index__(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class Index(object): |
| def __index__(self): return 2 |
| class NotIndex(object): pass |
| class NotIndex2(object): |
| def __index__(self): return None |
| a = [1, 2] |
| a * Index() #@ |
| a * NotIndex() #@ |
| a * NotIndex2() #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertIsInstance(first, nodes.List) |
| self.assertEqual([node.value for node in first.itered()], [1, 2, 1, 2]) |
| for rest in ast_nodes[1:]: |
| inferred = next(rest.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_subscript_supports__index__(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class Index(object): |
| def __index__(self): return 2 |
| class LambdaIndex(object): |
| __index__ = lambda self: self.foo |
| @property |
| def foo(self): return 1 |
| class NonIndex(object): |
| __index__ = lambda self: None |
| a = [1, 2, 3, 4] |
| a[Index()] #@ |
| a[LambdaIndex()] #@ |
| a[NonIndex()] #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertIsInstance(first, nodes.Const) |
| self.assertEqual(first.value, 3) |
| second = next(ast_nodes[1].infer()) |
| self.assertIsInstance(second, nodes.Const) |
| self.assertEqual(second.value, 2) |
| self.assertRaises(InferenceError, next, ast_nodes[2].infer()) |
| |
| def test_special_method_masquerading_as_another(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Info(object): |
| def __add__(self, other): |
| return "lala" |
| __or__ = __add__ |
| |
| f = Info() |
| f | Info() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, "lala") |
| |
| def test_unary_op_assignment(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): pass |
| def pos(self): |
| return 42 |
| A.__pos__ = pos |
| f = A() |
| +f #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def test_unary_op_classes(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Meta(type): |
| def __invert__(self): |
| return 42 |
| class A(object, metaclass=Meta): |
| pass |
| ~A |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| @unittest.skipUnless(HAS_SIX, "These tests require the six library") |
| def test_unary_op_classes_with_metaclass(self): |
| ast_node = extract_node( |
| """ |
| import six |
| class Meta(type): |
| def __invert__(self): |
| return 42 |
| class A(six.with_metaclass(Meta)): |
| pass |
| ~A |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 42) |
| |
| def _slicing_test_helper( |
| self, |
| pairs: Tuple[ |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| Tuple[str, Union[List[int], str]], |
| ], |
| cls: Union[ABCMeta, type], |
| get_elts: Callable, |
| ) -> None: |
| for code, expected in pairs: |
| ast_node = extract_node(code) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, cls) |
| self.assertEqual(get_elts(inferred), expected, ast_node.as_string()) |
| |
| def test_slicing_list(self) -> None: |
| pairs = ( |
| ("[1, 2, 3][:] #@", [1, 2, 3]), |
| ("[1, 2, 3][0:] #@", [1, 2, 3]), |
| ("[1, 2, 3][None:] #@", [1, 2, 3]), |
| ("[1, 2, 3][None:None] #@", [1, 2, 3]), |
| ("[1, 2, 3][0:-1] #@", [1, 2]), |
| ("[1, 2, 3][0:2] #@", [1, 2]), |
| ("[1, 2, 3][0:2:None] #@", [1, 2]), |
| ("[1, 2, 3][::] #@", [1, 2, 3]), |
| ("[1, 2, 3][::2] #@", [1, 3]), |
| ("[1, 2, 3][::-1] #@", [3, 2, 1]), |
| ("[1, 2, 3][0:2:2] #@", [1]), |
| ("[1, 2, 3, 4, 5, 6][0:4-1:2+0] #@", [1, 3]), |
| ) |
| self._slicing_test_helper( |
| pairs, nodes.List, lambda inferred: [elt.value for elt in inferred.elts] |
| ) |
| |
| def test_slicing_tuple(self) -> None: |
| pairs = ( |
| ("(1, 2, 3)[:] #@", [1, 2, 3]), |
| ("(1, 2, 3)[0:] #@", [1, 2, 3]), |
| ("(1, 2, 3)[None:] #@", [1, 2, 3]), |
| ("(1, 2, 3)[None:None] #@", [1, 2, 3]), |
| ("(1, 2, 3)[0:-1] #@", [1, 2]), |
| ("(1, 2, 3)[0:2] #@", [1, 2]), |
| ("(1, 2, 3)[0:2:None] #@", [1, 2]), |
| ("(1, 2, 3)[::] #@", [1, 2, 3]), |
| ("(1, 2, 3)[::2] #@", [1, 3]), |
| ("(1, 2, 3)[::-1] #@", [3, 2, 1]), |
| ("(1, 2, 3)[0:2:2] #@", [1]), |
| ("(1, 2, 3, 4, 5, 6)[0:4-1:2+0] #@", [1, 3]), |
| ) |
| self._slicing_test_helper( |
| pairs, nodes.Tuple, lambda inferred: [elt.value for elt in inferred.elts] |
| ) |
| |
| def test_slicing_str(self) -> None: |
| pairs = ( |
| ("'123'[:] #@", "123"), |
| ("'123'[0:] #@", "123"), |
| ("'123'[None:] #@", "123"), |
| ("'123'[None:None] #@", "123"), |
| ("'123'[0:-1] #@", "12"), |
| ("'123'[0:2] #@", "12"), |
| ("'123'[0:2:None] #@", "12"), |
| ("'123'[::] #@", "123"), |
| ("'123'[::2] #@", "13"), |
| ("'123'[::-1] #@", "321"), |
| ("'123'[0:2:2] #@", "1"), |
| ("'123456'[0:4-1:2+0] #@", "13"), |
| ) |
| self._slicing_test_helper(pairs, nodes.Const, lambda inferred: inferred.value) |
| |
| def test_invalid_slicing_primaries(self) -> None: |
| examples = [ |
| "(lambda x: x)[1:2]", |
| "1[2]", |
| "(1, 2, 3)[a:]", |
| "(1, 2, 3)[object:object]", |
| "(1, 2, 3)[1:object]", |
| "enumerate[2]", |
| ] |
| for code in examples: |
| node = extract_node(code) |
| self.assertRaises(InferenceError, next, node.infer()) |
| |
| def test_instance_slicing(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(object): |
| def __getitem__(self, index): |
| return [1, 2, 3, 4, 5][index] |
| A()[1:] #@ |
| A()[:2] #@ |
| A()[1:4] #@ |
| """ |
| ) |
| expected_values = [[2, 3, 4, 5], [1, 2], [2, 3, 4]] |
| for expected, node in zip(expected_values, ast_nodes): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.List) |
| self.assertEqual([elt.value for elt in inferred.elts], expected) |
| |
| def test_instance_slicing_slices(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| def __getitem__(self, index): |
| return index |
| A()[1:] #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Slice) |
| self.assertEqual(inferred.lower.value, 1) |
| self.assertIsNone(inferred.upper) |
| |
| def test_instance_slicing_fails(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(object): |
| def __getitem__(self, index): |
| return 1[index] |
| A()[4:5] #@ |
| A()[2:] #@ |
| """ |
| ) |
| for node in ast_nodes: |
| self.assertEqual(next(node.infer()), util.Uninferable) |
| |
| def test_type__new__with_metaclass(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Metaclass(type): |
| pass |
| class Entity(object): |
| pass |
| type.__new__(Metaclass, 'NewClass', (Entity,), {'a': 1}) #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, "NewClass") |
| metaclass = inferred.metaclass() |
| self.assertEqual(metaclass, inferred.root()["Metaclass"]) |
| ancestors = list(inferred.ancestors()) |
| self.assertEqual(len(ancestors), 2) |
| self.assertEqual(ancestors[0], inferred.root()["Entity"]) |
| attributes = inferred.getattr("a") |
| self.assertEqual(len(attributes), 1) |
| self.assertIsInstance(attributes[0], nodes.Const) |
| self.assertEqual(attributes[0].value, 1) |
| |
| def test_type__new__not_enough_arguments(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| type.__new__(type, 'foo') #@ |
| type.__new__(type, 'foo', ()) #@ |
| type.__new__(type, 'foo', (), {}, ()) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| def test_type__new__invalid_mcs_argument(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class Class(object): pass |
| type.__new__(1, 2, 3, 4) #@ |
| type.__new__(Class, 2, 3, 4) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| def test_type__new__invalid_name(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class Class(type): pass |
| type.__new__(Class, object, 1, 2) #@ |
| type.__new__(Class, 1, 1, 2) #@ |
| type.__new__(Class, [], 1, 2) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| def test_type__new__invalid_bases(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| type.__new__(type, 'a', 1, 2) #@ |
| type.__new__(type, 'a', [], 2) #@ |
| type.__new__(type, 'a', {}, 2) #@ |
| type.__new__(type, 'a', (1, ), 2) #@ |
| type.__new__(type, 'a', (object, 1), 2) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| def test_type__new__invalid_attrs(self) -> None: |
| type_error_nodes = extract_node( |
| """ |
| type.__new__(type, 'a', (), ()) #@ |
| type.__new__(type, 'a', (), object) #@ |
| type.__new__(type, 'a', (), 1) #@ |
| """ |
| ) |
| for node in type_error_nodes: |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| # Ignore invalid keys |
| ast_nodes = extract_node( |
| """ |
| type.__new__(type, 'a', (), {object: 1}) #@ |
| type.__new__(type, 'a', (), {1:2, "a":5}) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| |
| def test_type__new__metaclass_lookup(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Metaclass(type): |
| def test(cls): pass |
| @classmethod |
| def test1(cls): pass |
| attr = 42 |
| type.__new__(Metaclass, 'A', (), {}) #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| test = inferred.getattr("test") |
| self.assertEqual(len(test), 1) |
| self.assertIsInstance(test[0], BoundMethod) |
| self.assertIsInstance(test[0].bound, nodes.ClassDef) |
| self.assertEqual(test[0].bound, inferred) |
| test1 = inferred.getattr("test1") |
| self.assertEqual(len(test1), 1) |
| self.assertIsInstance(test1[0], BoundMethod) |
| self.assertIsInstance(test1[0].bound, nodes.ClassDef) |
| self.assertEqual(test1[0].bound, inferred.metaclass()) |
| attr = inferred.getattr("attr") |
| self.assertEqual(len(attr), 1) |
| self.assertIsInstance(attr[0], nodes.Const) |
| self.assertEqual(attr[0].value, 42) |
| |
| def test_type__new__metaclass_and_ancestors_lookup(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Book(object): |
| title = 'Ubik' |
| class MetaBook(type): |
| title = 'Grimus' |
| type.__new__(MetaBook, 'book', (Book, ), {'title':'Catch 22'}) #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| titles = [ |
| title.value |
| for attr in inferred.getattr("title") |
| for title in attr.inferred() |
| ] |
| self.assertEqual(titles, ["Catch 22", "Ubik", "Grimus"]) |
| |
| @pytest.mark.xfail(reason="Does not support function metaclasses") |
| def test_function_metaclasses(self): |
| # These are not supported right now, although |
| # they will be in the future. |
| ast_node = extract_node( |
| """ |
| class BookMeta(type): |
| author = 'Rushdie' |
| |
| def metaclass_function(*args): |
| return BookMeta |
| |
| class Book(object, metaclass=metaclass_function): |
| pass |
| Book #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| metaclass = inferred.metaclass() |
| self.assertIsInstance(metaclass, nodes.ClassDef) |
| self.assertEqual(metaclass.name, "BookMeta") |
| author = next(inferred.igetattr("author")) |
| self.assertIsInstance(author, nodes.Const) |
| self.assertEqual(author.value, "Rushdie") |
| |
| def test_subscript_inference_error(self) -> None: |
| # Used to raise StopIteration |
| ast_node = extract_node( |
| """ |
| class AttributeDict(dict): |
| def __getitem__(self, name): |
| return self |
| flow = AttributeDict() |
| flow['app'] = AttributeDict() |
| flow['app']['config'] = AttributeDict() |
| flow['app']['config']['doffing'] = AttributeDict() #@ |
| """ |
| ) |
| self.assertIsNone(helpers.safe_infer(ast_node.targets[0])) |
| |
| def test_classmethod_inferred_by_context(self) -> None: |
| ast_node = extract_node( |
| """ |
| class Super(object): |
| def instance(cls): |
| return cls() |
| instance = classmethod(instance) |
| |
| class Sub(Super): |
| def method(self): |
| return self |
| |
| # should see the Sub.instance() is returning a Sub |
| # instance, not a Super instance |
| Sub.instance().method() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| self.assertEqual(inferred.name, "Sub") |
| |
| def test_infer_call_result_invalid_dunder_call_on_instance(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A: |
| __call__ = 42 |
| class B: |
| __call__ = A() |
| class C: |
| __call = None |
| A() #@ |
| B() #@ |
| C() #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertRaises(InferenceError, next, inferred.infer_call_result(node)) |
| |
| def test_context_call_for_context_managers(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A: |
| def __enter__(self): |
| return self |
| class B: |
| __enter__ = lambda self: self |
| class C: |
| @property |
| def a(self): return A() |
| def __enter__(self): |
| return self.a |
| with A() as a: |
| a #@ |
| with B() as b: |
| b #@ |
| with C() as c: |
| c #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first_a = next(ast_nodes[0].infer()) |
| self.assertIsInstance(first_a, Instance) |
| self.assertEqual(first_a.name, "A") |
| second_b = next(ast_nodes[1].infer()) |
| self.assertIsInstance(second_b, Instance) |
| self.assertEqual(second_b.name, "B") |
| third_c = next(ast_nodes[2].infer()) |
| self.assertIsInstance(third_c, Instance) |
| self.assertEqual(third_c.name, "A") |
| |
| def test_metaclass_subclasses_arguments_are_classes_not_instances(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(type): |
| def test(cls): |
| return cls |
| class B(object, metaclass=A): |
| pass |
| |
| B.test() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, "B") |
| |
| @unittest.skipUnless(HAS_SIX, "These tests require the six library") |
| def test_with_metaclass_subclasses_arguments_are_classes_not_instances(self): |
| ast_node = extract_node( |
| """ |
| class A(type): |
| def test(cls): |
| return cls |
| import six |
| class B(six.with_metaclass(A)): |
| pass |
| |
| B.test() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, "B") |
| |
| @unittest.skipUnless(HAS_SIX, "These tests require the six library") |
| def test_with_metaclass_with_partial_imported_name(self): |
| ast_node = extract_node( |
| """ |
| class A(type): |
| def test(cls): |
| return cls |
| from six import with_metaclass |
| class B(with_metaclass(A)): |
| pass |
| |
| B.test() #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, "B") |
| |
| def test_infer_cls_in_class_methods(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(type): |
| def __call__(cls): |
| cls #@ |
| class B(object): |
| def __call__(cls): |
| cls #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertIsInstance(first, nodes.ClassDef) |
| second = next(ast_nodes[1].infer()) |
| self.assertIsInstance(second, Instance) |
| |
| @pytest.mark.xfail(reason="Metaclass arguments not inferred as classes") |
| def test_metaclass_arguments_are_classes_not_instances(self): |
| ast_node = extract_node( |
| """ |
| class A(type): |
| def test(cls): return cls |
| A.test() #@ |
| """ |
| ) |
| # This is not supported yet |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, "A") |
| |
| def test_metaclass_with_keyword_args(self) -> None: |
| ast_node = extract_node( |
| """ |
| class TestMetaKlass(type): |
| def __new__(mcs, name, bases, ns, kwo_arg): |
| return super().__new__(mcs, name, bases, ns) |
| |
| class TestKlass(metaclass=TestMetaKlass, kwo_arg=42): #@ |
| pass |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| |
| def test_metaclass_custom_dunder_call(self) -> None: |
| """The Metaclass __call__ should take precedence |
| over the default metaclass type call (initialization) |
| |
| See https://github.com/PyCQA/pylint/issues/2159 |
| """ |
| val = ( |
| extract_node( |
| """ |
| class _Meta(type): |
| def __call__(cls): |
| return 1 |
| class Clazz(metaclass=_Meta): |
| def __call__(self): |
| return 5.5 |
| |
| Clazz() #@ |
| """ |
| ) |
| .inferred()[0] |
| .value |
| ) |
| assert val == 1 |
| |
| def test_metaclass_custom_dunder_call_boundnode(self) -> None: |
| """The boundnode should be the calling class""" |
| cls = extract_node( |
| """ |
| class _Meta(type): |
| def __call__(cls): |
| return cls |
| class Clazz(metaclass=_Meta): |
| pass |
| Clazz() #@ |
| """ |
| ).inferred()[0] |
| assert isinstance(cls, nodes.ClassDef) and cls.name == "Clazz" |
| |
| def test_infer_subclass_attr_outer_class(self) -> None: |
| node = extract_node( |
| """ |
| class Outer: |
| data = 123 |
| |
| class Test(Outer): |
| pass |
| Test.data |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 123 |
| |
| def test_infer_subclass_attr_inner_class_works_indirectly(self) -> None: |
| node = extract_node( |
| """ |
| class Outer: |
| class Inner: |
| data = 123 |
| Inner = Outer.Inner |
| |
| class Test(Inner): |
| pass |
| Test.data |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 123 |
| |
| def test_infer_subclass_attr_inner_class(self) -> None: |
| clsdef_node, attr_node = extract_node( |
| """ |
| class Outer: |
| class Inner: |
| data = 123 |
| |
| class Test(Outer.Inner): |
| pass |
| Test #@ |
| Test.data #@ |
| """ |
| ) |
| clsdef = next(clsdef_node.infer()) |
| assert isinstance(clsdef, nodes.ClassDef) |
| inferred = next(clsdef.igetattr("data")) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 123 |
| # Inferring the value of .data via igetattr() worked before the |
| # old_boundnode fixes in infer_subscript, so it should have been |
| # possible to infer the subscript directly. It is the difference |
| # between these two cases that led to the discovery of the cause of the |
| # bug in https://github.com/PyCQA/astroid/issues/904 |
| inferred = next(attr_node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 123 |
| |
| def test_delayed_attributes_without_slots(self) -> None: |
| ast_node = extract_node( |
| """ |
| class A(object): |
| __slots__ = ('a', ) |
| a = A() |
| a.teta = 24 |
| a.a = 24 |
| a #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| with self.assertRaises(NotFoundError): |
| inferred.getattr("teta") |
| inferred.getattr("a") |
| |
| def test_lambda_as_methods(self) -> None: |
| ast_node = extract_node( |
| """ |
| class X: |
| m = lambda self, arg: self.z + arg |
| z = 24 |
| |
| X().m(4) #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 28) |
| |
| def test_inner_value_redefined_by_subclass(self) -> None: |
| ast_node = extract_node( |
| """ |
| class X(object): |
| M = lambda self, arg: "a" |
| x = 24 |
| def __init__(self): |
| x = 24 |
| self.m = self.M(x) |
| |
| class Y(X): |
| M = lambda self, arg: arg + 1 |
| def blurb(self): |
| self.m #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 25) |
| |
| @pytest.mark.xfail(reason="Cannot reuse inner value due to inference context reuse") |
| def test_inner_value_redefined_by_subclass_with_mro(self): |
| # This might work, but it currently doesn't due to not being able |
| # to reuse inference contexts. |
| ast_node = extract_node( |
| """ |
| class X(object): |
| M = lambda self, arg: arg + 1 |
| x = 24 |
| def __init__(self): |
| y = self |
| self.m = y.M(1) + y.z |
| |
| class C(object): |
| z = 24 |
| |
| class Y(X, C): |
| M = lambda self, arg: arg + 1 |
| def blurb(self): |
| self.m #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, 25) |
| |
| def test_getitem_of_class_raised_type_error(self) -> None: |
| # Test that we wrap an AttributeInferenceError |
| # and reraise it as a TypeError in Class.getitem |
| node = extract_node( |
| """ |
| def test(): |
| yield |
| test() |
| """ |
| ) |
| inferred = next(node.infer()) |
| with self.assertRaises(AstroidTypeError): |
| inferred.getitem(nodes.Const("4")) |
| |
| def test_infer_arg_called_type_is_uninferable(self) -> None: |
| node = extract_node( |
| """ |
| def func(type): |
| type #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert inferred is util.Uninferable |
| |
| def test_infer_arg_called_object_when_used_as_index_is_uninferable(self) -> None: |
| node = extract_node( |
| """ |
| def func(object): |
| ['list'][ |
| object #@ |
| ] |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert inferred is util.Uninferable |
| |
| @test_utils.require_version(minver="3.9") |
| def test_infer_arg_called_type_when_used_as_index_is_uninferable(self): |
| # https://github.com/PyCQA/astroid/pull/958 |
| node = extract_node( |
| """ |
| def func(type): |
| ['list'][ |
| type #@ |
| ] |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert not isinstance(inferred, nodes.ClassDef) # was inferred as builtins.type |
| assert inferred is util.Uninferable |
| |
| @test_utils.require_version(minver="3.9") |
| def test_infer_arg_called_type_when_used_as_subscript_is_uninferable(self): |
| # https://github.com/PyCQA/astroid/pull/958 |
| node = extract_node( |
| """ |
| def func(type): |
| type[0] #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert not isinstance(inferred, nodes.ClassDef) # was inferred as builtins.type |
| assert inferred is util.Uninferable |
| |
| @test_utils.require_version(minver="3.9") |
| def test_infer_arg_called_type_defined_in_outer_scope_is_uninferable(self): |
| # https://github.com/PyCQA/astroid/pull/958 |
| node = extract_node( |
| """ |
| def outer(type): |
| def inner(): |
| type[0] #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert not isinstance(inferred, nodes.ClassDef) # was inferred as builtins.type |
| assert inferred is util.Uninferable |
| |
| def test_infer_subclass_attr_instance_attr_indirect(self) -> None: |
| node = extract_node( |
| """ |
| class Parent: |
| def __init__(self): |
| self.data = 123 |
| |
| class Test(Parent): |
| pass |
| t = Test() |
| t |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, Instance) |
| const = next(inferred.igetattr("data")) |
| assert isinstance(const, nodes.Const) |
| assert const.value == 123 |
| |
| def test_infer_subclass_attr_instance_attr(self) -> None: |
| node = extract_node( |
| """ |
| class Parent: |
| def __init__(self): |
| self.data = 123 |
| |
| class Test(Parent): |
| pass |
| t = Test() |
| t.data |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 123 |
| |
| |
| class GetattrTest(unittest.TestCase): |
| def test_yes_when_unknown(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| from missing import Missing |
| getattr(1, Unknown) #@ |
| getattr(Unknown, 'a') #@ |
| getattr(Unknown, Unknown) #@ |
| getattr(Unknown, Unknown, Unknown) #@ |
| |
| getattr(Missing, 'a') #@ |
| getattr(Missing, Missing) #@ |
| getattr('a', Missing) #@ |
| getattr('a', Missing, Missing) #@ |
| """ |
| ) |
| for node in ast_nodes[:4]: |
| self.assertRaises(InferenceError, next, node.infer()) |
| |
| for node in ast_nodes[4:]: |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable, node) |
| |
| def test_attrname_not_string(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| getattr(1, 1) #@ |
| c = int |
| getattr(1, c) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| self.assertRaises(InferenceError, next, node.infer()) |
| |
| def test_attribute_missing(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| getattr(1, 'ala') #@ |
| getattr(int, 'ala') #@ |
| getattr(float, 'bala') #@ |
| getattr({}, 'portocala') #@ |
| """ |
| ) |
| for node in ast_nodes: |
| self.assertRaises(InferenceError, next, node.infer()) |
| |
| def test_default(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| getattr(1, 'ala', None) #@ |
| getattr(int, 'bala', int) #@ |
| getattr(int, 'bala', getattr(int, 'portocala', None)) #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertIsInstance(first, nodes.Const) |
| self.assertIsNone(first.value) |
| |
| second = next(ast_nodes[1].infer()) |
| self.assertIsInstance(second, nodes.ClassDef) |
| self.assertEqual(second.qname(), "builtins.int") |
| |
| third = next(ast_nodes[2].infer()) |
| self.assertIsInstance(third, nodes.Const) |
| self.assertIsNone(third.value) |
| |
| def test_lookup(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(object): |
| def test(self): pass |
| class B(A): |
| def test_b(self): pass |
| class C(A): pass |
| class E(C, B): |
| def test_e(self): pass |
| |
| getattr(A(), 'test') #@ |
| getattr(A, 'test') #@ |
| getattr(E(), 'test_b') #@ |
| getattr(E(), 'test') #@ |
| |
| class X(object): |
| def test(self): |
| getattr(self, 'test') #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertIsInstance(first, BoundMethod) |
| self.assertEqual(first.bound.name, "A") |
| |
| second = next(ast_nodes[1].infer()) |
| self.assertIsInstance(second, UnboundMethod) |
| self.assertIsInstance(second.parent, nodes.ClassDef) |
| self.assertEqual(second.parent.name, "A") |
| |
| third = next(ast_nodes[2].infer()) |
| self.assertIsInstance(third, BoundMethod) |
| # Bound to E, but the provider is B. |
| self.assertEqual(third.bound.name, "E") |
| self.assertEqual(third._proxied._proxied.parent.name, "B") |
| |
| fourth = next(ast_nodes[3].infer()) |
| self.assertIsInstance(fourth, BoundMethod) |
| self.assertEqual(fourth.bound.name, "E") |
| self.assertEqual(third._proxied._proxied.parent.name, "B") |
| |
| fifth = next(ast_nodes[4].infer()) |
| self.assertIsInstance(fifth, BoundMethod) |
| self.assertEqual(fifth.bound.name, "X") |
| |
| def test_lambda(self) -> None: |
| node = extract_node( |
| """ |
| getattr(lambda x: x, 'f') #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| |
| class HasattrTest(unittest.TestCase): |
| def test_inference_errors(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| from missing import Missing |
| |
| hasattr(Unknown, 'ala') #@ |
| |
| hasattr(Missing, 'bala') #@ |
| hasattr('portocala', Missing) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_attribute_is_missing(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A: pass |
| hasattr(int, 'ala') #@ |
| hasattr({}, 'bala') #@ |
| hasattr(A(), 'portocala') #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertFalse(inferred.value) |
| |
| def test_attribute_is_not_missing(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class A(object): |
| def test(self): pass |
| class B(A): |
| def test_b(self): pass |
| class C(A): pass |
| class E(C, B): |
| def test_e(self): pass |
| |
| hasattr(A(), 'test') #@ |
| hasattr(A, 'test') #@ |
| hasattr(E(), 'test_b') #@ |
| hasattr(E(), 'test') #@ |
| |
| class X(object): |
| def test(self): |
| hasattr(self, 'test') #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertTrue(inferred.value) |
| |
| def test_lambda(self) -> None: |
| node = extract_node( |
| """ |
| hasattr(lambda x: x, 'f') #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| |
| class BoolOpTest(unittest.TestCase): |
| def test_bool_ops(self) -> None: |
| expected = [ |
| ("1 and 2", 2), |
| ("0 and 2", 0), |
| ("1 or 2", 1), |
| ("0 or 2", 2), |
| ("0 or 0 or 1", 1), |
| ("1 and 2 and 3", 3), |
| ("1 and 2 or 3", 2), |
| ("1 and 0 or 3", 3), |
| ("1 or 0 and 2", 1), |
| ("(1 and 2) and (2 and 3)", 3), |
| ("not 2 and 3", False), |
| ("2 and not 3", False), |
| ("not 0 and 3", 3), |
| ("True and False", False), |
| ("not (True or False) and True", False), |
| ] |
| for code, expected_value in expected: |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_yes_when_unknown(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| from unknown import unknown, any, not_any |
| 0 and unknown #@ |
| unknown or 0 #@ |
| any or not_any and unknown #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_other_nodes(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| def test(): pass |
| test and 0 #@ |
| 1 and test #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| self.assertEqual(first.value, 0) |
| second = next(ast_nodes[1].infer()) |
| self.assertIsInstance(second, nodes.FunctionDef) |
| self.assertEqual(second.name, "test") |
| |
| |
| class TestCallable(unittest.TestCase): |
| def test_callable(self) -> None: |
| expected = [ |
| ("callable(len)", True), |
| ('callable("a")', False), |
| ("callable(callable)", True), |
| ("callable(lambda x, y: x+y)", True), |
| ("import os; __(callable(os))", False), |
| ("callable(int)", True), |
| ( |
| """ |
| def test(): pass |
| callable(test) #@""", |
| True, |
| ), |
| ( |
| """ |
| class C1: |
| def meth(self): pass |
| callable(C1) #@""", |
| True, |
| ), |
| ] |
| for code, expected_value in expected: |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_callable_methods(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| class C: |
| def test(self): pass |
| @staticmethod |
| def static(): pass |
| @classmethod |
| def class_method(cls): pass |
| def __call__(self): pass |
| class D(C): |
| pass |
| class NotReallyCallableDueToPythonMisfeature(object): |
| __call__ = 42 |
| callable(C.test) #@ |
| callable(C.static) #@ |
| callable(C.class_method) #@ |
| callable(C().test) #@ |
| callable(C().static) #@ |
| callable(C().class_method) #@ |
| C #@ |
| C() #@ |
| NotReallyCallableDueToPythonMisfeature() #@ |
| staticmethod #@ |
| classmethod #@ |
| property #@ |
| D #@ |
| D() #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertTrue(inferred) |
| |
| def test_inference_errors(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| from unknown import unknown |
| callable(unknown) #@ |
| def test(): |
| return unknown |
| callable(test()) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_not_callable(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| callable("") #@ |
| callable(1) #@ |
| callable(True) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertFalse(inferred.value) |
| |
| |
| class TestBool(unittest.TestCase): |
| def test_bool(self) -> None: |
| pairs = [ |
| ("bool()", False), |
| ("bool(1)", True), |
| ("bool(0)", False), |
| ("bool([])", False), |
| ("bool([1])", True), |
| ("bool({})", False), |
| ("bool(True)", True), |
| ("bool(False)", False), |
| ("bool(None)", False), |
| ("from unknown import Unknown; __(bool(Unknown))", util.Uninferable), |
| ] |
| for code, expected in pairs: |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| if expected is util.Uninferable: |
| self.assertEqual(expected, inferred) |
| else: |
| self.assertEqual(inferred.value, expected) |
| |
| def test_bool_bool_special_method(self) -> None: |
| ast_nodes = extract_node( |
| f""" |
| class FalseClass: |
| def {BOOL_SPECIAL_METHOD}(self): |
| return False |
| class TrueClass: |
| def {BOOL_SPECIAL_METHOD}(self): |
| return True |
| class C(object): |
| def __call__(self): |
| return False |
| class B(object): |
| {BOOL_SPECIAL_METHOD} = C() |
| class LambdaBoolFalse(object): |
| {BOOL_SPECIAL_METHOD} = lambda self: self.foo |
| @property |
| def foo(self): return 0 |
| class FalseBoolLen(object): |
| __len__ = lambda self: self.foo |
| @property |
| def foo(self): return 0 |
| bool(FalseClass) #@ |
| bool(TrueClass) #@ |
| bool(FalseClass()) #@ |
| bool(TrueClass()) #@ |
| bool(B()) #@ |
| bool(LambdaBoolFalse()) #@ |
| bool(FalseBoolLen()) #@ |
| """ |
| ) |
| expected = [True, True, False, True, False, False, False] |
| for node, expected_value in zip(ast_nodes, expected): |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_bool_instance_not_callable(self) -> None: |
| ast_nodes = extract_node( |
| f""" |
| class BoolInvalid(object): |
| {BOOL_SPECIAL_METHOD} = 42 |
| class LenInvalid(object): |
| __len__ = "a" |
| bool(BoolInvalid()) #@ |
| bool(LenInvalid()) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| |
| class TestType(unittest.TestCase): |
| def test_type(self) -> None: |
| pairs = [ |
| ("type(1)", "int"), |
| ("type(type)", "type"), |
| ("type(None)", "NoneType"), |
| ("type(object)", "type"), |
| ("type(dict())", "dict"), |
| ("type({})", "dict"), |
| ("type(frozenset())", "frozenset"), |
| ] |
| for code, expected in pairs: |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, expected) |
| |
| |
| class ArgumentsTest(unittest.TestCase): |
| @staticmethod |
| def _get_dict_value( |
| inferred: Dict, |
| ) -> Union[List[Tuple[str, int]], List[Tuple[str, str]]]: |
| items = inferred.items |
| return sorted((key.value, value.value) for key, value in items) |
| |
| @staticmethod |
| def _get_tuple_value(inferred: Tuple) -> Tuple[int, ...]: |
| elts = inferred.elts |
| return tuple(elt.value for elt in elts) |
| |
| def test_args(self) -> None: |
| expected_values = [ |
| (), |
| (1,), |
| (2, 3), |
| (4, 5), |
| (3,), |
| (), |
| (3, 4, 5), |
| (), |
| (), |
| (4,), |
| (4, 5), |
| (), |
| (3,), |
| (), |
| (), |
| (3,), |
| (42,), |
| ] |
| ast_nodes = extract_node( |
| """ |
| def func(*args): |
| return args |
| func() #@ |
| func(1) #@ |
| func(2, 3) #@ |
| func(*(4, 5)) #@ |
| def func(a, b, *args): |
| return args |
| func(1, 2, 3) #@ |
| func(1, 2) #@ |
| func(1, 2, 3, 4, 5) #@ |
| def func(a, b, c=42, *args): |
| return args |
| func(1, 2) #@ |
| func(1, 2, 3) #@ |
| func(1, 2, 3, 4) #@ |
| func(1, 2, 3, 4, 5) #@ |
| func = lambda a, b, *args: args |
| func(1, 2) #@ |
| func(1, 2, 3) #@ |
| func = lambda a, b=42, *args: args |
| func(1) #@ |
| func(1, 2) #@ |
| func(1, 2, 3) #@ |
| func(1, 2, *(42, )) #@ |
| """ |
| ) |
| for node, expected_value in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Tuple) |
| self.assertEqual(self._get_tuple_value(inferred), expected_value) |
| |
| def test_multiple_starred_args(self) -> None: |
| expected_values = [(1, 2, 3), (1, 4, 2, 3, 5, 6, 7)] |
| ast_nodes = extract_node( |
| """ |
| def func(a, b, *args): |
| return args |
| func(1, 2, *(1, ), *(2, 3)) #@ |
| func(1, 2, *(1, ), 4, *(2, 3), 5, *(6, 7)) #@ |
| """ |
| ) |
| for node, expected_value in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Tuple) |
| self.assertEqual(self._get_tuple_value(inferred), expected_value) |
| |
| def test_defaults(self) -> None: |
| expected_values = [42, 3, 41, 42] |
| ast_nodes = extract_node( |
| """ |
| def func(a, b, c=42, *args): |
| return c |
| func(1, 2) #@ |
| func(1, 2, 3) #@ |
| func(1, 2, c=41) #@ |
| func(1, 2, 42, 41) #@ |
| """ |
| ) |
| for node, expected_value in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_kwonly_args(self) -> None: |
| expected_values = [24, 24, 42, 23, 24, 24, 54] |
| ast_nodes = extract_node( |
| """ |
| def test(*, f, b): return f |
| test(f=24, b=33) #@ |
| def test(a, *, f): return f |
| test(1, f=24) #@ |
| def test(a, *, f=42): return f |
| test(1) #@ |
| test(1, f=23) #@ |
| def test(a, b, c=42, *args, f=24): |
| return f |
| test(1, 2, 3) #@ |
| test(1, 2, 3, 4) #@ |
| test(1, 2, 3, 4, 5, f=54) #@ |
| """ |
| ) |
| for node, expected_value in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, expected_value) |
| |
| def test_kwargs(self) -> None: |
| expected = [[("a", 1), ("b", 2), ("c", 3)], [("a", 1)], [("a", "b")]] |
| ast_nodes = extract_node( |
| """ |
| def test(**kwargs): |
| return kwargs |
| test(a=1, b=2, c=3) #@ |
| test(a=1) #@ |
| test(**{'a': 'b'}) #@ |
| """ |
| ) |
| for node, expected_value in zip(ast_nodes, expected): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Dict) |
| value = self._get_dict_value(inferred) |
| self.assertEqual(value, expected_value) |
| |
| def test_kwargs_and_other_named_parameters(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| def test(a=42, b=24, **kwargs): |
| return kwargs |
| test(42, 24, c=3, d=4) #@ |
| test(49, b=24, d=4) #@ |
| test(a=42, b=33, c=3, d=42) #@ |
| test(a=42, **{'c':42}) #@ |
| """ |
| ) |
| expected_values = [ |
| [("c", 3), ("d", 4)], |
| [("d", 4)], |
| [("c", 3), ("d", 42)], |
| [("c", 42)], |
| ] |
| for node, expected_value in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Dict) |
| value = self._get_dict_value(inferred) |
| self.assertEqual(value, expected_value) |
| |
| def test_kwargs_access_by_name(self) -> None: |
| expected_values = [42, 42, 42, 24] |
| ast_nodes = extract_node( |
| """ |
| def test(**kwargs): |
| return kwargs['f'] |
| test(f=42) #@ |
| test(**{'f': 42}) #@ |
| test(**dict(f=42)) #@ |
| def test(f=42, **kwargs): |
| return kwargs['l'] |
| test(l=24) #@ |
| """ |
| ) |
| for ast_node, value in zip(ast_nodes, expected_values): |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Const, inferred) |
| self.assertEqual(inferred.value, value) |
| |
| def test_multiple_kwargs(self) -> None: |
| expected_value = [("a", 1), ("b", 2), ("c", 3), ("d", 4), ("f", 42)] |
| ast_node = extract_node( |
| """ |
| def test(**kwargs): |
| return kwargs |
| test(a=1, b=2, **{'c': 3}, **{'d': 4}, f=42) #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.Dict) |
| value = self._get_dict_value(inferred) |
| self.assertEqual(value, expected_value) |
| |
| def test_kwargs_are_overridden(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| def test(f): |
| return f |
| test(f=23, **{'f': 34}) #@ |
| def test(f=None): |
| return f |
| test(f=23, **{'f':23}) #@ |
| """ |
| ) |
| for ast_node in ast_nodes: |
| inferred = next(ast_node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_fail_to_infer_args(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| def test(a, **kwargs): return a |
| test(*missing) #@ |
| test(*object) #@ |
| test(*1) #@ |
| |
| |
| def test(**kwargs): return kwargs |
| test(**miss) #@ |
| test(**(1, 2)) #@ |
| test(**1) #@ |
| test(**{misss:1}) #@ |
| test(**{object:1}) #@ |
| test(**{1:1}) #@ |
| test(**{'a':1, 'a':1}) #@ |
| |
| def test(a): return a |
| test() #@ |
| test(1, 2, 3) #@ |
| |
| from unknown import unknown |
| test(*unknown) #@ |
| def test(*args): return args |
| test(*unknown) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertEqual(inferred, util.Uninferable) |
| |
| def test_args_overwritten(self) -> None: |
| # https://github.com/PyCQA/astroid/issues/180 |
| node = extract_node( |
| """ |
| next = 42 |
| def wrapper(next=next): |
| next = 24 |
| def test(): |
| return next |
| return test |
| wrapper()() #@ |
| """ |
| ) |
| assert isinstance(node, nodes.NodeNG) |
| inferred = node.inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], nodes.Const, inferred[0]) |
| self.assertEqual(inferred[0].value, 24) |
| |
| |
| class SliceTest(unittest.TestCase): |
| def test_slice(self) -> None: |
| ast_nodes = [ |
| ("[1, 2, 3][slice(None)]", [1, 2, 3]), |
| ("[1, 2, 3][slice(None, None)]", [1, 2, 3]), |
| ("[1, 2, 3][slice(None, None, None)]", [1, 2, 3]), |
| ("[1, 2, 3][slice(1, None)]", [2, 3]), |
| ("[1, 2, 3][slice(None, 1, None)]", [1]), |
| ("[1, 2, 3][slice(0, 1)]", [1]), |
| ("[1, 2, 3][slice(0, 3, 2)]", [1, 3]), |
| ] |
| for node, expected_value in ast_nodes: |
| ast_node = extract_node(f"__({node})") |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.List) |
| self.assertEqual([elt.value for elt in inferred.elts], expected_value) |
| |
| def test_slice_inference_error(self) -> None: |
| ast_nodes = extract_node( |
| """ |
| from unknown import unknown |
| [1, 2, 3][slice(None, unknown, unknown)] #@ |
| [1, 2, 3][slice(None, missing, missing)] #@ |
| [1, 2, 3][slice(object, list, tuple)] #@ |
| [1, 2, 3][slice(b'a')] #@ |
| [1, 2, 3][slice(1, 'aa')] #@ |
| [1, 2, 3][slice(1, 2.0, 3.0)] #@ |
| [1, 2, 3][slice()] #@ |
| [1, 2, 3][slice(1, 2, 3, 4)] #@ |
| """ |
| ) |
| for node in ast_nodes: |
| self.assertRaises(InferenceError, next, node.infer()) |
| |
| def test_slice_attributes(self) -> None: |
| ast_nodes = [ |
| ("slice(2, 3, 4)", (2, 3, 4)), |
| ("slice(None, None, 4)", (None, None, 4)), |
| ("slice(None, 1, None)", (None, 1, None)), |
| ] |
| for code, values in ast_nodes: |
| lower, upper, step = values |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Slice) |
| lower_value = next(inferred.igetattr("start")) |
| self.assertIsInstance(lower_value, nodes.Const) |
| self.assertEqual(lower_value.value, lower) |
| higher_value = next(inferred.igetattr("stop")) |
| self.assertIsInstance(higher_value, nodes.Const) |
| self.assertEqual(higher_value.value, upper) |
| step_value = next(inferred.igetattr("step")) |
| self.assertIsInstance(step_value, nodes.Const) |
| self.assertEqual(step_value.value, step) |
| self.assertEqual(inferred.pytype(), "builtins.slice") |
| |
| def test_slice_type(self) -> None: |
| ast_node = extract_node("type(slice(None, None, None))") |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, "slice") |
| |
| |
| class CallSiteTest(unittest.TestCase): |
| @staticmethod |
| def _call_site_from_call(call: nodes.Call) -> CallSite: |
| return arguments.CallSite.from_call(call) |
| |
| def _test_call_site_pair( |
| self, code: str, expected_args: List[int], expected_keywords: Dict[str, int] |
| ) -> None: |
| ast_node = extract_node(code) |
| call_site = self._call_site_from_call(ast_node) |
| self.assertEqual(len(call_site.positional_arguments), len(expected_args)) |
| self.assertEqual( |
| [arg.value for arg in call_site.positional_arguments], expected_args |
| ) |
| self.assertEqual(len(call_site.keyword_arguments), len(expected_keywords)) |
| for keyword, value in expected_keywords.items(): |
| self.assertIn(keyword, call_site.keyword_arguments) |
| self.assertEqual(call_site.keyword_arguments[keyword].value, value) |
| |
| def _test_call_site( |
| self, pairs: List[Tuple[str, List[int], Dict[str, int]]] |
| ) -> None: |
| for pair in pairs: |
| self._test_call_site_pair(*pair) |
| |
| def test_call_site_starred_args(self) -> None: |
| pairs = [ |
| ( |
| "f(*(1, 2), *(2, 3), *(3, 4), **{'a':1}, **{'b': 2})", |
| [1, 2, 2, 3, 3, 4], |
| {"a": 1, "b": 2}, |
| ), |
| ( |
| "f(1, 2, *(3, 4), 5, *(6, 7), f=24, **{'c':3})", |
| [1, 2, 3, 4, 5, 6, 7], |
| {"f": 24, "c": 3}, |
| ), |
| # Too many fs passed into. |
| ("f(f=24, **{'f':24})", [], {}), |
| ] |
| self._test_call_site(pairs) |
| |
| def test_call_site(self) -> None: |
| pairs = [ |
| ("f(1, 2)", [1, 2], {}), |
| ("f(1, 2, *(1, 2))", [1, 2, 1, 2], {}), |
| ("f(a=1, b=2, c=3)", [], {"a": 1, "b": 2, "c": 3}), |
| ] |
| self._test_call_site(pairs) |
| |
| def _test_call_site_valid_arguments(self, values: List[str], invalid: bool) -> None: |
| for value in values: |
| ast_node = extract_node(value) |
| call_site = self._call_site_from_call(ast_node) |
| self.assertEqual(call_site.has_invalid_arguments(), invalid) |
| |
| def test_call_site_valid_arguments(self) -> None: |
| values = ["f(*lala)", "f(*1)", "f(*object)"] |
| self._test_call_site_valid_arguments(values, invalid=True) |
| values = ["f()", "f(*(1, ))", "f(1, 2, *(2, 3))"] |
| self._test_call_site_valid_arguments(values, invalid=False) |
| |
| def test_duplicated_keyword_arguments(self) -> None: |
| ast_node = extract_node('f(f=24, **{"f": 25})') |
| site = self._call_site_from_call(ast_node) |
| self.assertIn("f", site.duplicated_keywords) |
| |
| |
| class ObjectDunderNewTest(unittest.TestCase): |
| def test_object_dunder_new_is_inferred_if_decorator(self) -> None: |
| node = extract_node( |
| """ |
| @object.__new__ |
| class instance(object): |
| pass |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, Instance) |
| |
| |
| def test_augassign_recursion() -> None: |
| """Make sure inference doesn't throw a RecursionError |
| |
| Regression test for augmented assign dropping context.path |
| causing recursion errors |
| |
| """ |
| # infinitely recurses in python |
| code = """ |
| def rec(): |
| a = 0 |
| a += rec() |
| return a |
| rec() |
| """ |
| cls_node = extract_node(code) |
| assert next(cls_node.infer()) is util.Uninferable |
| |
| |
| def test_infer_custom_inherit_from_property() -> None: |
| node = extract_node( |
| """ |
| class custom_property(property): |
| pass |
| |
| class MyClass(object): |
| @custom_property |
| def my_prop(self): |
| return 1 |
| |
| MyClass().my_prop |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 1 |
| |
| |
| def test_cannot_infer_call_result_for_builtin_methods() -> None: |
| node = extract_node( |
| """ |
| a = "fast" |
| a |
| """ |
| ) |
| inferred = next(node.infer()) |
| lenmeth = next(inferred.igetattr("__len__")) |
| with pytest.raises(InferenceError): |
| next(lenmeth.infer_call_result(None, None)) |
| |
| |
| def test_unpack_dicts_in_assignment() -> None: |
| ast_nodes = extract_node( |
| """ |
| a, b = {1:2, 2:3} |
| a #@ |
| b #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| first_inferred = next(ast_nodes[0].infer()) |
| second_inferred = next(ast_nodes[1].infer()) |
| assert isinstance(first_inferred, nodes.Const) |
| assert first_inferred.value == 1 |
| assert isinstance(second_inferred, nodes.Const) |
| assert second_inferred.value == 2 |
| |
| |
| def test_slice_inference_in_for_loops() -> None: |
| node = extract_node( |
| """ |
| for a, (c, *b) in [(1, (2, 3, 4)), (4, (5, 6))]: |
| b #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.List) |
| assert inferred.as_string() == "[3, 4]" |
| |
| node = extract_node( |
| """ |
| for a, *b in [(1, 2, 3, 4)]: |
| b #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.List) |
| assert inferred.as_string() == "[2, 3, 4]" |
| |
| node = extract_node( |
| """ |
| for a, *b in [(1,)]: |
| b #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.List) |
| assert inferred.as_string() == "[]" |
| |
| |
| def test_slice_inference_in_for_loops_not_working() -> None: |
| ast_nodes = extract_node( |
| """ |
| from unknown import Unknown |
| for a, *b in something: |
| b #@ |
| for a, *b in Unknown: |
| b #@ |
| for a, *b in (1): |
| b #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| assert inferred == util.Uninferable |
| |
| |
| def test_unpacking_starred_and_dicts_in_assignment() -> None: |
| node = extract_node( |
| """ |
| a, *b = {1:2, 2:3, 3:4} |
| b |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.List) |
| assert inferred.as_string() == "[2, 3]" |
| |
| node = extract_node( |
| """ |
| a, *b = {1:2} |
| b |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.List) |
| assert inferred.as_string() == "[]" |
| |
| |
| def test_unpacking_starred_empty_list_in_assignment() -> None: |
| node = extract_node( |
| """ |
| a, *b, c = [1, 2] |
| b #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.List) |
| assert inferred.as_string() == "[]" |
| |
| |
| def test_regression_infinite_loop_decorator() -> None: |
| """Make sure decorators with the same names |
| as a decorated method do not cause an infinite loop |
| |
| See https://github.com/PyCQA/astroid/issues/375 |
| """ |
| code = """ |
| from functools import lru_cache |
| |
| class Foo(): |
| @lru_cache() |
| def lru_cache(self, value): |
| print('Computing {}'.format(value)) |
| return value |
| Foo().lru_cache(1) |
| """ |
| node = extract_node(code) |
| assert isinstance(node, nodes.NodeNG) |
| [result] = node.inferred() |
| assert result.value == 1 |
| |
| |
| def test_stop_iteration_in_int() -> None: |
| """Handle StopIteration error in infer_int.""" |
| code = """ |
| def f(lst): |
| if lst[0]: |
| return f(lst) |
| else: |
| args = lst[:1] |
| return int(args[0]) |
| |
| f([]) |
| """ |
| [first_result, second_result] = extract_node(code).inferred() |
| assert first_result is util.Uninferable |
| assert isinstance(second_result, Instance) |
| assert second_result.name == "int" |
| |
| |
| def test_call_on_instance_with_inherited_dunder_call_method() -> None: |
| """Stop inherited __call__ method from incorrectly returning wrong class |
| |
| See https://github.com/PyCQA/pylint/issues/2199 |
| """ |
| node = extract_node( |
| """ |
| class Base: |
| def __call__(self): |
| return self |
| |
| class Sub(Base): |
| pass |
| obj = Sub() |
| val = obj() |
| val #@ |
| """ |
| ) |
| assert isinstance(node, nodes.NodeNG) |
| [val] = node.inferred() |
| assert isinstance(val, Instance) |
| assert val.name == "Sub" |
| |
| |
| class TestInferencePropagation: |
| """Make sure function argument values are properly |
| propagated to sub functions""" |
| |
| @pytest.mark.xfail(reason="Relying on path copy") |
| def test_call_context_propagation(self): |
| n = extract_node( |
| """ |
| def chest(a): |
| return a * a |
| def best(a, b): |
| return chest(a) |
| def test(a, b, c): |
| return best(a, b) |
| test(4, 5, 6) #@ |
| """ |
| ) |
| assert next(n.infer()).as_string() == "16" |
| |
| def test_call_starargs_propagation(self) -> None: |
| code = """ |
| def foo(*args): |
| return args |
| def bar(*args): |
| return foo(*args) |
| bar(4, 5, 6, 7) #@ |
| """ |
| assert next(extract_node(code).infer()).as_string() == "(4, 5, 6, 7)" |
| |
| def test_call_kwargs_propagation(self) -> None: |
| code = """ |
| def b(**kwargs): |
| return kwargs |
| def f(**kwargs): |
| return b(**kwargs) |
| f(**{'f': 1}) #@ |
| """ |
| assert next(extract_node(code).infer()).as_string() == "{'f': 1}" |
| |
| |
| @pytest.mark.parametrize( |
| "op,result", |
| [ |
| ("<", False), |
| ("<=", True), |
| ("==", True), |
| (">=", True), |
| (">", False), |
| ("!=", False), |
| ], |
| ) |
| def test_compare(op, result) -> None: |
| code = f""" |
| 123 {op} 123 |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value == result |
| |
| |
| @pytest.mark.xfail(reason="uninferable") |
| @pytest.mark.parametrize( |
| "op,result", |
| [ |
| ("is", True), |
| ("is not", False), |
| ], |
| ) |
| def test_compare_identity(op, result) -> None: |
| code = f""" |
| obj = object() |
| obj {op} obj |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value == result |
| |
| |
| @pytest.mark.parametrize( |
| "op,result", |
| [ |
| ("in", True), |
| ("not in", False), |
| ], |
| ) |
| def test_compare_membership(op, result) -> None: |
| code = f""" |
| 1 {op} [1, 2, 3] |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value == result |
| |
| |
| @pytest.mark.parametrize( |
| "lhs,rhs,result", |
| [ |
| (1, 1, True), |
| (1, 1.1, True), |
| (1.1, 1, False), |
| (1.0, 1.0, True), |
| ("abc", "def", True), |
| ("abc", "", False), |
| ([], [1], True), |
| ((1, 2), (2, 3), True), |
| ((1, 0), (1,), False), |
| (True, True, True), |
| (True, False, False), |
| (False, 1, True), |
| (1 + 0j, 2 + 0j, util.Uninferable), |
| (+0.0, -0.0, True), |
| (0, "1", util.Uninferable), |
| (b"\x00", b"\x01", True), |
| ], |
| ) |
| def test_compare_lesseq_types(lhs, rhs, result) -> None: |
| code = f""" |
| {lhs!r} <= {rhs!r} |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value == result |
| |
| |
| def test_compare_chained() -> None: |
| code = """ |
| 3 < 5 > 3 |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value is True |
| |
| |
| def test_compare_inferred_members() -> None: |
| code = """ |
| a = 11 |
| b = 13 |
| a < b |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value is True |
| |
| |
| def test_compare_instance_members() -> None: |
| code = """ |
| class A: |
| value = 123 |
| class B: |
| @property |
| def value(self): |
| return 456 |
| A().value < B().value |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value is True |
| |
| |
| @pytest.mark.xfail(reason="unimplemented") |
| def test_compare_dynamic() -> None: |
| code = """ |
| class A: |
| def __le__(self, other): |
| return True |
| A() <= None |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value is True |
| |
| |
| def test_compare_uninferable_member() -> None: |
| code = """ |
| from unknown import UNKNOWN |
| 0 <= UNKNOWN |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred is util.Uninferable |
| |
| |
| def test_compare_chained_comparisons_shortcircuit_on_false() -> None: |
| code = """ |
| from unknown import UNKNOWN |
| 2 < 1 < UNKNOWN |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred.value is False |
| |
| |
| def test_compare_chained_comparisons_continue_on_true() -> None: |
| code = """ |
| from unknown import UNKNOWN |
| 1 < 2 < UNKNOWN |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred is util.Uninferable |
| |
| |
| @pytest.mark.xfail(reason="unimplemented") |
| def test_compare_known_false_branch() -> None: |
| code = """ |
| a = 'hello' |
| if 1 < 2: |
| a = 'goodbye' |
| a |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 1 |
| assert isinstance(inferred[0], nodes.Const) |
| assert inferred[0].value == "hello" |
| |
| |
| def test_compare_ifexp_constant() -> None: |
| code = """ |
| a = 'hello' if 1 < 2 else 'goodbye' |
| a |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 1 |
| assert isinstance(inferred[0], nodes.Const) |
| assert inferred[0].value == "hello" |
| |
| |
| def test_compare_typeerror() -> None: |
| code = """ |
| 123 <= "abc" |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 1 |
| assert inferred[0] is util.Uninferable |
| |
| |
| def test_compare_multiple_possibilites() -> None: |
| code = """ |
| from unknown import UNKNOWN |
| a = 1 |
| if UNKNOWN: |
| a = 2 |
| b = 3 |
| if UNKNOWN: |
| b = 4 |
| a < b |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 1 |
| # All possible combinations are true: (1 < 3), (1 < 4), (2 < 3), (2 < 4) |
| assert inferred[0].value is True |
| |
| |
| def test_compare_ambiguous_multiple_possibilites() -> None: |
| code = """ |
| from unknown import UNKNOWN |
| a = 1 |
| if UNKNOWN: |
| a = 3 |
| b = 2 |
| if UNKNOWN: |
| b = 4 |
| a < b |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 1 |
| # Not all possible combinations are true: (1 < 2), (1 < 4), (3 !< 2), (3 < 4) |
| assert inferred[0] is util.Uninferable |
| |
| |
| def test_compare_nonliteral() -> None: |
| code = """ |
| def func(a, b): |
| return (a, b) <= (1, 2) #@ |
| """ |
| return_node = extract_node(code) |
| node = return_node.value |
| inferred = list(node.infer()) # should not raise ValueError |
| assert len(inferred) == 1 |
| assert inferred[0] is util.Uninferable |
| |
| |
| def test_compare_unknown() -> None: |
| code = """ |
| def func(a): |
| if tuple() + (a[1],) in set(): |
| raise Exception() |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 1 |
| assert isinstance(inferred[0], nodes.FunctionDef) |
| |
| |
| def test_limit_inference_result_amount() -> None: |
| """Test setting limit inference result amount""" |
| code = """ |
| args = [] |
| |
| if True: |
| args += ['a'] |
| |
| if True: |
| args += ['b'] |
| |
| if True: |
| args += ['c'] |
| |
| if True: |
| args += ['d'] |
| |
| args #@ |
| """ |
| result = extract_node(code).inferred() |
| assert len(result) == 16 |
| with patch("astroid.manager.AstroidManager.max_inferable_values", 4): |
| result_limited = extract_node(code).inferred() |
| # Can't guarantee exact size |
| assert len(result_limited) < 16 |
| # Will not always be at the end |
| assert util.Uninferable in result_limited |
| |
| |
| def test_attribute_inference_should_not_access_base_classes() -> None: |
| """attributes of classes should mask ancestor attribues""" |
| code = """ |
| type.__new__ #@ |
| """ |
| res = extract_node(code).inferred() |
| assert len(res) == 1 |
| assert res[0].parent.name == "type" |
| |
| |
| def test_attribute_mro_object_inference() -> None: |
| """ |
| Inference should only infer results from the first available method |
| """ |
| inferred = extract_node( |
| """ |
| class A: |
| def foo(self): |
| return 1 |
| class B(A): |
| def foo(self): |
| return 2 |
| B().foo() #@ |
| """ |
| ).inferred() |
| assert len(inferred) == 1 |
| assert inferred[0].value == 2 |
| |
| |
| def test_inferred_sequence_unpacking_works() -> None: |
| inferred = next( |
| extract_node( |
| """ |
| def test(*args): |
| return (1, *args) |
| test(2) #@ |
| """ |
| ).infer() |
| ) |
| assert isinstance(inferred, nodes.Tuple) |
| assert len(inferred.elts) == 2 |
| assert [value.value for value in inferred.elts] == [1, 2] |
| |
| |
| def test_recursion_error_inferring_slice() -> None: |
| node = extract_node( |
| """ |
| class MyClass: |
| def __init__(self): |
| self._slice = slice(0, 10) |
| |
| def incr(self): |
| self._slice = slice(0, self._slice.stop + 1) |
| |
| def test(self): |
| self._slice #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, Slice) |
| |
| |
| def test_exception_lookup_last_except_handler_wins() -> None: |
| node = extract_node( |
| """ |
| try: |
| 1/0 |
| except ValueError as exc: |
| pass |
| try: |
| 1/0 |
| except OSError as exc: |
| exc #@ |
| """ |
| ) |
| assert isinstance(node, nodes.NodeNG) |
| inferred = node.inferred() |
| assert len(inferred) == 1 |
| inferred_exc = inferred[0] |
| assert isinstance(inferred_exc, Instance) |
| assert inferred_exc.name == "OSError" |
| |
| # Check that two except handlers on the same TryExcept works the same as separate |
| # TryExcepts |
| node = extract_node( |
| """ |
| try: |
| 1/0 |
| except ZeroDivisionError as exc: |
| pass |
| except ValueError as exc: |
| exc #@ |
| """ |
| ) |
| assert isinstance(node, nodes.NodeNG) |
| inferred = node.inferred() |
| assert len(inferred) == 1 |
| inferred_exc = inferred[0] |
| assert isinstance(inferred_exc, Instance) |
| assert inferred_exc.name == "ValueError" |
| |
| |
| def test_exception_lookup_name_bound_in_except_handler() -> None: |
| node = extract_node( |
| """ |
| try: |
| 1/0 |
| except ValueError: |
| name = 1 |
| try: |
| 1/0 |
| except OSError: |
| name = 2 |
| name #@ |
| """ |
| ) |
| assert isinstance(node, nodes.NodeNG) |
| inferred = node.inferred() |
| assert len(inferred) == 1 |
| inferred_exc = inferred[0] |
| assert isinstance(inferred_exc, nodes.Const) |
| assert inferred_exc.value == 2 |
| |
| |
| def test_builtin_inference_list_of_exceptions() -> None: |
| node = extract_node( |
| """ |
| tuple([ValueError, TypeError]) |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Tuple) |
| assert len(inferred.elts) == 2 |
| assert isinstance(inferred.elts[0], nodes.EvaluatedObject) |
| assert isinstance(inferred.elts[0].value, nodes.ClassDef) |
| assert inferred.elts[0].value.name == "ValueError" |
| assert isinstance(inferred.elts[1], nodes.EvaluatedObject) |
| assert isinstance(inferred.elts[1].value, nodes.ClassDef) |
| assert inferred.elts[1].value.name == "TypeError" |
| |
| # Test that inference of evaluated objects returns what is expected |
| first_elem = next(inferred.elts[0].infer()) |
| assert isinstance(first_elem, nodes.ClassDef) |
| assert first_elem.name == "ValueError" |
| |
| second_elem = next(inferred.elts[1].infer()) |
| assert isinstance(second_elem, nodes.ClassDef) |
| assert second_elem.name == "TypeError" |
| |
| # Test that as_string() also works |
| as_string = inferred.as_string() |
| assert as_string.strip() == "(ValueError, TypeError)" |
| |
| |
| def test_cannot_getattr_ann_assigns() -> None: |
| node = extract_node( |
| """ |
| class Cls: |
| ann: int |
| """ |
| ) |
| inferred = next(node.infer()) |
| with pytest.raises(AttributeInferenceError): |
| inferred.getattr("ann") |
| |
| # But if it had a value, then it would be okay. |
| node = extract_node( |
| """ |
| class Cls: |
| ann: int = 0 |
| """ |
| ) |
| inferred = next(node.infer()) |
| values = inferred.getattr("ann") |
| assert len(values) == 1 |
| |
| |
| def test_prevent_recursion_error_in_igetattr_and_context_manager_inference() -> None: |
| code = """ |
| class DummyContext(object): |
| def __enter__(self): |
| return self |
| def __exit__(self, ex_type, ex_value, ex_tb): |
| return True |
| |
| if False: |
| with DummyContext() as con: |
| pass |
| |
| with DummyContext() as con: |
| con.__enter__ #@ |
| """ |
| node = extract_node(code) |
| # According to the original issue raised that introduced this test |
| # (https://github.com/PyCQA/astroid/663, see 55076ca), this test was a |
| # non-regression check for StopIteration leaking out of inference and |
| # causing a RuntimeError. Hence, here just consume the inferred value |
| # without checking it and rely on pytest to fail on raise |
| next(node.infer()) |
| |
| |
| def test_infer_context_manager_with_unknown_args() -> None: |
| code = """ |
| class client_log(object): |
| def __init__(self, client): |
| self.client = client |
| def __enter__(self): |
| return self.client |
| def __exit__(self, exc_type, exc_value, traceback): |
| pass |
| |
| with client_log(None) as c: |
| c #@ |
| """ |
| node = extract_node(code) |
| assert next(node.infer()) is util.Uninferable |
| |
| # But if we know the argument, then it is easy |
| code = """ |
| class client_log(object): |
| def __init__(self, client=24): |
| self.client = client |
| def __enter__(self): |
| return self.client |
| def __exit__(self, exc_type, exc_value, traceback): |
| pass |
| |
| with client_log(None) as c: |
| c #@ |
| """ |
| node = extract_node(code) |
| assert isinstance(next(node.infer()), nodes.Const) |
| |
| |
| @pytest.mark.parametrize( |
| "code", |
| [ |
| """ |
| class Error(Exception): |
| pass |
| |
| a = Error() |
| a #@ |
| """, |
| """ |
| class Error(Exception): |
| def method(self): |
| self #@ |
| """, |
| ], |
| ) |
| def test_subclass_of_exception(code) -> None: |
| inferred = next(extract_node(code).infer()) |
| assert isinstance(inferred, Instance) |
| args = next(inferred.igetattr("args")) |
| assert isinstance(args, nodes.Tuple) |
| |
| |
| def test_ifexp_inference() -> None: |
| code = """ |
| def truth_branch(): |
| return 1 if True else 2 |
| |
| def false_branch(): |
| return 1 if False else 2 |
| |
| def both_branches(): |
| return 1 if unknown() else 2 |
| |
| truth_branch() #@ |
| false_branch() #@ |
| both_branches() #@ |
| """ |
| ast_nodes = extract_node(code) |
| assert isinstance(ast_nodes, list) |
| first = next(ast_nodes[0].infer()) |
| assert isinstance(first, nodes.Const) |
| assert first.value == 1 |
| |
| second = next(ast_nodes[1].infer()) |
| assert isinstance(second, nodes.Const) |
| assert second.value == 2 |
| |
| third = list(ast_nodes[2].infer()) |
| assert isinstance(third, list) |
| assert [third[0].value, third[1].value] == [1, 2] |
| |
| |
| def test_assert_last_function_returns_none_on_inference() -> None: |
| code = """ |
| def check_equal(a, b): |
| res = do_something_with_these(a, b) |
| assert a == b == res |
| |
| check_equal(a, b) |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value is None |
| |
| |
| @test_utils.require_version(minver="3.8") |
| def test_posonlyargs_inference() -> None: |
| code = """ |
| class A: |
| method = lambda self, b, /, c: b + c |
| |
| def __init__(self, other=(), /, **kw): |
| self #@ |
| A() #@ |
| A().method #@ |
| |
| """ |
| self_node, instance, lambda_method = extract_node(code) |
| inferred = next(self_node.infer()) |
| assert isinstance(inferred, Instance) |
| assert inferred.name == "A" |
| |
| inferred = next(instance.infer()) |
| assert isinstance(inferred, Instance) |
| assert inferred.name == "A" |
| |
| inferred = next(lambda_method.infer()) |
| assert isinstance(inferred, BoundMethod) |
| assert inferred.type == "method" |
| |
| |
| def test_infer_args_unpacking_of_self() -> None: |
| code = """ |
| class A: |
| def __init__(*args, **kwargs): |
| self, *args = args |
| self.data = {1: 2} |
| self #@ |
| A().data #@ |
| """ |
| self, data = extract_node(code) |
| inferred_self = next(self.infer()) |
| assert isinstance(inferred_self, Instance) |
| assert inferred_self.name == "A" |
| |
| inferred_data = next(data.infer()) |
| assert isinstance(inferred_data, nodes.Dict) |
| assert inferred_data.as_string() == "{1: 2}" |
| |
| |
| def test_infer_exception_instance_attributes() -> None: |
| code = """ |
| class UnsupportedFormatCharacter(Exception): |
| def __init__(self, index): |
| Exception.__init__(self, index) |
| self.index = index |
| |
| try: |
| 1/0 |
| except UnsupportedFormatCharacter as exc: |
| exc #@ |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, ExceptionInstance) |
| index = inferred.getattr("index") |
| assert len(index) == 1 |
| assert isinstance(index[0], nodes.AssignAttr) |
| |
| |
| @pytest.mark.parametrize( |
| "code,instance_name", |
| [ |
| ( |
| """ |
| class A: |
| def __enter__(self): |
| return self |
| def __exit__(self, err_type, err, traceback): |
| return |
| class B(A): |
| pass |
| with B() as b: |
| b #@ |
| """, |
| "B", |
| ), |
| ( |
| """ |
| class A: |
| def __enter__(self): |
| return A() |
| def __exit__(self, err_type, err, traceback): |
| return |
| class B(A): |
| pass |
| with B() as b: |
| b #@ |
| """, |
| "A", |
| ), |
| ( |
| """ |
| class A: |
| def test(self): |
| return A() |
| class B(A): |
| def test(self): |
| return A.test(self) |
| B().test() |
| """, |
| "A", |
| ), |
| ], |
| ) |
| def test_inference_is_limited_to_the_boundnode(code, instance_name) -> None: |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, Instance) |
| assert inferred.name == instance_name |
| |
| |
| def test_property_inference() -> None: |
| code = """ |
| class A: |
| @property |
| def test(self): |
| return 42 |
| |
| @test.setter |
| def test(self, value): |
| return "banco" |
| |
| A.test #@ |
| A().test #@ |
| A.test.fget(A) #@ |
| A.test.fset(A, "a_value") #@ |
| A.test.setter #@ |
| A.test.getter #@ |
| A.test.deleter #@ |
| """ |
| ( |
| prop, |
| prop_result, |
| prop_fget_result, |
| prop_fset_result, |
| prop_setter, |
| prop_getter, |
| prop_deleter, |
| ) = extract_node(code) |
| |
| inferred = next(prop.infer()) |
| assert isinstance(inferred, objects.Property) |
| assert inferred.pytype() == "builtins.property" |
| assert inferred.type == "property" |
| |
| inferred = next(prop_result.infer()) |
| print(prop_result.as_string()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 42 |
| |
| inferred = next(prop_fget_result.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 42 |
| |
| inferred = next(prop_fset_result.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == "banco" |
| |
| for prop_func in prop_setter, prop_getter, prop_deleter: |
| inferred = next(prop_func.infer()) |
| assert isinstance(inferred, nodes.FunctionDef) |
| |
| |
| def test_property_as_string() -> None: |
| code = """ |
| class A: |
| @property |
| def test(self): |
| return 42 |
| |
| A.test #@ |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, objects.Property) |
| property_body = textwrap.dedent( |
| """ |
| @property |
| def test(self): |
| return 42 |
| """ |
| ) |
| assert inferred.as_string().strip() == property_body.strip() |
| |
| |
| def test_property_callable_inference() -> None: |
| code = """ |
| class A: |
| def func(self): |
| return 42 |
| p = property(func) |
| A().p |
| """ |
| property_call = extract_node(code) |
| inferred = next(property_call.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 42 |
| |
| # Try with lambda as well |
| code = """ |
| class A: |
| p = property(lambda self: 42) |
| A().p |
| """ |
| property_call = extract_node(code) |
| inferred = next(property_call.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 42 |
| |
| |
| def test_recursion_error_inferring_builtin_containers() -> None: |
| node = extract_node( |
| """ |
| class Foo: |
| a = "foo" |
| inst = Foo() |
| |
| b = tuple([inst.a]) #@ |
| inst.a = b |
| """ |
| ) |
| helpers.safe_infer(node.targets[0]) |
| |
| |
| def test_inferaugassign_picking_parent_instead_of_stmt() -> None: |
| code = """ |
| from collections import namedtuple |
| SomeClass = namedtuple('SomeClass', ['name']) |
| items = [SomeClass(name='some name')] |
| |
| some_str = '' |
| some_str += ', '.join(__(item) for item in items) |
| """ |
| # item needs to be inferrd as `SomeClass` but it was inferred |
| # as a string because the entire `AugAssign` node was inferred |
| # as a string. |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, Instance) |
| assert inferred.name == "SomeClass" |
| |
| |
| def test_classmethod_from_builtins_inferred_as_bound() -> None: |
| code = """ |
| import builtins |
| |
| class Foo(): |
| @classmethod |
| def bar1(cls, text): |
| pass |
| |
| @builtins.classmethod |
| def bar2(cls, text): |
| pass |
| |
| Foo.bar1 #@ |
| Foo.bar2 #@ |
| """ |
| first_node, second_node = extract_node(code) |
| assert isinstance(next(first_node.infer()), BoundMethod) |
| assert isinstance(next(second_node.infer()), BoundMethod) |
| |
| |
| def test_infer_dict_passes_context() -> None: |
| code = """ |
| k = {} |
| (_ for k in __(dict(**k))) |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, Instance) |
| assert inferred.qname() == "builtins.dict" |
| |
| |
| @pytest.mark.parametrize( |
| "code,obj,obj_type", |
| [ |
| ( |
| """ |
| def klassmethod1(method): |
| @classmethod |
| def inner(cls): |
| return method(cls) |
| return inner |
| |
| class X(object): |
| @klassmethod1 |
| def x(cls): |
| return 'X' |
| X.x |
| """, |
| BoundMethod, |
| "classmethod", |
| ), |
| ( |
| """ |
| def staticmethod1(method): |
| @staticmethod |
| def inner(cls): |
| return method(cls) |
| return inner |
| |
| class X(object): |
| @staticmethod1 |
| def x(cls): |
| return 'X' |
| X.x |
| """, |
| nodes.FunctionDef, |
| "staticmethod", |
| ), |
| ( |
| """ |
| def klassmethod1(method): |
| def inner(cls): |
| return method(cls) |
| return classmethod(inner) |
| |
| class X(object): |
| @klassmethod1 |
| def x(cls): |
| return 'X' |
| X.x |
| """, |
| BoundMethod, |
| "classmethod", |
| ), |
| ( |
| """ |
| def staticmethod1(method): |
| def inner(cls): |
| return method(cls) |
| return staticmethod(inner) |
| |
| class X(object): |
| @staticmethod1 |
| def x(cls): |
| return 'X' |
| X.x |
| """, |
| nodes.FunctionDef, |
| "staticmethod", |
| ), |
| ], |
| ) |
| def test_custom_decorators_for_classmethod_and_staticmethods(code, obj, obj_type): |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, obj) |
| assert inferred.type == obj_type |
| |
| |
| @pytest.mark.skipif(not PY38_PLUS, reason="Needs dataclasses available") |
| @pytest.mark.skipif( |
| PY39_PLUS, |
| reason="Exact inference with dataclasses (replace function) in python3.9", |
| ) |
| def test_dataclasses_subscript_inference_recursion_error(): |
| code = """ |
| from dataclasses import dataclass, replace |
| |
| @dataclass |
| class ProxyConfig: |
| auth: str = "/auth" |
| |
| |
| a = ProxyConfig("") |
| test_dict = {"proxy" : {"auth" : "", "bla" : "f"}} |
| |
| foo = test_dict['proxy'] |
| replace(a, **test_dict['proxy']) # This fails |
| """ |
| node = extract_node(code) |
| # Reproduces only with safe_infer() |
| assert helpers.safe_infer(node) is None |
| |
| |
| @pytest.mark.skipif( |
| not PY39_PLUS, |
| reason="Exact inference with dataclasses (replace function) in python3.9", |
| ) |
| def test_dataclasses_subscript_inference_recursion_error_39(): |
| code = """ |
| from dataclasses import dataclass, replace |
| |
| @dataclass |
| class ProxyConfig: |
| auth: str = "/auth" |
| |
| |
| a = ProxyConfig("") |
| test_dict = {"proxy" : {"auth" : "", "bla" : "f"}} |
| |
| foo = test_dict['proxy'] |
| replace(a, **test_dict['proxy']) # This fails |
| """ |
| node = extract_node(code) |
| infer_val = helpers.safe_infer(node) |
| assert isinstance(infer_val, Instance) |
| assert infer_val.pytype() == ".ProxyConfig" |
| |
| |
| def test_self_reference_infer_does_not_trigger_recursion_error() -> None: |
| # Prevents https://github.com/PyCQA/pylint/issues/1285 |
| code = """ |
| def func(elems): |
| return elems |
| |
| class BaseModel(object): |
| |
| def __init__(self, *args, **kwargs): |
| self._reference = func(*self._reference.split('.')) |
| BaseModel()._reference |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred is util.Uninferable |
| |
| |
| def test_inferring_properties_multiple_time_does_not_mutate_locals_multiple_times() -> None: |
| code = """ |
| class A: |
| @property |
| def a(self): |
| return 42 |
| |
| A() |
| """ |
| node = extract_node(code) |
| # Infer the class |
| cls = next(node.infer()) |
| (prop,) = cls.getattr("a") |
| |
| # Try to infer the property function *multiple* times. `A.locals` should be modified only once |
| for _ in range(3): |
| prop.inferred() |
| a_locals = cls.locals["a"] |
| # [FunctionDef, Property] |
| assert len(a_locals) == 2 |
| |
| |
| def test_getattr_fails_on_empty_values() -> None: |
| code = """ |
| import collections |
| collections |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| with pytest.raises(InferenceError): |
| next(inferred.igetattr("")) |
| |
| with pytest.raises(AttributeInferenceError): |
| inferred.getattr("") |
| |
| |
| def test_infer_first_argument_of_static_method_in_metaclass() -> None: |
| code = """ |
| class My(type): |
| @staticmethod |
| def test(args): |
| args #@ |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert inferred is util.Uninferable |
| |
| |
| def test_recursion_error_metaclass_monkeypatching() -> None: |
| module = resources.build_file( |
| "data/metaclass_recursion/monkeypatch.py", "data.metaclass_recursion" |
| ) |
| cls = next(module.igetattr("MonkeyPatchClass")) |
| assert isinstance(cls, nodes.ClassDef) |
| assert cls.declared_metaclass() is None |
| |
| |
| @pytest.mark.xfail(reason="Cannot fully infer all the base classes properly.") |
| def test_recursion_error_self_reference_type_call() -> None: |
| # Fix for https://github.com/PyCQA/astroid/issues/199 |
| code = """ |
| class A(object): |
| pass |
| class SomeClass(object): |
| route_class = A |
| def __init__(self): |
| self.route_class = type('B', (self.route_class, ), {}) |
| self.route_class() #@ |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, Instance) |
| assert inferred.name == "B" |
| # TODO: Cannot infer [B, A, object] but at least the recursion error is gone. |
| assert [cls.name for cls in inferred.mro()] == ["B", "A", "object"] |
| |
| |
| def test_allow_retrieving_instance_attrs_and_special_attrs_for_functions() -> None: |
| code = """ |
| class A: |
| def test(self): |
| "a" |
| # Add `__doc__` to `FunctionDef.instance_attrs` via an `AugAssign` |
| test.__doc__ += 'b' |
| test #@ |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| attrs = inferred.getattr("__doc__") |
| # One from the `AugAssign`, one from the special attributes |
| assert len(attrs) == 2 |
| |
| |
| def test_implicit_parameters_bound_method() -> None: |
| code = """ |
| class A(type): |
| @classmethod |
| def test(cls, first): return first |
| def __new__(cls, name, bases, dictionary): |
| return super().__new__(cls, name, bases, dictionary) |
| |
| A.test #@ |
| A.__new__ #@ |
| """ |
| test, dunder_new = extract_node(code) |
| test = next(test.infer()) |
| assert isinstance(test, BoundMethod) |
| assert test.implicit_parameters() == 1 |
| |
| dunder_new = next(dunder_new.infer()) |
| assert isinstance(dunder_new, BoundMethod) |
| assert dunder_new.implicit_parameters() == 0 |
| |
| |
| def test_super_inference_of_abstract_property() -> None: |
| code = """ |
| from abc import abstractmethod |
| |
| class A: |
| @property |
| def test(self): |
| return "super" |
| |
| class C: |
| @property |
| @abstractmethod |
| def test(self): |
| "abstract method" |
| |
| class B(A, C): |
| |
| @property |
| def test(self): |
| super() #@ |
| |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| test = inferred.getattr("test") |
| assert len(test) == 2 |
| |
| |
| def test_infer_generated_setter() -> None: |
| code = """ |
| class A: |
| @property |
| def test(self): |
| pass |
| A.test.setter |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.FunctionDef) |
| assert isinstance(inferred.args, nodes.Arguments) |
| # This line used to crash because property generated functions |
| # did not have args properly set |
| assert list(inferred.nodes_of_class(nodes.Const)) == [] |
| |
| |
| def test_infer_list_of_uninferables_does_not_crash() -> None: |
| code = """ |
| x = [A] * 1 |
| f = [x, [A] * 2] |
| x = list(f) + [] # List[Uninferable] |
| tuple(x[0]) |
| """ |
| node = extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Tuple) |
| # Would not be able to infer the first element. |
| assert not inferred.elts |
| |
| |
| # https://github.com/PyCQA/astroid/issues/926 |
| def test_issue926_infer_stmts_referencing_same_name_is_not_uninferable() -> None: |
| code = """ |
| pair = [1, 2] |
| ex = pair[0] |
| if 1 + 1 == 2: |
| ex = pair[1] |
| ex |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 2 |
| assert isinstance(inferred[0], nodes.Const) |
| assert inferred[0].value == 1 |
| assert isinstance(inferred[1], nodes.Const) |
| assert inferred[1].value == 2 |
| |
| |
| # https://github.com/PyCQA/astroid/issues/926 |
| def test_issue926_binop_referencing_same_name_is_not_uninferable() -> None: |
| code = """ |
| pair = [1, 2] |
| ex = pair[0] + pair[1] |
| ex |
| """ |
| node = extract_node(code) |
| inferred = list(node.infer()) |
| assert len(inferred) == 1 |
| assert isinstance(inferred[0], nodes.Const) |
| assert inferred[0].value == 3 |
| |
| |
| def test_pylint_issue_4692_attribute_inference_error_in_infer_import_from() -> None: |
| """https://github.com/PyCQA/pylint/issues/4692""" |
| code = """ |
| import click |
| |
| |
| for name, item in click.__dict__.items(): |
| _ = isinstance(item, click.Command) and item != 'foo' |
| """ |
| node = extract_node(code) |
| with pytest.raises(InferenceError): |
| list(node.infer()) |
| |
| |
| def test_issue_1090_infer_yield_type_base_class() -> None: |
| code = """ |
| import contextlib |
| |
| class A: |
| @contextlib.contextmanager |
| def get(self): |
| yield self |
| |
| class B(A): |
| def play(): |
| pass |
| |
| with B().get() as b: |
| b |
| b |
| """ |
| node = extract_node(code) |
| assert next(node.infer()).pytype() == ".B" |
| |
| |
| def test_namespace_package() -> None: |
| """check that a file using namespace packages and relative imports is parseable""" |
| resources.build_file("data/beyond_top_level/import_package.py") |
| |
| |
| def test_namespace_package_same_name() -> None: |
| """check that a file using namespace packages and relative imports |
| with similar names is parseable""" |
| resources.build_file("data/beyond_top_level_two/a.py") |
| |
| |
| def test_relative_imports_init_package() -> None: |
| """check that relative imports within a package that uses __init__.py |
| still works""" |
| resources.build_file( |
| "data/beyond_top_level_three/module/sub_module/sub_sub_module/main.py" |
| ) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |