Skip to content

Commit 5cfbcbf

Browse files
[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 ec912f9)
1 parent 28ef038 commit 5cfbcbf

File tree

3 files changed

+24
-23
lines changed

3 files changed

+24
-23
lines changed

ChangeLog

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,15 @@ What's New in astroid 2.15.7?
1616
=============================
1717
Release date: TBA
1818

19-
20-
21-
What's New in astroid 2.15.7?
22-
=============================
23-
Release date: 2023-07-08
24-
2519
* Fix a crash when inferring a ``typing.TypeVar`` call.
2620

2721
Closes pylint-dev/pylint#8802
2822

23+
* Fix inference of functions with ``@functools.lru_cache`` decorators without
24+
parentheses.
25+
26+
Closes pylint-dev/pylint#8868
27+
2928

3029
What's New in astroid 2.15.6?
3130
=============================

astroid/brain/brain_functools.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,17 @@ def _looks_like_lru_cache(node) -> bool:
129129
if not node.decorators:
130130
return False
131131
for decorator in node.decorators.nodes:
132-
if not isinstance(decorator, Call):
132+
if not isinstance(decorator, (Attribute, Call)):
133133
continue
134134
if _looks_like_functools_member(decorator, "lru_cache"):
135135
return True
136136
return False
137137

138138

139-
def _looks_like_functools_member(node, member) -> bool:
140-
"""Check if the given Call node is a functools.partial call."""
139+
def _looks_like_functools_member(node: Attribute | Call, member: str) -> bool:
140+
"""Check if the given Call node is the wanted member of functools."""
141+
if isinstance(node, Attribute):
142+
return node.attrname == member
141143
if isinstance(node.func, Name):
142144
return node.func.name == member
143145
if isinstance(node.func, Attribute):

tests/test_object_model.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -809,26 +809,26 @@ def test_str_argument_not_required(self) -> None:
809809
assert not args.elts
810810

811811

812-
class LruCacheModelTest(unittest.TestCase):
813-
def test_lru_cache(self) -> None:
814-
ast_nodes = builder.extract_node(
815-
"""
812+
@pytest.mark.parametrize("parentheses", (True, False))
813+
def test_lru_cache(parentheses) -> None:
814+
ast_nodes = builder.extract_node(
815+
f"""
816816
import functools
817817
class Foo(object):
818-
@functools.lru_cache()
818+
@functools.lru_cache{"()" if parentheses else ""}
819819
def foo():
820820
pass
821821
f = Foo()
822822
f.foo.cache_clear #@
823823
f.foo.__wrapped__ #@
824824
f.foo.cache_info() #@
825825
"""
826-
)
827-
assert isinstance(ast_nodes, list)
828-
cache_clear = next(ast_nodes[0].infer())
829-
self.assertIsInstance(cache_clear, astroid.BoundMethod)
830-
wrapped = next(ast_nodes[1].infer())
831-
self.assertIsInstance(wrapped, astroid.FunctionDef)
832-
self.assertEqual(wrapped.name, "foo")
833-
cache_info = next(ast_nodes[2].infer())
834-
self.assertIsInstance(cache_info, astroid.Instance)
826+
)
827+
assert isinstance(ast_nodes, list)
828+
cache_clear = next(ast_nodes[0].infer())
829+
assert isinstance(cache_clear, astroid.BoundMethod)
830+
wrapped = next(ast_nodes[1].infer())
831+
assert isinstance(wrapped, astroid.FunctionDef)
832+
assert wrapped.name == "foo"
833+
cache_info = next(ast_nodes[2].infer())
834+
assert isinstance(cache_info, astroid.Instance)

0 commit comments

Comments
 (0)