Skip to content

Commit 528891c

Browse files
Fixes method-cache-max-size-none false positive (#7908)
For methods inheriting from ``Enum``. Closes #7857 Co-authored-by: Pierre Sassoulas <[email protected]>
1 parent b1012d4 commit 528891c

File tree

7 files changed

+33
-15
lines changed

7 files changed

+33
-15
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixes ``method-cache-max-size-none`` false positive for methods inheriting from ``Enum``.
2+
3+
Closes #7857

pylint/checkers/base/name_checker/checker.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -456,10 +456,8 @@ def visit_assignname(self, node: nodes.AssignName) -> None:
456456
elif isinstance(frame, nodes.ClassDef):
457457
if not list(frame.local_attr_ancestors(node.name)):
458458
for ancestor in frame.ancestors():
459-
if (
460-
ancestor.name == "Enum"
461-
and ancestor.root().name == "enum"
462-
or utils.is_assign_name_annotated_with(node, "Final")
459+
if utils.is_enum(ancestor) or utils.is_assign_name_annotated_with(
460+
node, "Final"
463461
):
464462
self._check_name("class_const", node.name, node)
465463
break

pylint/checkers/design_analysis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from astroid import nodes
1616

1717
from pylint.checkers import BaseChecker
18-
from pylint.checkers.utils import only_required_for_messages
18+
from pylint.checkers.utils import is_enum, only_required_for_messages
1919
from pylint.typing import MessageDefinitionTuple
2020

2121
if TYPE_CHECKING:
@@ -175,7 +175,7 @@ def _is_exempt_from_public_methods(node: astroid.ClassDef) -> bool:
175175

176176
# If it's a typing.Namedtuple, typing.TypedDict or an Enum
177177
for ancestor in node.ancestors():
178-
if ancestor.name == "Enum" and ancestor.root().name == "enum":
178+
if is_enum(ancestor):
179179
return True
180180
if ancestor.qname() in (TYPING_NAMEDTUPLE, TYPING_TYPEDDICT):
181181
return True

pylint/checkers/stdlib.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,13 +592,17 @@ def visit_boolop(self, node: nodes.BoolOp) -> None:
592592
)
593593
def visit_functiondef(self, node: nodes.FunctionDef) -> None:
594594
if node.decorators and isinstance(node.parent, nodes.ClassDef):
595-
self._check_lru_cache_decorators(node.decorators)
595+
self._check_lru_cache_decorators(node)
596596
self._check_dispatch_decorators(node)
597597

598-
def _check_lru_cache_decorators(self, decorators: nodes.Decorators) -> None:
598+
def _check_lru_cache_decorators(self, node: nodes.FunctionDef) -> None:
599599
"""Check if instance methods are decorated with functools.lru_cache."""
600+
if any(utils.is_enum(ancestor) for ancestor in node.parent.ancestors()):
601+
# method of class inheriting from Enum is exempt from this check.
602+
return
603+
600604
lru_cache_nodes: list[nodes.NodeNG] = []
601-
for d_node in decorators.nodes:
605+
for d_node in node.decorators.nodes:
602606
try:
603607
for infered_node in d_node.infer():
604608
q_name = infered_node.qname()

pylint/checkers/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,10 @@ def is_attribute_typed_annotation(
17241724
return False
17251725

17261726

1727+
def is_enum(node: nodes.ClassDef) -> bool:
1728+
return node.name == "Enum" and node.root().name == "enum" # type: ignore[no-any-return]
1729+
1730+
17271731
def is_assign_name_annotated_with(node: nodes.AssignName, typing_name: str) -> bool:
17281732
"""Test if AssignName node has `typing_name` annotation.
17291733

tests/functional/m/method_cache_max_size_none.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import functools as aliased_functools
77
from functools import lru_cache
88
from functools import lru_cache as aliased_cache
9+
from enum import Enum
910

1011

1112
@lru_cache
@@ -78,3 +79,11 @@ def my_func(self, param):
7879
@lru_cache(maxsize=None)
7980
def my_func(param):
8081
return param + 1
82+
83+
84+
class Class(Enum):
85+
A = 1
86+
87+
@lru_cache(maxsize=None)
88+
def func(self) -> None:
89+
pass
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
method-cache-max-size-none:25:5:25:20:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
2-
method-cache-max-size-none:29:5:29:30:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
3-
method-cache-max-size-none:33:5:33:38:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
4-
method-cache-max-size-none:37:5:37:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
5-
method-cache-max-size-none:42:5:42:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
1+
method-cache-max-size-none:26:5:26:20:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
2+
method-cache-max-size-none:30:5:30:30:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
3+
method-cache-max-size-none:34:5:34:38:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
4+
method-cache-max-size-none:38:5:38:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
65
method-cache-max-size-none:43:5:43:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
7-
method-cache-max-size-none:73:5:73:40:MyClassWithMethodsAndMaxSize.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
6+
method-cache-max-size-none:44:5:44:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
7+
method-cache-max-size-none:74:5:74:40:MyClassWithMethodsAndMaxSize.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE

0 commit comments

Comments
 (0)