Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b821868

Browse files
authored
bpo-36772 Allow lru_cache to be used as decorator without making a function call (GH-13048)
1 parent aaf47ca commit b821868

5 files changed

Lines changed: 53 additions & 12 deletions

File tree

Doc/library/functools.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ The :mod:`functools` module defines the following functions:
7676
.. versionadded:: 3.2
7777

7878

79-
.. decorator:: lru_cache(maxsize=128, typed=False)
79+
.. decorator:: lru_cache(user_function)
80+
lru_cache(maxsize=128, typed=False)
8081

8182
Decorator to wrap a function with a memoizing callable that saves up to the
8283
*maxsize* most recent calls. It can save time when an expensive or I/O bound
@@ -90,6 +91,15 @@ The :mod:`functools` module defines the following functions:
9091
differ in their keyword argument order and may have two separate cache
9192
entries.
9293

94+
If *user_function* is specified, it must be a callable. This allows the
95+
*lru_cache* decorator to be applied directly to a user function, leaving
96+
the *maxsize* at its default value of 128::
97+
98+
@lru_cache
99+
def count_vowels(sentence):
100+
sentence = sentence.casefold()
101+
return sum(sentence.count(vowel) for vowel in 'aeiou')
102+
93103
If *maxsize* is set to ``None``, the LRU feature is disabled and the cache can
94104
grow without bound. The LRU feature performs best when *maxsize* is a
95105
power-of-two.
@@ -165,6 +175,9 @@ The :mod:`functools` module defines the following functions:
165175
.. versionchanged:: 3.3
166176
Added the *typed* option.
167177

178+
.. versionchanged:: 3.8
179+
Added the *user_function* option.
180+
168181
.. decorator:: total_ordering
169182

170183
Given a class defining one or more rich comparison ordering methods, this

Doc/whatsnew/3.8.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,23 @@ where the DLL is stored (if a full or partial path is used to load the initial
291291
DLL) and paths added by :func:`~os.add_dll_directory`.
292292

293293

294+
functools
295+
---------
296+
297+
:func:`functools.lru_cache` can now be used as a straight decorator rather
298+
than as a function returning a decorator. So both of these are now supported::
299+
300+
@lru_cache
301+
def f(x):
302+
...
303+
304+
@lru_cache(maxsize=256)
305+
def f(x):
306+
...
307+
308+
(Contributed by Raymond Hettinger in :issue:`36772`.)
309+
310+
294311
datetime
295312
--------
296313

Lib/functools.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -518,14 +518,18 @@ def lru_cache(maxsize=128, typed=False):
518518
# The internals of the lru_cache are encapsulated for thread safety and
519519
# to allow the implementation to change (including a possible C version).
520520

521-
# Early detection of an erroneous call to @lru_cache without any arguments
522-
# resulting in the inner function being passed to maxsize instead of an
523-
# integer or None. Negative maxsize is treated as 0.
524521
if isinstance(maxsize, int):
522+
# Negative maxsize is treated as 0
525523
if maxsize < 0:
526524
maxsize = 0
525+
elif callable(maxsize) and isinstance(typed, bool):
526+
# The user_function was passed in directly via the maxsize argument
527+
user_function, maxsize = maxsize, 128
528+
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
529+
return update_wrapper(wrapper, user_function)
527530
elif maxsize is not None:
528-
raise TypeError('Expected maxsize to be an integer or None')
531+
raise TypeError(
532+
'Expected first argument to be an integer, a callable, or None')
529533

530534
def decorating_function(user_function):
531535
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)

Lib/test/test_functools.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,18 @@ def f(x):
12511251
self.assertEqual(misses, 4)
12521252
self.assertEqual(currsize, 2)
12531253

1254+
def test_lru_no_args(self):
1255+
@self.module.lru_cache
1256+
def square(x):
1257+
return x ** 2
1258+
1259+
self.assertEqual(list(map(square, [10, 20, 10])),
1260+
[100, 400, 100])
1261+
self.assertEqual(square.cache_info().hits, 1)
1262+
self.assertEqual(square.cache_info().misses, 2)
1263+
self.assertEqual(square.cache_info().maxsize, 128)
1264+
self.assertEqual(square.cache_info().currsize, 2)
1265+
12541266
def test_lru_bug_35780(self):
12551267
# C version of the lru_cache was not checking to see if
12561268
# the user function call has already modified the cache
@@ -1582,13 +1594,6 @@ def __eq__(self, other):
15821594
self.assertEqual(test_func(DoubleEq(2)), # Trigger a re-entrant __eq__ call
15831595
DoubleEq(2)) # Verify the correct return value
15841596

1585-
def test_early_detection_of_bad_call(self):
1586-
# Issue #22184
1587-
with self.assertRaises(TypeError):
1588-
@functools.lru_cache
1589-
def f():
1590-
pass
1591-
15921597
def test_lru_method(self):
15931598
class X(int):
15941599
f_cnt = 0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
functools.lru_cache() can now be used as a straight decorator in
2+
addition to its existing usage as a function that returns a decorator.

0 commit comments

Comments
 (0)