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.
- {cachetools-5.5.2 → cachetools-6.0.0}/CHANGELOG.rst +29 -0
- {cachetools-5.5.2/src/cachetools.egg-info → cachetools-6.0.0}/PKG-INFO +14 -12
- {cachetools-5.5.2 → cachetools-6.0.0}/README.rst +10 -7
- {cachetools-5.5.2 → cachetools-6.0.0}/docs/conf.py +2 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/docs/index.rst +80 -58
- {cachetools-5.5.2 → cachetools-6.0.0}/setup.cfg +1 -3
- {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools/__init__.py +23 -112
- cachetools-5.5.2/src/cachetools/_decorators.py → cachetools-6.0.0/src/cachetools/_cached.py +106 -13
- cachetools-6.0.0/src/cachetools/_cachedmethod.py +128 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools/func.py +5 -25
- {cachetools-5.5.2 → cachetools-6.0.0/src/cachetools.egg-info}/PKG-INFO +14 -12
- {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools.egg-info/SOURCES.txt +2 -2
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_cached.py +163 -0
- cachetools-6.0.0/tests/test_cachedmethod.py +383 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_func.py +0 -12
- {cachetools-5.5.2 → cachetools-6.0.0}/tox.ini +2 -4
- cachetools-5.5.2/tests/test_cachedmethod.py +0 -234
- cachetools-5.5.2/tests/test_mru.py +0 -63
- {cachetools-5.5.2 → cachetools-6.0.0}/LICENSE +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/MANIFEST.in +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/pyproject.toml +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/setup.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools/keys.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools.egg-info/dependency_links.txt +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/src/cachetools.egg-info/top_level.txt +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/__init__.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_cache.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_fifo.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_keys.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_lfu.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_lru.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_rr.py +0 -0
- {cachetools-5.5.2 → cachetools-6.0.0}/tests/test_tlru.py +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cachetools
|
|
3
|
-
Version:
|
|
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.
|
|
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
|
|
120
|
-
- cacheing_: Pure Python Cacheing Library
|
|
121
|
-
- CacheToolsUtils_:
|
|
122
|
-
|
|
123
|
-
- shelved-cache_: Persistent cache for Python
|
|
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://
|
|
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
|
|
94
|
-
- cacheing_: Pure Python Cacheing Library
|
|
95
|
-
- CacheToolsUtils_:
|
|
96
|
-
|
|
97
|
-
- shelved-cache_: Persistent cache for Python
|
|
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://
|
|
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
|
-
|
|
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
|
|
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
|
|
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 `
|
|
345
|
-
available as :attr:`cache`, :attr:`cache_key
|
|
346
|
-
:attr:`cache_lock` attributes of the
|
|
347
|
-
These can be used for clearing the
|
|
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
|
|
387
|
-
|
|
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 `
|
|
484
|
-
functions.
|
|
485
|
-
for class methods) as their sole argument to
|
|
486
|
-
lock object for the method's
|
|
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
|
-
|
|
492
|
-
|
|
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(
|
|
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(
|
|
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 #
|
|
559
|
-
print("RFC #
|
|
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 #
|
|
566
|
-
RFC #
|
|
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
|
-
|
|
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
|
-
|
|
647
|
-
|
|
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:
|
|
719
|
-
.. _cache algorithm:
|
|
720
|
-
..
|
|
721
|
-
..
|
|
722
|
-
..
|
|
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.
|
|
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__ = "
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
632
|
+
return _wrapper(func, cache, key, lock, condition, info=make_info)
|
|
663
633
|
else:
|
|
664
|
-
|
|
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
|
-
|
|
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
|