Issue 10593: Adopt Nick's suggestion for an lru_cache with maxsize=None.
diff --git a/Lib/functools.py b/Lib/functools.py
index 0fbf3cd..d450634 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -119,6 +119,9 @@
def lru_cache(maxsize=100):
"""Least-recently-used cache decorator.
+ If *maxsize* is set to None, the LRU features are disabled and the cache
+ can grow without bound.
+
Arguments to the cached function must be hashable.
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
@@ -136,32 +139,51 @@
def decorating_function(user_function,
tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
- cache = OrderedDict() # ordered least recent to most recent
- cache_popitem = cache.popitem
- cache_renew = cache.move_to_end
hits = misses = 0
kwd_mark = object() # separates positional and keyword args
lock = Lock()
- @wraps(user_function)
- def wrapper(*args, **kwds):
- nonlocal hits, misses
- key = args
- if kwds:
- key += (kwd_mark,) + tuple(sorted(kwds.items()))
- try:
- with lock:
+ if maxsize is None:
+ cache = dict() # simple cache without ordering or size limit
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ nonlocal hits, misses
+ key = args
+ if kwds:
+ key += (kwd_mark,) + tuple(sorted(kwds.items()))
+ try:
result = cache[key]
- cache_renew(key) # record recent use of this key
hits += 1
- except KeyError:
- result = user_function(*args, **kwds)
- with lock:
- cache[key] = result # record recent use of this key
+ except KeyError:
+ result = user_function(*args, **kwds)
+ cache[key] = result
misses += 1
- if len(cache) > maxsize:
- cache_popitem(0) # purge least recently used cache entry
- return result
+ return result
+ else:
+ cache = OrderedDict() # ordered least recent to most recent
+ cache_popitem = cache.popitem
+ cache_renew = cache.move_to_end
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ nonlocal hits, misses
+ key = args
+ if kwds:
+ key += (kwd_mark,) + tuple(sorted(kwds.items()))
+ try:
+ with lock:
+ result = cache[key]
+ cache_renew(key) # record recent use of this key
+ hits += 1
+ except KeyError:
+ result = user_function(*args, **kwds)
+ with lock:
+ cache[key] = result # record recent use of this key
+ misses += 1
+ if len(cache) > maxsize:
+ cache_popitem(0) # purge least recently used cache entry
+ return result
def cache_info():
"""Report cache statistics"""
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index 8f48e9e..e5921a7 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -586,6 +586,20 @@
self.assertEqual(misses, 4)
self.assertEqual(currsize, 2)
+ def test_lru_with_maxsize_none(self):
+ @functools.lru_cache(maxsize=None)
+ def fib(n):
+ if n < 2:
+ return n
+ return fib(n-1) + fib(n-2)
+ self.assertEqual([fib(n) for n in range(16)],
+ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
+ self.assertEqual(fib.cache_info(),
+ functools._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
+ fib.cache_clear()
+ self.assertEqual(fib.cache_info(),
+ functools._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
+
def test_main(verbose=None):
test_classes = (
TestPartial,