cachetools 7.1.8__tar.gz → 7.2.1__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-7.1.8 → cachetools-7.2.1}/CHANGELOG.rst +24 -0
- {cachetools-7.1.8/src/cachetools.egg-info → cachetools-7.2.1}/PKG-INFO +2 -1
- {cachetools-7.1.8 → cachetools-7.2.1}/docs/index.rst +39 -29
- {cachetools-7.1.8 → cachetools-7.2.1}/pyproject.toml +1 -3
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/__init__.py +6 -7
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/_cached.py +10 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/_cachedmethod.py +2 -0
- {cachetools-7.1.8 → cachetools-7.2.1/src/cachetools.egg-info}/PKG-INFO +2 -1
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_cached.py +57 -11
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_lfu.py +21 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_rr.py +15 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/LICENSE +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/MANIFEST.in +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/README.rst +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/docs/conf.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/setup.cfg +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/__init__.pyi +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/func.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/func.pyi +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/keys.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/keys.pyi +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools/py.typed +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools.egg-info/SOURCES.txt +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools.egg-info/dependency_links.txt +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/src/cachetools.egg-info/top_level.txt +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/__init__.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_cache.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_cachedmethod.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_classmethod.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_fifo.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_func.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_keys.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_lru.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_threading.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_tlru.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tests/test_ttl.py +0 -0
- {cachetools-7.1.8 → cachetools-7.2.1}/tox.ini +0 -0
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
v7.2.1 (2026-10-05)
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
- Improve error handling for ``RRCache.popitem()`` when the cache is
|
|
5
|
+
empty.
|
|
6
|
+
|
|
7
|
+
- Minor style and documentation improvements.
|
|
8
|
+
|
|
9
|
+
- Update CI environment.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
v7.2.0 (2026-09-16)
|
|
13
|
+
===================
|
|
14
|
+
|
|
15
|
+
- Deprecate use of ``cache=None`` to suppress caching with the
|
|
16
|
+
``@cached`` decorator.
|
|
17
|
+
|
|
18
|
+
- Add support for Python 3.15.
|
|
19
|
+
|
|
20
|
+
- Minor test improvements.
|
|
21
|
+
|
|
22
|
+
- Minor documentation updates.
|
|
23
|
+
|
|
24
|
+
|
|
1
25
|
v7.1.8 (2026-08-31)
|
|
2
26
|
===================
|
|
3
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cachetools
|
|
3
|
-
Version: 7.1
|
|
3
|
+
Version: 7.2.1
|
|
4
4
|
Summary: Extensible memoizing collections and decorators
|
|
5
5
|
Author-email: Thomas Kemmer <tkemmer@computer.org>
|
|
6
6
|
Maintainer-email: Thomas Kemmer <tkemmer@computer.org>
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
24
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
26
|
Requires-Python: >=3.10
|
|
26
27
|
Description-Content-Type: text/x-rst
|
|
@@ -261,7 +261,7 @@ key is not found:
|
|
|
261
261
|
>>> class PepStore(LRUCache):
|
|
262
262
|
... def __missing__(self, key):
|
|
263
263
|
... """Retrieve text of a Python Enhancement Proposal"""
|
|
264
|
-
... url = '
|
|
264
|
+
... url = 'https://peps.python.org/pep-%04d/' % key
|
|
265
265
|
... with urllib.request.urlopen(url) as s:
|
|
266
266
|
... pep = s.read()
|
|
267
267
|
... self[key] = pep # store text in cache
|
|
@@ -364,7 +364,7 @@ often called with the same arguments:
|
|
|
364
364
|
@cached(cache=LRUCache(maxsize=640*1024, getsizeof=len), lock=threading.Lock())
|
|
365
365
|
def get_pep(num):
|
|
366
366
|
'Retrieve text of a Python Enhancement Proposal'
|
|
367
|
-
url = '
|
|
367
|
+
url = 'https://peps.python.org/pep-%04d/' % num
|
|
368
368
|
with urllib.request.urlopen(url) as s:
|
|
369
369
|
return s.read()
|
|
370
370
|
|
|
@@ -396,7 +396,7 @@ often called with the same arguments:
|
|
|
396
396
|
|
|
397
397
|
>>> @cached(cache=LRUCache(maxsize=32), info=True)
|
|
398
398
|
... def get_pep(num):
|
|
399
|
-
... url = '
|
|
399
|
+
... url = 'https://peps.python.org/pep-%04d/' % num
|
|
400
400
|
... with urllib.request.urlopen(url) as s:
|
|
401
401
|
... return s.read()
|
|
402
402
|
|
|
@@ -451,7 +451,7 @@ often called with the same arguments:
|
|
|
451
451
|
|
|
452
452
|
@cached(cache=LRUCache(maxsize=10), info=True)
|
|
453
453
|
def _get_pep_wrapped(num):
|
|
454
|
-
url = "
|
|
454
|
+
url = "https://peps.python.org/pep-%04d/" % num
|
|
455
455
|
try:
|
|
456
456
|
with urllib.request.urlopen(url) as s:
|
|
457
457
|
return s.read()
|
|
@@ -519,6 +519,13 @@ often called with the same arguments:
|
|
|
519
519
|
>>> foo(1)
|
|
520
520
|
>>> foo(a=1)
|
|
521
521
|
|
|
522
|
+
.. deprecated:: 7.2
|
|
523
|
+
|
|
524
|
+
Passing :const:`None` as `cache` parameter is deprecated, and
|
|
525
|
+
support will be removed in the next major version. This is
|
|
526
|
+
mainly for consistency with :func:`cachedmethod`, which no
|
|
527
|
+
longer supports returning :const:`None` from `cache(self)`.
|
|
528
|
+
|
|
522
529
|
|
|
523
530
|
.. decorator:: cachedmethod(cache, key=cachetools.keys.methodkey, lock=None, condition=None, info=False)
|
|
524
531
|
|
|
@@ -566,7 +573,7 @@ often called with the same arguments:
|
|
|
566
573
|
... @cachedmethod(lambda self: self.cache, lock=lambda self: self.lock, info=True)
|
|
567
574
|
... def get(self, num):
|
|
568
575
|
... """Retrieve text of a Python Enhancement Proposal"""
|
|
569
|
-
... url = '
|
|
576
|
+
... url = 'https://peps.python.org/pep-%04d/' % num
|
|
570
577
|
... with urllib.request.urlopen(url) as s:
|
|
571
578
|
... return s.read()
|
|
572
579
|
|
|
@@ -620,7 +627,7 @@ often called with the same arguments:
|
|
|
620
627
|
@cachedmethod(lambda self: self.cache, key=partial(methodkey, method='pep'))
|
|
621
628
|
def get_pep(self, num):
|
|
622
629
|
"""Retrieve text of a Python Enhancement Proposal"""
|
|
623
|
-
url = '
|
|
630
|
+
url = 'https://peps.python.org/pep-%04d/' % num
|
|
624
631
|
with urllib.request.urlopen(url) as s:
|
|
625
632
|
return s.read()
|
|
626
633
|
|
|
@@ -649,31 +656,33 @@ often called with the same arguments:
|
|
|
649
656
|
|
|
650
657
|
.. versionchanged:: 7.0
|
|
651
658
|
|
|
652
|
-
|
|
653
|
-
|
|
659
|
+
Added the `info` option for reporting per-instance cache
|
|
660
|
+
statistics.
|
|
654
661
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
662
|
+
:func:`cachedmethod` attributes (:attr:`cache`,
|
|
663
|
+
:attr:`cache_lock`, etc.) are now implemented as properties for
|
|
664
|
+
instance methods, and are finally officially supported.
|
|
658
665
|
|
|
659
|
-
|
|
660
|
-
|
|
666
|
+
Require :attr:`__dict__` to be a mutable mapping to support
|
|
667
|
+
per-instance :func:`cache_info()`.
|
|
661
668
|
|
|
662
|
-
|
|
669
|
+
Returning :const:`None` from `cache(self)` is no longer supported.
|
|
663
670
|
|
|
664
671
|
.. deprecated:: 7.0
|
|
665
672
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
673
|
+
Using :func:`cachedmethod` with :func:`classmethod` is
|
|
674
|
+
deprecated, and support will be removed in the next major
|
|
675
|
+
version. This is mostly due to the fact that chaining
|
|
676
|
+
descriptors, i.e. using descriptors with :func:`classmethod`,
|
|
677
|
+
was deprecated in Python 3.11 and removed in Python 3.13. When
|
|
678
|
+
using :func:`cache_info()`, an instance method is already
|
|
679
|
+
required.
|
|
672
680
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
681
|
+
Using :func:`cachedmethod` with an instance that does not
|
|
682
|
+
provide a mutable :attr:`__dict__` attribute is deprecated, and
|
|
683
|
+
may inflict a noticeable performance penalty. When using
|
|
684
|
+
:func:`cache_info()`, :attr:`__dict__` already has to be a
|
|
685
|
+
mutable mapping.
|
|
677
686
|
|
|
678
687
|
|
|
679
688
|
*****************************************************************
|
|
@@ -747,8 +756,9 @@ this module provides several memoizing function decorators with a
|
|
|
747
756
|
similar API. All these decorators wrap a function with a memoizing
|
|
748
757
|
callable that saves up to the `maxsize` most recent calls, using
|
|
749
758
|
different caching strategies. If `maxsize` is set to :const:`None`,
|
|
750
|
-
the
|
|
751
|
-
|
|
759
|
+
the size limit is disabled and the cache can grow without bound.
|
|
760
|
+
For :func:`ttl_cache`, the time-to-live still applies, so cached
|
|
761
|
+
results expire after `ttl` even without a size limit.
|
|
752
762
|
|
|
753
763
|
If the optional argument `typed` is set to :const:`True`, function
|
|
754
764
|
arguments of different types will be cached separately. For example,
|
|
@@ -817,7 +827,7 @@ all the decorators in this module are thread-safe by default.
|
|
|
817
827
|
.. _cache algorithm: https://en.wikipedia.org/wiki/Cache_algorithms
|
|
818
828
|
.. _cache stampede: https://en.wikipedia.org/wiki/Cache_stampede
|
|
819
829
|
.. _condition variable: https://docs.python.org/3/library/threading.html#condition-objects
|
|
820
|
-
.. _context manager: https://docs.python.org/
|
|
821
|
-
.. _mapping: https://docs.python.org/
|
|
822
|
-
.. _mutable: https://docs.python.org/
|
|
830
|
+
.. _context manager: https://docs.python.org/3/glossary.html#term-context-manager
|
|
831
|
+
.. _mapping: https://docs.python.org/3/glossary.html#term-mapping
|
|
832
|
+
.. _mutable: https://docs.python.org/3/glossary.html#term-mutable
|
|
823
833
|
.. _thread-safe: https://en.wikipedia.org/wiki/Thread_safety
|
|
@@ -27,6 +27,7 @@ classifiers = [
|
|
|
27
27
|
"Programming Language :: Python :: 3.12",
|
|
28
28
|
"Programming Language :: Python :: 3.13",
|
|
29
29
|
"Programming Language :: Python :: 3.14",
|
|
30
|
+
"Programming Language :: Python :: 3.15",
|
|
30
31
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
31
32
|
]
|
|
32
33
|
|
|
@@ -49,9 +50,6 @@ version = {attr = "cachetools.__version__"}
|
|
|
49
50
|
|
|
50
51
|
[tool.pyright]
|
|
51
52
|
typeCheckingMode = "standard"
|
|
52
|
-
reportFunctionMemberAccess = "information"
|
|
53
|
-
reportOptionalContextManager = "warning"
|
|
54
|
-
reportOptionalMemberAccess = "warning"
|
|
55
53
|
|
|
56
54
|
[tool.ruff.lint]
|
|
57
55
|
ignore = ["DTZ005", "UP031"]
|
|
@@ -12,7 +12,7 @@ __all__ = (
|
|
|
12
12
|
"cachedmethod",
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
__version__ = "7.1
|
|
15
|
+
__version__ = "7.2.1"
|
|
16
16
|
|
|
17
17
|
import collections
|
|
18
18
|
import collections.abc
|
|
@@ -52,7 +52,7 @@ class Cache(collections.abc.MutableMapping):
|
|
|
52
52
|
def __init__(self, maxsize, getsizeof=None):
|
|
53
53
|
if maxsize < 0:
|
|
54
54
|
raise ValueError("maxsize must be non-negative")
|
|
55
|
-
if getsizeof:
|
|
55
|
+
if getsizeof is not None:
|
|
56
56
|
self.getsizeof = getsizeof
|
|
57
57
|
if self.getsizeof is not Cache.getsizeof:
|
|
58
58
|
self.__size = {}
|
|
@@ -357,12 +357,11 @@ class RRCache(Cache):
|
|
|
357
357
|
|
|
358
358
|
def popitem(self):
|
|
359
359
|
"""Remove and return a random `(key, value)` pair."""
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
except IndexError:
|
|
360
|
+
keys = self.__keys
|
|
361
|
+
if not keys:
|
|
363
362
|
raise KeyError("%s is empty" % type(self).__name__) from None
|
|
364
|
-
|
|
365
|
-
|
|
363
|
+
key = self.__choice(keys)
|
|
364
|
+
return (key, self.pop(key))
|
|
366
365
|
|
|
367
366
|
def clear(self):
|
|
368
367
|
Cache.clear(self)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""Function decorator helpers."""
|
|
2
2
|
|
|
3
|
+
# pyright: reportFunctionMemberAccess=false
|
|
4
|
+
|
|
3
5
|
__all__ = ()
|
|
4
6
|
|
|
5
7
|
import functools
|
|
8
|
+
import warnings
|
|
6
9
|
|
|
7
10
|
# At least for now, the implementation prefers clarity and performance
|
|
8
11
|
# over ease of maintenance, thus providing separate wrappers for
|
|
@@ -227,6 +230,13 @@ def _uncached(func):
|
|
|
227
230
|
|
|
228
231
|
|
|
229
232
|
def _wrapper(func, cache, key, lock=None, cond=None, info=None):
|
|
233
|
+
if cache is None:
|
|
234
|
+
warnings.warn(
|
|
235
|
+
"@cachetools.cached(cache=None) is deprecated",
|
|
236
|
+
DeprecationWarning,
|
|
237
|
+
stacklevel=3,
|
|
238
|
+
)
|
|
239
|
+
|
|
230
240
|
if info is not None:
|
|
231
241
|
if cache is None:
|
|
232
242
|
wrapper = _uncached_info(func, info)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cachetools
|
|
3
|
-
Version: 7.1
|
|
3
|
+
Version: 7.2.1
|
|
4
4
|
Summary: Extensible memoizing collections and decorators
|
|
5
5
|
Author-email: Thomas Kemmer <tkemmer@computer.org>
|
|
6
6
|
Maintainer-email: Thomas Kemmer <tkemmer@computer.org>
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
24
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
26
|
Requires-Python: >=3.10
|
|
26
27
|
Description-Content-Type: text/x-rst
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import unittest
|
|
2
|
+
import warnings
|
|
2
3
|
|
|
3
4
|
import cachetools
|
|
4
5
|
import cachetools.keys
|
|
@@ -17,6 +18,9 @@ class DecoratorTestMixin(_TestCaseProtocol):
|
|
|
17
18
|
self.count = 0
|
|
18
19
|
return self.count
|
|
19
20
|
|
|
21
|
+
def error_func(self, *args, **kwargs):
|
|
22
|
+
raise ValueError("test error")
|
|
23
|
+
|
|
20
24
|
def test_decorator(self):
|
|
21
25
|
cache = self.cache(2)
|
|
22
26
|
wrapper = cachetools.cached(cache)(self.func)
|
|
@@ -86,7 +90,7 @@ class DecoratorTestMixin(_TestCaseProtocol):
|
|
|
86
90
|
self.assertEqual(wrapper(1), 1)
|
|
87
91
|
self.assertEqual(lock.count, 5)
|
|
88
92
|
|
|
89
|
-
def
|
|
93
|
+
def test_decorator_cond(self):
|
|
90
94
|
cache = self.cache(2)
|
|
91
95
|
lock = cond = CountedCondition()
|
|
92
96
|
wrapper = cachetools.cached(cache, condition=cond)(self.func)
|
|
@@ -105,7 +109,7 @@ class DecoratorTestMixin(_TestCaseProtocol):
|
|
|
105
109
|
self.assertEqual(cond.wait_count, 3)
|
|
106
110
|
self.assertEqual(cond.notify_count, 2)
|
|
107
111
|
|
|
108
|
-
def
|
|
112
|
+
def test_decorator_lock_cond(self):
|
|
109
113
|
cache = self.cache(2)
|
|
110
114
|
lock = CountedLock()
|
|
111
115
|
cond = CountedCondition()
|
|
@@ -125,6 +129,26 @@ class DecoratorTestMixin(_TestCaseProtocol):
|
|
|
125
129
|
self.assertEqual(cond.wait_count, 3)
|
|
126
130
|
self.assertEqual(cond.notify_count, 2)
|
|
127
131
|
|
|
132
|
+
def test_decorator_cond_error(self):
|
|
133
|
+
cache = self.cache(2)
|
|
134
|
+
lock = cond = CountedCondition()
|
|
135
|
+
wrapper = cachetools.cached(cache, condition=cond)(self.error_func)
|
|
136
|
+
|
|
137
|
+
with self.assertRaises(ValueError):
|
|
138
|
+
wrapper(0)
|
|
139
|
+
self.assertEqual(len(cache), 0)
|
|
140
|
+
self.assertEqual(lock.count, 2)
|
|
141
|
+
self.assertEqual(cond.wait_count, 1)
|
|
142
|
+
self.assertEqual(cond.notify_count, 1)
|
|
143
|
+
|
|
144
|
+
# verify pending set is cleaned up, otherwise this might deadlock
|
|
145
|
+
with self.assertRaises(ValueError):
|
|
146
|
+
wrapper(0)
|
|
147
|
+
self.assertEqual(len(cache), 0)
|
|
148
|
+
self.assertEqual(lock.count, 4)
|
|
149
|
+
self.assertEqual(cond.wait_count, 2)
|
|
150
|
+
self.assertEqual(cond.notify_count, 2)
|
|
151
|
+
|
|
128
152
|
def test_decorator_wrapped(self):
|
|
129
153
|
cache = self.cache(2)
|
|
130
154
|
wrapper = cachetools.cached(cache)(self.func)
|
|
@@ -158,7 +182,7 @@ class DecoratorTestMixin(_TestCaseProtocol):
|
|
|
158
182
|
self.assertIs(wrapper.cache_lock, lock)
|
|
159
183
|
self.assertIs(wrapper.cache_condition, None)
|
|
160
184
|
|
|
161
|
-
def
|
|
185
|
+
def test_decorator_attributes_cond(self):
|
|
162
186
|
cache = self.cache(2)
|
|
163
187
|
lock = cond = CountedCondition()
|
|
164
188
|
wrapper = cachetools.cached(cache, condition=cond)(self.func)
|
|
@@ -187,7 +211,7 @@ class DecoratorTestMixin(_TestCaseProtocol):
|
|
|
187
211
|
self.assertEqual(len(cache), 0)
|
|
188
212
|
self.assertEqual(lock.count, 3)
|
|
189
213
|
|
|
190
|
-
def
|
|
214
|
+
def test_decorator_clear_cond(self):
|
|
191
215
|
cache = self.cache(2)
|
|
192
216
|
lock = cond = CountedCondition()
|
|
193
217
|
wrapper = cachetools.cached(cache, condition=cond)(self.func)
|
|
@@ -241,7 +265,7 @@ class CacheWrapperTest(unittest.TestCase, DecoratorTestMixin):
|
|
|
241
265
|
self.assertEqual(wrapper.cache_info(), (0, 0, 2, 0))
|
|
242
266
|
self.assertEqual(lock.count, 11)
|
|
243
267
|
|
|
244
|
-
def
|
|
268
|
+
def test_decorator_cond_info(self):
|
|
245
269
|
cache = self.cache(2)
|
|
246
270
|
lock = cond = CountedCondition()
|
|
247
271
|
wrapper = cachetools.cached(cache, condition=cond, info=True)(self.func)
|
|
@@ -265,7 +289,7 @@ class CacheWrapperTest(unittest.TestCase, DecoratorTestMixin):
|
|
|
265
289
|
self.assertEqual(wrapper.cache_info(), (0, 0, 2, 0))
|
|
266
290
|
self.assertEqual(lock.count, 13)
|
|
267
291
|
|
|
268
|
-
def
|
|
292
|
+
def test_decorator_lock_cond_info(self):
|
|
269
293
|
cache = self.cache(2)
|
|
270
294
|
lock = CountedLock()
|
|
271
295
|
cond = CountedCondition()
|
|
@@ -310,7 +334,7 @@ class CacheWrapperTest(unittest.TestCase, DecoratorTestMixin):
|
|
|
310
334
|
self.assertEqual(len(cache), 0)
|
|
311
335
|
self.assertEqual(lock.count, 2)
|
|
312
336
|
|
|
313
|
-
def
|
|
337
|
+
def test_zero_size_cache_decorator_cond(self):
|
|
314
338
|
cache = self.cache(0)
|
|
315
339
|
lock = cond = CountedCondition()
|
|
316
340
|
wrapper = cachetools.cached(cache, condition=cond)(self.func)
|
|
@@ -342,6 +366,20 @@ class CacheWrapperTest(unittest.TestCase, DecoratorTestMixin):
|
|
|
342
366
|
self.assertEqual(wrapper.cache_info(), (0, 1, 0, 0))
|
|
343
367
|
self.assertEqual(lock.count, 4)
|
|
344
368
|
|
|
369
|
+
def test_zero_size_cache_decorator_cond_info(self):
|
|
370
|
+
cache = self.cache(0)
|
|
371
|
+
lock = cond = CountedCondition()
|
|
372
|
+
wrapper = cachetools.cached(cache, condition=cond, info=True)(self.func)
|
|
373
|
+
|
|
374
|
+
self.assertEqual(len(cache), 0)
|
|
375
|
+
self.assertEqual(wrapper.cache_info(), (0, 0, 0, 0))
|
|
376
|
+
self.assertEqual(lock.count, 1)
|
|
377
|
+
self.assertEqual(wrapper(0), 0)
|
|
378
|
+
self.assertEqual(len(cache), 0)
|
|
379
|
+
self.assertEqual(lock.count, 4)
|
|
380
|
+
self.assertEqual(wrapper.cache_info(), (0, 1, 0, 0))
|
|
381
|
+
self.assertEqual(lock.count, 5)
|
|
382
|
+
|
|
345
383
|
|
|
346
384
|
class DictWrapperTest(unittest.TestCase, DecoratorTestMixin):
|
|
347
385
|
def cache(self, minsize):
|
|
@@ -367,26 +405,34 @@ class NoneWrapperTest(unittest.TestCase):
|
|
|
367
405
|
return args + tuple(kwargs.items())
|
|
368
406
|
|
|
369
407
|
def test_decorator(self):
|
|
370
|
-
|
|
408
|
+
with warnings.catch_warnings(record=True) as w:
|
|
409
|
+
wrapper = cachetools.cached(None)(self.func)
|
|
410
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
371
411
|
|
|
372
412
|
self.assertEqual(wrapper(0), (0,))
|
|
373
413
|
self.assertEqual(wrapper(1), (1,))
|
|
374
414
|
self.assertEqual(wrapper(1, foo="bar"), (1, ("foo", "bar")))
|
|
375
415
|
|
|
376
416
|
def test_decorator_attributes(self):
|
|
377
|
-
|
|
417
|
+
with warnings.catch_warnings(record=True) as w:
|
|
418
|
+
wrapper = cachetools.cached(None)(self.func)
|
|
419
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
378
420
|
|
|
379
421
|
self.assertIs(wrapper.cache, None)
|
|
380
422
|
self.assertIs(wrapper.cache_key, cachetools.keys.hashkey)
|
|
381
423
|
self.assertIs(wrapper.cache_lock, None)
|
|
382
424
|
|
|
383
425
|
def test_decorator_clear(self):
|
|
384
|
-
|
|
426
|
+
with warnings.catch_warnings(record=True) as w:
|
|
427
|
+
wrapper = cachetools.cached(None)(self.func)
|
|
428
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
385
429
|
|
|
386
430
|
wrapper.cache_clear() # no-op
|
|
387
431
|
|
|
388
432
|
def test_decorator_info(self):
|
|
389
|
-
|
|
433
|
+
with warnings.catch_warnings(record=True) as w:
|
|
434
|
+
wrapper = cachetools.cached(None, info=True)(self.func)
|
|
435
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
390
436
|
|
|
391
437
|
self.assertEqual(wrapper.cache_info(), (0, 0, 0, 0))
|
|
392
438
|
self.assertEqual(wrapper(0), (0,))
|
|
@@ -31,6 +31,27 @@ class LFUCacheTest(unittest.TestCase, CacheTestMixin):
|
|
|
31
31
|
self.assertEqual(cache[1], 1)
|
|
32
32
|
self.assertEqual(cache[4], 4)
|
|
33
33
|
|
|
34
|
+
def test_lfu_tie(self):
|
|
35
|
+
cache = LFUCache[int, int](maxsize=3)
|
|
36
|
+
|
|
37
|
+
cache[1] = 1
|
|
38
|
+
cache[1]
|
|
39
|
+
cache[2] = 2
|
|
40
|
+
cache[3] = 3
|
|
41
|
+
cache[4] = 4
|
|
42
|
+
|
|
43
|
+
self.assertEqual(len(cache), 3)
|
|
44
|
+
self.assertIn(1, cache)
|
|
45
|
+
self.assertIn(4, cache)
|
|
46
|
+
self.assertTrue(2 in cache or 3 in cache)
|
|
47
|
+
self.assertTrue(2 not in cache or 3 not in cache)
|
|
48
|
+
|
|
49
|
+
cache[5] = 5
|
|
50
|
+
self.assertEqual(len(cache), 3)
|
|
51
|
+
self.assertIn(1, cache)
|
|
52
|
+
self.assertIn(5, cache)
|
|
53
|
+
self.assertTrue(2 in cache or 3 in cache or 4 in cache)
|
|
54
|
+
|
|
34
55
|
def test_lfu_getsizeof(self):
|
|
35
56
|
cache = LFUCache[int, int](maxsize=3, getsizeof=lambda x: x)
|
|
36
57
|
|
|
@@ -86,3 +86,18 @@ class RRCacheTest(unittest.TestCase, CacheTestMixin):
|
|
|
86
86
|
def test_rr_default_choice(self):
|
|
87
87
|
cache = RRCache[int, int](maxsize=2)
|
|
88
88
|
self.assertIs(cache.choice, random.choice)
|
|
89
|
+
|
|
90
|
+
def test_rr_popitem_empty_custom_choice(self):
|
|
91
|
+
# a custom `choice` function only has to handle non-empty
|
|
92
|
+
# sequences, so an empty cache has to be detected beforehand
|
|
93
|
+
for choice in (min, max, lambda seq: next(iter(seq))):
|
|
94
|
+
cache = RRCache[int, int](maxsize=2, choice=choice)
|
|
95
|
+
|
|
96
|
+
with self.assertRaises(KeyError):
|
|
97
|
+
cache.popitem()
|
|
98
|
+
|
|
99
|
+
cache[1] = 1
|
|
100
|
+
self.assertEqual((1, 1), cache.popitem())
|
|
101
|
+
|
|
102
|
+
with self.assertRaises(KeyError):
|
|
103
|
+
cache.popitem()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|