| # Copyright (c) 2013-2014 Google, Inc. |
| # Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com> |
| # Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com> |
| # Copyright (c) 2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> |
| # Copyright (c) 2015 raylu <lurayl@gmail.com> |
| # Copyright (c) 2015 Philip Lorenz <philip@bithub.de> |
| # Copyright (c) 2016 Florian Bruhin <me@the-compiler.org> |
| # Copyright (c) 2017-2018, 2020-2021 hippo91 <guillaume.peillex@gmail.com> |
| # Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com> |
| # Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com> |
| # Copyright (c) 2017 David Euresti <github@euresti.com> |
| # Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com> |
| # Copyright (c) 2018 Tomas Gavenciak <gavento@ucw.cz> |
| # Copyright (c) 2018 David Poirier <david-poirier-csn@users.noreply.github.com> |
| # Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi> |
| # Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com> |
| # Copyright (c) 2018 Anthony Sottile <asottile@umich.edu> |
| # Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com> |
| # Copyright (c) 2018 Ahmed Azzaoui <ahmed.azzaoui@engie.com> |
| # Copyright (c) 2019-2020 Bryce Guinta <bryce.guinta@protonmail.com> |
| # Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk> |
| # Copyright (c) 2019 Tomas Novak <ext.Tomas.Novak@skoda-auto.cz> |
| # Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com> |
| # Copyright (c) 2019 Grygorii Iermolenko <gyermolenko@gmail.com> |
| # Copyright (c) 2020 David Gilman <davidgilman1@gmail.com> |
| # Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com> |
| # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> |
| # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com> |
| # Copyright (c) 2021 Joshua Cannon <joshua.cannon@ni.com> |
| # Copyright (c) 2021 Craig Franklin <craigjfranklin@gmail.com> |
| # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com> |
| # Copyright (c) 2021 Jonathan Striebel <jstriebel@users.noreply.github.com> |
| # Copyright (c) 2021 Dimitri Prybysh <dmand@yandex.ru> |
| # Copyright (c) 2021 David Liu <david@cs.toronto.edu> |
| # Copyright (c) 2021 pre-commit-ci[bot] <bot@noreply.github.com> |
| # Copyright (c) 2021 Alphadelta14 <alpha@alphaservcomputing.solutions> |
| # Copyright (c) 2021 Tim Martin <tim@asymptotic.co.uk> |
| # Copyright (c) 2021 Andrew Haigh <hello@nelf.in> |
| # Copyright (c) 2021 Artsiom Kaval <lezeroq@gmail.com> |
| # Copyright (c) 2021 Damien Baty <damien@damienbaty.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 basic functionality in astroid.brain.""" |
| import io |
| import queue |
| import re |
| import sys |
| import unittest |
| from typing import Any, List |
| |
| import pytest |
| |
| import astroid |
| from astroid import MANAGER, bases, builder, nodes, objects, test_utils, util |
| from astroid.bases import Instance |
| from astroid.const import PY37_PLUS |
| from astroid.exceptions import AttributeInferenceError, InferenceError |
| from astroid.nodes.node_classes import Const |
| from astroid.nodes.scoped_nodes import ClassDef |
| |
| try: |
| import multiprocessing # pylint: disable=unused-import |
| |
| HAS_MULTIPROCESSING = True |
| except ImportError: |
| HAS_MULTIPROCESSING = False |
| |
| |
| try: |
| import nose # pylint: disable=unused-import |
| |
| HAS_NOSE = True |
| except ImportError: |
| HAS_NOSE = False |
| |
| try: |
| import dateutil # pylint: disable=unused-import |
| |
| HAS_DATEUTIL = True |
| except ImportError: |
| HAS_DATEUTIL = False |
| |
| try: |
| import attr as attr_module # pylint: disable=unused-import |
| |
| HAS_ATTR = True |
| except ImportError: |
| HAS_ATTR = False |
| |
| try: |
| import six # pylint: disable=unused-import |
| |
| HAS_SIX = True |
| except ImportError: |
| HAS_SIX = False |
| |
| |
| def assertEqualMro(klass: ClassDef, expected_mro: List[str]) -> None: |
| """Check mro names.""" |
| assert [member.qname() for member in klass.mro()] == expected_mro |
| |
| |
| class HashlibTest(unittest.TestCase): |
| def _assert_hashlib_class(self, class_obj: ClassDef) -> None: |
| self.assertIn("update", class_obj) |
| self.assertIn("digest", class_obj) |
| self.assertIn("hexdigest", class_obj) |
| self.assertIn("block_size", class_obj) |
| self.assertIn("digest_size", class_obj) |
| self.assertEqual(len(class_obj["__init__"].args.args), 2) |
| self.assertEqual(len(class_obj["__init__"].args.defaults), 1) |
| self.assertEqual(len(class_obj["update"].args.args), 2) |
| self.assertEqual(len(class_obj["digest"].args.args), 1) |
| self.assertEqual(len(class_obj["hexdigest"].args.args), 1) |
| |
| def test_hashlib(self) -> None: |
| """Tests that brain extensions for hashlib work.""" |
| hashlib_module = MANAGER.ast_from_module_name("hashlib") |
| for class_name in ("md5", "sha1"): |
| class_obj = hashlib_module[class_name] |
| self._assert_hashlib_class(class_obj) |
| |
| def test_hashlib_py36(self) -> None: |
| hashlib_module = MANAGER.ast_from_module_name("hashlib") |
| for class_name in ("sha3_224", "sha3_512", "shake_128"): |
| class_obj = hashlib_module[class_name] |
| self._assert_hashlib_class(class_obj) |
| for class_name in ("blake2b", "blake2s"): |
| class_obj = hashlib_module[class_name] |
| self.assertEqual(len(class_obj["__init__"].args.args), 2) |
| |
| |
| class CollectionsDequeTests(unittest.TestCase): |
| def _inferred_queue_instance(self) -> Instance: |
| node = builder.extract_node( |
| """ |
| import collections |
| q = collections.deque([]) |
| q |
| """ |
| ) |
| return next(node.infer()) |
| |
| def test_deque(self) -> None: |
| inferred = self._inferred_queue_instance() |
| self.assertTrue(inferred.getattr("__len__")) |
| |
| def test_deque_py35methods(self) -> None: |
| inferred = self._inferred_queue_instance() |
| self.assertIn("copy", inferred.locals) |
| self.assertIn("insert", inferred.locals) |
| self.assertIn("index", inferred.locals) |
| |
| @test_utils.require_version(maxver="3.8") |
| def test_deque_not_py39methods(self): |
| inferred = self._inferred_queue_instance() |
| with self.assertRaises(AttributeInferenceError): |
| inferred.getattr("__class_getitem__") |
| |
| @test_utils.require_version(minver="3.9") |
| def test_deque_py39methods(self): |
| inferred = self._inferred_queue_instance() |
| self.assertTrue(inferred.getattr("__class_getitem__")) |
| |
| |
| class OrderedDictTest(unittest.TestCase): |
| def _inferred_ordered_dict_instance(self) -> Instance: |
| node = builder.extract_node( |
| """ |
| import collections |
| d = collections.OrderedDict() |
| d |
| """ |
| ) |
| return next(node.infer()) |
| |
| def test_ordered_dict_py34method(self) -> None: |
| inferred = self._inferred_ordered_dict_instance() |
| self.assertIn("move_to_end", inferred.locals) |
| |
| |
| class NamedTupleTest(unittest.TestCase): |
| def test_namedtuple_base(self) -> None: |
| klass = builder.extract_node( |
| """ |
| from collections import namedtuple |
| |
| class X(namedtuple("X", ["a", "b", "c"])): |
| pass |
| """ |
| ) |
| assert isinstance(klass, nodes.ClassDef) |
| self.assertEqual( |
| [anc.name for anc in klass.ancestors()], ["X", "tuple", "object"] |
| ) |
| for anc in klass.ancestors(): |
| self.assertFalse(anc.parent is None) |
| |
| def test_namedtuple_inference(self) -> None: |
| klass = builder.extract_node( |
| """ |
| from collections import namedtuple |
| |
| name = "X" |
| fields = ["a", "b", "c"] |
| class X(namedtuple(name, fields)): |
| pass |
| """ |
| ) |
| assert isinstance(klass, nodes.ClassDef) |
| base = next(base for base in klass.ancestors() if base.name == "X") |
| self.assertSetEqual({"a", "b", "c"}, set(base.instance_attrs)) |
| |
| def test_namedtuple_inference_failure(self) -> None: |
| klass = builder.extract_node( |
| """ |
| from collections import namedtuple |
| |
| def foo(fields): |
| return __(namedtuple("foo", fields)) |
| """ |
| ) |
| self.assertIs(util.Uninferable, next(klass.infer())) |
| |
| def test_namedtuple_advanced_inference(self) -> None: |
| # urlparse return an object of class ParseResult, which has a |
| # namedtuple call and a mixin as base classes |
| result = builder.extract_node( |
| """ |
| from urllib.parse import urlparse |
| |
| result = __(urlparse('gopher://')) |
| """ |
| ) |
| instance = next(result.infer()) |
| self.assertGreaterEqual(len(instance.getattr("scheme")), 1) |
| self.assertGreaterEqual(len(instance.getattr("port")), 1) |
| with self.assertRaises(AttributeInferenceError): |
| instance.getattr("foo") |
| self.assertGreaterEqual(len(instance.getattr("geturl")), 1) |
| self.assertEqual(instance.name, "ParseResult") |
| |
| def test_namedtuple_instance_attrs(self) -> None: |
| result = builder.extract_node( |
| """ |
| from collections import namedtuple |
| namedtuple('a', 'a b c')(1, 2, 3) #@ |
| """ |
| ) |
| inferred = next(result.infer()) |
| for name, attr in inferred.instance_attrs.items(): |
| self.assertEqual(attr[0].attrname, name) |
| |
| def test_namedtuple_uninferable_fields(self) -> None: |
| node = builder.extract_node( |
| """ |
| x = [A] * 2 |
| from collections import namedtuple |
| l = namedtuple('a', x) |
| l(1) |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) |
| |
| def test_namedtuple_access_class_fields(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "field other") |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIn("field", inferred.locals) |
| self.assertIn("other", inferred.locals) |
| |
| def test_namedtuple_rename_keywords(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "abc def", rename=True) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIn("abc", inferred.locals) |
| self.assertIn("_1", inferred.locals) |
| |
| def test_namedtuple_rename_duplicates(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "abc abc abc", rename=True) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIn("abc", inferred.locals) |
| self.assertIn("_1", inferred.locals) |
| self.assertIn("_2", inferred.locals) |
| |
| def test_namedtuple_rename_uninferable(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "a b c", rename=UNINFERABLE) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIn("a", inferred.locals) |
| self.assertIn("b", inferred.locals) |
| self.assertIn("c", inferred.locals) |
| |
| def test_namedtuple_func_form(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple(typename="Tuple", field_names="a b c", rename=UNINFERABLE) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.name, "Tuple") |
| self.assertIn("a", inferred.locals) |
| self.assertIn("b", inferred.locals) |
| self.assertIn("c", inferred.locals) |
| |
| def test_namedtuple_func_form_args_and_kwargs(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", field_names="a b c", rename=UNINFERABLE) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertEqual(inferred.name, "Tuple") |
| self.assertIn("a", inferred.locals) |
| self.assertIn("b", inferred.locals) |
| self.assertIn("c", inferred.locals) |
| |
| def test_namedtuple_bases_are_actually_names_not_nodes(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", field_names="a b c", rename=UNINFERABLE) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, astroid.ClassDef) |
| self.assertIsInstance(inferred.bases[0], astroid.Name) |
| self.assertEqual(inferred.bases[0].name, "tuple") |
| |
| def test_invalid_label_does_not_crash_inference(self) -> None: |
| code = """ |
| import collections |
| a = collections.namedtuple( 'a', ['b c'] ) |
| a |
| """ |
| node = builder.extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.ClassDef) |
| assert "b" not in inferred.locals |
| assert "c" not in inferred.locals |
| |
| def test_no_rename_duplicates_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "abc abc") |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) # would raise ValueError |
| |
| def test_no_rename_keywords_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "abc def") |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) # would raise ValueError |
| |
| def test_no_rename_nonident_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "123 456") |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) # would raise ValueError |
| |
| def test_no_rename_underscore_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", "_1") |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) # would raise ValueError |
| |
| def test_invalid_typename_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("123", "abc") |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) # would raise ValueError |
| |
| def test_keyword_typename_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("while", "abc") |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) # would raise ValueError |
| |
| def test_typeerror_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| Tuple = namedtuple("Tuple", [123, 456]) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| # namedtuple converts all arguments to strings so these should be too |
| # and catch on the isidentifier() check |
| self.assertIs(util.Uninferable, inferred) |
| |
| def test_pathological_str_does_not_crash_inference(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import namedtuple |
| class Invalid: |
| def __str__(self): |
| return 123 # will raise TypeError |
| Tuple = namedtuple("Tuple", [Invalid()]) |
| Tuple #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) |
| |
| |
| class DefaultDictTest(unittest.TestCase): |
| def test_1(self) -> None: |
| node = builder.extract_node( |
| """ |
| from collections import defaultdict |
| |
| X = defaultdict(int) |
| X[0] |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(util.Uninferable, inferred) |
| |
| |
| class ModuleExtenderTest(unittest.TestCase): |
| def test_extension_modules(self) -> None: |
| transformer = MANAGER._transform |
| for extender, _ in transformer.transforms[nodes.Module]: |
| n = nodes.Module("__main__", None) |
| extender(n) |
| |
| |
| @unittest.skipUnless(HAS_NOSE, "This test requires nose library.") |
| class NoseBrainTest(unittest.TestCase): |
| def test_nose_tools(self): |
| methods = builder.extract_node( |
| """ |
| from nose.tools import assert_equal |
| from nose.tools import assert_equals |
| from nose.tools import assert_true |
| assert_equal = assert_equal #@ |
| assert_true = assert_true #@ |
| assert_equals = assert_equals #@ |
| """ |
| ) |
| assert isinstance(methods, list) |
| assert_equal = next(methods[0].value.infer()) |
| assert_true = next(methods[1].value.infer()) |
| assert_equals = next(methods[2].value.infer()) |
| |
| self.assertIsInstance(assert_equal, astroid.BoundMethod) |
| self.assertIsInstance(assert_true, astroid.BoundMethod) |
| self.assertIsInstance(assert_equals, astroid.BoundMethod) |
| self.assertEqual(assert_equal.qname(), "unittest.case.TestCase.assertEqual") |
| self.assertEqual(assert_true.qname(), "unittest.case.TestCase.assertTrue") |
| self.assertEqual(assert_equals.qname(), "unittest.case.TestCase.assertEqual") |
| |
| |
| @unittest.skipUnless(HAS_SIX, "These tests require the six library") |
| class SixBrainTest(unittest.TestCase): |
| def test_attribute_access(self) -> None: |
| ast_nodes = builder.extract_node( |
| """ |
| import six |
| six.moves.http_client #@ |
| six.moves.urllib_parse #@ |
| six.moves.urllib_error #@ |
| six.moves.urllib.request #@ |
| """ |
| ) |
| assert isinstance(ast_nodes, list) |
| http_client = next(ast_nodes[0].infer()) |
| self.assertIsInstance(http_client, nodes.Module) |
| self.assertEqual(http_client.name, "http.client") |
| |
| urllib_parse = next(ast_nodes[1].infer()) |
| self.assertIsInstance(urllib_parse, nodes.Module) |
| self.assertEqual(urllib_parse.name, "urllib.parse") |
| urljoin = next(urllib_parse.igetattr("urljoin")) |
| urlencode = next(urllib_parse.igetattr("urlencode")) |
| self.assertIsInstance(urljoin, nodes.FunctionDef) |
| self.assertEqual(urljoin.qname(), "urllib.parse.urljoin") |
| self.assertIsInstance(urlencode, nodes.FunctionDef) |
| self.assertEqual(urlencode.qname(), "urllib.parse.urlencode") |
| |
| urllib_error = next(ast_nodes[2].infer()) |
| self.assertIsInstance(urllib_error, nodes.Module) |
| self.assertEqual(urllib_error.name, "urllib.error") |
| urlerror = next(urllib_error.igetattr("URLError")) |
| self.assertIsInstance(urlerror, nodes.ClassDef) |
| content_too_short = next(urllib_error.igetattr("ContentTooShortError")) |
| self.assertIsInstance(content_too_short, nodes.ClassDef) |
| |
| urllib_request = next(ast_nodes[3].infer()) |
| self.assertIsInstance(urllib_request, nodes.Module) |
| self.assertEqual(urllib_request.name, "urllib.request") |
| urlopen = next(urllib_request.igetattr("urlopen")) |
| urlretrieve = next(urllib_request.igetattr("urlretrieve")) |
| self.assertIsInstance(urlopen, nodes.FunctionDef) |
| self.assertEqual(urlopen.qname(), "urllib.request.urlopen") |
| self.assertIsInstance(urlretrieve, nodes.FunctionDef) |
| self.assertEqual(urlretrieve.qname(), "urllib.request.urlretrieve") |
| |
| def test_from_imports(self) -> None: |
| ast_node = builder.extract_node( |
| """ |
| from six.moves import http_client |
| http_client.HTTPSConnection #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| qname = "http.client.HTTPSConnection" |
| self.assertEqual(inferred.qname(), qname) |
| |
| def test_from_submodule_imports(self) -> None: |
| """Make sure ulrlib submodules can be imported from |
| |
| See PyCQA/pylint#1640 for relevant issue |
| """ |
| ast_node = builder.extract_node( |
| """ |
| from six.moves.urllib.parse import urlparse |
| urlparse #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.FunctionDef) |
| |
| def test_with_metaclass_subclasses_inheritance(self) -> None: |
| ast_node = builder.extract_node( |
| """ |
| class A(type): |
| def test(cls): |
| return cls |
| |
| class C: |
| pass |
| |
| import six |
| class B(six.with_metaclass(A, C)): |
| pass |
| |
| B #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertEqual(inferred.name, "B") |
| self.assertIsInstance(inferred.bases[0], nodes.Call) |
| ancestors = tuple(inferred.ancestors()) |
| self.assertIsInstance(ancestors[0], nodes.ClassDef) |
| self.assertEqual(ancestors[0].name, "C") |
| self.assertIsInstance(ancestors[1], nodes.ClassDef) |
| self.assertEqual(ancestors[1].name, "object") |
| |
| def test_six_with_metaclass_with_additional_transform(self) -> None: |
| def transform_class(cls: Any) -> ClassDef: |
| if cls.name == "A": |
| cls._test_transform = 314 |
| return cls |
| |
| MANAGER.register_transform(nodes.ClassDef, transform_class) |
| try: |
| ast_node = builder.extract_node( |
| """ |
| import six |
| class A(six.with_metaclass(type, object)): |
| pass |
| |
| A #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| assert getattr(inferred, "_test_transform", None) == 314 |
| finally: |
| MANAGER.unregister_transform(nodes.ClassDef, transform_class) |
| |
| |
| @unittest.skipUnless( |
| HAS_MULTIPROCESSING, |
| "multiprocesing is required for this test, but " |
| "on some platforms it is missing " |
| "(Jython for instance)", |
| ) |
| class MultiprocessingBrainTest(unittest.TestCase): |
| def test_multiprocessing_module_attributes(self) -> None: |
| # Test that module attributes are working, |
| # especially on Python 3.4+, where they are obtained |
| # from a context. |
| module = builder.extract_node( |
| """ |
| import multiprocessing |
| """ |
| ) |
| assert isinstance(module, nodes.Import) |
| module = module.do_import_module("multiprocessing") |
| cpu_count = next(module.igetattr("cpu_count")) |
| self.assertIsInstance(cpu_count, astroid.BoundMethod) |
| |
| def test_module_name(self) -> None: |
| module = builder.extract_node( |
| """ |
| import multiprocessing |
| multiprocessing.SyncManager() |
| """ |
| ) |
| inferred_sync_mgr = next(module.infer()) |
| module = inferred_sync_mgr.root() |
| self.assertEqual(module.name, "multiprocessing.managers") |
| |
| def test_multiprocessing_manager(self) -> None: |
| # Test that we have the proper attributes |
| # for a multiprocessing.managers.SyncManager |
| module = builder.parse( |
| """ |
| import multiprocessing |
| manager = multiprocessing.Manager() |
| queue = manager.Queue() |
| joinable_queue = manager.JoinableQueue() |
| event = manager.Event() |
| rlock = manager.RLock() |
| bounded_semaphore = manager.BoundedSemaphore() |
| condition = manager.Condition() |
| barrier = manager.Barrier() |
| pool = manager.Pool() |
| list = manager.list() |
| dict = manager.dict() |
| value = manager.Value() |
| array = manager.Array() |
| namespace = manager.Namespace() |
| """ |
| ) |
| ast_queue = next(module["queue"].infer()) |
| self.assertEqual(ast_queue.qname(), f"{queue.__name__}.Queue") |
| |
| joinable_queue = next(module["joinable_queue"].infer()) |
| self.assertEqual(joinable_queue.qname(), f"{queue.__name__}.Queue") |
| |
| event = next(module["event"].infer()) |
| event_name = "threading.Event" |
| self.assertEqual(event.qname(), event_name) |
| |
| rlock = next(module["rlock"].infer()) |
| rlock_name = "threading._RLock" |
| self.assertEqual(rlock.qname(), rlock_name) |
| |
| bounded_semaphore = next(module["bounded_semaphore"].infer()) |
| semaphore_name = "threading.BoundedSemaphore" |
| self.assertEqual(bounded_semaphore.qname(), semaphore_name) |
| |
| pool = next(module["pool"].infer()) |
| pool_name = "multiprocessing.pool.Pool" |
| self.assertEqual(pool.qname(), pool_name) |
| |
| for attr in ("list", "dict"): |
| obj = next(module[attr].infer()) |
| self.assertEqual(obj.qname(), f"builtins.{attr}") |
| |
| # pypy's implementation of array.__spec__ return None. This causes problems for this inference. |
| if not hasattr(sys, "pypy_version_info"): |
| array = next(module["array"].infer()) |
| self.assertEqual(array.qname(), "array.array") |
| |
| manager = next(module["manager"].infer()) |
| # Verify that we have these attributes |
| self.assertTrue(manager.getattr("start")) |
| self.assertTrue(manager.getattr("shutdown")) |
| |
| |
| class ThreadingBrainTest(unittest.TestCase): |
| def test_lock(self) -> None: |
| lock_instance = builder.extract_node( |
| """ |
| import threading |
| threading.Lock() |
| """ |
| ) |
| inferred = next(lock_instance.infer()) |
| self.assert_is_valid_lock(inferred) |
| |
| acquire_method = inferred.getattr("acquire")[0] |
| parameters = [param.name for param in acquire_method.args.args[1:]] |
| assert parameters == ["blocking", "timeout"] |
| |
| assert inferred.getattr("locked") |
| |
| def test_rlock(self) -> None: |
| self._test_lock_object("RLock") |
| |
| def test_semaphore(self) -> None: |
| self._test_lock_object("Semaphore") |
| |
| def test_boundedsemaphore(self) -> None: |
| self._test_lock_object("BoundedSemaphore") |
| |
| def _test_lock_object(self, object_name: str) -> None: |
| lock_instance = builder.extract_node( |
| f""" |
| import threading |
| threading.{object_name}() |
| """ |
| ) |
| inferred = next(lock_instance.infer()) |
| self.assert_is_valid_lock(inferred) |
| |
| def assert_is_valid_lock(self, inferred: Instance) -> None: |
| self.assertIsInstance(inferred, astroid.Instance) |
| self.assertEqual(inferred.root().name, "threading") |
| for method in ("acquire", "release", "__enter__", "__exit__"): |
| self.assertIsInstance(next(inferred.igetattr(method)), astroid.BoundMethod) |
| |
| |
| class EnumBrainTest(unittest.TestCase): |
| def test_simple_enum(self) -> None: |
| module = builder.parse( |
| """ |
| import enum |
| |
| class MyEnum(enum.Enum): |
| one = "one" |
| two = "two" |
| |
| def mymethod(self, x): |
| return 5 |
| |
| """ |
| ) |
| |
| enumeration = next(module["MyEnum"].infer()) |
| one = enumeration["one"] |
| self.assertEqual(one.pytype(), ".MyEnum.one") |
| |
| for propname in ("name", "value"): |
| prop = next(iter(one.getattr(propname))) |
| self.assertIn("builtins.property", prop.decoratornames()) |
| |
| meth = one.getattr("mymethod")[0] |
| self.assertIsInstance(meth, astroid.FunctionDef) |
| |
| def test_looks_like_enum_false_positive(self) -> None: |
| # Test that a class named Enumeration is not considered a builtin enum. |
| module = builder.parse( |
| """ |
| class Enumeration(object): |
| def __init__(self, name, enum_list): |
| pass |
| test = 42 |
| """ |
| ) |
| enumeration = module["Enumeration"] |
| test = next(enumeration.igetattr("test")) |
| self.assertEqual(test.value, 42) |
| |
| def test_user_enum_false_positive(self) -> None: |
| # Test that a user-defined class named Enum is not considered a builtin enum. |
| ast_node = astroid.extract_node( |
| """ |
| class Enum: |
| pass |
| |
| class Color(Enum): |
| red = 1 |
| |
| Color.red #@ |
| """ |
| ) |
| assert isinstance(ast_node, nodes.NodeNG) |
| inferred = ast_node.inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], astroid.Const) |
| self.assertEqual(inferred[0].value, 1) |
| |
| def test_ignores_with_nodes_from_body_of_enum(self) -> None: |
| code = """ |
| import enum |
| |
| class Error(enum.Enum): |
| Foo = "foo" |
| Bar = "bar" |
| with "error" as err: |
| pass |
| """ |
| node = builder.extract_node(code) |
| inferred = next(node.infer()) |
| assert "err" in inferred.locals |
| assert len(inferred.locals["err"]) == 1 |
| |
| def test_enum_multiple_base_classes(self) -> None: |
| module = builder.parse( |
| """ |
| import enum |
| |
| class Mixin: |
| pass |
| |
| class MyEnum(Mixin, enum.Enum): |
| one = 1 |
| """ |
| ) |
| enumeration = next(module["MyEnum"].infer()) |
| one = enumeration["one"] |
| |
| clazz = one.getattr("__class__")[0] |
| self.assertTrue( |
| clazz.is_subtype_of(".Mixin"), |
| "Enum instance should share base classes with generating class", |
| ) |
| |
| def test_int_enum(self) -> None: |
| module = builder.parse( |
| """ |
| import enum |
| |
| class MyEnum(enum.IntEnum): |
| one = 1 |
| """ |
| ) |
| |
| enumeration = next(module["MyEnum"].infer()) |
| one = enumeration["one"] |
| |
| clazz = one.getattr("__class__")[0] |
| self.assertTrue( |
| clazz.is_subtype_of("builtins.int"), |
| "IntEnum based enums should be a subtype of int", |
| ) |
| |
| def test_enum_func_form_is_class_not_instance(self) -> None: |
| cls, instance = builder.extract_node( |
| """ |
| from enum import Enum |
| f = Enum('Audience', ['a', 'b', 'c']) |
| f #@ |
| f(1) #@ |
| """ |
| ) |
| inferred_cls = next(cls.infer()) |
| self.assertIsInstance(inferred_cls, bases.Instance) |
| inferred_instance = next(instance.infer()) |
| self.assertIsInstance(inferred_instance, bases.Instance) |
| self.assertIsInstance(next(inferred_instance.igetattr("name")), nodes.Const) |
| self.assertIsInstance(next(inferred_instance.igetattr("value")), nodes.Const) |
| |
| def test_enum_func_form_iterable(self) -> None: |
| instance = builder.extract_node( |
| """ |
| from enum import Enum |
| Animal = Enum('Animal', 'ant bee cat dog') |
| Animal |
| """ |
| ) |
| inferred = next(instance.infer()) |
| self.assertIsInstance(inferred, astroid.Instance) |
| self.assertTrue(inferred.getattr("__iter__")) |
| |
| def test_enum_func_form_subscriptable(self) -> None: |
| instance, name = builder.extract_node( |
| """ |
| from enum import Enum |
| Animal = Enum('Animal', 'ant bee cat dog') |
| Animal['ant'] #@ |
| Animal['ant'].name #@ |
| """ |
| ) |
| instance = next(instance.infer()) |
| self.assertIsInstance(instance, astroid.Instance) |
| |
| inferred = next(name.infer()) |
| self.assertIsInstance(inferred, astroid.Const) |
| |
| def test_enum_func_form_has_dunder_members(self) -> None: |
| instance = builder.extract_node( |
| """ |
| from enum import Enum |
| Animal = Enum('Animal', 'ant bee cat dog') |
| for i in Animal.__members__: |
| i #@ |
| """ |
| ) |
| instance = next(instance.infer()) |
| self.assertIsInstance(instance, astroid.Const) |
| self.assertIsInstance(instance.value, str) |
| |
| def test_infer_enum_value_as_the_right_type(self) -> None: |
| string_value, int_value = builder.extract_node( |
| """ |
| from enum import Enum |
| class A(Enum): |
| a = 'a' |
| b = 1 |
| A.a.value #@ |
| A.b.value #@ |
| """ |
| ) |
| inferred_string = string_value.inferred() |
| assert any( |
| isinstance(elem, astroid.Const) and elem.value == "a" |
| for elem in inferred_string |
| ) |
| |
| inferred_int = int_value.inferred() |
| assert any( |
| isinstance(elem, astroid.Const) and elem.value == 1 for elem in inferred_int |
| ) |
| |
| def test_mingled_single_and_double_quotes_does_not_crash(self) -> None: |
| node = builder.extract_node( |
| """ |
| from enum import Enum |
| class A(Enum): |
| a = 'x"y"' |
| A.a.value #@ |
| """ |
| ) |
| inferred_string = next(node.infer()) |
| assert inferred_string.value == 'x"y"' |
| |
| def test_special_characters_does_not_crash(self) -> None: |
| node = builder.extract_node( |
| """ |
| import enum |
| class Example(enum.Enum): |
| NULL = '\\N{NULL}' |
| Example.NULL.value |
| """ |
| ) |
| inferred_string = next(node.infer()) |
| assert inferred_string.value == "\N{NULL}" |
| |
| def test_dont_crash_on_for_loops_in_body(self) -> None: |
| node = builder.extract_node( |
| """ |
| |
| class Commands(IntEnum): |
| _ignore_ = 'Commands index' |
| _init_ = 'value string' |
| |
| BEL = 0x07, 'Bell' |
| Commands = vars() |
| for index in range(4): |
| Commands[f'DC{index + 1}'] = 0x11 + index, f'Device Control {index + 1}' |
| |
| Commands |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.ClassDef) |
| |
| def test_enum_tuple_list_values(self) -> None: |
| tuple_node, list_node = builder.extract_node( |
| """ |
| import enum |
| |
| class MyEnum(enum.Enum): |
| a = (1, 2) |
| b = [2, 4] |
| MyEnum.a.value #@ |
| MyEnum.b.value #@ |
| """ |
| ) |
| inferred_tuple_node = next(tuple_node.infer()) |
| inferred_list_node = next(list_node.infer()) |
| assert isinstance(inferred_tuple_node, astroid.Tuple) |
| assert isinstance(inferred_list_node, astroid.List) |
| assert inferred_tuple_node.as_string() == "(1, 2)" |
| assert inferred_list_node.as_string() == "[2, 4]" |
| |
| def test_enum_starred_is_skipped(self) -> None: |
| code = """ |
| from enum import Enum |
| class ContentType(Enum): |
| TEXT, PHOTO, VIDEO, GIF, YOUTUBE, *_ = [1, 2, 3, 4, 5, 6] |
| ContentType.TEXT #@ |
| """ |
| node = astroid.extract_node(code) |
| next(node.infer()) |
| |
| def test_enum_name_is_str_on_self(self) -> None: |
| code = """ |
| from enum import Enum |
| class TestEnum(Enum): |
| def func(self): |
| self.name #@ |
| self.value #@ |
| TestEnum.name #@ |
| TestEnum.value #@ |
| """ |
| i_name, i_value, c_name, c_value = astroid.extract_node(code) |
| |
| # <instance>.name should be a string, <class>.name should be a property (that |
| # forwards the lookup to __getattr__) |
| inferred = next(i_name.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.pytype() == "builtins.str" |
| inferred = next(c_name.infer()) |
| assert isinstance(inferred, objects.Property) |
| |
| # Inferring .value should not raise InferenceError. It is probably Uninferable |
| # but we don't particularly care |
| next(i_value.infer()) |
| next(c_value.infer()) |
| |
| def test_enum_name_and_value_members_override_dynamicclassattr(self) -> None: |
| code = """ |
| from enum import Enum |
| class TrickyEnum(Enum): |
| name = 1 |
| value = 2 |
| |
| def func(self): |
| self.name #@ |
| self.value #@ |
| TrickyEnum.name #@ |
| TrickyEnum.value #@ |
| """ |
| i_name, i_value, c_name, c_value = astroid.extract_node(code) |
| |
| # All of these cases should be inferred as enum members |
| inferred = next(i_name.infer()) |
| assert isinstance(inferred, bases.Instance) |
| assert inferred.pytype() == ".TrickyEnum.name" |
| inferred = next(c_name.infer()) |
| assert isinstance(inferred, bases.Instance) |
| assert inferred.pytype() == ".TrickyEnum.name" |
| inferred = next(i_value.infer()) |
| assert isinstance(inferred, bases.Instance) |
| assert inferred.pytype() == ".TrickyEnum.value" |
| inferred = next(c_value.infer()) |
| assert isinstance(inferred, bases.Instance) |
| assert inferred.pytype() == ".TrickyEnum.value" |
| |
| def test_enum_subclass_member_name(self) -> None: |
| ast_node = astroid.extract_node( |
| """ |
| from enum import Enum |
| |
| class EnumSubclass(Enum): |
| pass |
| |
| class Color(EnumSubclass): |
| red = 1 |
| |
| Color.red.name #@ |
| """ |
| ) |
| assert isinstance(ast_node, nodes.NodeNG) |
| inferred = ast_node.inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], astroid.Const) |
| self.assertEqual(inferred[0].value, "red") |
| |
| def test_enum_subclass_member_value(self) -> None: |
| ast_node = astroid.extract_node( |
| """ |
| from enum import Enum |
| |
| class EnumSubclass(Enum): |
| pass |
| |
| class Color(EnumSubclass): |
| red = 1 |
| |
| Color.red.value #@ |
| """ |
| ) |
| assert isinstance(ast_node, nodes.NodeNG) |
| inferred = ast_node.inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], astroid.Const) |
| self.assertEqual(inferred[0].value, 1) |
| |
| def test_enum_subclass_member_method(self) -> None: |
| # See Pylint issue #2626 |
| ast_node = astroid.extract_node( |
| """ |
| from enum import Enum |
| |
| class EnumSubclass(Enum): |
| def hello_pylint(self) -> str: |
| return self.name |
| |
| class Color(EnumSubclass): |
| red = 1 |
| |
| Color.red.hello_pylint() #@ |
| """ |
| ) |
| assert isinstance(ast_node, nodes.NodeNG) |
| inferred = ast_node.inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], astroid.Const) |
| self.assertEqual(inferred[0].value, "red") |
| |
| def test_enum_subclass_different_modules(self) -> None: |
| # See Pylint issue #2626 |
| astroid.extract_node( |
| """ |
| from enum import Enum |
| |
| class EnumSubclass(Enum): |
| pass |
| """, |
| "a", |
| ) |
| ast_node = astroid.extract_node( |
| """ |
| from a import EnumSubclass |
| |
| class Color(EnumSubclass): |
| red = 1 |
| |
| Color.red.value #@ |
| """ |
| ) |
| assert isinstance(ast_node, nodes.NodeNG) |
| inferred = ast_node.inferred() |
| self.assertEqual(len(inferred), 1) |
| self.assertIsInstance(inferred[0], astroid.Const) |
| self.assertEqual(inferred[0].value, 1) |
| |
| def test_members_member_ignored(self) -> None: |
| ast_node = builder.extract_node( |
| """ |
| from enum import Enum |
| class Animal(Enum): |
| a = 1 |
| __members__ = {} |
| Animal.__members__ #@ |
| """ |
| ) |
| |
| inferred = next(ast_node.infer()) |
| self.assertIsInstance(inferred, astroid.Dict) |
| self.assertTrue(inferred.locals) |
| |
| |
| @unittest.skipUnless(HAS_DATEUTIL, "This test requires the dateutil library.") |
| class DateutilBrainTest(unittest.TestCase): |
| def test_parser(self): |
| module = builder.parse( |
| """ |
| from dateutil.parser import parse |
| d = parse('2000-01-01') |
| """ |
| ) |
| d_type = next(module["d"].infer()) |
| self.assertEqual(d_type.qname(), "datetime.datetime") |
| |
| |
| class PytestBrainTest(unittest.TestCase): |
| def test_pytest(self) -> None: |
| ast_node = builder.extract_node( |
| """ |
| import pytest |
| pytest #@ |
| """ |
| ) |
| module = next(ast_node.infer()) |
| attrs = [ |
| "deprecated_call", |
| "warns", |
| "exit", |
| "fail", |
| "skip", |
| "importorskip", |
| "xfail", |
| "mark", |
| "raises", |
| "freeze_includes", |
| "set_trace", |
| "fixture", |
| "yield_fixture", |
| ] |
| for attr in attrs: |
| self.assertIn(attr, module) |
| |
| |
| def streams_are_fine(): |
| """Check if streams are being overwritten, |
| for example, by pytest |
| |
| stream inference will not work if they are overwritten |
| |
| PY3 only |
| """ |
| for stream in (sys.stdout, sys.stderr, sys.stdin): |
| if not isinstance(stream, io.IOBase): |
| return False |
| return True |
| |
| |
| class IOBrainTest(unittest.TestCase): |
| @unittest.skipUnless( |
| streams_are_fine(), |
| "Needs Python 3 io model / doesn't work with plain pytest." |
| "use pytest -s for this test to work", |
| ) |
| def test_sys_streams(self): |
| for name in ("stdout", "stderr", "stdin"): |
| node = astroid.extract_node( |
| f""" |
| import sys |
| sys.{name} |
| """ |
| ) |
| inferred = next(node.infer()) |
| buffer_attr = next(inferred.igetattr("buffer")) |
| self.assertIsInstance(buffer_attr, astroid.Instance) |
| self.assertEqual(buffer_attr.name, "BufferedWriter") |
| raw = next(buffer_attr.igetattr("raw")) |
| self.assertIsInstance(raw, astroid.Instance) |
| self.assertEqual(raw.name, "FileIO") |
| |
| |
| @test_utils.require_version("3.9") |
| class TypeBrain(unittest.TestCase): |
| def test_type_subscript(self): |
| """ |
| Check that type object has the __class_getitem__ method |
| when it is used as a subscript |
| """ |
| src = builder.extract_node( |
| """ |
| a: type[int] = int |
| """ |
| ) |
| val_inf = src.annotation.value.inferred()[0] |
| self.assertIsInstance(val_inf, astroid.ClassDef) |
| self.assertEqual(val_inf.name, "type") |
| meth_inf = val_inf.getattr("__class_getitem__")[0] |
| self.assertIsInstance(meth_inf, astroid.FunctionDef) |
| |
| def test_invalid_type_subscript(self): |
| """ |
| Check that a type (str for example) that inherits |
| from type does not have __class_getitem__ method even |
| when it is used as a subscript |
| """ |
| src = builder.extract_node( |
| """ |
| a: str[int] = "abc" |
| """ |
| ) |
| val_inf = src.annotation.value.inferred()[0] |
| self.assertIsInstance(val_inf, astroid.ClassDef) |
| self.assertEqual(val_inf.name, "str") |
| with self.assertRaises(AttributeInferenceError): |
| # pylint: disable=expression-not-assigned |
| # noinspection PyStatementEffect |
| val_inf.getattr("__class_getitem__")[0] |
| |
| @test_utils.require_version(minver="3.9") |
| def test_builtin_subscriptable(self): |
| """ |
| Starting with python3.9 builtin type such as list are subscriptable |
| """ |
| for typename in ("tuple", "list", "dict", "set", "frozenset"): |
| src = f""" |
| {typename:s}[int] |
| """ |
| right_node = builder.extract_node(src) |
| inferred = next(right_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertIsInstance(inferred.getattr("__iter__")[0], nodes.FunctionDef) |
| |
| |
| def check_metaclass_is_abc(node: nodes.ClassDef): |
| meta = node.metaclass() |
| assert isinstance(meta, nodes.ClassDef) |
| assert meta.name == "ABCMeta" |
| |
| |
| class CollectionsBrain(unittest.TestCase): |
| def test_collections_object_not_subscriptable(self) -> None: |
| """ |
| Test that unsubscriptable types are detected |
| Hashable is not subscriptable even with python39 |
| """ |
| wrong_node = builder.extract_node( |
| """ |
| import collections.abc |
| collections.abc.Hashable[int] |
| """ |
| ) |
| with self.assertRaises(InferenceError): |
| next(wrong_node.infer()) |
| right_node = builder.extract_node( |
| """ |
| import collections.abc |
| collections.abc.Hashable |
| """ |
| ) |
| inferred = next(right_node.infer()) |
| check_metaclass_is_abc(inferred) |
| assertEqualMro( |
| inferred, |
| [ |
| "_collections_abc.Hashable", |
| "builtins.object", |
| ], |
| ) |
| with self.assertRaises(AttributeInferenceError): |
| inferred.getattr("__class_getitem__") |
| |
| @test_utils.require_version(minver="3.9") |
| def test_collections_object_subscriptable(self): |
| """Starting with python39 some object of collections module are subscriptable. Test one of them""" |
| right_node = builder.extract_node( |
| """ |
| import collections.abc |
| collections.abc.MutableSet[int] |
| """ |
| ) |
| inferred = next(right_node.infer()) |
| check_metaclass_is_abc(inferred) |
| assertEqualMro( |
| inferred, |
| [ |
| "_collections_abc.MutableSet", |
| "_collections_abc.Set", |
| "_collections_abc.Collection", |
| "_collections_abc.Sized", |
| "_collections_abc.Iterable", |
| "_collections_abc.Container", |
| "builtins.object", |
| ], |
| ) |
| self.assertIsInstance( |
| inferred.getattr("__class_getitem__")[0], nodes.FunctionDef |
| ) |
| |
| @test_utils.require_version(maxver="3.9") |
| def test_collections_object_not_yet_subscriptable(self): |
| """ |
| Test that unsubscriptable types are detected as such. |
| Until python39 MutableSet of the collections module is not subscriptable. |
| """ |
| wrong_node = builder.extract_node( |
| """ |
| import collections.abc |
| collections.abc.MutableSet[int] |
| """ |
| ) |
| with self.assertRaises(InferenceError): |
| next(wrong_node.infer()) |
| right_node = builder.extract_node( |
| """ |
| import collections.abc |
| collections.abc.MutableSet |
| """ |
| ) |
| inferred = next(right_node.infer()) |
| check_metaclass_is_abc(inferred) |
| assertEqualMro( |
| inferred, |
| [ |
| "_collections_abc.MutableSet", |
| "_collections_abc.Set", |
| "_collections_abc.Collection", |
| "_collections_abc.Sized", |
| "_collections_abc.Iterable", |
| "_collections_abc.Container", |
| "builtins.object", |
| ], |
| ) |
| with self.assertRaises(AttributeInferenceError): |
| inferred.getattr("__class_getitem__") |
| |
| @test_utils.require_version(minver="3.9") |
| def test_collections_object_subscriptable_2(self): |
| """Starting with python39 Iterator in the collection.abc module is subscriptable""" |
| node = builder.extract_node( |
| """ |
| import collections.abc |
| class Derived(collections.abc.Iterator[int]): |
| pass |
| """ |
| ) |
| inferred = next(node.infer()) |
| check_metaclass_is_abc(inferred) |
| assertEqualMro( |
| inferred, |
| [ |
| ".Derived", |
| "_collections_abc.Iterator", |
| "_collections_abc.Iterable", |
| "builtins.object", |
| ], |
| ) |
| |
| @test_utils.require_version(maxver="3.9") |
| def test_collections_object_not_yet_subscriptable_2(self): |
| """Before python39 Iterator in the collection.abc module is not subscriptable""" |
| node = builder.extract_node( |
| """ |
| import collections.abc |
| collections.abc.Iterator[int] |
| """ |
| ) |
| with self.assertRaises(InferenceError): |
| next(node.infer()) |
| |
| @test_utils.require_version(minver="3.9") |
| def test_collections_object_subscriptable_3(self): |
| """With python39 ByteString class of the colletions module is subscritable (but not the same class from typing module)""" |
| right_node = builder.extract_node( |
| """ |
| import collections.abc |
| collections.abc.ByteString[int] |
| """ |
| ) |
| inferred = next(right_node.infer()) |
| check_metaclass_is_abc(inferred) |
| self.assertIsInstance( |
| inferred.getattr("__class_getitem__")[0], nodes.FunctionDef |
| ) |
| |
| @test_utils.require_version(minver="3.9") |
| def test_collections_object_subscriptable_4(self): |
| """Multiple inheritance with subscriptable collection class""" |
| node = builder.extract_node( |
| """ |
| import collections.abc |
| class Derived(collections.abc.Hashable, collections.abc.Iterator[int]): |
| pass |
| """ |
| ) |
| inferred = next(node.infer()) |
| assertEqualMro( |
| inferred, |
| [ |
| ".Derived", |
| "_collections_abc.Hashable", |
| "_collections_abc.Iterator", |
| "_collections_abc.Iterable", |
| "builtins.object", |
| ], |
| ) |
| |
| |
| class TypingBrain(unittest.TestCase): |
| def test_namedtuple_base(self) -> None: |
| klass = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| |
| class X(NamedTuple("X", [("a", int), ("b", str), ("c", bytes)])): |
| pass |
| """ |
| ) |
| self.assertEqual( |
| [anc.name for anc in klass.ancestors()], ["X", "tuple", "object"] |
| ) |
| for anc in klass.ancestors(): |
| self.assertFalse(anc.parent is None) |
| |
| def test_namedtuple_can_correctly_access_methods(self) -> None: |
| klass, called = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| |
| class X(NamedTuple): #@ |
| a: int |
| b: int |
| def as_string(self): |
| return '%s' % self.a |
| def as_integer(self): |
| return 2 + 3 |
| X().as_integer() #@ |
| """ |
| ) |
| self.assertEqual(len(klass.getattr("as_string")), 1) |
| inferred = next(called.infer()) |
| self.assertIsInstance(inferred, astroid.Const) |
| self.assertEqual(inferred.value, 5) |
| |
| def test_namedtuple_inference(self) -> None: |
| klass = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| |
| class X(NamedTuple("X", [("a", int), ("b", str), ("c", bytes)])): |
| pass |
| """ |
| ) |
| base = next(base for base in klass.ancestors() if base.name == "X") |
| self.assertSetEqual({"a", "b", "c"}, set(base.instance_attrs)) |
| |
| def test_namedtuple_inference_nonliteral(self) -> None: |
| # Note: NamedTuples in mypy only work with literals. |
| klass = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| |
| name = "X" |
| fields = [("a", int), ("b", str), ("c", bytes)] |
| NamedTuple(name, fields) |
| """ |
| ) |
| inferred = next(klass.infer()) |
| self.assertIsInstance(inferred, astroid.Instance) |
| self.assertEqual(inferred.qname(), "typing.NamedTuple") |
| |
| def test_namedtuple_instance_attrs(self) -> None: |
| result = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| NamedTuple("A", [("a", int), ("b", str), ("c", bytes)])(1, 2, 3) #@ |
| """ |
| ) |
| inferred = next(result.infer()) |
| for name, attr in inferred.instance_attrs.items(): |
| self.assertEqual(attr[0].attrname, name) |
| |
| def test_namedtuple_simple(self) -> None: |
| result = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| NamedTuple("A", [("a", int), ("b", str), ("c", bytes)]) |
| """ |
| ) |
| inferred = next(result.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertSetEqual({"a", "b", "c"}, set(inferred.instance_attrs)) |
| |
| def test_namedtuple_few_args(self) -> None: |
| result = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| NamedTuple("A") |
| """ |
| ) |
| inferred = next(result.infer()) |
| self.assertIsInstance(inferred, astroid.Instance) |
| self.assertEqual(inferred.qname(), "typing.NamedTuple") |
| |
| def test_namedtuple_few_fields(self) -> None: |
| result = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| NamedTuple("A", [("a",), ("b", str), ("c", bytes)]) |
| """ |
| ) |
| inferred = next(result.infer()) |
| self.assertIsInstance(inferred, astroid.Instance) |
| self.assertEqual(inferred.qname(), "typing.NamedTuple") |
| |
| def test_namedtuple_class_form(self) -> None: |
| result = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| |
| class Example(NamedTuple): |
| CLASS_ATTR = "class_attr" |
| mything: int |
| |
| Example(mything=1) |
| """ |
| ) |
| inferred = next(result.infer()) |
| self.assertIsInstance(inferred, astroid.Instance) |
| |
| class_attr = inferred.getattr("CLASS_ATTR")[0] |
| self.assertIsInstance(class_attr, astroid.AssignName) |
| const = next(class_attr.infer()) |
| self.assertEqual(const.value, "class_attr") |
| |
| def test_namedtuple_inferred_as_class(self) -> None: |
| node = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| NamedTuple |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.ClassDef) |
| assert inferred.name == "NamedTuple" |
| |
| def test_namedtuple_bug_pylint_4383(self) -> None: |
| """Inference of 'NamedTuple' function shouldn't cause InferenceError. |
| |
| https://github.com/PyCQA/pylint/issues/4383 |
| """ |
| node = builder.extract_node( |
| """ |
| if True: |
| def NamedTuple(): |
| pass |
| NamedTuple |
| """ |
| ) |
| next(node.infer()) |
| |
| def test_typing_types(self) -> None: |
| ast_nodes = builder.extract_node( |
| """ |
| from typing import TypeVar, Iterable, Tuple, NewType, Dict, Union |
| TypeVar('MyTypeVar', int, float, complex) #@ |
| Iterable[Tuple[MyTypeVar, MyTypeVar]] #@ |
| TypeVar('AnyStr', str, bytes) #@ |
| NewType('UserId', str) #@ |
| Dict[str, str] #@ |
| Union[int, str] #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef, node.as_string()) |
| |
| def test_namedtuple_nested_class(self): |
| result = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| |
| class Example(NamedTuple): |
| class Foo: |
| bar = "bar" |
| |
| Example |
| """ |
| ) |
| inferred = next(result.infer()) |
| self.assertIsInstance(inferred, astroid.ClassDef) |
| |
| class_def_attr = inferred.getattr("Foo")[0] |
| self.assertIsInstance(class_def_attr, astroid.ClassDef) |
| attr_def = class_def_attr.getattr("bar")[0] |
| attr = next(attr_def.infer()) |
| self.assertEqual(attr.value, "bar") |
| |
| @test_utils.require_version(minver="3.7") |
| def test_tuple_type(self): |
| node = builder.extract_node( |
| """ |
| from typing import Tuple |
| Tuple[int, int] |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.ClassDef) |
| assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef) |
| assert inferred.qname() == "typing.Tuple" |
| |
| @test_utils.require_version(minver="3.7") |
| def test_callable_type(self): |
| node = builder.extract_node( |
| """ |
| from typing import Callable, Any |
| Callable[..., Any] |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.ClassDef) |
| assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef) |
| assert inferred.qname() == "typing.Callable" |
| |
| @test_utils.require_version(minver="3.7") |
| def test_typing_generic_subscriptable(self): |
| """Test typing.Generic is subscriptable with __class_getitem__ (added in PY37)""" |
| node = builder.extract_node( |
| """ |
| from typing import Generic, TypeVar |
| T = TypeVar('T') |
| Generic[T] |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.ClassDef) |
| assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef) |
| |
| @test_utils.require_version(minver="3.9") |
| def test_typing_annotated_subscriptable(self): |
| """Test typing.Annotated is subscriptable with __class_getitem__""" |
| node = builder.extract_node( |
| """ |
| import typing |
| typing.Annotated[str, "data"] |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.ClassDef) |
| assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef) |
| |
| @test_utils.require_version(minver="3.7") |
| def test_typing_generic_slots(self): |
| """Test slots for Generic subclass.""" |
| node = builder.extract_node( |
| """ |
| from typing import Generic, TypeVar |
| T = TypeVar('T') |
| class A(Generic[T]): |
| __slots__ = ['value'] |
| def __init__(self, value): |
| self.value = value |
| """ |
| ) |
| inferred = next(node.infer()) |
| slots = inferred.slots() |
| assert len(slots) == 1 |
| assert isinstance(slots[0], nodes.Const) |
| assert slots[0].value == "value" |
| |
| def test_has_dunder_args(self) -> None: |
| ast_node = builder.extract_node( |
| """ |
| from typing import Union |
| NumericTypes = Union[int, float] |
| NumericTypes.__args__ #@ |
| """ |
| ) |
| inferred = next(ast_node.infer()) |
| assert isinstance(inferred, nodes.Tuple) |
| |
| def test_typing_namedtuple_dont_crash_on_no_fields(self) -> None: |
| node = builder.extract_node( |
| """ |
| from typing import NamedTuple |
| |
| Bar = NamedTuple("bar", []) |
| |
| Bar() |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, astroid.Instance) |
| |
| @test_utils.require_version("3.8") |
| def test_typed_dict(self): |
| code = builder.extract_node( |
| """ |
| from typing import TypedDict |
| class CustomTD(TypedDict): #@ |
| var: int |
| CustomTD(var=1) #@ |
| """ |
| ) |
| inferred_base = next(code[0].bases[0].infer()) |
| assert isinstance(inferred_base, nodes.ClassDef) |
| assert inferred_base.qname() == "typing.TypedDict" |
| typedDict_base = next(inferred_base.bases[0].infer()) |
| assert typedDict_base.qname() == "builtins.dict" |
| |
| # Test TypedDict has `__call__` method |
| local_call = inferred_base.locals.get("__call__", None) |
| assert local_call and len(local_call) == 1 |
| assert isinstance(local_call[0], nodes.Name) and local_call[0].name == "dict" |
| |
| # Test TypedDict instance is callable |
| assert next(code[1].infer()).callable() is True |
| |
| @test_utils.require_version(minver="3.7") |
| def test_typing_alias_type(self): |
| """ |
| Test that the type aliased thanks to typing._alias function are |
| correctly inferred. |
| typing_alias function is introduced with python37 |
| """ |
| node = builder.extract_node( |
| """ |
| from typing import TypeVar, MutableSet |
| |
| T = TypeVar("T") |
| MutableSet[T] |
| |
| class Derived1(MutableSet[T]): |
| pass |
| """ |
| ) |
| inferred = next(node.infer()) |
| assertEqualMro( |
| inferred, |
| [ |
| ".Derived1", |
| "typing.MutableSet", |
| "_collections_abc.MutableSet", |
| "_collections_abc.Set", |
| "_collections_abc.Collection", |
| "_collections_abc.Sized", |
| "_collections_abc.Iterable", |
| "_collections_abc.Container", |
| "builtins.object", |
| ], |
| ) |
| |
| @test_utils.require_version(minver="3.7.2") |
| def test_typing_alias_type_2(self): |
| """ |
| Test that the type aliased thanks to typing._alias function are |
| correctly inferred. |
| typing_alias function is introduced with python37. |
| OrderedDict in the typing module appears only with python 3.7.2 |
| """ |
| node = builder.extract_node( |
| """ |
| import typing |
| class Derived2(typing.OrderedDict[int, str]): |
| pass |
| """ |
| ) |
| inferred = next(node.infer()) |
| assertEqualMro( |
| inferred, |
| [ |
| ".Derived2", |
| "typing.OrderedDict", |
| "collections.OrderedDict", |
| "builtins.dict", |
| "builtins.object", |
| ], |
| ) |
| |
| @test_utils.require_version(minver="3.7") |
| def test_typing_object_not_subscriptable(self): |
| """Hashable is not subscriptable""" |
| wrong_node = builder.extract_node( |
| """ |
| import typing |
| typing.Hashable[int] |
| """ |
| ) |
| with self.assertRaises(InferenceError): |
| next(wrong_node.infer()) |
| right_node = builder.extract_node( |
| """ |
| import typing |
| typing.Hashable |
| """ |
| ) |
| inferred = next(right_node.infer()) |
| assertEqualMro( |
| inferred, |
| [ |
| "typing.Hashable", |
| "_collections_abc.Hashable", |
| "builtins.object", |
| ], |
| ) |
| with self.assertRaises(AttributeInferenceError): |
| inferred.getattr("__class_getitem__") |
| |
| @test_utils.require_version(minver="3.7") |
| def test_typing_object_subscriptable(self): |
| """Test that MutableSet is subscriptable""" |
| right_node = builder.extract_node( |
| """ |
| import typing |
| typing.MutableSet[int] |
| """ |
| ) |
| inferred = next(right_node.infer()) |
| assertEqualMro( |
| inferred, |
| [ |
| "typing.MutableSet", |
| "_collections_abc.MutableSet", |
| "_collections_abc.Set", |
| "_collections_abc.Collection", |
| "_collections_abc.Sized", |
| "_collections_abc.Iterable", |
| "_collections_abc.Container", |
| "builtins.object", |
| ], |
| ) |
| self.assertIsInstance( |
| inferred.getattr("__class_getitem__")[0], nodes.FunctionDef |
| ) |
| |
| @test_utils.require_version(minver="3.7") |
| def test_typing_object_subscriptable_2(self): |
| """Multiple inheritance with subscriptable typing alias""" |
| node = builder.extract_node( |
| """ |
| import typing |
| class Derived(typing.Hashable, typing.Iterator[int]): |
| pass |
| """ |
| ) |
| inferred = next(node.infer()) |
| assertEqualMro( |
| inferred, |
| [ |
| ".Derived", |
| "typing.Hashable", |
| "_collections_abc.Hashable", |
| "typing.Iterator", |
| "_collections_abc.Iterator", |
| "_collections_abc.Iterable", |
| "builtins.object", |
| ], |
| ) |
| |
| @test_utils.require_version(minver="3.7") |
| def test_typing_object_notsubscriptable_3(self): |
| """Until python39 ByteString class of the typing module is not subscritable (whereas it is in the collections module)""" |
| right_node = builder.extract_node( |
| """ |
| import typing |
| typing.ByteString |
| """ |
| ) |
| inferred = next(right_node.infer()) |
| check_metaclass_is_abc(inferred) |
| with self.assertRaises(AttributeInferenceError): |
| self.assertIsInstance( |
| inferred.getattr("__class_getitem__")[0], nodes.FunctionDef |
| ) |
| |
| @test_utils.require_version(minver="3.9") |
| def test_typing_object_builtin_subscriptable(self): |
| """ |
| Test that builtins alias, such as typing.List, are subscriptable |
| """ |
| for typename in ("List", "Dict", "Set", "FrozenSet", "Tuple"): |
| src = f""" |
| import typing |
| typing.{typename:s}[int] |
| """ |
| right_node = builder.extract_node(src) |
| inferred = next(right_node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| self.assertIsInstance(inferred.getattr("__iter__")[0], nodes.FunctionDef) |
| |
| @staticmethod |
| @test_utils.require_version(minver="3.9") |
| def test_typing_type_subscriptable(): |
| node = builder.extract_node( |
| """ |
| from typing import Type |
| Type[int] |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.ClassDef) |
| assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef) |
| assert inferred.qname() == "typing.Type" |
| |
| def test_typing_cast(self) -> None: |
| node = builder.extract_node( |
| """ |
| from typing import cast |
| class A: |
| pass |
| |
| b = 42 |
| a = cast(A, b) |
| a |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 42 |
| |
| def test_typing_cast_attribute(self) -> None: |
| node = builder.extract_node( |
| """ |
| import typing |
| class A: |
| pass |
| |
| b = 42 |
| a = typing.cast(A, b) |
| a |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, nodes.Const) |
| assert inferred.value == 42 |
| |
| |
| class ReBrainTest(unittest.TestCase): |
| def test_regex_flags(self) -> None: |
| names = [name for name in dir(re) if name.isupper()] |
| re_ast = MANAGER.ast_from_module_name("re") |
| for name in names: |
| self.assertIn(name, re_ast) |
| self.assertEqual(next(re_ast[name].infer()).value, getattr(re, name)) |
| |
| @test_utils.require_version(minver="3.7", maxver="3.9") |
| def test_re_pattern_unsubscriptable(self): |
| """ |
| re.Pattern and re.Match are unsubscriptable until PY39. |
| re.Pattern and re.Match were added in PY37. |
| """ |
| right_node1 = builder.extract_node( |
| """ |
| import re |
| re.Pattern |
| """ |
| ) |
| inferred1 = next(right_node1.infer()) |
| assert isinstance(inferred1, nodes.ClassDef) |
| with self.assertRaises(AttributeInferenceError): |
| assert isinstance( |
| inferred1.getattr("__class_getitem__")[0], nodes.FunctionDef |
| ) |
| |
| right_node2 = builder.extract_node( |
| """ |
| import re |
| re.Pattern |
| """ |
| ) |
| inferred2 = next(right_node2.infer()) |
| assert isinstance(inferred2, nodes.ClassDef) |
| with self.assertRaises(AttributeInferenceError): |
| assert isinstance( |
| inferred2.getattr("__class_getitem__")[0], nodes.FunctionDef |
| ) |
| |
| wrong_node1 = builder.extract_node( |
| """ |
| import re |
| re.Pattern[int] |
| """ |
| ) |
| with self.assertRaises(InferenceError): |
| next(wrong_node1.infer()) |
| |
| wrong_node2 = builder.extract_node( |
| """ |
| import re |
| re.Match[int] |
| """ |
| ) |
| with self.assertRaises(InferenceError): |
| next(wrong_node2.infer()) |
| |
| @test_utils.require_version(minver="3.9") |
| def test_re_pattern_subscriptable(self): |
| """Test re.Pattern and re.Match are subscriptable in PY39+""" |
| node1 = builder.extract_node( |
| """ |
| import re |
| re.Pattern[str] |
| """ |
| ) |
| inferred1 = next(node1.infer()) |
| assert isinstance(inferred1, nodes.ClassDef) |
| assert isinstance(inferred1.getattr("__class_getitem__")[0], nodes.FunctionDef) |
| |
| node2 = builder.extract_node( |
| """ |
| import re |
| re.Match[str] |
| """ |
| ) |
| inferred2 = next(node2.infer()) |
| assert isinstance(inferred2, nodes.ClassDef) |
| assert isinstance(inferred2.getattr("__class_getitem__")[0], nodes.FunctionDef) |
| |
| |
| class BrainFStrings(unittest.TestCase): |
| def test_no_crash_on_const_reconstruction(self) -> None: |
| node = builder.extract_node( |
| """ |
| max_width = 10 |
| |
| test1 = f'{" ":{max_width+4}}' |
| print(f'"{test1}"') |
| |
| test2 = f'[{"7":>{max_width}}:0]' |
| test2 |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIs(inferred, util.Uninferable) |
| |
| |
| class BrainNamedtupleAnnAssignTest(unittest.TestCase): |
| def test_no_crash_on_ann_assign_in_namedtuple(self) -> None: |
| node = builder.extract_node( |
| """ |
| from enum import Enum |
| from typing import Optional |
| |
| class A(Enum): |
| B: str = 'B' |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.ClassDef) |
| |
| |
| class BrainUUIDTest(unittest.TestCase): |
| def test_uuid_has_int_member(self) -> None: |
| node = builder.extract_node( |
| """ |
| import uuid |
| u = uuid.UUID('{12345678-1234-5678-1234-567812345678}') |
| u.int |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| |
| |
| @unittest.skipUnless(HAS_ATTR, "These tests require the attr library") |
| class AttrsTest(unittest.TestCase): |
| def test_attr_transform(self) -> None: |
| module = astroid.parse( |
| """ |
| import attr |
| from attr import attrs, attrib, field |
| |
| @attr.s |
| class Foo: |
| |
| d = attr.ib(attr.Factory(dict)) |
| |
| f = Foo() |
| f.d['answer'] = 42 |
| |
| @attr.s(slots=True) |
| class Bar: |
| d = attr.ib(attr.Factory(dict)) |
| |
| g = Bar() |
| g.d['answer'] = 42 |
| |
| @attrs |
| class Bah: |
| d = attrib(attr.Factory(dict)) |
| |
| h = Bah() |
| h.d['answer'] = 42 |
| |
| @attr.attrs |
| class Bai: |
| d = attr.attrib(attr.Factory(dict)) |
| |
| i = Bai() |
| i.d['answer'] = 42 |
| |
| @attr.define |
| class Spam: |
| d = field(default=attr.Factory(dict)) |
| |
| j = Spam(d=1) |
| j.d['answer'] = 42 |
| |
| @attr.mutable |
| class Eggs: |
| d = attr.field(default=attr.Factory(dict)) |
| |
| k = Eggs(d=1) |
| k.d['answer'] = 42 |
| |
| @attr.frozen |
| class Eggs: |
| d = attr.field(default=attr.Factory(dict)) |
| |
| l = Eggs(d=1) |
| l.d['answer'] = 42 |
| """ |
| ) |
| |
| for name in ("f", "g", "h", "i", "j", "k", "l"): |
| should_be_unknown = next(module.getattr(name)[0].infer()).getattr("d")[0] |
| self.assertIsInstance(should_be_unknown, astroid.Unknown) |
| |
| def test_special_attributes(self) -> None: |
| """Make sure special attrs attributes exist""" |
| |
| code = """ |
| import attr |
| |
| @attr.s |
| class Foo: |
| pass |
| Foo() |
| """ |
| foo_inst = next(astroid.extract_node(code).infer()) |
| [attr_node] = foo_inst.getattr("__attrs_attrs__") |
| # Prevents https://github.com/PyCQA/pylint/issues/1884 |
| assert isinstance(attr_node, nodes.Unknown) |
| |
| def test_dont_consider_assignments_but_without_attrs(self) -> None: |
| code = """ |
| import attr |
| |
| class Cls: pass |
| @attr.s |
| class Foo: |
| temp = Cls() |
| temp.prop = 5 |
| bar_thing = attr.ib(default=temp) |
| Foo() |
| """ |
| next(astroid.extract_node(code).infer()) |
| |
| def test_attrs_with_annotation(self) -> None: |
| code = """ |
| import attr |
| |
| @attr.s |
| class Foo: |
| bar: int = attr.ib(default=5) |
| Foo() |
| """ |
| should_be_unknown = next(astroid.extract_node(code).infer()).getattr("bar")[0] |
| self.assertIsInstance(should_be_unknown, astroid.Unknown) |
| |
| |
| class RandomSampleTest(unittest.TestCase): |
| def test_inferred_successfully(self) -> None: |
| node = astroid.extract_node( |
| """ |
| import random |
| random.sample([1, 2], 2) #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, astroid.List) |
| elems = sorted(elem.value for elem in inferred.elts) |
| self.assertEqual(elems, [1, 2]) |
| |
| def test_no_crash_on_evaluatedobject(self) -> None: |
| node = astroid.extract_node( |
| """ |
| from random import sample |
| class A: pass |
| sample(list({1: A()}.values()), 1)""" |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.List) |
| assert len(inferred.elts) == 1 |
| assert isinstance(inferred.elts[0], nodes.Call) |
| |
| |
| class SubprocessTest(unittest.TestCase): |
| """Test subprocess brain""" |
| |
| def test_subprocess_args(self) -> None: |
| """Make sure the args attribute exists for Popen |
| |
| Test for https://github.com/PyCQA/pylint/issues/1860""" |
| name = astroid.extract_node( |
| """ |
| import subprocess |
| p = subprocess.Popen(['ls']) |
| p #@ |
| """ |
| ) |
| [inst] = name.inferred() |
| self.assertIsInstance(next(inst.igetattr("args")), nodes.List) |
| |
| def test_subprcess_check_output(self) -> None: |
| code = """ |
| import subprocess |
| |
| subprocess.check_output(['echo', 'hello']); |
| """ |
| node = astroid.extract_node(code) |
| inferred = next(node.infer()) |
| # Can be either str or bytes |
| assert isinstance(inferred, astroid.Const) |
| assert isinstance(inferred.value, (str, bytes)) |
| |
| @test_utils.require_version("3.9") |
| def test_popen_does_not_have_class_getitem(self): |
| code = """import subprocess; subprocess.Popen""" |
| node = astroid.extract_node(code) |
| inferred = next(node.infer()) |
| assert "__class_getitem__" in inferred |
| |
| |
| class TestIsinstanceInference: |
| """Test isinstance builtin inference""" |
| |
| def test_type_type(self) -> None: |
| assert _get_result("isinstance(type, type)") == "True" |
| |
| def test_object_type(self) -> None: |
| assert _get_result("isinstance(object, type)") == "True" |
| |
| def test_type_object(self) -> None: |
| assert _get_result("isinstance(type, object)") == "True" |
| |
| def test_isinstance_int_true(self) -> None: |
| """Make sure isinstance can check builtin int types""" |
| assert _get_result("isinstance(1, int)") == "True" |
| |
| def test_isinstance_int_false(self) -> None: |
| assert _get_result("isinstance('a', int)") == "False" |
| |
| def test_isinstance_object_true(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(object): |
| pass |
| isinstance(Bar(), object) |
| """ |
| ) |
| == "True" |
| ) |
| |
| def test_isinstance_object_true3(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(object): |
| pass |
| isinstance(Bar(), Bar) |
| """ |
| ) |
| == "True" |
| ) |
| |
| def test_isinstance_class_false(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Foo(object): |
| pass |
| class Bar(object): |
| pass |
| isinstance(Bar(), Foo) |
| """ |
| ) |
| == "False" |
| ) |
| |
| def test_isinstance_type_false(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(object): |
| pass |
| isinstance(Bar(), type) |
| """ |
| ) |
| == "False" |
| ) |
| |
| def test_isinstance_str_true(self) -> None: |
| """Make sure isinstance can check bultin str types""" |
| assert _get_result("isinstance('a', str)") == "True" |
| |
| def test_isinstance_str_false(self) -> None: |
| assert _get_result("isinstance(1, str)") == "False" |
| |
| def test_isinstance_tuple_argument(self) -> None: |
| """obj just has to be an instance of ANY class/type on the right""" |
| assert _get_result("isinstance(1, (str, int))") == "True" |
| |
| def test_isinstance_type_false2(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| isinstance(1, type) |
| """ |
| ) |
| == "False" |
| ) |
| |
| def test_isinstance_object_true2(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(type): |
| pass |
| mainbar = Bar("Bar", tuple(), {}) |
| isinstance(mainbar, object) |
| """ |
| ) |
| == "True" |
| ) |
| |
| def test_isinstance_type_true(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(type): |
| pass |
| mainbar = Bar("Bar", tuple(), {}) |
| isinstance(mainbar, type) |
| """ |
| ) |
| == "True" |
| ) |
| |
| def test_isinstance_edge_case(self) -> None: |
| """isinstance allows bad type short-circuting""" |
| assert _get_result("isinstance(1, (int, 1))") == "True" |
| |
| def test_uninferable_bad_type(self) -> None: |
| """The second argument must be a class or a tuple of classes""" |
| with pytest.raises(InferenceError): |
| _get_result_node("isinstance(int, 1)") |
| |
| def test_uninferable_keywords(self) -> None: |
| """isinstance does not allow keywords""" |
| with pytest.raises(InferenceError): |
| _get_result_node("isinstance(1, class_or_tuple=int)") |
| |
| def test_too_many_args(self) -> None: |
| """isinstance must have two arguments""" |
| with pytest.raises(InferenceError): |
| _get_result_node("isinstance(1, int, str)") |
| |
| def test_first_param_is_uninferable(self) -> None: |
| with pytest.raises(InferenceError): |
| _get_result_node("isinstance(something, int)") |
| |
| |
| class TestIssubclassBrain: |
| """Test issubclass() builtin inference""" |
| |
| def test_type_type(self) -> None: |
| assert _get_result("issubclass(type, type)") == "True" |
| |
| def test_object_type(self) -> None: |
| assert _get_result("issubclass(object, type)") == "False" |
| |
| def test_type_object(self) -> None: |
| assert _get_result("issubclass(type, object)") == "True" |
| |
| def test_issubclass_same_class(self) -> None: |
| assert _get_result("issubclass(int, int)") == "True" |
| |
| def test_issubclass_not_the_same_class(self) -> None: |
| assert _get_result("issubclass(str, int)") == "False" |
| |
| def test_issubclass_object_true(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(object): |
| pass |
| issubclass(Bar, object) |
| """ |
| ) |
| == "True" |
| ) |
| |
| def test_issubclass_same_user_defined_class(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(object): |
| pass |
| issubclass(Bar, Bar) |
| """ |
| ) |
| == "True" |
| ) |
| |
| def test_issubclass_different_user_defined_classes(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Foo(object): |
| pass |
| class Bar(object): |
| pass |
| issubclass(Bar, Foo) |
| """ |
| ) |
| == "False" |
| ) |
| |
| def test_issubclass_type_false(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(object): |
| pass |
| issubclass(Bar, type) |
| """ |
| ) |
| == "False" |
| ) |
| |
| def test_isinstance_tuple_argument(self) -> None: |
| """obj just has to be a subclass of ANY class/type on the right""" |
| assert _get_result("issubclass(int, (str, int))") == "True" |
| |
| def test_isinstance_object_true2(self) -> None: |
| assert ( |
| _get_result( |
| """ |
| class Bar(type): |
| pass |
| issubclass(Bar, object) |
| """ |
| ) |
| == "True" |
| ) |
| |
| def test_issubclass_short_circuit(self) -> None: |
| """issubclasss allows bad type short-circuting""" |
| assert _get_result("issubclass(int, (int, 1))") == "True" |
| |
| def test_uninferable_bad_type(self) -> None: |
| """The second argument must be a class or a tuple of classes""" |
| # Should I subclass |
| with pytest.raises(InferenceError): |
| _get_result_node("issubclass(int, 1)") |
| |
| def test_uninferable_keywords(self) -> None: |
| """issubclass does not allow keywords""" |
| with pytest.raises(InferenceError): |
| _get_result_node("issubclass(int, class_or_tuple=int)") |
| |
| def test_too_many_args(self) -> None: |
| """issubclass must have two arguments""" |
| with pytest.raises(InferenceError): |
| _get_result_node("issubclass(int, int, str)") |
| |
| |
| def _get_result_node(code: str) -> Const: |
| node = next(astroid.extract_node(code).infer()) |
| return node |
| |
| |
| def _get_result(code: str) -> str: |
| return _get_result_node(code).as_string() |
| |
| |
| class TestLenBuiltinInference: |
| def test_len_list(self) -> None: |
| # Uses .elts |
| node = astroid.extract_node( |
| """ |
| len(['a','b','c']) |
| """ |
| ) |
| node = next(node.infer()) |
| assert node.as_string() == "3" |
| assert isinstance(node, nodes.Const) |
| |
| def test_len_tuple(self) -> None: |
| node = astroid.extract_node( |
| """ |
| len(('a','b','c')) |
| """ |
| ) |
| node = next(node.infer()) |
| assert node.as_string() == "3" |
| |
| def test_len_var(self) -> None: |
| # Make sure argument is inferred |
| node = astroid.extract_node( |
| """ |
| a = [1,2,'a','b','c'] |
| len(a) |
| """ |
| ) |
| node = next(node.infer()) |
| assert node.as_string() == "5" |
| |
| def test_len_dict(self) -> None: |
| # Uses .items |
| node = astroid.extract_node( |
| """ |
| a = {'a': 1, 'b': 2} |
| len(a) |
| """ |
| ) |
| node = next(node.infer()) |
| assert node.as_string() == "2" |
| |
| def test_len_set(self) -> None: |
| node = astroid.extract_node( |
| """ |
| len({'a'}) |
| """ |
| ) |
| inferred_node = next(node.infer()) |
| assert inferred_node.as_string() == "1" |
| |
| def test_len_object(self) -> None: |
| """Test len with objects that implement the len protocol""" |
| node = astroid.extract_node( |
| """ |
| class A: |
| def __len__(self): |
| return 57 |
| len(A()) |
| """ |
| ) |
| inferred_node = next(node.infer()) |
| assert inferred_node.as_string() == "57" |
| |
| def test_len_class_with_metaclass(self) -> None: |
| """Make sure proper len method is located""" |
| cls_node, inst_node = astroid.extract_node( |
| """ |
| class F2(type): |
| def __new__(cls, name, bases, attrs): |
| return super().__new__(cls, name, bases, {}) |
| def __len__(self): |
| return 57 |
| class F(metaclass=F2): |
| def __len__(self): |
| return 4 |
| len(F) #@ |
| len(F()) #@ |
| """ |
| ) |
| assert next(cls_node.infer()).as_string() == "57" |
| assert next(inst_node.infer()).as_string() == "4" |
| |
| def test_len_object_failure(self) -> None: |
| """If taking the length of a class, do not use an instance method""" |
| node = astroid.extract_node( |
| """ |
| class F: |
| def __len__(self): |
| return 57 |
| len(F) |
| """ |
| ) |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| def test_len_string(self) -> None: |
| node = astroid.extract_node( |
| """ |
| len("uwu") |
| """ |
| ) |
| assert next(node.infer()).as_string() == "3" |
| |
| def test_len_generator_failure(self) -> None: |
| node = astroid.extract_node( |
| """ |
| def gen(): |
| yield 'a' |
| yield 'b' |
| len(gen()) |
| """ |
| ) |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| def test_len_failure_missing_variable(self) -> None: |
| node = astroid.extract_node( |
| """ |
| len(a) |
| """ |
| ) |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| def test_len_bytes(self) -> None: |
| node = astroid.extract_node( |
| """ |
| len(b'uwu') |
| """ |
| ) |
| assert next(node.infer()).as_string() == "3" |
| |
| def test_int_subclass_result(self) -> None: |
| """Check that a subclass of an int can still be inferred |
| |
| This test does not properly infer the value passed to the |
| int subclass (5) but still returns a proper integer as we |
| fake the result of the `len()` call. |
| """ |
| node = astroid.extract_node( |
| """ |
| class IntSubclass(int): |
| pass |
| |
| class F: |
| def __len__(self): |
| return IntSubclass(5) |
| len(F()) |
| """ |
| ) |
| assert next(node.infer()).as_string() == "0" |
| |
| @pytest.mark.xfail(reason="Can't use list special astroid fields") |
| def test_int_subclass_argument(self): |
| """I am unable to access the length of an object which |
| subclasses list""" |
| node = astroid.extract_node( |
| """ |
| class ListSubclass(list): |
| pass |
| len(ListSubclass([1,2,3,4,4])) |
| """ |
| ) |
| assert next(node.infer()).as_string() == "5" |
| |
| def test_len_builtin_inference_attribute_error_str(self) -> None: |
| """Make sure len builtin doesn't raise an AttributeError |
| on instances of str or bytes |
| |
| See https://github.com/PyCQA/pylint/issues/1942 |
| """ |
| code = 'len(str("F"))' |
| try: |
| next(astroid.extract_node(code).infer()) |
| except InferenceError: |
| pass |
| |
| def test_len_builtin_inference_recursion_error_self_referential_attribute( |
| self, |
| ) -> None: |
| """Make sure len calls do not trigger |
| recursion errors for self referential assignment |
| |
| See https://github.com/PyCQA/pylint/issues/2734 |
| """ |
| code = """ |
| class Data: |
| def __init__(self): |
| self.shape = [] |
| |
| data = Data() |
| data.shape = len(data.shape) |
| data.shape #@ |
| """ |
| try: |
| astroid.extract_node(code).inferred() |
| except RecursionError: |
| pytest.fail("Inference call should not trigger a recursion error") |
| |
| |
| def test_infer_str() -> None: |
| ast_nodes = astroid.extract_node( |
| """ |
| str(s) #@ |
| str('a') #@ |
| str(some_object()) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Const) |
| |
| node = astroid.extract_node( |
| """ |
| str(s='') #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Instance) |
| assert inferred.qname() == "builtins.str" |
| |
| |
| def test_infer_int() -> None: |
| ast_nodes = astroid.extract_node( |
| """ |
| int(0) #@ |
| int('1') #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Const) |
| |
| ast_nodes = astroid.extract_node( |
| """ |
| int(s='') #@ |
| int('2.5') #@ |
| int('something else') #@ |
| int(unknown) #@ |
| int(b'a') #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Instance) |
| assert inferred.qname() == "builtins.int" |
| |
| |
| def test_infer_dict_from_keys() -> None: |
| bad_nodes = astroid.extract_node( |
| """ |
| dict.fromkeys() #@ |
| dict.fromkeys(1, 2, 3) #@ |
| dict.fromkeys(a=1) #@ |
| """ |
| ) |
| for node in bad_nodes: |
| with pytest.raises(InferenceError): |
| next(node.infer()) |
| |
| # Test uninferable values |
| good_nodes = astroid.extract_node( |
| """ |
| from unknown import Unknown |
| dict.fromkeys(some_value) #@ |
| dict.fromkeys(some_other_value) #@ |
| dict.fromkeys([Unknown(), Unknown()]) #@ |
| dict.fromkeys([Unknown(), Unknown()]) #@ |
| """ |
| ) |
| for node in good_nodes: |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Dict) |
| assert inferred.items == [] |
| |
| # Test inferrable values |
| |
| # from a dictionary's keys |
| from_dict = astroid.extract_node( |
| """ |
| dict.fromkeys({'a':2, 'b': 3, 'c': 3}) #@ |
| """ |
| ) |
| inferred = next(from_dict.infer()) |
| assert isinstance(inferred, astroid.Dict) |
| itered = inferred.itered() |
| assert all(isinstance(elem, astroid.Const) for elem in itered) |
| actual_values = [elem.value for elem in itered] |
| assert sorted(actual_values) == ["a", "b", "c"] |
| |
| # from a string |
| from_string = astroid.extract_node( |
| """ |
| dict.fromkeys('abc') |
| """ |
| ) |
| inferred = next(from_string.infer()) |
| assert isinstance(inferred, astroid.Dict) |
| itered = inferred.itered() |
| assert all(isinstance(elem, astroid.Const) for elem in itered) |
| actual_values = [elem.value for elem in itered] |
| assert sorted(actual_values) == ["a", "b", "c"] |
| |
| # from bytes |
| from_bytes = astroid.extract_node( |
| """ |
| dict.fromkeys(b'abc') |
| """ |
| ) |
| inferred = next(from_bytes.infer()) |
| assert isinstance(inferred, astroid.Dict) |
| itered = inferred.itered() |
| assert all(isinstance(elem, astroid.Const) for elem in itered) |
| actual_values = [elem.value for elem in itered] |
| assert sorted(actual_values) == [97, 98, 99] |
| |
| # From list/set/tuple |
| from_others = astroid.extract_node( |
| """ |
| dict.fromkeys(('a', 'b', 'c')) #@ |
| dict.fromkeys(['a', 'b', 'c']) #@ |
| dict.fromkeys({'a', 'b', 'c'}) #@ |
| """ |
| ) |
| for node in from_others: |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Dict) |
| itered = inferred.itered() |
| assert all(isinstance(elem, astroid.Const) for elem in itered) |
| actual_values = [elem.value for elem in itered] |
| assert sorted(actual_values) == ["a", "b", "c"] |
| |
| |
| class TestFunctoolsPartial: |
| def test_invalid_functools_partial_calls(self) -> None: |
| ast_nodes = astroid.extract_node( |
| """ |
| from functools import partial |
| from unknown import Unknown |
| |
| def test(a, b, c): |
| return a + b + c |
| |
| partial() #@ |
| partial(test) #@ |
| partial(func=test) #@ |
| partial(some_func, a=1) #@ |
| partial(Unknown, a=1) #@ |
| partial(2, a=1) #@ |
| partial(test, unknown=1) #@ |
| """ |
| ) |
| for node in ast_nodes: |
| inferred = next(node.infer()) |
| assert isinstance(inferred, (astroid.FunctionDef, astroid.Instance)) |
| assert inferred.qname() in { |
| "functools.partial", |
| "functools.partial.newfunc", |
| } |
| |
| def test_inferred_partial_function_calls(self) -> None: |
| ast_nodes = astroid.extract_node( |
| """ |
| from functools import partial |
| def test(a, b): |
| return a + b |
| partial(test, 1)(3) #@ |
| partial(test, b=4)(3) #@ |
| partial(test, b=4)(a=3) #@ |
| def other_test(a, b, *, c=1): |
| return (a + b) * c |
| |
| partial(other_test, 1, 2)() #@ |
| partial(other_test, 1, 2)(c=4) #@ |
| partial(other_test, c=4)(1, 3) #@ |
| partial(other_test, 4, c=4)(4) #@ |
| partial(other_test, 4, c=4)(b=5) #@ |
| test(1, 2) #@ |
| partial(other_test, 1, 2)(c=3) #@ |
| partial(test, b=4)(a=3) #@ |
| """ |
| ) |
| expected_values = [4, 7, 7, 3, 12, 16, 32, 36, 3, 9, 7] |
| for node, expected_value in zip(ast_nodes, expected_values): |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Const) |
| assert inferred.value == expected_value |
| |
| def test_partial_assignment(self) -> None: |
| """Make sure partials are not assigned to original scope.""" |
| ast_nodes = astroid.extract_node( |
| """ |
| from functools import partial |
| def test(a, b): #@ |
| return a + b |
| test2 = partial(test, 1) |
| test2 #@ |
| def test3_scope(a): |
| test3 = partial(test, a) |
| test3 #@ |
| """ |
| ) |
| func1, func2, func3 = ast_nodes |
| assert func1.parent.scope() == func2.parent.scope() |
| assert func1.parent.scope() != func3.parent.scope() |
| partial_func3 = next(func3.infer()) |
| # use scope of parent, so that it doesn't just refer to self |
| scope = partial_func3.parent.scope() |
| assert scope.name == "test3_scope", "parented by closure" |
| |
| def test_partial_does_not_affect_scope(self) -> None: |
| """Make sure partials are not automatically assigned.""" |
| ast_nodes = astroid.extract_node( |
| """ |
| from functools import partial |
| def test(a, b): |
| return a + b |
| def scope(): |
| test2 = partial(test, 1) |
| test2 #@ |
| """ |
| ) |
| test2 = next(ast_nodes.infer()) |
| mod_scope = test2.root() |
| scope = test2.parent.scope() |
| assert set(mod_scope) == {"test", "scope", "partial"} |
| assert set(scope) == {"test2"} |
| |
| def test_multiple_partial_args(self) -> None: |
| "Make sure partials remember locked-in args." |
| ast_node = astroid.extract_node( |
| """ |
| from functools import partial |
| def test(a, b, c, d, e=5): |
| return a + b + c + d + e |
| test1 = partial(test, 1) |
| test2 = partial(test1, 2) |
| test3 = partial(test2, 3) |
| test3(4, e=6) #@ |
| """ |
| ) |
| expected_args = [1, 2, 3, 4] |
| expected_keywords = {"e": 6} |
| |
| call_site = astroid.arguments.CallSite.from_call(ast_node) |
| called_func = next(ast_node.func.infer()) |
| called_args = called_func.filled_args + call_site.positional_arguments |
| called_keywords = {**called_func.filled_keywords, **call_site.keyword_arguments} |
| assert len(called_args) == len(expected_args) |
| assert [arg.value for arg in called_args] == expected_args |
| assert len(called_keywords) == len(expected_keywords) |
| |
| for keyword, value in expected_keywords.items(): |
| assert keyword in called_keywords |
| assert called_keywords[keyword].value == value |
| |
| |
| def test_http_client_brain() -> None: |
| node = astroid.extract_node( |
| """ |
| from http.client import OK |
| OK |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Instance) |
| |
| |
| @pytest.mark.skipif(not PY37_PLUS, reason="Needs 3.7+") |
| def test_http_status_brain() -> None: |
| node = astroid.extract_node( |
| """ |
| import http |
| http.HTTPStatus.CONTINUE.phrase |
| """ |
| ) |
| inferred = next(node.infer()) |
| # Cannot infer the exact value but the field is there. |
| assert inferred is util.Uninferable |
| |
| node = astroid.extract_node( |
| """ |
| import http |
| http.HTTPStatus(200).phrase |
| """ |
| ) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, astroid.Const) |
| |
| |
| def test_oserror_model() -> None: |
| node = astroid.extract_node( |
| """ |
| try: |
| 1/0 |
| except OSError as exc: |
| exc #@ |
| """ |
| ) |
| inferred = next(node.infer()) |
| strerror = next(inferred.igetattr("strerror")) |
| assert isinstance(strerror, astroid.Const) |
| assert strerror.value == "" |
| |
| |
| @pytest.mark.skipif(not PY37_PLUS, reason="Dynamic module attributes since Python 3.7") |
| def test_crypt_brain() -> None: |
| module = MANAGER.ast_from_module_name("crypt") |
| dynamic_attrs = [ |
| "METHOD_SHA512", |
| "METHOD_SHA256", |
| "METHOD_BLOWFISH", |
| "METHOD_MD5", |
| "METHOD_CRYPT", |
| ] |
| for attr in dynamic_attrs: |
| assert attr in module |
| |
| |
| @pytest.mark.parametrize( |
| "code,expected_class,expected_value", |
| [ |
| ("'hey'.encode()", astroid.Const, b""), |
| ("b'hey'.decode()", astroid.Const, ""), |
| ("'hey'.encode().decode()", astroid.Const, ""), |
| ], |
| ) |
| def test_str_and_bytes(code, expected_class, expected_value): |
| node = astroid.extract_node(code) |
| inferred = next(node.infer()) |
| assert isinstance(inferred, expected_class) |
| assert inferred.value == expected_value |
| |
| |
| def test_no_recursionerror_on_self_referential_length_check() -> None: |
| """ |
| Regression test for https://github.com/PyCQA/astroid/issues/777 |
| |
| This test should only raise an InferenceError and no RecursionError. |
| """ |
| with pytest.raises(InferenceError): |
| node = astroid.extract_node( |
| """ |
| class Crash: |
| def __len__(self) -> int: |
| return len(self) |
| len(Crash()) #@ |
| """ |
| ) |
| assert isinstance(node, nodes.NodeNG) |
| node.inferred() |
| |
| |
| def test_inference_on_outer_referential_length_check() -> None: |
| """ |
| Regression test for https://github.com/PyCQA/pylint/issues/5244 |
| See also https://github.com/PyCQA/astroid/pull/1234 |
| |
| This test should succeed without any error. |
| """ |
| node = astroid.extract_node( |
| """ |
| class A: |
| def __len__(self) -> int: |
| return 42 |
| |
| class Crash: |
| def __len__(self) -> int: |
| a = A() |
| return len(a) |
| |
| len(Crash()) #@ |
| """ |
| ) |
| inferred = node.inferred() |
| assert len(inferred) == 1 |
| assert isinstance(inferred[0], nodes.Const) |
| assert inferred[0].value == 42 |
| |
| |
| def test_no_attributeerror_on_self_referential_length_check() -> None: |
| """ |
| Regression test for https://github.com/PyCQA/pylint/issues/5244 |
| See also https://github.com/PyCQA/astroid/pull/1234 |
| |
| This test should only raise an InferenceError and no AttributeError. |
| """ |
| with pytest.raises(InferenceError): |
| node = astroid.extract_node( |
| """ |
| class MyClass: |
| def some_func(self): |
| return lambda: 42 |
| |
| def __len__(self): |
| return len(self.some_func()) |
| |
| len(MyClass()) #@ |
| """ |
| ) |
| assert isinstance(node, nodes.NodeNG) |
| node.inferred() |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |