cachetools 5.3.3__tar.gz → 5.5.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.3.3 → cachetools-5.5.0}/CHANGELOG.rst +26 -0
- {cachetools-5.3.3/src/cachetools.egg-info → cachetools-5.5.0}/PKG-INFO +1 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/docs/index.rst +69 -10
- {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools/__init__.py +21 -6
- {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools/func.py +4 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools/keys.py +6 -1
- {cachetools-5.3.3 → cachetools-5.5.0/src/cachetools.egg-info}/PKG-INFO +1 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/__init__.py +0 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_cache.py +0 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_cachedmethod.py +21 -21
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_fifo.py +0 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_func.py +8 -6
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_keys.py +35 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_lfu.py +0 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_lru.py +0 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_mru.py +17 -4
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_rr.py +0 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_tlru.py +8 -5
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_ttl.py +12 -7
- {cachetools-5.3.3 → cachetools-5.5.0}/tox.ini +1 -1
- {cachetools-5.3.3 → cachetools-5.5.0}/LICENSE +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/MANIFEST.in +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/README.rst +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/docs/conf.py +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/pyproject.toml +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/setup.cfg +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/setup.py +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools.egg-info/SOURCES.txt +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools.egg-info/dependency_links.txt +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools.egg-info/top_level.txt +0 -0
- {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_cached.py +0 -0
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
v5.5.0 (2024-08-18)
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
- ``TTLCache.expire()`` returns iterable of expired ``(key, value)``
|
|
5
|
+
pairs.
|
|
6
|
+
|
|
7
|
+
- ``TLRUCache.expire()`` returns iterable of expired ``(key, value)``
|
|
8
|
+
pairs.
|
|
9
|
+
|
|
10
|
+
- Documentation improvements.
|
|
11
|
+
|
|
12
|
+
- Update CI environment.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
v5.4.0 (2024-07-15)
|
|
16
|
+
===================
|
|
17
|
+
|
|
18
|
+
- Add the ``keys.typedmethodkey`` decorator.
|
|
19
|
+
|
|
20
|
+
- Deprecate ``MRUCache`` class.
|
|
21
|
+
|
|
22
|
+
- Deprecate ``@func.mru_cache`` decorator.
|
|
23
|
+
|
|
24
|
+
- Update CI environment.
|
|
25
|
+
|
|
26
|
+
|
|
1
27
|
v5.3.3 (2024-02-26)
|
|
2
28
|
===================
|
|
3
29
|
|
|
@@ -26,6 +26,8 @@ method calls.
|
|
|
26
26
|
from unittest import mock
|
|
27
27
|
urllib = mock.MagicMock()
|
|
28
28
|
|
|
29
|
+
import time
|
|
30
|
+
|
|
29
31
|
|
|
30
32
|
Cache implementations
|
|
31
33
|
=====================
|
|
@@ -96,6 +98,12 @@ computed when the item is inserted into the cache.
|
|
|
96
98
|
This class discards the most recently used items first to make
|
|
97
99
|
space when necessary.
|
|
98
100
|
|
|
101
|
+
.. deprecated:: 5.4
|
|
102
|
+
|
|
103
|
+
`MRUCache` has been deprecated due to lack of use, to reduce
|
|
104
|
+
maintenance. Please choose another cache implementation that suits
|
|
105
|
+
your needs.
|
|
106
|
+
|
|
99
107
|
.. autoclass:: RRCache(maxsize, choice=random.choice, getsizeof=None)
|
|
100
108
|
:members: choice, popitem
|
|
101
109
|
|
|
@@ -147,6 +155,8 @@ computed when the item is inserted into the cache.
|
|
|
147
155
|
items that have expired by the current value returned by
|
|
148
156
|
:attr:`timer`.
|
|
149
157
|
|
|
158
|
+
:returns: An iterable of expired `(key, value)` pairs.
|
|
159
|
+
|
|
150
160
|
.. autoclass:: TLRUCache(maxsize, ttu, timer=time.monotonic, getsizeof=None)
|
|
151
161
|
:members: popitem, timer, ttu
|
|
152
162
|
|
|
@@ -158,18 +168,29 @@ computed when the item is inserted into the cache.
|
|
|
158
168
|
value of `timer()`.
|
|
159
169
|
|
|
160
170
|
.. testcode::
|
|
161
|
-
|
|
162
|
-
from datetime import datetime, timedelta
|
|
163
|
-
|
|
171
|
+
|
|
164
172
|
def my_ttu(_key, value, now):
|
|
165
|
-
# assume value.
|
|
166
|
-
|
|
173
|
+
# assume value.ttu contains the item's time-to-use in seconds
|
|
174
|
+
# note that the _key argument is ignored in this example
|
|
175
|
+
return now + value.ttu
|
|
167
176
|
|
|
168
|
-
cache = TLRUCache(maxsize=10, ttu=my_ttu
|
|
177
|
+
cache = TLRUCache(maxsize=10, ttu=my_ttu)
|
|
169
178
|
|
|
170
179
|
The expression `ttu(key, value, timer())` defines the expiration
|
|
171
180
|
time of a cache item, and must be comparable against later results
|
|
172
|
-
of `timer()`.
|
|
181
|
+
of `timer()`. As with :class:`TTLCache`, a custom `timer` function
|
|
182
|
+
can be supplied, which does not have to return a numeric value.
|
|
183
|
+
|
|
184
|
+
.. testcode::
|
|
185
|
+
|
|
186
|
+
from datetime import datetime, timedelta
|
|
187
|
+
|
|
188
|
+
def datetime_ttu(_key, value, now):
|
|
189
|
+
# assume now to be of type datetime.datetime, and
|
|
190
|
+
# value.hours to contain the item's time-to-use in hours
|
|
191
|
+
return now + timedelta(hours=value.hours)
|
|
192
|
+
|
|
193
|
+
cache = TLRUCache(maxsize=10, ttu=datetime_ttu, timer=datetime.now)
|
|
173
194
|
|
|
174
195
|
Items that expire because they have exceeded their time-to-use will
|
|
175
196
|
be no longer accessible, and will be removed eventually. If no
|
|
@@ -187,6 +208,8 @@ computed when the item is inserted into the cache.
|
|
|
187
208
|
items that have expired by the current value returned by
|
|
188
209
|
:attr:`timer`.
|
|
189
210
|
|
|
211
|
+
:returns: An iterable of expired `(key, value)` pairs.
|
|
212
|
+
|
|
190
213
|
|
|
191
214
|
Extending cache classes
|
|
192
215
|
=======================
|
|
@@ -211,6 +234,31 @@ cache, this can be achieved by overriding this method in a subclass:
|
|
|
211
234
|
>>> c['c'] = 3
|
|
212
235
|
Key "a" evicted with value "1"
|
|
213
236
|
|
|
237
|
+
With :class:`TTLCache` and :class:`TLRUCache`, items may also be
|
|
238
|
+
removed after they expire. In this case, :meth:`popitem` will *not*
|
|
239
|
+
be called, but :meth:`expire` will be called from the next mutating
|
|
240
|
+
operation and will return an iterable of the expired `(key, value)`
|
|
241
|
+
pairs. By overrding :meth:`expire`, a subclass will be able to track
|
|
242
|
+
expired items:
|
|
243
|
+
|
|
244
|
+
.. doctest::
|
|
245
|
+
:pyversion: >= 3
|
|
246
|
+
|
|
247
|
+
>>> class ExpCache(TTLCache):
|
|
248
|
+
... def expire(self, time=None):
|
|
249
|
+
... items = super().expire(time)
|
|
250
|
+
... print(f"Expired items: {items}")
|
|
251
|
+
... return items
|
|
252
|
+
|
|
253
|
+
>>> c = ExpCache(maxsize=10, ttl=1.0)
|
|
254
|
+
>>> c['a'] = 1
|
|
255
|
+
Expired items: []
|
|
256
|
+
>>> c['b'] = 2
|
|
257
|
+
Expired items: []
|
|
258
|
+
>>> time.sleep(1.5)
|
|
259
|
+
>>> c['c'] = 3
|
|
260
|
+
Expired items: [('a', 1), ('b', 2)]
|
|
261
|
+
|
|
214
262
|
Similar to the standard library's :class:`collections.defaultdict`,
|
|
215
263
|
subclasses of :class:`Cache` may implement a :meth:`__missing__`
|
|
216
264
|
method which is called by :meth:`Cache.__getitem__` if the requested
|
|
@@ -416,7 +464,7 @@ often called with the same arguments:
|
|
|
416
464
|
|
|
417
465
|
.. testcode::
|
|
418
466
|
|
|
419
|
-
class CachedPEPs
|
|
467
|
+
class CachedPEPs:
|
|
420
468
|
|
|
421
469
|
def __init__(self, cachesize):
|
|
422
470
|
self.cache = LRUCache(maxsize=cachesize)
|
|
@@ -444,7 +492,7 @@ often called with the same arguments:
|
|
|
444
492
|
|
|
445
493
|
.. testcode::
|
|
446
494
|
|
|
447
|
-
class CachedReferences
|
|
495
|
+
class CachedReferences:
|
|
448
496
|
|
|
449
497
|
def __init__(self, cachesize):
|
|
450
498
|
self.cache = LRUCache(maxsize=cachesize)
|
|
@@ -491,7 +539,7 @@ functions with the :func:`cached` and :func:`cachedmethod` decorators:
|
|
|
491
539
|
|
|
492
540
|
.. autofunction:: methodkey
|
|
493
541
|
|
|
494
|
-
This function is
|
|
542
|
+
This function is similar to :func:`hashkey`, but ignores its
|
|
495
543
|
first positional argument, i.e. `self` when used with the
|
|
496
544
|
:func:`cachedmethod` decorator.
|
|
497
545
|
|
|
@@ -502,6 +550,12 @@ functions with the :func:`cached` and :func:`cachedmethod` decorators:
|
|
|
502
550
|
``typedkey(3)`` and ``typedkey(3.0)`` will return different
|
|
503
551
|
results.
|
|
504
552
|
|
|
553
|
+
.. autofunction:: typedmethodkey
|
|
554
|
+
|
|
555
|
+
This function is similar to :func:`typedkey`, but ignores its
|
|
556
|
+
first positional argument, i.e. `self` when used with the
|
|
557
|
+
:func:`cachedmethod` decorator.
|
|
558
|
+
|
|
505
559
|
These functions can also be helpful when implementing custom key
|
|
506
560
|
functions for handling some non-hashable arguments. For example,
|
|
507
561
|
calling the following function with a dictionary as its `env` argument
|
|
@@ -598,6 +652,11 @@ all the decorators in this module are thread-safe by default.
|
|
|
598
652
|
saves up to `maxsize` results based on a Most Recently Used (MRU)
|
|
599
653
|
algorithm.
|
|
600
654
|
|
|
655
|
+
.. deprecated:: 5.4
|
|
656
|
+
|
|
657
|
+
The `mru_cache` decorator has been deprecated due to lack of use.
|
|
658
|
+
Please choose a decorator based on some other algorithm.
|
|
659
|
+
|
|
601
660
|
.. decorator:: rr_cache(user_function)
|
|
602
661
|
rr_cache(maxsize=128, choice=random.choice, typed=False)
|
|
603
662
|
|
|
@@ -13,7 +13,7 @@ __all__ = (
|
|
|
13
13
|
"cachedmethod",
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
__version__ = "5.
|
|
16
|
+
__version__ = "5.5.0"
|
|
17
17
|
|
|
18
18
|
import collections
|
|
19
19
|
import collections.abc
|
|
@@ -26,7 +26,6 @@ from . import keys
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class _DefaultSize:
|
|
29
|
-
|
|
30
29
|
__slots__ = ()
|
|
31
30
|
|
|
32
31
|
def __getitem__(self, _):
|
|
@@ -241,6 +240,10 @@ class MRUCache(Cache):
|
|
|
241
240
|
"""Most Recently Used (MRU) cache implementation."""
|
|
242
241
|
|
|
243
242
|
def __init__(self, maxsize, getsizeof=None):
|
|
243
|
+
from warnings import warn
|
|
244
|
+
|
|
245
|
+
warn("MRUCache is deprecated", DeprecationWarning, stacklevel=2)
|
|
246
|
+
|
|
244
247
|
Cache.__init__(self, maxsize, getsizeof)
|
|
245
248
|
self.__order = collections.OrderedDict()
|
|
246
249
|
|
|
@@ -374,7 +377,6 @@ class TTLCache(_TimedCache):
|
|
|
374
377
|
"""LRU Cache implementation with per-item time-to-live (TTL) value."""
|
|
375
378
|
|
|
376
379
|
class _Link:
|
|
377
|
-
|
|
378
380
|
__slots__ = ("key", "expires", "next", "prev")
|
|
379
381
|
|
|
380
382
|
def __init__(self, key=None, expires=None):
|
|
@@ -465,19 +467,26 @@ class TTLCache(_TimedCache):
|
|
|
465
467
|
return self.__ttl
|
|
466
468
|
|
|
467
469
|
def expire(self, time=None):
|
|
468
|
-
"""Remove expired items from the cache
|
|
470
|
+
"""Remove expired items from the cache and return an iterable of the
|
|
471
|
+
expired `(key, value)` pairs.
|
|
472
|
+
|
|
473
|
+
"""
|
|
469
474
|
if time is None:
|
|
470
475
|
time = self.timer()
|
|
471
476
|
root = self.__root
|
|
472
477
|
curr = root.next
|
|
473
478
|
links = self.__links
|
|
479
|
+
expired = []
|
|
474
480
|
cache_delitem = Cache.__delitem__
|
|
481
|
+
cache_getitem = Cache.__getitem__
|
|
475
482
|
while curr is not root and not (time < curr.expires):
|
|
483
|
+
expired.append((curr.key, cache_getitem(self, curr.key)))
|
|
476
484
|
cache_delitem(self, curr.key)
|
|
477
485
|
del links[curr.key]
|
|
478
486
|
next = curr.next
|
|
479
487
|
curr.unlink()
|
|
480
488
|
curr = next
|
|
489
|
+
return expired
|
|
481
490
|
|
|
482
491
|
def popitem(self):
|
|
483
492
|
"""Remove and return the `(key, value)` pair least recently used that
|
|
@@ -504,7 +513,6 @@ class TLRUCache(_TimedCache):
|
|
|
504
513
|
|
|
505
514
|
@functools.total_ordering
|
|
506
515
|
class _Item:
|
|
507
|
-
|
|
508
516
|
__slots__ = ("key", "expires", "removed")
|
|
509
517
|
|
|
510
518
|
def __init__(self, key=None, expires=None):
|
|
@@ -579,7 +587,10 @@ class TLRUCache(_TimedCache):
|
|
|
579
587
|
return self.__ttu
|
|
580
588
|
|
|
581
589
|
def expire(self, time=None):
|
|
582
|
-
"""Remove expired items from the cache
|
|
590
|
+
"""Remove expired items from the cache and return an iterable of the
|
|
591
|
+
expired `(key, value)` pairs.
|
|
592
|
+
|
|
593
|
+
"""
|
|
583
594
|
if time is None:
|
|
584
595
|
time = self.timer()
|
|
585
596
|
items = self.__items
|
|
@@ -588,12 +599,16 @@ class TLRUCache(_TimedCache):
|
|
|
588
599
|
if len(order) > len(items) * 2:
|
|
589
600
|
self.__order = order = [item for item in order if not item.removed]
|
|
590
601
|
heapq.heapify(order)
|
|
602
|
+
expired = []
|
|
591
603
|
cache_delitem = Cache.__delitem__
|
|
604
|
+
cache_getitem = Cache.__getitem__
|
|
592
605
|
while order and (order[0].removed or not (time < order[0].expires)):
|
|
593
606
|
item = heapq.heappop(order)
|
|
594
607
|
if not item.removed:
|
|
608
|
+
expired.append((item.key, cache_getitem(self, item.key)))
|
|
595
609
|
cache_delitem(self, item.key)
|
|
596
610
|
del items[item.key]
|
|
611
|
+
return expired
|
|
597
612
|
|
|
598
613
|
def popitem(self):
|
|
599
614
|
"""Remove and return the `(key, value)` pair least recently used that
|
|
@@ -82,6 +82,10 @@ def mru_cache(maxsize=128, typed=False):
|
|
|
82
82
|
up to `maxsize` results based on a Most Recently Used (MRU)
|
|
83
83
|
algorithm.
|
|
84
84
|
"""
|
|
85
|
+
from warnings import warn
|
|
86
|
+
|
|
87
|
+
warn("@mru_cache is deprecated", DeprecationWarning, stacklevel=2)
|
|
88
|
+
|
|
85
89
|
if maxsize is None:
|
|
86
90
|
return _cache({}, None, typed)
|
|
87
91
|
elif callable(maxsize):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Key functions for memoizing decorators."""
|
|
2
2
|
|
|
3
|
-
__all__ = ("hashkey", "methodkey", "typedkey")
|
|
3
|
+
__all__ = ("hashkey", "methodkey", "typedkey", "typedmethodkey")
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class _HashedTuple(tuple):
|
|
@@ -55,3 +55,8 @@ def typedkey(*args, **kwargs):
|
|
|
55
55
|
key += tuple(type(v) for v in args)
|
|
56
56
|
key += tuple(type(v) for _, v in sorted(kwargs.items()))
|
|
57
57
|
return key
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def typedmethodkey(self, *args, **kwargs):
|
|
61
|
+
"""Return a typed cache key for use with cached methods."""
|
|
62
|
+
return typedkey(*args, **kwargs)
|
|
@@ -14,8 +14,8 @@ class Cached:
|
|
|
14
14
|
self.count += 1
|
|
15
15
|
return count
|
|
16
16
|
|
|
17
|
-
@cachedmethod(lambda self: self.cache, key=keys.
|
|
18
|
-
def
|
|
17
|
+
@cachedmethod(lambda self: self.cache, key=keys.typedmethodkey)
|
|
18
|
+
def get_typedmethod(self, value):
|
|
19
19
|
count = self.count
|
|
20
20
|
self.count += 1
|
|
21
21
|
return count
|
|
@@ -67,16 +67,16 @@ class CachedMethodTest(unittest.TestCase):
|
|
|
67
67
|
cached.cache.clear()
|
|
68
68
|
self.assertEqual(cached.get(1), 2)
|
|
69
69
|
|
|
70
|
-
def
|
|
70
|
+
def test_typedmethod_dict(self):
|
|
71
71
|
cached = Cached(LRUCache(maxsize=2))
|
|
72
72
|
|
|
73
|
-
self.assertEqual(cached.
|
|
74
|
-
self.assertEqual(cached.
|
|
75
|
-
self.assertEqual(cached.
|
|
76
|
-
self.assertEqual(cached.
|
|
77
|
-
self.assertEqual(cached.
|
|
78
|
-
self.assertEqual(cached.
|
|
79
|
-
self.assertEqual(cached.
|
|
73
|
+
self.assertEqual(cached.get_typedmethod(0), 0)
|
|
74
|
+
self.assertEqual(cached.get_typedmethod(1), 1)
|
|
75
|
+
self.assertEqual(cached.get_typedmethod(1), 1)
|
|
76
|
+
self.assertEqual(cached.get_typedmethod(1.0), 2)
|
|
77
|
+
self.assertEqual(cached.get_typedmethod(1.0), 2)
|
|
78
|
+
self.assertEqual(cached.get_typedmethod(0.0), 3)
|
|
79
|
+
self.assertEqual(cached.get_typedmethod(0), 4)
|
|
80
80
|
|
|
81
81
|
def test_lru(self):
|
|
82
82
|
cached = Cached(LRUCache(maxsize=2))
|
|
@@ -90,16 +90,16 @@ class CachedMethodTest(unittest.TestCase):
|
|
|
90
90
|
cached.cache.clear()
|
|
91
91
|
self.assertEqual(cached.get(1), 2)
|
|
92
92
|
|
|
93
|
-
def
|
|
93
|
+
def test_typedmethod_lru(self):
|
|
94
94
|
cached = Cached(LRUCache(maxsize=2))
|
|
95
95
|
|
|
96
|
-
self.assertEqual(cached.
|
|
97
|
-
self.assertEqual(cached.
|
|
98
|
-
self.assertEqual(cached.
|
|
99
|
-
self.assertEqual(cached.
|
|
100
|
-
self.assertEqual(cached.
|
|
101
|
-
self.assertEqual(cached.
|
|
102
|
-
self.assertEqual(cached.
|
|
96
|
+
self.assertEqual(cached.get_typedmethod(0), 0)
|
|
97
|
+
self.assertEqual(cached.get_typedmethod(1), 1)
|
|
98
|
+
self.assertEqual(cached.get_typedmethod(1), 1)
|
|
99
|
+
self.assertEqual(cached.get_typedmethod(1.0), 2)
|
|
100
|
+
self.assertEqual(cached.get_typedmethod(1.0), 2)
|
|
101
|
+
self.assertEqual(cached.get_typedmethod(0.0), 3)
|
|
102
|
+
self.assertEqual(cached.get_typedmethod(0), 4)
|
|
103
103
|
|
|
104
104
|
def test_nospace(self):
|
|
105
105
|
cached = Cached(LRUCache(maxsize=0))
|
|
@@ -141,10 +141,10 @@ class CachedMethodTest(unittest.TestCase):
|
|
|
141
141
|
self.assertEqual(cached.get(1), 2)
|
|
142
142
|
self.assertEqual(cached.get(1.0), 2)
|
|
143
143
|
|
|
144
|
-
ref = cached.
|
|
144
|
+
ref = cached.get_typedmethod(1)
|
|
145
145
|
self.assertEqual(ref, 3)
|
|
146
|
-
self.assertEqual(cached.
|
|
147
|
-
self.assertEqual(cached.
|
|
146
|
+
self.assertEqual(cached.get_typedmethod(1), 3)
|
|
147
|
+
self.assertEqual(cached.get_typedmethod(1.0), 4)
|
|
148
148
|
|
|
149
149
|
cached.cache.clear()
|
|
150
150
|
self.assertEqual(cached.get(1), 5)
|
|
@@ -100,30 +100,32 @@ class DecoratorTestMixin:
|
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
class FIFODecoratorTest(unittest.TestCase, DecoratorTestMixin):
|
|
103
|
-
|
|
104
103
|
DECORATOR = staticmethod(cachetools.func.fifo_cache)
|
|
105
104
|
|
|
106
105
|
|
|
107
106
|
class LFUDecoratorTest(unittest.TestCase, DecoratorTestMixin):
|
|
108
|
-
|
|
109
107
|
DECORATOR = staticmethod(cachetools.func.lfu_cache)
|
|
110
108
|
|
|
111
109
|
|
|
112
110
|
class LRUDecoratorTest(unittest.TestCase, DecoratorTestMixin):
|
|
113
|
-
|
|
114
111
|
DECORATOR = staticmethod(cachetools.func.lru_cache)
|
|
115
112
|
|
|
116
113
|
|
|
117
114
|
class MRUDecoratorTest(unittest.TestCase, DecoratorTestMixin):
|
|
115
|
+
def decorator(self, maxsize, **kwargs):
|
|
116
|
+
import warnings
|
|
118
117
|
|
|
119
|
-
|
|
118
|
+
with warnings.catch_warnings(record=True) as w:
|
|
119
|
+
warnings.simplefilter("always")
|
|
120
|
+
d = cachetools.func.mru_cache(maxsize, **kwargs)
|
|
121
|
+
self.assertNotEqual(len(w), 0)
|
|
122
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
123
|
+
return d
|
|
120
124
|
|
|
121
125
|
|
|
122
126
|
class RRDecoratorTest(unittest.TestCase, DecoratorTestMixin):
|
|
123
|
-
|
|
124
127
|
DECORATOR = staticmethod(cachetools.func.rr_cache)
|
|
125
128
|
|
|
126
129
|
|
|
127
130
|
class TTLDecoratorTest(unittest.TestCase, DecoratorTestMixin):
|
|
128
|
-
|
|
129
131
|
DECORATOR = staticmethod(cachetools.func.ttl_cache)
|
|
@@ -21,6 +21,24 @@ class CacheKeysTest(unittest.TestCase):
|
|
|
21
21
|
self.assertEqual(key(1, 2, 3), key(1.0, 2.0, 3.0))
|
|
22
22
|
self.assertEqual(hash(key(1, 2, 3)), hash(key(1.0, 2.0, 3.0)))
|
|
23
23
|
|
|
24
|
+
def methodkey(self, key=cachetools.keys.methodkey):
|
|
25
|
+
# similar to hashkey(), but ignores its first positional argument
|
|
26
|
+
self.assertEqual(key("x"), key("y"))
|
|
27
|
+
self.assertEqual(hash(key("x")), hash(key("y")))
|
|
28
|
+
self.assertEqual(key("x", 1, 2, 3), key("y", 1, 2, 3))
|
|
29
|
+
self.assertEqual(hash(key("x", 1, 2, 3)), hash(key("y", 1, 2, 3)))
|
|
30
|
+
self.assertEqual(key("x", 1, 2, 3, x=0), key("y", 1, 2, 3, x=0))
|
|
31
|
+
self.assertEqual(hash(key("x", 1, 2, 3, x=0)), hash(key("y", 1, 2, 3, x=0)))
|
|
32
|
+
self.assertNotEqual(key("x", 1, 2, 3), key("x", 3, 2, 1))
|
|
33
|
+
self.assertNotEqual(key("x", 1, 2, 3), key("x", 1, 2, 3, x=None))
|
|
34
|
+
self.assertNotEqual(key("x", 1, 2, 3, x=0), key("x", 1, 2, 3, x=None))
|
|
35
|
+
self.assertNotEqual(key("x", 1, 2, 3, x=0), key("x", 1, 2, 3, y=0))
|
|
36
|
+
with self.assertRaises(TypeError):
|
|
37
|
+
hash("x", key({}))
|
|
38
|
+
# untyped keys compare equal
|
|
39
|
+
self.assertEqual(key("x", 1, 2, 3), key("y", 1.0, 2.0, 3.0))
|
|
40
|
+
self.assertEqual(hash(key("x", 1, 2, 3)), hash(key("y", 1.0, 2.0, 3.0)))
|
|
41
|
+
|
|
24
42
|
def test_typedkey(self, key=cachetools.keys.typedkey):
|
|
25
43
|
self.assertEqual(key(), key())
|
|
26
44
|
self.assertEqual(hash(key()), hash(key()))
|
|
@@ -37,6 +55,23 @@ class CacheKeysTest(unittest.TestCase):
|
|
|
37
55
|
# typed keys compare unequal
|
|
38
56
|
self.assertNotEqual(key(1, 2, 3), key(1.0, 2.0, 3.0))
|
|
39
57
|
|
|
58
|
+
def test_typedmethodkey(self, key=cachetools.keys.typedmethodkey):
|
|
59
|
+
# similar to typedkey(), but ignores its first positional argument
|
|
60
|
+
self.assertEqual(key("x"), key("y"))
|
|
61
|
+
self.assertEqual(hash(key("x")), hash(key("y")))
|
|
62
|
+
self.assertEqual(key("x", 1, 2, 3), key("y", 1, 2, 3))
|
|
63
|
+
self.assertEqual(hash(key("x", 1, 2, 3)), hash(key("y", 1, 2, 3)))
|
|
64
|
+
self.assertEqual(key("x", 1, 2, 3, x=0), key("y", 1, 2, 3, x=0))
|
|
65
|
+
self.assertEqual(hash(key("x", 1, 2, 3, x=0)), hash(key("y", 1, 2, 3, x=0)))
|
|
66
|
+
self.assertNotEqual(key("x", 1, 2, 3), key("x", 3, 2, 1))
|
|
67
|
+
self.assertNotEqual(key("x", 1, 2, 3), key("x", 1, 2, 3, x=None))
|
|
68
|
+
self.assertNotEqual(key("x", 1, 2, 3, x=0), key("x", 1, 2, 3, x=None))
|
|
69
|
+
self.assertNotEqual(key("x", 1, 2, 3, x=0), key("x", 1, 2, 3, y=0))
|
|
70
|
+
with self.assertRaises(TypeError):
|
|
71
|
+
hash(key("x", {}))
|
|
72
|
+
# typed keys compare unequal
|
|
73
|
+
self.assertNotEqual(key("x", 1, 2, 3), key("x", 1.0, 2.0, 3.0))
|
|
74
|
+
|
|
40
75
|
def test_addkeys(self, key=cachetools.keys.hashkey):
|
|
41
76
|
self.assertIsInstance(key(), tuple)
|
|
42
77
|
self.assertIsInstance(key(1, 2, 3) + key(4, 5, 6), type(key()))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import unittest
|
|
2
|
+
import warnings
|
|
2
3
|
|
|
3
4
|
from cachetools import MRUCache
|
|
4
5
|
|
|
@@ -6,11 +7,15 @@ from . import CacheTestMixin
|
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class MRUCacheTest(unittest.TestCase, CacheTestMixin):
|
|
9
|
-
|
|
10
|
+
# TODO: method to create cache that can be overridden
|
|
10
11
|
Cache = MRUCache
|
|
11
12
|
|
|
12
13
|
def test_evict__writes_only(self):
|
|
13
|
-
|
|
14
|
+
with warnings.catch_warnings(record=True) as w:
|
|
15
|
+
warnings.simplefilter("always")
|
|
16
|
+
cache = MRUCache(maxsize=2)
|
|
17
|
+
self.assertEqual(len(w), 1)
|
|
18
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
14
19
|
|
|
15
20
|
cache[1] = 1
|
|
16
21
|
cache[2] = 2
|
|
@@ -22,7 +27,11 @@ class MRUCacheTest(unittest.TestCase, CacheTestMixin):
|
|
|
22
27
|
assert 3 in cache
|
|
23
28
|
|
|
24
29
|
def test_evict__with_access(self):
|
|
25
|
-
|
|
30
|
+
with warnings.catch_warnings(record=True) as w:
|
|
31
|
+
warnings.simplefilter("always")
|
|
32
|
+
cache = MRUCache(maxsize=2)
|
|
33
|
+
self.assertEqual(len(w), 1)
|
|
34
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
26
35
|
|
|
27
36
|
cache[1] = 1
|
|
28
37
|
cache[2] = 2
|
|
@@ -34,7 +43,11 @@ class MRUCacheTest(unittest.TestCase, CacheTestMixin):
|
|
|
34
43
|
assert 3 in cache
|
|
35
44
|
|
|
36
45
|
def test_evict__with_delete(self):
|
|
37
|
-
|
|
46
|
+
with warnings.catch_warnings(record=True) as w:
|
|
47
|
+
warnings.simplefilter("always")
|
|
48
|
+
cache = MRUCache(maxsize=2)
|
|
49
|
+
self.assertEqual(len(w), 1)
|
|
50
|
+
self.assertIs(w[0].category, DeprecationWarning)
|
|
38
51
|
|
|
39
52
|
cache[1] = 1
|
|
40
53
|
cache[2] = 2
|
|
@@ -29,7 +29,6 @@ class TLRUTestCache(TLRUCache):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class TLRUCacheTest(unittest.TestCase, CacheTestMixin):
|
|
32
|
-
|
|
33
32
|
Cache = TLRUTestCache
|
|
34
33
|
|
|
35
34
|
def test_ttu(self):
|
|
@@ -157,28 +156,32 @@ class TLRUCacheTest(unittest.TestCase, CacheTestMixin):
|
|
|
157
156
|
self.assertEqual(2, cache[2])
|
|
158
157
|
self.assertEqual(3, cache[3])
|
|
159
158
|
|
|
160
|
-
cache.expire()
|
|
159
|
+
items = cache.expire()
|
|
160
|
+
self.assertEqual(set(), set(items))
|
|
161
161
|
self.assertEqual({1, 2, 3}, set(cache))
|
|
162
162
|
self.assertEqual(3, len(cache))
|
|
163
163
|
self.assertEqual(1, cache[1])
|
|
164
164
|
self.assertEqual(2, cache[2])
|
|
165
165
|
self.assertEqual(3, cache[3])
|
|
166
166
|
|
|
167
|
-
cache.expire(3)
|
|
167
|
+
items = cache.expire(3)
|
|
168
|
+
self.assertEqual({(1, 1)}, set(items))
|
|
168
169
|
self.assertEqual({2, 3}, set(cache))
|
|
169
170
|
self.assertEqual(2, len(cache))
|
|
170
171
|
self.assertNotIn(1, cache)
|
|
171
172
|
self.assertEqual(2, cache[2])
|
|
172
173
|
self.assertEqual(3, cache[3])
|
|
173
174
|
|
|
174
|
-
cache.expire(4)
|
|
175
|
+
items = cache.expire(4)
|
|
176
|
+
self.assertEqual({(2, 2)}, set(items))
|
|
175
177
|
self.assertEqual({3}, set(cache))
|
|
176
178
|
self.assertEqual(1, len(cache))
|
|
177
179
|
self.assertNotIn(1, cache)
|
|
178
180
|
self.assertNotIn(2, cache)
|
|
179
181
|
self.assertEqual(3, cache[3])
|
|
180
182
|
|
|
181
|
-
cache.expire(5)
|
|
183
|
+
items = cache.expire(5)
|
|
184
|
+
self.assertEqual({(3, 3)}, set(items))
|
|
182
185
|
self.assertEqual(set(), set(cache))
|
|
183
186
|
self.assertEqual(0, len(cache))
|
|
184
187
|
self.assertNotIn(1, cache)
|
|
@@ -26,7 +26,6 @@ class TTLTestCache(TTLCache):
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class TTLCacheTest(unittest.TestCase, CacheTestMixin):
|
|
29
|
-
|
|
30
29
|
Cache = TTLTestCache
|
|
31
30
|
|
|
32
31
|
def test_ttl(self):
|
|
@@ -132,28 +131,32 @@ class TTLCacheTest(unittest.TestCase, CacheTestMixin):
|
|
|
132
131
|
self.assertEqual(2, cache[2])
|
|
133
132
|
self.assertEqual(3, cache[3])
|
|
134
133
|
|
|
135
|
-
cache.expire()
|
|
134
|
+
items = cache.expire()
|
|
135
|
+
self.assertEqual(set(), set(items))
|
|
136
136
|
self.assertEqual({1, 2, 3}, set(cache))
|
|
137
137
|
self.assertEqual(3, len(cache))
|
|
138
138
|
self.assertEqual(1, cache[1])
|
|
139
139
|
self.assertEqual(2, cache[2])
|
|
140
140
|
self.assertEqual(3, cache[3])
|
|
141
141
|
|
|
142
|
-
cache.expire(3)
|
|
142
|
+
items = cache.expire(3)
|
|
143
|
+
self.assertEqual({(1, 1)}, set(items))
|
|
143
144
|
self.assertEqual({2, 3}, set(cache))
|
|
144
145
|
self.assertEqual(2, len(cache))
|
|
145
146
|
self.assertNotIn(1, cache)
|
|
146
147
|
self.assertEqual(2, cache[2])
|
|
147
148
|
self.assertEqual(3, cache[3])
|
|
148
149
|
|
|
149
|
-
cache.expire(4)
|
|
150
|
+
items = cache.expire(4)
|
|
151
|
+
self.assertEqual({(2, 2)}, set(items))
|
|
150
152
|
self.assertEqual({3}, set(cache))
|
|
151
153
|
self.assertEqual(1, len(cache))
|
|
152
154
|
self.assertNotIn(1, cache)
|
|
153
155
|
self.assertNotIn(2, cache)
|
|
154
156
|
self.assertEqual(3, cache[3])
|
|
155
157
|
|
|
156
|
-
cache.expire(5)
|
|
158
|
+
items = cache.expire(5)
|
|
159
|
+
self.assertEqual({(3, 3)}, set(items))
|
|
157
160
|
self.assertEqual(set(), set(cache))
|
|
158
161
|
self.assertEqual(0, len(cache))
|
|
159
162
|
self.assertNotIn(1, cache)
|
|
@@ -192,7 +195,9 @@ class TTLCacheTest(unittest.TestCase, CacheTestMixin):
|
|
|
192
195
|
|
|
193
196
|
cache[1] = 1
|
|
194
197
|
self.assertEqual(1, len(cache))
|
|
195
|
-
cache.expire(datetime.now())
|
|
198
|
+
items = cache.expire(datetime.now())
|
|
199
|
+
self.assertEqual([], list(items))
|
|
196
200
|
self.assertEqual(1, len(cache))
|
|
197
|
-
cache.expire(datetime.now() + timedelta(days=1))
|
|
201
|
+
items = cache.expire(datetime.now() + timedelta(days=1))
|
|
202
|
+
self.assertEqual([(1, 1)], list(items))
|
|
198
203
|
self.assertEqual(0, len(cache))
|
|
@@ -32,7 +32,7 @@ commands =
|
|
|
32
32
|
deps =
|
|
33
33
|
flake8
|
|
34
34
|
flake8-black; implementation_name == "cpython"
|
|
35
|
-
black==22.12.0; implementation_name == "cpython"
|
|
35
|
+
black==22.12.0; implementation_name == "cpython" and python_version < "3.8"
|
|
36
36
|
flake8-bugbear
|
|
37
37
|
flake8-import-order
|
|
38
38
|
commands =
|
|
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
|