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.
Files changed (31) hide show
  1. {cachetools-5.3.3 → cachetools-5.5.0}/CHANGELOG.rst +26 -0
  2. {cachetools-5.3.3/src/cachetools.egg-info → cachetools-5.5.0}/PKG-INFO +1 -1
  3. {cachetools-5.3.3 → cachetools-5.5.0}/docs/index.rst +69 -10
  4. {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools/__init__.py +21 -6
  5. {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools/func.py +4 -0
  6. {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools/keys.py +6 -1
  7. {cachetools-5.3.3 → cachetools-5.5.0/src/cachetools.egg-info}/PKG-INFO +1 -1
  8. {cachetools-5.3.3 → cachetools-5.5.0}/tests/__init__.py +0 -1
  9. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_cache.py +0 -1
  10. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_cachedmethod.py +21 -21
  11. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_fifo.py +0 -1
  12. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_func.py +8 -6
  13. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_keys.py +35 -0
  14. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_lfu.py +0 -1
  15. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_lru.py +0 -1
  16. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_mru.py +17 -4
  17. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_rr.py +0 -1
  18. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_tlru.py +8 -5
  19. {cachetools-5.3.3 → cachetools-5.5.0}/tests/test_ttl.py +12 -7
  20. {cachetools-5.3.3 → cachetools-5.5.0}/tox.ini +1 -1
  21. {cachetools-5.3.3 → cachetools-5.5.0}/LICENSE +0 -0
  22. {cachetools-5.3.3 → cachetools-5.5.0}/MANIFEST.in +0 -0
  23. {cachetools-5.3.3 → cachetools-5.5.0}/README.rst +0 -0
  24. {cachetools-5.3.3 → cachetools-5.5.0}/docs/conf.py +0 -0
  25. {cachetools-5.3.3 → cachetools-5.5.0}/pyproject.toml +0 -0
  26. {cachetools-5.3.3 → cachetools-5.5.0}/setup.cfg +0 -0
  27. {cachetools-5.3.3 → cachetools-5.5.0}/setup.py +0 -0
  28. {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools.egg-info/SOURCES.txt +0 -0
  29. {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools.egg-info/dependency_links.txt +0 -0
  30. {cachetools-5.3.3 → cachetools-5.5.0}/src/cachetools.egg-info/top_level.txt +0 -0
  31. {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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cachetools
3
- Version: 5.3.3
3
+ Version: 5.5.0
4
4
  Summary: Extensible memoizing collections and decorators
5
5
  Home-page: https://github.com/tkem/cachetools/
6
6
  Author: Thomas Kemmer
@@ -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.ttl contains the item's time-to-live in hours
166
- return now + timedelta(hours=value.ttl)
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, timer=datetime.now)
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(object):
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(object):
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 equivalent to :func:`hashkey`, but ignores its
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.3.3"
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cachetools
3
- Version: 5.3.3
3
+ Version: 5.5.0
4
4
  Summary: Extensible memoizing collections and decorators
5
5
  Home-page: https://github.com/tkem/cachetools/
6
6
  Author: Thomas Kemmer
@@ -2,7 +2,6 @@ import unittest
2
2
 
3
3
 
4
4
  class CacheTestMixin:
5
-
6
5
  Cache = None
7
6
 
8
7
  def test_defaults(self):
@@ -6,5 +6,4 @@ from . import CacheTestMixin
6
6
 
7
7
 
8
8
  class CacheTest(unittest.TestCase, CacheTestMixin):
9
-
10
9
  Cache = cachetools.Cache
@@ -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.typedkey)
18
- def get_typed(self, value):
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 test_typed_dict(self):
70
+ def test_typedmethod_dict(self):
71
71
  cached = Cached(LRUCache(maxsize=2))
72
72
 
73
- self.assertEqual(cached.get_typed(0), 0)
74
- self.assertEqual(cached.get_typed(1), 1)
75
- self.assertEqual(cached.get_typed(1), 1)
76
- self.assertEqual(cached.get_typed(1.0), 2)
77
- self.assertEqual(cached.get_typed(1.0), 2)
78
- self.assertEqual(cached.get_typed(0.0), 3)
79
- self.assertEqual(cached.get_typed(0), 4)
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 test_typed_lru(self):
93
+ def test_typedmethod_lru(self):
94
94
  cached = Cached(LRUCache(maxsize=2))
95
95
 
96
- self.assertEqual(cached.get_typed(0), 0)
97
- self.assertEqual(cached.get_typed(1), 1)
98
- self.assertEqual(cached.get_typed(1), 1)
99
- self.assertEqual(cached.get_typed(1.0), 2)
100
- self.assertEqual(cached.get_typed(1.0), 2)
101
- self.assertEqual(cached.get_typed(0.0), 3)
102
- self.assertEqual(cached.get_typed(0), 4)
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.get_typed(1)
144
+ ref = cached.get_typedmethod(1)
145
145
  self.assertEqual(ref, 3)
146
- self.assertEqual(cached.get_typed(1), 3)
147
- self.assertEqual(cached.get_typed(1.0), 4)
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)
@@ -6,7 +6,6 @@ from . import CacheTestMixin
6
6
 
7
7
 
8
8
  class LRUCacheTest(unittest.TestCase, CacheTestMixin):
9
-
10
9
  Cache = FIFOCache
11
10
 
12
11
  def test_fifo(self):
@@ -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
- DECORATOR = staticmethod(cachetools.func.mru_cache)
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()))
@@ -6,7 +6,6 @@ from . import CacheTestMixin
6
6
 
7
7
 
8
8
  class LFUCacheTest(unittest.TestCase, CacheTestMixin):
9
-
10
9
  Cache = LFUCache
11
10
 
12
11
  def test_lfu(self):
@@ -6,7 +6,6 @@ from . import CacheTestMixin
6
6
 
7
7
 
8
8
  class LRUCacheTest(unittest.TestCase, CacheTestMixin):
9
-
10
9
  Cache = LRUCache
11
10
 
12
11
  def test_lru(self):
@@ -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
- cache = MRUCache(maxsize=2)
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
- cache = MRUCache(maxsize=2)
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
- cache = MRUCache(maxsize=2)
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
@@ -6,7 +6,6 @@ from . import CacheTestMixin
6
6
 
7
7
 
8
8
  class RRCacheTest(unittest.TestCase, CacheTestMixin):
9
-
10
9
  Cache = RRCache
11
10
 
12
11
  def test_rr(self):
@@ -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