Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion Doc/library/functools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ The :mod:`functools` module defines the following functions:
.. versionadded:: 3.2


.. decorator:: lru_cache(maxsize=128, typed=False)
.. decorator:: lru_cache(user_function)
Comment thread
rhettinger marked this conversation as resolved.
lru_cache(maxsize=128, typed=False)

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

If *user_function* is specified, it must be a callable. This allows the
*lru_cache* decorator to be applied directly to a user function, leaving
the *maxsize* at its default value of 128::

@lru_cache
def count_vowels(sentence):
sentence = sentence.casefold()
return sum(sentence.count(vowel) for vowel in 'aeiou')

If *maxsize* is set to ``None``, the LRU feature is disabled and the cache can
grow without bound. The LRU feature performs best when *maxsize* is a
power-of-two.
Expand Down Expand Up @@ -165,6 +175,9 @@ The :mod:`functools` module defines the following functions:
.. versionchanged:: 3.3
Added the *typed* option.

.. versionchanged:: 3.8
Added the *user_function* option.

.. decorator:: total_ordering

Given a class defining one or more rich comparison ordering methods, this
Expand Down
17 changes: 17 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,23 @@ where the DLL is stored (if a full or partial path is used to load the initial
DLL) and paths added by :func:`~os.add_dll_directory`.


functools
---------

:func:`functools.lru_cache` can now be used as a straight decorator rather
than as a function returning a decorator. So both of these are now supported::

@lru_cache
def f(x):
...

@lru_cache(maxsize=256)
def f(x):
...

(Contributed by Raymond Hettinger in :issue:`36772`.)


datetime
--------

Expand Down
12 changes: 8 additions & 4 deletions Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,14 +517,18 @@ def lru_cache(maxsize=128, typed=False):
# The internals of the lru_cache are encapsulated for thread safety and
# to allow the implementation to change (including a possible C version).

# Early detection of an erroneous call to @lru_cache without any arguments
# resulting in the inner function being passed to maxsize instead of an
# integer or None. Negative maxsize is treated as 0.
if isinstance(maxsize, int):
# Negative maxsize is treated as 0
if maxsize < 0:
maxsize = 0
elif callable(maxsize) and isinstance(typed, bool):
Comment thread
rhettinger marked this conversation as resolved.
# The user_function was passed in directly via the maxsize argument
user_function, maxsize = maxsize, 128
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
return update_wrapper(wrapper, user_function)
elif maxsize is not None:
raise TypeError('Expected maxsize to be an integer or None')
raise TypeError(
'Expected first argument to be an integer, a callable, or None')

def decorating_function(user_function):
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
Expand Down
19 changes: 12 additions & 7 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,18 @@ def f(x):
self.assertEqual(misses, 4)
self.assertEqual(currsize, 2)

def test_lru_no_args(self):
@self.module.lru_cache
def square(x):
return x ** 2

self.assertEqual(list(map(square, [10, 20, 10])),
[100, 400, 100])
self.assertEqual(square.cache_info().hits, 1)
self.assertEqual(square.cache_info().misses, 2)
self.assertEqual(square.cache_info().maxsize, 128)
self.assertEqual(square.cache_info().currsize, 2)

def test_lru_bug_35780(self):
# C version of the lru_cache was not checking to see if
# the user function call has already modified the cache
Expand Down Expand Up @@ -1582,13 +1594,6 @@ def __eq__(self, other):
self.assertEqual(test_func(DoubleEq(2)), # Trigger a re-entrant __eq__ call
DoubleEq(2)) # Verify the correct return value

def test_early_detection_of_bad_call(self):
# Issue #22184
with self.assertRaises(TypeError):
@functools.lru_cache
def f():
pass

def test_lru_method(self):
class X(int):
f_cnt = 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
functools.lru_cache() can now be used as a straight decorator in
addition to its existing usage as a function that returns a decorator.