cachetools 5.5.2__tar.gz → 6.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {cachetools-5.5.2 → cachetools-6.0.0}/CHANGELOG.rst +29 -0
  2. {cachetools-5.5.2/src/cachetools.egg-info → cachetools-6.0.0}/PKG-INFO +14 -12
  3. {cachetools-5.5.2 → cachetools-6.0.0}/README.rst +10 -7
  4. {cachetools-5.5.2 → cachetools-6.0.0}/docs/conf.py +2 -0
  5. {cachetools-5.5.2 → cachetools-6.0.0}/docs/index.rst +80 -58
  6. {cachetools-5.5.2 → cachetools-6.0.0}/setup.cfg +1 -3
  7. {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools/__init__.py +23 -112
  8. cachetools-5.5.2/src/cachetools/_decorators.py → cachetools-6.0.0/src/cachetools/_cached.py +106 -13
  9. cachetools-6.0.0/src/cachetools/_cachedmethod.py +128 -0
  10. {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools/func.py +5 -25
  11. {cachetools-5.5.2 → cachetools-6.0.0/src/cachetools.egg-info}/PKG-INFO +14 -12
  12. {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools.egg-info/SOURCES.txt +2 -2
  13. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_cached.py +163 -0
  14. cachetools-6.0.0/tests/test_cachedmethod.py +383 -0
  15. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_func.py +0 -12
  16. {cachetools-5.5.2 → cachetools-6.0.0}/tox.ini +2 -4
  17. cachetools-5.5.2/tests/test_cachedmethod.py +0 -234
  18. cachetools-5.5.2/tests/test_mru.py +0 -63
  19. {cachetools-5.5.2 → cachetools-6.0.0}/LICENSE +0 -0
  20. {cachetools-5.5.2 → cachetools-6.0.0}/MANIFEST.in +0 -0
  21. {cachetools-5.5.2 → cachetools-6.0.0}/pyproject.toml +0 -0
  22. {cachetools-5.5.2 → cachetools-6.0.0}/setup.py +0 -0
  23. {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools/keys.py +0 -0
  24. {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools.egg-info/dependency_links.txt +0 -0
  25. {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools.egg-info/top_level.txt +0 -0
  26. {cachetools-5.5.2 → cachetools-6.0.0}/tests/__init__.py +0 -0
  27. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_cache.py +0 -0
  28. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_fifo.py +0 -0
  29. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_keys.py +0 -0
  30. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_lfu.py +0 -0
  31. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_lru.py +0 -0
  32. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_rr.py +0 -0
  33. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_tlru.py +0 -0
  34. {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_ttl.py +0 -0
@@ -1,3 +1,32 @@
1
+ v6.0.0 (2025-05-23)
2
+ ===================
3
+
4
+ - Require Python 3.9 or later (breaking change).
5
+
6
+ - Remove ``MRUCache`` and the ``@func.mru_cache`` decorator (breaking
7
+ change).
8
+
9
+ - Add an optional ``condition`` parameter to the ``@cached`` and
10
+ ``@cachedmethod`` decorators, which, when used with a
11
+ ``threading.Condition`` instance, should improve `cache stampede
12
+ <https://en.wikipedia.org/wiki/Cache_stampede>`_ issues in massively
13
+ parallel environments. Note that this will inflict some performance
14
+ penalty, and therefore has to be enabled explicitly.
15
+
16
+ - Convert the ``cachetools.func`` decorators to use a
17
+ ``threading.Condition`` instance to deal with `cache stampede
18
+ <https://en.wikipedia.org/wiki/Cache_stampede>`_ issues. Note that
19
+ this *may* result in a noticable performance degradation, depending
20
+ on your actual use case.
21
+
22
+ - Deprecate support for ``cache(self)`` returning ``None`` to suppress
23
+ caching with the ``@cachedmethod`` decorator.
24
+
25
+ - Improve documentation.
26
+
27
+ - Update CI environment.
28
+
29
+
1
30
  v5.5.2 (2025-02-20)
2
31
  ===================
3
32
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: cachetools
3
- Version: 5.5.2
3
+ Version: 6.0.0
4
4
  Summary: Extensible memoizing collections and decorators
5
5
  Home-page: https://github.com/tkem/cachetools/
6
6
  Author: Thomas Kemmer
@@ -13,16 +13,15 @@ Classifier: License :: OSI Approved :: MIT License
13
13
  Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.7
17
- Classifier: Programming Language :: Python :: 3.8
18
16
  Classifier: Programming Language :: Python :: 3.9
19
17
  Classifier: Programming Language :: Python :: 3.10
20
18
  Classifier: Programming Language :: Python :: 3.11
21
19
  Classifier: Programming Language :: Python :: 3.12
22
20
  Classifier: Programming Language :: Python :: 3.13
23
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
- Requires-Python: >=3.7
22
+ Requires-Python: >=3.9
25
23
  License-File: LICENSE
24
+ Dynamic: license-file
26
25
 
27
26
  cachetools
28
27
  ========================================================================
@@ -116,11 +115,12 @@ Project Resources
116
115
  Related Projects
117
116
  ------------------------------------------------------------------------
118
117
 
119
- - asyncache_: Helpers to use cachetools with async functions
120
- - cacheing_: Pure Python Cacheing Library
121
- - CacheToolsUtils_: Cachetools Utilities
122
- - kids.cache_: Kids caching library
123
- - shelved-cache_: Persistent cache for Python cachetools
118
+ - asyncache_: Helpers to use cachetools_ with asyncio.
119
+ - cacheing_: Pure Python Cacheing Library.
120
+ - CacheToolsUtils_: Stackable cache classes for sharing, encryption,
121
+ statistics *and more* on top of cachetools_, redis_ and memcached_.
122
+ - shelved-cache_: Persistent cache implementation for Python
123
+ cachetools_.
124
124
 
125
125
 
126
126
  License
@@ -145,7 +145,9 @@ Licensed under the `MIT License`_.
145
145
  .. _MIT License: https://raw.github.com/tkem/cachetools/master/LICENSE
146
146
 
147
147
  .. _asyncache: https://pypi.org/project/asyncache/
148
- .. _cacheing: https://github.com/breid48/cacheing
148
+ .. _cacheing: https://pypi.org/project/cacheing/
149
149
  .. _CacheToolsUtils: https://pypi.org/project/CacheToolsUtils/
150
- .. _kids.cache: https://pypi.org/project/kids.cache/
151
150
  .. _shelved-cache: https://pypi.org/project/shelved-cache/
151
+ .. _cachetools: https://pypi.org/project/cachetools/
152
+ .. _redis: https://redis.io/
153
+ .. _memcached: https://memcached.org/
@@ -90,11 +90,12 @@ Project Resources
90
90
  Related Projects
91
91
  ------------------------------------------------------------------------
92
92
 
93
- - asyncache_: Helpers to use cachetools with async functions
94
- - cacheing_: Pure Python Cacheing Library
95
- - CacheToolsUtils_: Cachetools Utilities
96
- - kids.cache_: Kids caching library
97
- - shelved-cache_: Persistent cache for Python cachetools
93
+ - asyncache_: Helpers to use cachetools_ with asyncio.
94
+ - cacheing_: Pure Python Cacheing Library.
95
+ - CacheToolsUtils_: Stackable cache classes for sharing, encryption,
96
+ statistics *and more* on top of cachetools_, redis_ and memcached_.
97
+ - shelved-cache_: Persistent cache implementation for Python
98
+ cachetools_.
98
99
 
99
100
 
100
101
  License
@@ -119,7 +120,9 @@ Licensed under the `MIT License`_.
119
120
  .. _MIT License: https://raw.github.com/tkem/cachetools/master/LICENSE
120
121
 
121
122
  .. _asyncache: https://pypi.org/project/asyncache/
122
- .. _cacheing: https://github.com/breid48/cacheing
123
+ .. _cacheing: https://pypi.org/project/cacheing/
123
124
  .. _CacheToolsUtils: https://pypi.org/project/CacheToolsUtils/
124
- .. _kids.cache: https://pypi.org/project/kids.cache/
125
125
  .. _shelved-cache: https://pypi.org/project/shelved-cache/
126
+ .. _cachetools: https://pypi.org/project/cachetools/
127
+ .. _redis: https://redis.io/
128
+ .. _memcached: https://memcached.org/
@@ -26,8 +26,10 @@ extensions = [
26
26
  "sphinx.ext.autodoc",
27
27
  "sphinx.ext.coverage",
28
28
  "sphinx.ext.doctest",
29
+ "sphinx.ext.intersphinx",
29
30
  "sphinx.ext.todo",
30
31
  ]
31
32
  exclude_patterns = ["_build"]
32
33
  master_doc = "index"
33
34
  html_theme = "classic"
35
+ intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
@@ -52,7 +52,7 @@ Therefore, :class:`Cache` provides a :meth:`getsizeof` method, which
52
52
  returns the size of a given `value`. The default implementation of
53
53
  :meth:`getsizeof` returns :const:`1` irrespective of its argument,
54
54
  making the cache's size equal to the number of its items, or
55
- ``len(cache)``. For convenience, all cache classes accept an optional
55
+ `len(cache)`. For convenience, all cache classes accept an optional
56
56
  named constructor parameter `getsizeof`, which may specify a function
57
57
  of one argument used to retrieve the size of an item's value.
58
58
 
@@ -98,18 +98,6 @@ inserted into the cache.
98
98
  This class discards the least recently used items first to make
99
99
  space when necessary.
100
100
 
101
- .. autoclass:: MRUCache(maxsize, getsizeof=None)
102
- :members: popitem
103
-
104
- This class discards the most recently used items first to make
105
- space when necessary.
106
-
107
- .. deprecated:: 5.4
108
-
109
- `MRUCache` has been deprecated due to lack of use, to reduce
110
- maintenance. Please choose another cache implementation that suits
111
- your needs.
112
-
113
101
  .. autoclass:: RRCache(maxsize, choice=random.choice, getsizeof=None)
114
102
  :members: choice, popitem
115
103
 
@@ -174,7 +162,7 @@ inserted into the cache.
174
162
  value of `timer()`.
175
163
 
176
164
  .. testcode::
177
-
165
+
178
166
  def my_ttu(_key, value, now):
179
167
  # assume value.ttu contains the item's time-to-use in seconds
180
168
  # note that the _key argument is ignored in this example
@@ -311,7 +299,7 @@ often called with the same arguments:
311
299
  >>> fib(42)
312
300
  267914296
313
301
 
314
- .. decorator:: cached(cache, key=cachetools.keys.hashkey, lock=None, info=False)
302
+ .. decorator:: cached(cache, key=cachetools.keys.hashkey, lock=None, condition=None, info=False)
315
303
 
316
304
  Decorator to wrap a function with a memoizing callable that saves
317
305
  results in a cache.
@@ -333,19 +321,45 @@ often called with the same arguments:
333
321
  implementing the `context manager`_ protocol. Any access to the
334
322
  cache will then be nested in a ``with lock:`` statement. This can
335
323
  be used for synchronizing thread access to the cache by providing a
336
- :class:`threading.Lock` instance, for example.
324
+ :class:`threading.Lock` or :class:`threading.RLock` instance, for
325
+ example.
337
326
 
338
327
  .. note::
339
328
 
340
329
  The `lock` context manager is used only to guard access to the
341
330
  cache object. The underlying wrapped function will be called
342
- outside the `with` statement, and must be thread-safe by itself.
331
+ outside the `with` statement to allow concurrent execution, and
332
+ therefore must be `thread-safe`_ by itself.
333
+
334
+ If `condition` is not :const:`None`, it must specify a `condition
335
+ variable`_, i.e. an object providing :func:`wait()`,
336
+ :func:`wait_for()`, :func:`notify()` and :func:`notify_all()`
337
+ methods as defined by :class:`threading.Condition`. Using a
338
+ `condition` variable will prevent concurrent execution of the
339
+ wrapped function with *identical* parameters, or cache keys.
340
+ Instead, a calling thread will check if an identical function call
341
+ is already executing, and will then :func:`wait()` for the pending
342
+ call to finish. The executing thread will :func:`notify()` any
343
+ waiting threads as soon as the function completes, which will then
344
+ return the cached function result.
345
+
346
+ .. note::
347
+
348
+ Although providing a `lock` alone is generally sufficient to
349
+ make :func:`cached` `thread-safe`_, it may still be subject to
350
+ `cache stampede`_ issues under high load, depending on your
351
+ actual use case. Providing a `condition` variable will mitigate
352
+ these situations, but will inflict some performance penalty.
353
+
354
+ If no separate `lock` parameter is provided, `condition` must also
355
+ implement the `context manager`_ protocol, and will also be used to
356
+ guard access to the cache.
343
357
 
344
- The decorator's `cache`, `key` and `lock` parameters are also
345
- available as :attr:`cache`, :attr:`cache_key` and
346
- :attr:`cache_lock` attributes of the memoizing wrapper function.
347
- These can be used for clearing the cache or invalidating individual
348
- cache items, for example.
358
+ The decorator's `cache`, `key`, `lock` and `condition` parameters
359
+ are also available as :attr:`cache`, :attr:`cache_key`,
360
+ :attr:`cache_lock` and :attr:`cache_condition` attributes of the
361
+ memoizing wrapper function. These can be used for clearing the
362
+ cache or invalidating individual cache items, for example.
349
363
 
350
364
  .. testcode::
351
365
 
@@ -383,8 +397,8 @@ often called with the same arguments:
383
397
 
384
398
  .. note::
385
399
 
386
- Note that this will inflict a - probably minor - performance
387
- penalty, so it has to be explicitly enabled.
400
+ Note that this will inflict some performance penalty, so it has
401
+ to be enabled explicitly.
388
402
 
389
403
  .. doctest::
390
404
  :pyversion: >= 3
@@ -474,22 +488,23 @@ often called with the same arguments:
474
488
  print(e, "-", _get_pep_wrapped.cache_info())
475
489
 
476
490
 
477
- .. decorator:: cachedmethod(cache, key=cachetools.keys.methodkey, lock=None)
491
+ .. decorator:: cachedmethod(cache, key=cachetools.keys.methodkey, lock=None, condition=None)
478
492
 
479
493
  Decorator to wrap a class or instance method with a memoizing
480
494
  callable that saves results in a (possibly shared) cache.
481
495
 
482
496
  The main difference between this and the :func:`cached` function
483
- decorator is that `cache` and `lock` are not passed objects, but
484
- functions. Both will be called with :const:`self` (or :const:`cls`
485
- for class methods) as their sole argument to retrieve the cache or
486
- lock object for the method's respective instance or class.
497
+ decorator is that `cache`, `lock` and `condition` are not passed
498
+ objects, but functions. Those will be called with :const:`self`
499
+ (or :const:`cls` for class methods) as their sole argument to
500
+ retrieve the cache, lock, or condition object for the method's
501
+ respective instance or class.
487
502
 
488
503
  .. note::
489
504
 
490
505
  As with :func:`cached`, the context manager obtained by calling
491
- ``lock(self)`` will only guard access to the cache itself. It
492
- is the user's responsibility to handle concurrent calls to the
506
+ `lock(self)` will only guard access to the cache itself. It is
507
+ the user's responsibility to handle concurrent calls to the
493
508
  underlying wrapped method in a multithreaded environment.
494
509
 
495
510
  The `key` function will be called as `key(self, *args, **kwargs)`
@@ -507,6 +522,9 @@ often called with the same arguments:
507
522
 
508
523
  .. testcode::
509
524
 
525
+ from cachetools.keys import hashkey
526
+ from functools import partial
527
+
510
528
  class CachedPEPs:
511
529
 
512
530
  def __init__(self, cachesize):
@@ -535,19 +553,22 @@ often called with the same arguments:
535
553
 
536
554
  .. testcode::
537
555
 
556
+ from cachetools.keys import methodkey
557
+ from functools import partial
558
+
538
559
  class CachedReferences:
539
560
 
540
561
  def __init__(self, cachesize):
541
562
  self.cache = LRUCache(maxsize=cachesize)
542
563
 
543
- @cachedmethod(lambda self: self.cache, key=partial(hashkey, 'pep'))
564
+ @cachedmethod(lambda self: self.cache, key=partial(methodkey, method='pep'))
544
565
  def get_pep(self, num):
545
566
  """Retrieve text of a Python Enhancement Proposal"""
546
567
  url = 'http://www.python.org/dev/peps/pep-%04d/' % num
547
568
  with urllib.request.urlopen(url) as s:
548
569
  return s.read()
549
570
 
550
- @cachedmethod(lambda self: self.cache, key=partial(hashkey, 'rfc'))
571
+ @cachedmethod(lambda self: self.cache, key=partial(methodkey, method='rfc'))
551
572
  def get_rfc(self, num):
552
573
  """Retrieve text of an IETF Request for Comments"""
553
574
  url = 'https://tools.ietf.org/rfc/rfc%d.txt' % num
@@ -555,15 +576,26 @@ often called with the same arguments:
555
576
  return s.read()
556
577
 
557
578
  docs = CachedReferences(cachesize=100)
558
- print("PEP #1: %s" % docs.get_pep(1))
559
- print("RFC #1: %s" % docs.get_rfc(1))
579
+ print("PEP #20: %s" % docs.get_pep(20))
580
+ print("RFC #20: %s" % docs.get_rfc(20))
581
+ assert len(docs.cache) == 2
560
582
 
561
583
  .. testoutput::
562
584
  :hide:
563
585
  :options: +ELLIPSIS
564
586
 
565
- PEP #1: ...
566
- RFC #1: ...
587
+ PEP #20: ...
588
+ RFC #20: ...
589
+
590
+ Note how keyword arguments are used with :func:`functools.partial`
591
+ to create distinct cache keys, to avoid issues with
592
+ :func:`methodkey` skipping its initial `self` argument.
593
+
594
+ .. deprecated:: 6.0
595
+
596
+ Support for `cache(self)` returning :const:`None` to suppress any
597
+ caching has been deprecated. `cache(self)` should always return a
598
+ valid cache object.
567
599
 
568
600
 
569
601
  *****************************************************************
@@ -590,8 +622,7 @@ functions with the :func:`cached` and :func:`cachedmethod` decorators:
590
622
 
591
623
  This function is similar to :func:`hashkey`, but arguments of
592
624
  different types will yield distinct cache keys. For example,
593
- ``typedkey(3)`` and ``typedkey(3.0)`` will return different
594
- results.
625
+ `typedkey(3)` and `typedkey(3.0)` will return different results.
595
626
 
596
627
  .. autofunction:: typedmethodkey
597
628
 
@@ -643,8 +674,8 @@ without bound.
643
674
 
644
675
  If the optional argument `typed` is set to :const:`True`, function
645
676
  arguments of different types will be cached separately. For example,
646
- ``f(3)`` and ``f(3.0)`` will be treated as distinct calls with
647
- distinct results.
677
+ `f(3)` and `f(3.0)` will be treated as distinct calls with distinct
678
+ results.
648
679
 
649
680
  If a `user_function` is specified instead, it must be a callable.
650
681
  This allows the decorator to be applied directly to a user function,
@@ -688,18 +719,6 @@ all the decorators in this module are thread-safe by default.
688
719
  saves up to `maxsize` results based on a Least Recently Used (LRU)
689
720
  algorithm.
690
721
 
691
- .. decorator:: mru_cache(user_function)
692
- mru_cache(maxsize=128, typed=False)
693
-
694
- Decorator that wraps a function with a memoizing callable that
695
- saves up to `maxsize` results based on a Most Recently Used (MRU)
696
- algorithm.
697
-
698
- .. deprecated:: 5.4
699
-
700
- The `mru_cache` decorator has been deprecated due to lack of use.
701
- Please choose a decorator based on some other algorithm.
702
-
703
722
  .. decorator:: rr_cache(user_function)
704
723
  rr_cache(maxsize=128, choice=random.choice, typed=False)
705
724
 
@@ -715,8 +734,11 @@ all the decorators in this module are thread-safe by default.
715
734
  algorithm with a per-item time-to-live (TTL) value.
716
735
 
717
736
 
718
- .. _@lru_cache: http://docs.python.org/3/library/functools.html#functools.lru_cache
719
- .. _cache algorithm: http://en.wikipedia.org/wiki/Cache_algorithms
720
- .. _context manager: http://docs.python.org/dev/glossary.html#term-context-manager
721
- .. _mapping: http://docs.python.org/dev/glossary.html#term-mapping
722
- .. _mutable: http://docs.python.org/dev/glossary.html#term-mutable
737
+ .. _@lru_cache: https://docs.python.org/3/library/functools.html#functools.lru_cache
738
+ .. _cache algorithm: https://en.wikipedia.org/wiki/Cache_algorithms
739
+ .. _cache stampede: https://en.wikipedia.org/wiki/Cache_stampede
740
+ .. _condition variable: https://docs.python.org/3/library/threading.html#condition-objects
741
+ .. _context manager: https://docs.python.org/dev/glossary.html#term-context-manager
742
+ .. _mapping: https://docs.python.org/dev/glossary.html#term-mapping
743
+ .. _mutable: https://docs.python.org/dev/glossary.html#term-mutable
744
+ .. _thread-safe: https://en.wikipedia.org/wiki/Thread_safety
@@ -16,8 +16,6 @@ classifiers =
16
16
  Operating System :: OS Independent
17
17
  Programming Language :: Python
18
18
  Programming Language :: Python :: 3
19
- Programming Language :: Python :: 3.7
20
- Programming Language :: Python :: 3.8
21
19
  Programming Language :: Python :: 3.9
22
20
  Programming Language :: Python :: 3.10
23
21
  Programming Language :: Python :: 3.11
@@ -29,7 +27,7 @@ classifiers =
29
27
  package_dir =
30
28
  = src
31
29
  packages = find:
32
- python_requires = >= 3.7
30
+ python_requires = >= 3.9
33
31
 
34
32
  [options.packages.find]
35
33
  where = src
@@ -5,7 +5,6 @@ __all__ = (
5
5
  "FIFOCache",
6
6
  "LFUCache",
7
7
  "LRUCache",
8
- "MRUCache",
9
8
  "RRCache",
10
9
  "TLRUCache",
11
10
  "TTLCache",
@@ -13,7 +12,7 @@ __all__ = (
13
12
  "cachedmethod",
14
13
  )
15
14
 
16
- __version__ = "5.5.2"
15
+ __version__ = "6.0.0"
17
16
 
18
17
  import collections
19
18
  import collections.abc
@@ -23,7 +22,6 @@ import random
23
22
  import time
24
23
 
25
24
  from . import keys
26
- from ._decorators import _cached_wrapper
27
25
 
28
26
 
29
27
  class _DefaultSize:
@@ -210,12 +208,12 @@ class LRUCache(Cache):
210
208
  def __getitem__(self, key, cache_getitem=Cache.__getitem__):
211
209
  value = cache_getitem(self, key)
212
210
  if key in self: # __missing__ may not store item
213
- self.__update(key)
211
+ self.__touch(key)
214
212
  return value
215
213
 
216
214
  def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
217
215
  cache_setitem(self, key, value)
218
- self.__update(key)
216
+ self.__touch(key)
219
217
 
220
218
  def __delitem__(self, key, cache_delitem=Cache.__delitem__):
221
219
  cache_delitem(self, key)
@@ -230,54 +228,14 @@ class LRUCache(Cache):
230
228
  else:
231
229
  return (key, self.pop(key))
232
230
 
233
- def __update(self, key):
231
+ def __touch(self, key):
232
+ """Mark as recently used"""
234
233
  try:
235
234
  self.__order.move_to_end(key)
236
235
  except KeyError:
237
236
  self.__order[key] = None
238
237
 
239
238
 
240
- class MRUCache(Cache):
241
- """Most Recently Used (MRU) cache implementation."""
242
-
243
- def __init__(self, maxsize, getsizeof=None):
244
- from warnings import warn
245
-
246
- warn("MRUCache is deprecated", DeprecationWarning, stacklevel=2)
247
-
248
- Cache.__init__(self, maxsize, getsizeof)
249
- self.__order = collections.OrderedDict()
250
-
251
- def __getitem__(self, key, cache_getitem=Cache.__getitem__):
252
- value = cache_getitem(self, key)
253
- if key in self: # __missing__ may not store item
254
- self.__update(key)
255
- return value
256
-
257
- def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
258
- cache_setitem(self, key, value)
259
- self.__update(key)
260
-
261
- def __delitem__(self, key, cache_delitem=Cache.__delitem__):
262
- cache_delitem(self, key)
263
- del self.__order[key]
264
-
265
- def popitem(self):
266
- """Remove and return the `(key, value)` pair most recently used."""
267
- try:
268
- key = next(iter(self.__order))
269
- except StopIteration:
270
- raise KeyError("%s is empty" % type(self).__name__) from None
271
- else:
272
- return (key, self.pop(key))
273
-
274
- def __update(self, key):
275
- try:
276
- self.__order.move_to_end(key, last=False)
277
- except KeyError:
278
- self.__order[key] = None
279
-
280
-
281
239
  class RRCache(Cache):
282
240
  """Random Replacement (RR) cache implementation."""
283
241
 
@@ -636,11 +594,23 @@ _CacheInfo = collections.namedtuple(
636
594
  )
637
595
 
638
596
 
639
- def cached(cache, key=keys.hashkey, lock=None, info=False):
597
+ def cached(cache, key=keys.hashkey, lock=None, condition=None, info=False):
640
598
  """Decorator to wrap a function with a memoizing callable that saves
641
599
  results in a cache.
642
600
 
643
601
  """
602
+ from ._cached import _wrapper
603
+
604
+ if isinstance(condition, bool):
605
+ from warnings import warn
606
+
607
+ warn(
608
+ "passing `info` as positional parameter is deprecated",
609
+ DeprecationWarning,
610
+ stacklevel=2,
611
+ )
612
+ info = condition
613
+ condition = None
644
614
 
645
615
  def decorator(func):
646
616
  if info:
@@ -659,80 +629,21 @@ def cached(cache, key=keys.hashkey, lock=None, info=False):
659
629
  def make_info(hits, misses):
660
630
  return _CacheInfo(hits, misses, 0, 0)
661
631
 
662
- wrapper = _cached_wrapper(func, cache, key, lock, make_info)
632
+ return _wrapper(func, cache, key, lock, condition, info=make_info)
663
633
  else:
664
- wrapper = _cached_wrapper(func, cache, key, lock, None)
665
-
666
- wrapper.cache = cache
667
- wrapper.cache_key = key
668
- wrapper.cache_lock = lock
669
-
670
- return functools.update_wrapper(wrapper, func)
634
+ return _wrapper(func, cache, key, lock, condition)
671
635
 
672
636
  return decorator
673
637
 
674
638
 
675
- def cachedmethod(cache, key=keys.methodkey, lock=None):
639
+ def cachedmethod(cache, key=keys.methodkey, lock=None, condition=None):
676
640
  """Decorator to wrap a class or instance method with a memoizing
677
641
  callable that saves results in a cache.
678
642
 
679
643
  """
644
+ from ._cachedmethod import _wrapper
680
645
 
681
646
  def decorator(method):
682
- if lock is None:
683
-
684
- def wrapper(self, *args, **kwargs):
685
- c = cache(self)
686
- if c is None:
687
- return method(self, *args, **kwargs)
688
- k = key(self, *args, **kwargs)
689
- try:
690
- return c[k]
691
- except KeyError:
692
- pass # key not found
693
- v = method(self, *args, **kwargs)
694
- try:
695
- c[k] = v
696
- except ValueError:
697
- pass # value too large
698
- return v
699
-
700
- def clear(self):
701
- c = cache(self)
702
- if c is not None:
703
- c.clear()
704
-
705
- else:
706
-
707
- def wrapper(self, *args, **kwargs):
708
- c = cache(self)
709
- if c is None:
710
- return method(self, *args, **kwargs)
711
- k = key(self, *args, **kwargs)
712
- try:
713
- with lock(self):
714
- return c[k]
715
- except KeyError:
716
- pass # key not found
717
- v = method(self, *args, **kwargs)
718
- # in case of a race, prefer the item already in the cache
719
- try:
720
- with lock(self):
721
- return c.setdefault(k, v)
722
- except ValueError:
723
- return v # value too large
724
-
725
- def clear(self):
726
- c = cache(self)
727
- if c is not None:
728
- with lock(self):
729
- c.clear()
730
-
731
- wrapper.cache = cache
732
- wrapper.cache_key = key
733
- wrapper.cache_lock = lock
734
- wrapper.cache_clear = clear
735
-
736
- return functools.update_wrapper(wrapper, method)
647
+ return _wrapper(method, cache, key, lock, condition)
737
648
 
738
649
  return decorator