From 8763111aae725a8ff5ce65fbd105daeaaec3da24 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sun, 14 May 2023 12:48:27 -0400 Subject: [PATCH 01/13] Handle ``objects.Super`` in `helpers.object_type()` (#2177) (cherry picked from commit b186f683da0896d2fbed0f2aae3497b29ca93266) --- ChangeLog | 3 +++ astroid/helpers.py | 4 ++-- tests/test_helpers.py | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 993426bd98..0308eda295 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,9 @@ What's New in astroid 2.15.5? ============================= Release date: TBA +* Handle ``objects.Super`` in ``helpers.object_type()``. + + Refs pylint-dev/pylint#8554 What's New in astroid 2.15.4? diff --git a/astroid/helpers.py b/astroid/helpers.py index 24dba6d7bc..bb19bbf4bd 100644 --- a/astroid/helpers.py +++ b/astroid/helpers.py @@ -8,7 +8,7 @@ from collections.abc import Generator -from astroid import bases, manager, nodes, raw_building, util +from astroid import bases, manager, nodes, objects, raw_building, util from astroid.context import CallContext, InferenceContext from astroid.exceptions import ( AstroidTypeError, @@ -65,7 +65,7 @@ def _object_type( raise InferenceError elif isinstance(inferred, util.UninferableBase): yield inferred - elif isinstance(inferred, (bases.Proxy, nodes.Slice)): + elif isinstance(inferred, (bases.Proxy, nodes.Slice, objects.Super)): yield inferred._proxied else: # pragma: no cover raise AssertionError(f"We don't handle {type(inferred)} currently") diff --git a/tests/test_helpers.py b/tests/test_helpers.py index fe97eb6466..90182a23ee 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -42,6 +42,7 @@ def test_object_type(self) -> None: ("type", self._extract("type")), ("object", self._extract("type")), ("object()", self._extract("object")), + ("super()", self._extract("super")), ("lambda: None", self._build_custom_builtin("function")), ("len", self._build_custom_builtin("builtin_function_or_method")), ("None", self._build_custom_builtin("NoneType")), From 554d93ee2718fb4131ee2b9733253cd4f1fc6619 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 1 May 2023 21:48:06 +0200 Subject: [PATCH 02/13] Fix urllib3 tests following the release of v2.0.0 (#2162) --- requirements_test_brain.txt | 2 +- tests/test_modutils.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/requirements_test_brain.txt b/requirements_test_brain.txt index b4778baea8..2014e07aee 100644 --- a/requirements_test_brain.txt +++ b/requirements_test_brain.txt @@ -8,5 +8,5 @@ regex types-python-dateutil six types-six -urllib3 +urllib3>1,<2 typing_extensions>=4.4.0 diff --git a/tests/test_modutils.py b/tests/test_modutils.py index 0c8bee8880..04f5eeed68 100644 --- a/tests/test_modutils.py +++ b/tests/test_modutils.py @@ -28,9 +28,9 @@ try: import urllib3 # pylint: disable=unused-import - HAS_URLLIB3 = True + HAS_URLLIB3_V1 = urllib3.__version__.startswith("1") except ImportError: - HAS_URLLIB3 = False + HAS_URLLIB3_V1 = False def _get_file_from_object(obj) -> str: @@ -547,8 +547,9 @@ def test_is_module_name_part_of_extension_package_whitelist_success(self) -> Non ) -@pytest.mark.skipif(not HAS_URLLIB3, reason="This test requires urllib3.") +@pytest.mark.skipif(not HAS_URLLIB3_V1, reason="This test requires urllib3 < 2.") def test_file_info_from_modpath__SixMetaPathImporter() -> None: + """Six is not backported anymore in urllib3 v2.0.0+""" assert modutils.file_info_from_modpath(["urllib3.packages.six.moves.http_client"]) From 8523ba827006d56a770a1f6efa77215718ef26c0 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 14 May 2023 19:31:36 +0200 Subject: [PATCH 03/13] Bump astroid to 2.15.5, update changelog --- ChangeLog | 8 +++++++- astroid/__pkginfo__.py | 2 +- tbump.toml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0308eda295..f29525cc14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,10 +8,16 @@ Release date: TBA -What's New in astroid 2.15.5? +What's New in astroid 2.15.6? ============================= Release date: TBA + + +What's New in astroid 2.15.5? +============================= +Release date: 2023-05-14 + * Handle ``objects.Super`` in ``helpers.object_type()``. Refs pylint-dev/pylint#8554 diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py index edbc6c68f4..ab8f0f49b5 100644 --- a/astroid/__pkginfo__.py +++ b/astroid/__pkginfo__.py @@ -2,5 +2,5 @@ # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt -__version__ = "2.15.4" +__version__ = "2.15.5" version = __version__ diff --git a/tbump.toml b/tbump.toml index 0a54b00fd9..492ff3cfc6 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/PyCQA/astroid" [version] -current = "2.15.4" +current = "2.15.5" regex = ''' ^(?P0|[1-9]\d*) \. From bbf9ce4ed23691aa20716430ee001dd7186d686a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 Jun 2023 07:54:41 -0400 Subject: [PATCH 04/13] Fix brain dict regression (#2204) (#2207) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix regression resulting in ignored pylint settings We get rid of the immutable instance attributes in AstroidManager, ensuring that these always mutate the global state instead of instance's. This fixes a regression introduced in commit bbcc58bd52e7f295b77a8618b19b2364625590a2. Fixes #2200 (cherry picked from commit b08166bf452852348fdf2c0c6aceb89997094742) Co-authored-by: Josef Kemetmüller --- ChangeLog | 5 +++++ astroid/manager.py | 18 ++++++++++++++++-- tests/test_regrtest.py | 24 +++++++++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index f29525cc14..ae4fabe9d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,11 @@ What's New in astroid 2.16.0? Release date: TBA +* Fix a regression in 2.12.0 where settings in AstroidManager would be ignored. + Most notably this addresses pylint-dev/pylint#7433. + + Refs #2204 + What's New in astroid 2.15.6? ============================= diff --git a/astroid/manager.py b/astroid/manager.py index 965dd5a57c..96006c5f6c 100644 --- a/astroid/manager.py +++ b/astroid/manager.py @@ -70,13 +70,27 @@ def __init__(self) -> None: self.astroid_cache = AstroidManager.brain["astroid_cache"] self._mod_file_cache = AstroidManager.brain["_mod_file_cache"] self._failed_import_hooks = AstroidManager.brain["_failed_import_hooks"] - self.always_load_extensions = AstroidManager.brain["always_load_extensions"] - self.optimize_ast = AstroidManager.brain["optimize_ast"] self.extension_package_whitelist = AstroidManager.brain[ "extension_package_whitelist" ] self._transform = AstroidManager.brain["_transform"] + @property + def always_load_extensions(self) -> bool: + return AstroidManager.brain["always_load_extensions"] + + @always_load_extensions.setter + def always_load_extensions(self, value: bool) -> None: + AstroidManager.brain["always_load_extensions"] = value + + @property + def optimize_ast(self) -> bool: + return AstroidManager.brain["optimize_ast"] + + @optimize_ast.setter + def optimize_ast(self, value: bool) -> None: + AstroidManager.brain["optimize_ast"] = value + @property def register_transform(self): # This and unregister_transform below are exported for convenience diff --git a/tests/test_regrtest.py b/tests/test_regrtest.py index 783f1cc1b1..197c6eef48 100644 --- a/tests/test_regrtest.py +++ b/tests/test_regrtest.py @@ -9,7 +9,7 @@ import pytest -from astroid import MANAGER, Instance, bases, nodes, parse, test_utils +from astroid import MANAGER, Instance, bases, manager, nodes, parse, test_utils from astroid.builder import AstroidBuilder, _extract_single_node, extract_node from astroid.const import PY38_PLUS from astroid.context import InferenceContext @@ -37,6 +37,24 @@ def tearDown(self) -> None: sys.path.pop(0) sys.path_importer_cache.pop(resources.find("data"), None) + def test_manager_instance_attributes_reference_global_MANAGER(self) -> None: + for expected in (True, False): + with mock.patch.dict( + manager.AstroidManager.brain, + values={"always_load_extensions": expected}, + ): + assert ( + MANAGER.always_load_extensions + == manager.AstroidManager.brain["always_load_extensions"] + ) + with mock.patch.dict( + manager.AstroidManager.brain, + values={"optimize_ast": expected}, + ): + assert ( + MANAGER.optimize_ast == manager.AstroidManager.brain["optimize_ast"] + ) + def test_module_path(self) -> None: man = test_utils.brainless_manager() mod = man.ast_from_module_name("package.import_package_subpackage_module") @@ -50,9 +68,9 @@ def test_module_path(self) -> None: self.assertEqual(module.name, "package.subpackage.module") def test_package_sidepackage(self) -> None: - manager = test_utils.brainless_manager() + brainless_manager = test_utils.brainless_manager() assert "package.sidepackage" not in MANAGER.astroid_cache - package = manager.ast_from_module_name("absimp") + package = brainless_manager.ast_from_module_name("absimp") self.assertIsInstance(package, nodes.Module) self.assertTrue(package.package) subpackage = next(package.getattr("sidepackage")[0].infer()) From 3f6276d2d7eac14782563406add2c77815a9907b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 10 Jun 2023 12:22:00 -0400 Subject: [PATCH 05/13] Harden get_module_part() against "." (#2202) (#2203) (cherry picked from commit bd78ab0a70d1341db4ac5cdb4ef899ff91033679) Co-authored-by: Jacob Walls --- ChangeLog | 9 +++++++++ astroid/modutils.py | 3 ++- tests/test_modutils.py | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index ae4fabe9d7..92e0be95fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,6 +19,15 @@ Release date: TBA +What's New in astroid 2.15.6? +============================= +Release date: 2023-05-14 + +* Harden ``get_module_part()`` against ``"."``. + + Closes pylint-dev/pylint#8749 + + What's New in astroid 2.15.5? ============================= Release date: 2023-05-14 diff --git a/astroid/modutils.py b/astroid/modutils.py index f05b5f89c6..51bdeea5b8 100644 --- a/astroid/modutils.py +++ b/astroid/modutils.py @@ -433,7 +433,8 @@ def get_module_part(dotted_name: str, context_file: str | None = None) -> str: ), "explicit relative import, but no context_file?" path = [] # prevent resolving the import non-relatively starti = 1 - while parts[starti] == "": # for all further dots: change context + # for all further dots: change context + while starti < len(parts) and parts[starti] == "": starti += 1 assert ( context_file is not None diff --git a/tests/test_modutils.py b/tests/test_modutils.py index 04f5eeed68..1231c2f018 100644 --- a/tests/test_modutils.py +++ b/tests/test_modutils.py @@ -147,6 +147,9 @@ def test_get_module_part_exception(self) -> None: ImportError, modutils.get_module_part, "unknown.module", modutils.__file__ ) + def test_get_module_part_only_dot(self) -> None: + self.assertEqual(modutils.get_module_part(".", modutils.__file__), ".") + class ModPathFromFileTest(unittest.TestCase): """Given an absolute file path return the python module's path as a list.""" From 2eb869e016dad4fd1bfd88d2ad39dc196f8d0583 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 08:43:20 -0400 Subject: [PATCH 06/13] Avoid expensive list/tuple multiplication operations (#2228) (#2229) (cherry picked from commit 1a318a0c025c6474053bbac863648c715a054de6) Co-authored-by: Jacob Walls --- ChangeLog | 4 ++++ astroid/protocols.py | 14 ++++++++++---- tests/test_protocols.py | 7 +++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 92e0be95fa..e02189772a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,10 @@ Release date: 2023-05-14 Closes pylint-dev/pylint#8749 +* Avoid expensive list/tuple multiplication operations that would result in ``MemoryError``. + + Closes pylint-dev/pylint#8748 + What's New in astroid 2.15.5? ============================= diff --git a/astroid/protocols.py b/astroid/protocols.py index dcc9e2b87a..d866f73326 100644 --- a/astroid/protocols.py +++ b/astroid/protocols.py @@ -167,16 +167,19 @@ def const_infer_binary_op( def _multiply_seq_by_int( self: _TupleListNodeT, opnode: nodes.AugAssign | nodes.BinOp, - other: nodes.Const, + value: int, context: InferenceContext, ) -> _TupleListNodeT: node = self.__class__(parent=opnode) + if value > 1e8: + node.elts = [util.Uninferable] + return node filtered_elts = ( helpers.safe_infer(elt, context) or util.Uninferable for elt in self.elts if not isinstance(elt, util.UninferableBase) ) - node.elts = list(filtered_elts) * other.value + node.elts = list(filtered_elts) * value return node @@ -225,14 +228,17 @@ def tl_infer_binary_op( if not isinstance(other.value, int): yield not_implemented return - yield _multiply_seq_by_int(self, opnode, other, context) + yield _multiply_seq_by_int(self, opnode, other.value, context) elif isinstance(other, bases.Instance) and operator == "*": # Verify if the instance supports __index__. as_index = helpers.class_instance_as_index(other) if not as_index: yield util.Uninferable + elif not isinstance(as_index.value, int): # pragma: no cover + # already checked by class_instance_as_index() but faster than casting + raise AssertionError("Please open a bug report.") else: - yield _multiply_seq_by_int(self, opnode, as_index, context) + yield _multiply_seq_by_int(self, opnode, as_index.value, context) else: yield not_implemented diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 48351bcfb0..06f1f5f5c4 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -279,6 +279,13 @@ def test_uninferable_exponents() -> None: parsed = extract_node("None ** 2") assert parsed.inferred() == [Uninferable] + @staticmethod + def test_uninferable_list_multiplication() -> None: + """Attempting to calculate the result is prohibitively expensive.""" + parsed = extract_node("[0] * 123456789") + element = parsed.inferred()[0].elts[0] + assert element.value is Uninferable + @pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions") def test_named_expr_inference() -> None: From 30c3112bda109183d2e31b098b8bb7fdd7a1fd9d Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 8 Jul 2023 20:23:30 +0200 Subject: [PATCH 07/13] Bump astroid to 2.15.6, update changelog --- ChangeLog | 5 ++--- astroid/__pkginfo__.py | 2 +- tbump.toml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index e02189772a..6bc62e7479 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,8 +12,7 @@ Release date: TBA Refs #2204 - -What's New in astroid 2.15.6? +What's New in astroid 2.15.7? ============================= Release date: TBA @@ -21,7 +20,7 @@ Release date: TBA What's New in astroid 2.15.6? ============================= -Release date: 2023-05-14 +Release date: 2023-07-08 * Harden ``get_module_part()`` against ``"."``. diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py index ab8f0f49b5..467ef987aa 100644 --- a/astroid/__pkginfo__.py +++ b/astroid/__pkginfo__.py @@ -2,5 +2,5 @@ # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt -__version__ = "2.15.5" +__version__ = "2.15.6" version = __version__ diff --git a/tbump.toml b/tbump.toml index 492ff3cfc6..2a62def48d 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/PyCQA/astroid" [version] -current = "2.15.5" +current = "2.15.6" regex = ''' ^(?P0|[1-9]\d*) \. From 28ef0382bc6b95221b6ab2a3d1b3be0b8cd6fb20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 13:35:23 +0200 Subject: [PATCH 08/13] [Backport maintenance/2.15.x] Fix a crash when inferring a `typing.TypeVar` call. (#2254) * Fix a crash when inferring a `typing.TypeVar` call. (#2239) Closes pylint-dev/pylint#8802 (cherry picked from commit 89dfb4857670a67920d0f3ab88857d697787901d) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- ChangeLog | 9 +++++++++ astroid/brain/brain_typing.py | 13 +++++++++++-- tests/brain/test_typing.py | 27 +++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/brain/test_typing.py diff --git a/ChangeLog b/ChangeLog index 6bc62e7479..147a2f9d07 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,15 @@ Release date: TBA +What's New in astroid 2.15.7? +============================= +Release date: 2023-07-08 + +* Fix a crash when inferring a ``typing.TypeVar`` call. + + Closes pylint-dev/pylint#8802 + + What's New in astroid 2.15.6? ============================= Release date: 2023-07-08 diff --git a/astroid/brain/brain_typing.py b/astroid/brain/brain_typing.py index e0a9dfd178..2cd5a32893 100644 --- a/astroid/brain/brain_typing.py +++ b/astroid/brain/brain_typing.py @@ -121,7 +121,9 @@ def looks_like_typing_typevar_or_newtype(node) -> bool: return False -def infer_typing_typevar_or_newtype(node, context_itton=None): +def infer_typing_typevar_or_newtype( + node: Call, context_itton: context.InferenceContext | None = None +) -> Iterator[ClassDef]: """Infer a typing.TypeVar(...) or typing.NewType(...) call.""" try: func = next(node.func.infer(context=context_itton)) @@ -137,7 +139,14 @@ def infer_typing_typevar_or_newtype(node, context_itton=None): raise UseInferenceDefault typename = node.args[0].as_string().strip("'") - node = extract_node(TYPING_TYPE_TEMPLATE.format(typename)) + node = ClassDef( + name=typename, + lineno=node.lineno, + col_offset=node.col_offset, + parent=node.parent, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + ) return node.infer(context=context_itton) diff --git a/tests/brain/test_typing.py b/tests/brain/test_typing.py new file mode 100644 index 0000000000..4fdff77ae9 --- /dev/null +++ b/tests/brain/test_typing.py @@ -0,0 +1,27 @@ +# 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 +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +from astroid import builder, nodes + + +def test_infer_typevar() -> None: + """ + Regression test for: https://github.com/pylint-dev/pylint/issues/8802 + + Test that an inferred `typing.TypeVar()` call produces a `nodes.ClassDef` + node. + """ + assign_node = builder.extract_node( + """ + from typing import TypeVar + MyType = TypeVar('My.Type') + """ + ) + call = assign_node.value + inferred = next(call.infer()) + assert isinstance(inferred, nodes.ClassDef) + assert inferred.name == "My.Type" From 5cfbcbf46d4ec4a43b4bf0ff2e81a5990be92449 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 10:13:58 -0700 Subject: [PATCH 09/13] [Backport maintenance/2.15.x] Fix inference involving `@functools.lru_cache` decorator (#2260) * Fix inference involving `@functools.lru_cache` decorator (#2259) (cherry picked from commit ec912f9d039457e513b4bc375d85810142ac1edd) --- ChangeLog | 11 +++++------ astroid/brain/brain_functools.py | 8 +++++--- tests/test_object_model.py | 28 ++++++++++++++-------------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index 147a2f9d07..1cfba953a2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,16 +16,15 @@ What's New in astroid 2.15.7? ============================= Release date: TBA - - -What's New in astroid 2.15.7? -============================= -Release date: 2023-07-08 - * Fix a crash when inferring a ``typing.TypeVar`` call. Closes pylint-dev/pylint#8802 +* Fix inference of functions with ``@functools.lru_cache`` decorators without + parentheses. + + Closes pylint-dev/pylint#8868 + What's New in astroid 2.15.6? ============================= diff --git a/astroid/brain/brain_functools.py b/astroid/brain/brain_functools.py index f6a9830d3d..645c54e723 100644 --- a/astroid/brain/brain_functools.py +++ b/astroid/brain/brain_functools.py @@ -129,15 +129,17 @@ def _looks_like_lru_cache(node) -> bool: if not node.decorators: return False for decorator in node.decorators.nodes: - if not isinstance(decorator, Call): + if not isinstance(decorator, (Attribute, Call)): continue if _looks_like_functools_member(decorator, "lru_cache"): return True return False -def _looks_like_functools_member(node, member) -> bool: - """Check if the given Call node is a functools.partial call.""" +def _looks_like_functools_member(node: Attribute | Call, member: str) -> bool: + """Check if the given Call node is the wanted member of functools.""" + if isinstance(node, Attribute): + return node.attrname == member if isinstance(node.func, Name): return node.func.name == member if isinstance(node.func, Attribute): diff --git a/tests/test_object_model.py b/tests/test_object_model.py index 8f41eda543..e578c5643b 100644 --- a/tests/test_object_model.py +++ b/tests/test_object_model.py @@ -809,13 +809,13 @@ def test_str_argument_not_required(self) -> None: assert not args.elts -class LruCacheModelTest(unittest.TestCase): - def test_lru_cache(self) -> None: - ast_nodes = builder.extract_node( - """ +@pytest.mark.parametrize("parentheses", (True, False)) +def test_lru_cache(parentheses) -> None: + ast_nodes = builder.extract_node( + f""" import functools class Foo(object): - @functools.lru_cache() + @functools.lru_cache{"()" if parentheses else ""} def foo(): pass f = Foo() @@ -823,12 +823,12 @@ def foo(): f.foo.__wrapped__ #@ f.foo.cache_info() #@ """ - ) - assert isinstance(ast_nodes, list) - cache_clear = next(ast_nodes[0].infer()) - self.assertIsInstance(cache_clear, astroid.BoundMethod) - wrapped = next(ast_nodes[1].infer()) - self.assertIsInstance(wrapped, astroid.FunctionDef) - self.assertEqual(wrapped.name, "foo") - cache_info = next(ast_nodes[2].infer()) - self.assertIsInstance(cache_info, astroid.Instance) + ) + assert isinstance(ast_nodes, list) + cache_clear = next(ast_nodes[0].infer()) + assert isinstance(cache_clear, astroid.BoundMethod) + wrapped = next(ast_nodes[1].infer()) + assert isinstance(wrapped, astroid.FunctionDef) + assert wrapped.name == "foo" + cache_info = next(ast_nodes[2].infer()) + assert isinstance(cache_info, astroid.Instance) From 0e0dd9c12ed9df1c6763fb0d50a5c70f423ddce6 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 23 Sep 2023 14:04:39 -0400 Subject: [PATCH 10/13] [Backport maintenance/2.15.x] Make `sys.argv` uninferable because it never is (#2244) (#2297) * Make `sys.argv` uninferable because it never is (#2244) It's impossible to infer the value it will have outside of static analysis where it's our own value. See https://github.com/pylint-dev/pylint/issues/7710 (cherry picked from commit ea78827c9a812be0bdcfce3bf760f5656733f8ad) Co-authored-by: Pierre Sassoulas --- astroid/inference.py | 7 ++++++- tests/test_inference.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/astroid/inference.py b/astroid/inference.py index 65d03d3021..05eb13434a 100644 --- a/astroid/inference.py +++ b/astroid/inference.py @@ -369,7 +369,12 @@ def infer_attribute( context.constraints[self.attrname] = constraint.get_constraints( self, frame=frame ) - yield from owner.igetattr(self.attrname, context) + if self.attrname == "argv" and owner.name == "sys": + # sys.argv will never be inferable during static analysis + # It's value would be the args passed to the linter itself + yield util.Uninferable + else: + yield from owner.igetattr(self.attrname, context) except ( AttributeInferenceError, InferenceError, diff --git a/tests/test_inference.py b/tests/test_inference.py index 86fdbcf4ae..1a398c1a6c 100644 --- a/tests/test_inference.py +++ b/tests/test_inference.py @@ -7102,3 +7102,18 @@ def test_old_style_string_formatting_with_specs(self) -> None: inferred = next(node.infer()) assert isinstance(inferred, nodes.Const) assert inferred.value == "My name is Daniel, I'm 12.00" + + +def test_sys_argv_uninferable() -> None: + """Regression test for https://github.com/pylint-dev/pylint/issues/7710.""" + a: nodes.List = extract_node( + textwrap.dedent( + """ + import sys + + sys.argv""" + ) + ) + sys_argv_value = list(a._infer()) + assert len(sys_argv_value) == 1 + assert sys_argv_value[0] is util.Uninferable From 1f62beb2248f2f65eeb64cfe7a01a2db66243005 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 23 Sep 2023 15:37:42 -0400 Subject: [PATCH 11/13] Infer user-defined enum classes by checking if the class is a subtype of ``enum.Enum`` (#2277) (#2298) (cherry picked from commit c5352d5) Co-authored-by: Jacob Walls Co-authored-by: Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> --- ChangeLog | 4 +++ astroid/brain/brain_namedtuple_enum.py | 18 +------------ tests/brain/test_enum.py | 36 ++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1cfba953a2..c718af0773 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,10 @@ Release date: TBA Closes pylint-dev/pylint#8802 +* Infer user-defined enum classes by checking if the class is a subtype of ``enum.Enum``. + + Closes pylint-dev/pylint#8897 + * Fix inference of functions with ``@functools.lru_cache`` decorators without parentheses. diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py index 36b703610f..36ee653605 100644 --- a/astroid/brain/brain_namedtuple_enum.py +++ b/astroid/brain/brain_namedtuple_enum.py @@ -20,7 +20,6 @@ AstroidTypeError, AstroidValueError, InferenceError, - MroError, UseInferenceDefault, ) from astroid.manager import AstroidManager @@ -31,14 +30,6 @@ from typing_extensions import Final -ENUM_BASE_NAMES = { - "Enum", - "IntEnum", - "enum.Enum", - "enum.IntEnum", - "IntFlag", - "enum.IntFlag", -} ENUM_QNAME: Final[str] = "enum.Enum" TYPING_NAMEDTUPLE_QUALIFIED: Final = { "typing.NamedTuple", @@ -606,14 +597,7 @@ def _get_namedtuple_fields(node: nodes.Call) -> str: def _is_enum_subclass(cls: astroid.ClassDef) -> bool: """Return whether cls is a subclass of an Enum.""" - try: - return any( - klass.name in ENUM_BASE_NAMES - and getattr(klass.root(), "name", None) == "enum" - for klass in cls.mro() - ) - except MroError: - return False + return cls.is_subtype_of("enum.Enum") AstroidManager().register_transform( diff --git a/tests/brain/test_enum.py b/tests/brain/test_enum.py index 9d95d2ffbb..085d00c133 100644 --- a/tests/brain/test_enum.py +++ b/tests/brain/test_enum.py @@ -493,3 +493,39 @@ def pear(self): for node in (attribute_nodes[1], name_nodes[1]): with pytest.raises(InferenceError): node.inferred() + + def test_local_enum_child_class_inference(self) -> None: + """Originally reported in https://github.com/pylint-dev/pylint/issues/8897 + + Test that a user-defined enum class is inferred when it subclasses + another user-defined enum class. + """ + enum_class_node, enum_member_value_node = astroid.extract_node( + """ + import sys + + from enum import Enum + + if sys.version_info >= (3, 11): + from enum import StrEnum + else: + class StrEnum(str, Enum): + pass + + + class Color(StrEnum): #@ + RED = "red" + + + Color.RED.value #@ + """ + ) + assert "RED" in enum_class_node.locals + + enum_members = enum_class_node.locals["__members__"][0].items + assert len(enum_members) == 1 + _, name = enum_members[0] + assert name.name == "RED" + + inferred_enum_member_value_node = next(enum_member_value_node.infer()) + assert inferred_enum_member_value_node.value == "red" From 29b42e5e9745b172d5980511d14efeac745a5a82 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 23 Sep 2023 15:47:55 -0400 Subject: [PATCH 12/13] Bump astroid to 2.15.7, update changelog --- ChangeLog | 14 +++++++------- astroid/__pkginfo__.py | 2 +- tbump.toml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index c718af0773..5c003823e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,19 +2,14 @@ astroid's ChangeLog =================== -What's New in astroid 2.16.0? +What's New in astroid 2.15.8? ============================= Release date: TBA -* Fix a regression in 2.12.0 where settings in AstroidManager would be ignored. - Most notably this addresses pylint-dev/pylint#7433. - - Refs #2204 - What's New in astroid 2.15.7? ============================= -Release date: TBA +Release date: 2023-09-23 * Fix a crash when inferring a ``typing.TypeVar`` call. @@ -42,6 +37,11 @@ Release date: 2023-07-08 Closes pylint-dev/pylint#8748 +* Fix a regression in 2.12.0 where settings in AstroidManager would be ignored. + Most notably this addresses pylint-dev/pylint#7433. + + Refs #2204 + What's New in astroid 2.15.5? ============================= diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py index 467ef987aa..9244a941c6 100644 --- a/astroid/__pkginfo__.py +++ b/astroid/__pkginfo__.py @@ -2,5 +2,5 @@ # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt -__version__ = "2.15.6" +__version__ = "2.15.7" version = __version__ diff --git a/tbump.toml b/tbump.toml index 2a62def48d..fd1ad18098 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/PyCQA/astroid" [version] -current = "2.15.6" +current = "2.15.7" regex = ''' ^(?P0|[1-9]\d*) \. From ba7cf31d5521c58353d03bd0f94b96d0826dbcfe Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 23 Sep 2023 17:59:25 -0400 Subject: [PATCH 13/13] [skip ci] Add missing news Follow-up to ea78827. --- ChangeLog | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4a69e027df..5989889f74 100644 --- a/ChangeLog +++ b/ChangeLog @@ -218,10 +218,14 @@ Release date: TBA Refs pylint-dev/pylint#8598 +* Fix false positives for ``no-member`` and ``invalid-name`` when using the ``_name_``, ``_value_`` and ``_ignore_`` sunders in Enums. + + Closes pylint-dev/pylint#9015 + What's New in astroid 2.15.7? ============================= -Release date: TBA +Release date: 2023-09-23 * Fix a crash when inferring a ``typing.TypeVar`` call. @@ -231,15 +235,16 @@ Release date: TBA Closes pylint-dev/pylint#8897 -* Fix false positives for ``no-member`` and ``invalid-name`` when using the ``_name_``, ``_value_`` and ``_ignore_`` sunders in Enums. - - Closes pylint-dev/pylint#9015 - * Fix inference of functions with ``@functools.lru_cache`` decorators without parentheses. Closes pylint-dev/pylint#8868 +* Make ``sys.argv`` uninferable because it never is. (It's impossible to infer + the value it will have outside of static analysis where it's our own value.) + + Refs pylint-dev/pylint#7710 + What's New in astroid 2.15.6? ============================= @@ -262,6 +267,41 @@ Release date: 2023-07-08 Refs #2204 +What's New in astroid 2.15.7? +============================= +Release date: 2023-09-23 + +* Fix a crash when inferring a ``typing.TypeVar`` call. + + Closes pylint-dev/pylint#8802 + +* Infer user-defined enum classes by checking if the class is a subtype of ``enum.Enum``. + + Closes pylint-dev/pylint#8897 + +* Fix inference of functions with ``@functools.lru_cache`` decorators without + parentheses. + + Closes pylint-dev/pylint#8868 + + +What's New in astroid 2.15.6? +============================= +Release date: 2023-07-08 + +* Harden ``get_module_part()`` against ``"."``. + + Closes pylint-dev/pylint#8749 + +* Avoid expensive list/tuple multiplication operations that would result in ``MemoryError``. + + Closes pylint-dev/pylint#8748 + +* Fix a regression in 2.12.0 where settings in AstroidManager would be ignored. + Most notably this addresses pylint-dev/pylint#7433. + + Refs #2204 + What's New in astroid 2.15.5? =============================