cachetools 7.0.2__tar.gz → 7.0.4__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.0.2 → cachetools-7.0.4}/CHANGELOG.rst +15 -0
- {cachetools-7.0.2/src/cachetools.egg-info → cachetools-7.0.4}/PKG-INFO +1 -1
- {cachetools-7.0.2 → cachetools-7.0.4}/docs/index.rst +15 -8
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools/__init__.py +3 -3
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools/_cachedmethod.py +10 -5
- {cachetools-7.0.2 → cachetools-7.0.4/src/cachetools.egg-info}/PKG-INFO +1 -1
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_cachedmethod.py +28 -6
- {cachetools-7.0.2 → cachetools-7.0.4}/LICENSE +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/MANIFEST.in +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/README.rst +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/docs/conf.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/pyproject.toml +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/setup.cfg +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools/_cached.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools/func.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools/keys.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools.egg-info/SOURCES.txt +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools.egg-info/dependency_links.txt +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/src/cachetools.egg-info/top_level.txt +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/__init__.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_cache.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_cached.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_classmethod.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_fifo.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_func.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_keys.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_lfu.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_lru.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_rr.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_threading.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_tlru.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tests/test_ttl.py +0 -0
- {cachetools-7.0.2 → cachetools-7.0.4}/tox.ini +0 -0
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
v7.0.4 (2026-03-08)
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
- Fix and properly document ``@cachedmethod.cache_key`` behavior.
|
|
5
|
+
|
|
6
|
+
- Minor documentation improvements.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
v7.0.3 (2026-03-05)
|
|
10
|
+
===================
|
|
11
|
+
|
|
12
|
+
- Fix ``DeprecationWarning`` when creating an autospec mock with
|
|
13
|
+
``@cachedmethod`` decorations.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
v7.0.2 (2026-03-02)
|
|
2
17
|
===================
|
|
3
18
|
|
|
@@ -370,13 +370,10 @@ often called with the same arguments:
|
|
|
370
370
|
with urllib.request.urlopen(url) as s:
|
|
371
371
|
return s.read()
|
|
372
372
|
|
|
373
|
-
#
|
|
373
|
+
# remove (pop) an individual cached PEP from the cache
|
|
374
|
+
key = get_pep.cache_key(42)
|
|
374
375
|
with get_pep.cache_lock:
|
|
375
|
-
get_pep.cache.
|
|
376
|
-
|
|
377
|
-
# always use the key function for accessing cache items
|
|
378
|
-
with get_pep.cache_lock:
|
|
379
|
-
get_pep.cache.pop(get_pep.cache_key(42), None)
|
|
376
|
+
get_pep.cache.pop(key, None)
|
|
380
377
|
|
|
381
378
|
For the common use case of clearing or invalidating the cache, the
|
|
382
379
|
decorator also provides a :func:`cache_clear()` function which
|
|
@@ -582,6 +579,15 @@ often called with the same arguments:
|
|
|
582
579
|
>>> peps.get.cache_info()
|
|
583
580
|
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)
|
|
584
581
|
|
|
582
|
+
>>> # remove an individual cached PEP from the cache
|
|
583
|
+
>>> key = peps.get.cache_key(320)
|
|
584
|
+
>>> with peps.get.cache_lock:
|
|
585
|
+
... del peps.get.cache[key]
|
|
586
|
+
|
|
587
|
+
>>> peps.get.cache_info()
|
|
588
|
+
CacheInfo(hits=3, misses=8, maxsize=32, currsize=7)
|
|
589
|
+
|
|
590
|
+
>>> # remove all cached PEPs and clear cache info
|
|
585
591
|
>>> peps.get.cache_clear()
|
|
586
592
|
|
|
587
593
|
>>> peps.get.cache_info()
|
|
@@ -590,8 +596,9 @@ often called with the same arguments:
|
|
|
590
596
|
The `key` function will be called as `key(self, *args, **kwargs)`
|
|
591
597
|
to retrieve a suitable cache key. Note that the default `key`
|
|
592
598
|
function, :func:`cachetools.keys.methodkey`, ignores its first
|
|
593
|
-
argument, i.e. :const:`self`. This has mostly historical
|
|
594
|
-
but also ensures that :const:`self` does not have to be
|
|
599
|
+
implicit argument, i.e. :const:`self`. This has mostly historical
|
|
600
|
+
reasons, but also ensures that :const:`self` does not have to be
|
|
601
|
+
hashable.
|
|
595
602
|
|
|
596
603
|
You may provide a different `key` function,
|
|
597
604
|
e.g. :func:`cachetools.keys.hashkey`, if you need :const:`self` to
|
|
@@ -12,7 +12,7 @@ __all__ = (
|
|
|
12
12
|
"cachedmethod",
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
__version__ = "7.0.
|
|
15
|
+
__version__ = "7.0.4"
|
|
16
16
|
|
|
17
17
|
import collections
|
|
18
18
|
import collections.abc
|
|
@@ -748,8 +748,8 @@ def cached(cache, key=keys.hashkey, lock=None, condition=None, info=False):
|
|
|
748
748
|
|
|
749
749
|
|
|
750
750
|
def cachedmethod(cache, key=keys.methodkey, lock=None, condition=None, info=False):
|
|
751
|
-
"""Decorator to wrap a
|
|
752
|
-
|
|
751
|
+
"""Decorator to wrap a method with a memoizing callable that saves
|
|
752
|
+
results in a cache.
|
|
753
753
|
|
|
754
754
|
"""
|
|
755
755
|
from ._cachedmethod import _wrapper
|
|
@@ -48,7 +48,7 @@ class _WrapperBase:
|
|
|
48
48
|
|
|
49
49
|
@property
|
|
50
50
|
def cache_key(self):
|
|
51
|
-
return self.__key
|
|
51
|
+
return functools.partial(self.__key, self._obj)
|
|
52
52
|
|
|
53
53
|
@property
|
|
54
54
|
def cache_lock(self):
|
|
@@ -77,7 +77,12 @@ class _DescriptorBase:
|
|
|
77
77
|
|
|
78
78
|
def __get__(self, obj, objtype=None):
|
|
79
79
|
wrapper = self.Wrapper(obj)
|
|
80
|
-
if
|
|
80
|
+
if obj is None:
|
|
81
|
+
# Return the wrapper itself without modification when accessed
|
|
82
|
+
# through the class to support class-level introspection, such
|
|
83
|
+
# as for mocking with autospec=True in unittest.mock.
|
|
84
|
+
pass
|
|
85
|
+
elif self.__attrname is not None:
|
|
81
86
|
# replace descriptor instance with wrapper in instance dict
|
|
82
87
|
try:
|
|
83
88
|
# In case of a race condition where another thread already replaced
|
|
@@ -148,7 +153,7 @@ def _condition_info(method, cache, key, lock, cond, info):
|
|
|
148
153
|
cache = self.cache
|
|
149
154
|
lock = self.cache_lock
|
|
150
155
|
cond = self.cache_condition
|
|
151
|
-
key = self.cache_key(
|
|
156
|
+
key = self.cache_key(*args, **kwargs)
|
|
152
157
|
|
|
153
158
|
with lock:
|
|
154
159
|
cond.wait_for(lambda: key not in self.__pending)
|
|
@@ -194,7 +199,7 @@ def _locked_info(method, cache, key, lock, info):
|
|
|
194
199
|
def __call__(self, *args, **kwargs):
|
|
195
200
|
cache = self.cache
|
|
196
201
|
lock = self.cache_lock
|
|
197
|
-
key = self.cache_key(
|
|
202
|
+
key = self.cache_key(*args, **kwargs)
|
|
198
203
|
with lock:
|
|
199
204
|
try:
|
|
200
205
|
result = cache[key]
|
|
@@ -233,7 +238,7 @@ def _unlocked_info(method, cache, key, info):
|
|
|
233
238
|
|
|
234
239
|
def __call__(self, *args, **kwargs):
|
|
235
240
|
cache = self.cache
|
|
236
|
-
key = self.cache_key(
|
|
241
|
+
key = self.cache_key(*args, **kwargs)
|
|
237
242
|
try:
|
|
238
243
|
result = cache[key]
|
|
239
244
|
self.__hits += 1
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import unittest
|
|
2
|
+
import unittest.mock
|
|
2
3
|
import warnings
|
|
3
4
|
|
|
4
5
|
|
|
@@ -369,24 +370,26 @@ class MethodDecoratorTestMixin:
|
|
|
369
370
|
cached = Cached(cache)
|
|
370
371
|
|
|
371
372
|
self.assertIs(cached.get.cache, cache)
|
|
372
|
-
self.assertIs(cached.get.cache_key, keys.methodkey)
|
|
373
373
|
self.assertIs(cached.get.cache_lock, None)
|
|
374
374
|
self.assertIs(cached.get.cache_condition, None)
|
|
375
|
+
self.assertEqual(cached.get.cache_key(42), keys.methodkey(cached, 42))
|
|
375
376
|
|
|
376
377
|
self.assertIs(cached.get_lock.cache, cache)
|
|
377
|
-
self.assertIs(cached.get_lock.cache_key, keys.methodkey)
|
|
378
378
|
self.assertIs(cached.get_lock.cache_lock, cached.lock)
|
|
379
379
|
self.assertIs(cached.get_lock.cache_condition, None)
|
|
380
|
+
self.assertEqual(cached.get_lock.cache_key(42), keys.methodkey(cached, 42))
|
|
380
381
|
|
|
381
382
|
self.assertIs(cached.get_cond.cache, cache)
|
|
382
|
-
self.assertIs(cached.get_cond.cache_key, keys.methodkey)
|
|
383
383
|
self.assertIs(cached.get_cond.cache_lock, cached.cond)
|
|
384
384
|
self.assertIs(cached.get_cond.cache_condition, cached.cond)
|
|
385
|
+
self.assertEqual(cached.get_cond.cache_key(42), keys.methodkey(cached, 42))
|
|
385
386
|
|
|
386
387
|
self.assertIs(cached.get_lock_cond_info.cache, cache)
|
|
387
|
-
self.assertIs(cached.get_lock_cond_info.cache_key, keys.methodkey)
|
|
388
388
|
self.assertIs(cached.get_lock_cond_info.cache_lock, cached.lock)
|
|
389
389
|
self.assertIs(cached.get_lock_cond_info.cache_condition, cached.cond)
|
|
390
|
+
self.assertEqual(
|
|
391
|
+
cached.get_lock_cond_info.cache_key(42), keys.methodkey(cached, 42)
|
|
392
|
+
)
|
|
390
393
|
|
|
391
394
|
def test_decorator_clear(self):
|
|
392
395
|
cache = self.cache(2)
|
|
@@ -602,15 +605,25 @@ class CacheMethodTest(unittest.TestCase, MethodDecoratorTestMixin):
|
|
|
602
605
|
self.assertEqual(cached1.get_info.cache_info(), (0, 0, 2, 0))
|
|
603
606
|
self.assertEqual(cached2.get_info.cache_info(), (0, 0, 2, 0))
|
|
604
607
|
|
|
608
|
+
# hits/misses are counted by instance
|
|
605
609
|
self.assertEqual(cached1.get_info(0), 0)
|
|
606
|
-
|
|
607
610
|
self.assertEqual(cached1.get_info.cache_info(), (0, 1, 2, 1))
|
|
608
611
|
self.assertEqual(cached2.get_info.cache_info(), (0, 0, 2, 1))
|
|
609
612
|
|
|
613
|
+
# default methodkey discards "self", so results will be shared
|
|
614
|
+
# across instances
|
|
610
615
|
self.assertEqual(cached2.get_info(0), 0)
|
|
611
|
-
|
|
612
616
|
self.assertEqual(cached1.get_info.cache_info(), (0, 1, 2, 1))
|
|
613
617
|
self.assertEqual(cached2.get_info.cache_info(), (1, 0, 2, 1))
|
|
618
|
+
self.assertEqual(cached1.get_info(0), 0)
|
|
619
|
+
self.assertEqual(cached1.get_info.cache_info(), (1, 1, 2, 1))
|
|
620
|
+
self.assertEqual(cached2.get_info.cache_info(), (1, 0, 2, 1))
|
|
621
|
+
self.assertEqual(cached1.get_info(1), 1)
|
|
622
|
+
self.assertEqual(cached1.get_info.cache_info(), (1, 2, 2, 2))
|
|
623
|
+
self.assertEqual(cached2.get_info.cache_info(), (1, 0, 2, 2))
|
|
624
|
+
self.assertEqual(cached2.get_info(1), 1)
|
|
625
|
+
self.assertEqual(cached1.get_info.cache_info(), (1, 2, 2, 2))
|
|
626
|
+
self.assertEqual(cached2.get_info.cache_info(), (2, 0, 2, 2))
|
|
614
627
|
|
|
615
628
|
def test_value_too_large(self):
|
|
616
629
|
cache = self.cache(1, getsizeof=lambda x: x)
|
|
@@ -673,3 +686,12 @@ class NoneMethodTest(unittest.TestCase):
|
|
|
673
686
|
|
|
674
687
|
with self.assertRaises(TypeError):
|
|
675
688
|
wrapper.cache_info()
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
class AutospecTest(unittest.TestCase):
|
|
692
|
+
def test_autospec_no_warnings(self):
|
|
693
|
+
|
|
694
|
+
with warnings.catch_warnings(record=True) as w:
|
|
695
|
+
warnings.simplefilter("always")
|
|
696
|
+
unittest.mock.create_autospec(Cached, instance=True)
|
|
697
|
+
self.assertEqual(len(w), 0)
|
|
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
|