cachetools 5.5.1__tar.gz → 5.5.2__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.1 → cachetools-5.5.2}/CHANGELOG.rst +10 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/LICENSE +1 -1
- {cachetools-5.5.1/src/cachetools.egg-info → cachetools-5.5.2}/PKG-INFO +2 -2
- {cachetools-5.5.1 → cachetools-5.5.2}/README.rst +1 -1
- {cachetools-5.5.1 → cachetools-5.5.2}/docs/conf.py +1 -1
- {cachetools-5.5.1 → cachetools-5.5.2}/docs/index.rst +12 -6
- {cachetools-5.5.1 → cachetools-5.5.2}/src/cachetools/__init__.py +7 -128
- cachetools-5.5.2/src/cachetools/_decorators.py +152 -0
- {cachetools-5.5.1 → cachetools-5.5.2/src/cachetools.egg-info}/PKG-INFO +2 -2
- {cachetools-5.5.1 → cachetools-5.5.2}/src/cachetools.egg-info/SOURCES.txt +1 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_cached.py +24 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tox.ini +1 -1
- {cachetools-5.5.1 → cachetools-5.5.2}/MANIFEST.in +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/pyproject.toml +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/setup.cfg +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/setup.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/src/cachetools/func.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/src/cachetools/keys.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/src/cachetools.egg-info/dependency_links.txt +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/src/cachetools.egg-info/top_level.txt +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/__init__.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_cache.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_cachedmethod.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_fifo.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_func.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_keys.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_lfu.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_lru.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_mru.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_rr.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_tlru.py +0 -0
- {cachetools-5.5.1 → cachetools-5.5.2}/tests/test_ttl.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: cachetools
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.2
|
|
4
4
|
Summary: Extensible memoizing collections and decorators
|
|
5
5
|
Home-page: https://github.com/tkem/cachetools/
|
|
6
6
|
Author: Thomas Kemmer
|
|
@@ -126,7 +126,7 @@ Related Projects
|
|
|
126
126
|
License
|
|
127
127
|
------------------------------------------------------------------------
|
|
128
128
|
|
|
129
|
-
Copyright (c) 2014-
|
|
129
|
+
Copyright (c) 2014-2025 Thomas Kemmer.
|
|
130
130
|
|
|
131
131
|
Licensed under the `MIT License`_.
|
|
132
132
|
|
|
@@ -41,6 +41,12 @@ of the cache. When a cache is full, :meth:`Cache.__setitem__()` calls
|
|
|
41
41
|
:meth:`self.popitem()` repeatedly until there is enough room for the
|
|
42
42
|
item to be added.
|
|
43
43
|
|
|
44
|
+
.. note::
|
|
45
|
+
|
|
46
|
+
Please be aware that `maxsize` must be a positive number. If you
|
|
47
|
+
really want your cache to grow without bounds, use
|
|
48
|
+
:const:`math.inf` or something similar.
|
|
49
|
+
|
|
44
50
|
In general, a cache's size is the total size of its item's values.
|
|
45
51
|
Therefore, :class:`Cache` provides a :meth:`getsizeof` method, which
|
|
46
52
|
returns the size of a given `value`. The default implementation of
|
|
@@ -50,12 +56,12 @@ making the cache's size equal to the number of its items, or
|
|
|
50
56
|
named constructor parameter `getsizeof`, which may specify a function
|
|
51
57
|
of one argument used to retrieve the size of an item's value.
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
The values of a :class:`Cache` are mutable by default, as are e.g. the
|
|
60
|
+
values of a :class:`dict`. It is the user's responsibility to take
|
|
61
|
+
care that cached values are not accidentally modified. This is
|
|
62
|
+
especially important when using a custom `getsizeof` function, since
|
|
63
|
+
the size of an item's value will only be computed when the item is
|
|
64
|
+
inserted into the cache.
|
|
59
65
|
|
|
60
66
|
.. note::
|
|
61
67
|
|
|
@@ -13,7 +13,7 @@ __all__ = (
|
|
|
13
13
|
"cachedmethod",
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
__version__ = "5.5.
|
|
16
|
+
__version__ = "5.5.2"
|
|
17
17
|
|
|
18
18
|
import collections
|
|
19
19
|
import collections.abc
|
|
@@ -23,6 +23,7 @@ import random
|
|
|
23
23
|
import time
|
|
24
24
|
|
|
25
25
|
from . import keys
|
|
26
|
+
from ._decorators import _cached_wrapper
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class _DefaultSize:
|
|
@@ -643,150 +644,28 @@ def cached(cache, key=keys.hashkey, lock=None, info=False):
|
|
|
643
644
|
|
|
644
645
|
def decorator(func):
|
|
645
646
|
if info:
|
|
646
|
-
hits = misses = 0
|
|
647
|
-
|
|
648
647
|
if isinstance(cache, Cache):
|
|
649
648
|
|
|
650
|
-
def
|
|
651
|
-
nonlocal hits, misses
|
|
649
|
+
def make_info(hits, misses):
|
|
652
650
|
return _CacheInfo(hits, misses, cache.maxsize, cache.currsize)
|
|
653
651
|
|
|
654
652
|
elif isinstance(cache, collections.abc.Mapping):
|
|
655
653
|
|
|
656
|
-
def
|
|
657
|
-
nonlocal hits, misses
|
|
654
|
+
def make_info(hits, misses):
|
|
658
655
|
return _CacheInfo(hits, misses, None, len(cache))
|
|
659
656
|
|
|
660
657
|
else:
|
|
661
658
|
|
|
662
|
-
def
|
|
663
|
-
nonlocal hits, misses
|
|
659
|
+
def make_info(hits, misses):
|
|
664
660
|
return _CacheInfo(hits, misses, 0, 0)
|
|
665
661
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
def wrapper(*args, **kwargs):
|
|
669
|
-
nonlocal misses
|
|
670
|
-
misses += 1
|
|
671
|
-
return func(*args, **kwargs)
|
|
672
|
-
|
|
673
|
-
def cache_clear():
|
|
674
|
-
nonlocal hits, misses
|
|
675
|
-
hits = misses = 0
|
|
676
|
-
|
|
677
|
-
cache_info = getinfo
|
|
678
|
-
|
|
679
|
-
elif lock is None:
|
|
680
|
-
|
|
681
|
-
def wrapper(*args, **kwargs):
|
|
682
|
-
nonlocal hits, misses
|
|
683
|
-
k = key(*args, **kwargs)
|
|
684
|
-
try:
|
|
685
|
-
result = cache[k]
|
|
686
|
-
hits += 1
|
|
687
|
-
return result
|
|
688
|
-
except KeyError:
|
|
689
|
-
misses += 1
|
|
690
|
-
v = func(*args, **kwargs)
|
|
691
|
-
try:
|
|
692
|
-
cache[k] = v
|
|
693
|
-
except ValueError:
|
|
694
|
-
pass # value too large
|
|
695
|
-
return v
|
|
696
|
-
|
|
697
|
-
def cache_clear():
|
|
698
|
-
nonlocal hits, misses
|
|
699
|
-
cache.clear()
|
|
700
|
-
hits = misses = 0
|
|
701
|
-
|
|
702
|
-
cache_info = getinfo
|
|
703
|
-
|
|
704
|
-
else:
|
|
705
|
-
|
|
706
|
-
def wrapper(*args, **kwargs):
|
|
707
|
-
nonlocal hits, misses
|
|
708
|
-
k = key(*args, **kwargs)
|
|
709
|
-
try:
|
|
710
|
-
with lock:
|
|
711
|
-
result = cache[k]
|
|
712
|
-
hits += 1
|
|
713
|
-
return result
|
|
714
|
-
except KeyError:
|
|
715
|
-
with lock:
|
|
716
|
-
misses += 1
|
|
717
|
-
v = func(*args, **kwargs)
|
|
718
|
-
# in case of a race, prefer the item already in the cache
|
|
719
|
-
try:
|
|
720
|
-
with lock:
|
|
721
|
-
return cache.setdefault(k, v)
|
|
722
|
-
except ValueError:
|
|
723
|
-
return v # value too large
|
|
724
|
-
|
|
725
|
-
def cache_clear():
|
|
726
|
-
nonlocal hits, misses
|
|
727
|
-
with lock:
|
|
728
|
-
cache.clear()
|
|
729
|
-
hits = misses = 0
|
|
730
|
-
|
|
731
|
-
def cache_info():
|
|
732
|
-
with lock:
|
|
733
|
-
return getinfo()
|
|
734
|
-
|
|
662
|
+
wrapper = _cached_wrapper(func, cache, key, lock, make_info)
|
|
735
663
|
else:
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
def wrapper(*args, **kwargs):
|
|
739
|
-
return func(*args, **kwargs)
|
|
740
|
-
|
|
741
|
-
def cache_clear():
|
|
742
|
-
pass
|
|
743
|
-
|
|
744
|
-
elif lock is None:
|
|
745
|
-
|
|
746
|
-
def wrapper(*args, **kwargs):
|
|
747
|
-
k = key(*args, **kwargs)
|
|
748
|
-
try:
|
|
749
|
-
return cache[k]
|
|
750
|
-
except KeyError:
|
|
751
|
-
pass # key not found
|
|
752
|
-
v = func(*args, **kwargs)
|
|
753
|
-
try:
|
|
754
|
-
cache[k] = v
|
|
755
|
-
except ValueError:
|
|
756
|
-
pass # value too large
|
|
757
|
-
return v
|
|
758
|
-
|
|
759
|
-
def cache_clear():
|
|
760
|
-
cache.clear()
|
|
761
|
-
|
|
762
|
-
else:
|
|
763
|
-
|
|
764
|
-
def wrapper(*args, **kwargs):
|
|
765
|
-
k = key(*args, **kwargs)
|
|
766
|
-
try:
|
|
767
|
-
with lock:
|
|
768
|
-
return cache[k]
|
|
769
|
-
except KeyError:
|
|
770
|
-
pass # key not found
|
|
771
|
-
v = func(*args, **kwargs)
|
|
772
|
-
# in case of a race, prefer the item already in the cache
|
|
773
|
-
try:
|
|
774
|
-
with lock:
|
|
775
|
-
return cache.setdefault(k, v)
|
|
776
|
-
except ValueError:
|
|
777
|
-
return v # value too large
|
|
778
|
-
|
|
779
|
-
def cache_clear():
|
|
780
|
-
with lock:
|
|
781
|
-
cache.clear()
|
|
782
|
-
|
|
783
|
-
cache_info = None
|
|
664
|
+
wrapper = _cached_wrapper(func, cache, key, lock, None)
|
|
784
665
|
|
|
785
666
|
wrapper.cache = cache
|
|
786
667
|
wrapper.cache_key = key
|
|
787
668
|
wrapper.cache_lock = lock
|
|
788
|
-
wrapper.cache_clear = cache_clear
|
|
789
|
-
wrapper.cache_info = cache_info
|
|
790
669
|
|
|
791
670
|
return functools.update_wrapper(wrapper, func)
|
|
792
671
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Extensible memoizing decorator helpers."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _cached_locked_info(func, cache, key, lock, info):
|
|
5
|
+
hits = misses = 0
|
|
6
|
+
|
|
7
|
+
def wrapper(*args, **kwargs):
|
|
8
|
+
nonlocal hits, misses
|
|
9
|
+
k = key(*args, **kwargs)
|
|
10
|
+
with lock:
|
|
11
|
+
try:
|
|
12
|
+
result = cache[k]
|
|
13
|
+
hits += 1
|
|
14
|
+
return result
|
|
15
|
+
except KeyError:
|
|
16
|
+
misses += 1
|
|
17
|
+
v = func(*args, **kwargs)
|
|
18
|
+
with lock:
|
|
19
|
+
try:
|
|
20
|
+
# in case of a race, prefer the item already in the cache
|
|
21
|
+
return cache.setdefault(k, v)
|
|
22
|
+
except ValueError:
|
|
23
|
+
return v # value too large
|
|
24
|
+
|
|
25
|
+
def cache_clear():
|
|
26
|
+
nonlocal hits, misses
|
|
27
|
+
with lock:
|
|
28
|
+
cache.clear()
|
|
29
|
+
hits = misses = 0
|
|
30
|
+
|
|
31
|
+
def cache_info():
|
|
32
|
+
with lock:
|
|
33
|
+
return info(hits, misses)
|
|
34
|
+
|
|
35
|
+
wrapper.cache_clear = cache_clear
|
|
36
|
+
wrapper.cache_info = cache_info
|
|
37
|
+
return wrapper
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _cached_unlocked_info(func, cache, key, info):
|
|
41
|
+
hits = misses = 0
|
|
42
|
+
|
|
43
|
+
def wrapper(*args, **kwargs):
|
|
44
|
+
nonlocal hits, misses
|
|
45
|
+
k = key(*args, **kwargs)
|
|
46
|
+
try:
|
|
47
|
+
result = cache[k]
|
|
48
|
+
hits += 1
|
|
49
|
+
return result
|
|
50
|
+
except KeyError:
|
|
51
|
+
misses += 1
|
|
52
|
+
v = func(*args, **kwargs)
|
|
53
|
+
try:
|
|
54
|
+
cache[k] = v
|
|
55
|
+
except ValueError:
|
|
56
|
+
pass # value too large
|
|
57
|
+
return v
|
|
58
|
+
|
|
59
|
+
def cache_clear():
|
|
60
|
+
nonlocal hits, misses
|
|
61
|
+
cache.clear()
|
|
62
|
+
hits = misses = 0
|
|
63
|
+
|
|
64
|
+
wrapper.cache_clear = cache_clear
|
|
65
|
+
wrapper.cache_info = lambda: info(hits, misses)
|
|
66
|
+
return wrapper
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _uncached_info(func, info):
|
|
70
|
+
misses = 0
|
|
71
|
+
|
|
72
|
+
def wrapper(*args, **kwargs):
|
|
73
|
+
nonlocal misses
|
|
74
|
+
misses += 1
|
|
75
|
+
return func(*args, **kwargs)
|
|
76
|
+
|
|
77
|
+
def cache_clear():
|
|
78
|
+
nonlocal misses
|
|
79
|
+
misses = 0
|
|
80
|
+
|
|
81
|
+
wrapper.cache_clear = cache_clear
|
|
82
|
+
wrapper.cache_info = lambda: info(0, misses)
|
|
83
|
+
return wrapper
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _cached_locked(func, cache, key, lock):
|
|
87
|
+
def wrapper(*args, **kwargs):
|
|
88
|
+
k = key(*args, **kwargs)
|
|
89
|
+
with lock:
|
|
90
|
+
try:
|
|
91
|
+
return cache[k]
|
|
92
|
+
except KeyError:
|
|
93
|
+
pass # key not found
|
|
94
|
+
v = func(*args, **kwargs)
|
|
95
|
+
with lock:
|
|
96
|
+
try:
|
|
97
|
+
# in case of a race, prefer the item already in the cache
|
|
98
|
+
return cache.setdefault(k, v)
|
|
99
|
+
except ValueError:
|
|
100
|
+
return v # value too large
|
|
101
|
+
|
|
102
|
+
def cache_clear():
|
|
103
|
+
with lock:
|
|
104
|
+
cache.clear()
|
|
105
|
+
|
|
106
|
+
wrapper.cache_clear = cache_clear
|
|
107
|
+
return wrapper
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _cached_unlocked(func, cache, key):
|
|
111
|
+
def wrapper(*args, **kwargs):
|
|
112
|
+
k = key(*args, **kwargs)
|
|
113
|
+
try:
|
|
114
|
+
return cache[k]
|
|
115
|
+
except KeyError:
|
|
116
|
+
pass # key not found
|
|
117
|
+
v = func(*args, **kwargs)
|
|
118
|
+
try:
|
|
119
|
+
cache[k] = v
|
|
120
|
+
except ValueError:
|
|
121
|
+
pass # value too large
|
|
122
|
+
return v
|
|
123
|
+
|
|
124
|
+
wrapper.cache_clear = lambda: cache.clear()
|
|
125
|
+
return wrapper
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _uncached(func):
|
|
129
|
+
def wrapper(*args, **kwargs):
|
|
130
|
+
return func(*args, **kwargs)
|
|
131
|
+
|
|
132
|
+
wrapper.cache_clear = lambda: None
|
|
133
|
+
return wrapper
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _cached_wrapper(func, cache, key, lock, info):
|
|
137
|
+
if info is not None:
|
|
138
|
+
if cache is None:
|
|
139
|
+
wrapper = _uncached_info(func, info)
|
|
140
|
+
elif lock is None:
|
|
141
|
+
wrapper = _cached_unlocked_info(func, cache, key, info)
|
|
142
|
+
else:
|
|
143
|
+
wrapper = _cached_locked_info(func, cache, key, lock, info)
|
|
144
|
+
else:
|
|
145
|
+
if cache is None:
|
|
146
|
+
wrapper = _uncached(func)
|
|
147
|
+
elif lock is None:
|
|
148
|
+
wrapper = _cached_unlocked(func, cache, key)
|
|
149
|
+
else:
|
|
150
|
+
wrapper = _cached_locked(func, cache, key, lock)
|
|
151
|
+
wrapper.cache_info = None
|
|
152
|
+
return wrapper
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: cachetools
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.2
|
|
4
4
|
Summary: Extensible memoizing collections and decorators
|
|
5
5
|
Home-page: https://github.com/tkem/cachetools/
|
|
6
6
|
Author: Thomas Kemmer
|
|
@@ -126,7 +126,7 @@ Related Projects
|
|
|
126
126
|
License
|
|
127
127
|
------------------------------------------------------------------------
|
|
128
128
|
|
|
129
|
-
Copyright (c) 2014-
|
|
129
|
+
Copyright (c) 2014-2025 Thomas Kemmer.
|
|
130
130
|
|
|
131
131
|
Licensed under the `MIT License`_.
|
|
132
132
|
|
|
@@ -164,6 +164,30 @@ class CacheWrapperTest(unittest.TestCase, DecoratorTestMixin):
|
|
|
164
164
|
self.assertEqual(len(cache), 0)
|
|
165
165
|
self.assertEqual(wrapper.cache_info(), (0, 0, 2, 0))
|
|
166
166
|
|
|
167
|
+
def test_decorator_lock_info(self):
|
|
168
|
+
cache = self.cache(2)
|
|
169
|
+
lock = CountedLock()
|
|
170
|
+
wrapper = cachetools.cached(cache, lock=lock, info=True)(self.func)
|
|
171
|
+
self.assertEqual(wrapper.cache_info(), (0, 0, 2, 0))
|
|
172
|
+
self.assertEqual(lock.count, 1)
|
|
173
|
+
self.assertEqual(wrapper(0), 0)
|
|
174
|
+
self.assertEqual(lock.count, 3)
|
|
175
|
+
self.assertEqual(wrapper.cache_info(), (0, 1, 2, 1))
|
|
176
|
+
self.assertEqual(lock.count, 4)
|
|
177
|
+
self.assertEqual(wrapper(1), 1)
|
|
178
|
+
self.assertEqual(lock.count, 6)
|
|
179
|
+
self.assertEqual(wrapper.cache_info(), (0, 2, 2, 2))
|
|
180
|
+
self.assertEqual(lock.count, 7)
|
|
181
|
+
self.assertEqual(wrapper(0), 0)
|
|
182
|
+
self.assertEqual(lock.count, 8)
|
|
183
|
+
self.assertEqual(wrapper.cache_info(), (1, 2, 2, 2))
|
|
184
|
+
self.assertEqual(lock.count, 9)
|
|
185
|
+
wrapper.cache_clear()
|
|
186
|
+
self.assertEqual(lock.count, 10)
|
|
187
|
+
self.assertEqual(len(cache), 0)
|
|
188
|
+
self.assertEqual(wrapper.cache_info(), (0, 0, 2, 0))
|
|
189
|
+
self.assertEqual(lock.count, 11)
|
|
190
|
+
|
|
167
191
|
def test_zero_size_cache_decorator(self):
|
|
168
192
|
cache = self.cache(0)
|
|
169
193
|
wrapper = cachetools.cached(cache)(self.func)
|
|
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
|