cachebox 5.2.1__cp312-cp312-musllinux_1_2_armv7l.whl

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.
cachebox/_cachebox.py ADDED
@@ -0,0 +1,2198 @@
1
+ import copy as _std_copy
2
+ import typing
3
+ from datetime import datetime, timedelta
4
+
5
+ from . import _core
6
+ from ._core import BaseCacheImpl
7
+
8
+ KT = typing.TypeVar("KT")
9
+ VT = typing.TypeVar("VT")
10
+ DT = typing.TypeVar("DT")
11
+
12
+
13
+ def _items_to_str(items: typing.Iterable[typing.Any], length) -> str:
14
+ if length <= 50:
15
+ return "{" + ", ".join(f"{k!r}: {v!r}" for k, v in items) + "}"
16
+
17
+ c = 0
18
+ left = []
19
+
20
+ while c < length:
21
+ k, v = next(items) # type: ignore[call-overload]
22
+
23
+ if c <= 50:
24
+ left.append(f"{k!r}: {v!r}")
25
+
26
+ else:
27
+ break
28
+
29
+ c += 1
30
+
31
+ return "{%s, ... %d more ...}" % (", ".join(left), length - c)
32
+
33
+
34
+ class IteratorView(typing.Generic[VT]):
35
+ __slots__ = ("iterator", "func")
36
+
37
+ def __init__(self, iterator, func: typing.Callable[[tuple], typing.Any]):
38
+ self.iterator = iterator
39
+ self.func = func
40
+
41
+ def __iter__(self):
42
+ self.iterator = self.iterator.__iter__()
43
+ return self
44
+
45
+ def __next__(self) -> VT:
46
+ return self.func(self.iterator.__next__())
47
+
48
+
49
+ class Cache(BaseCacheImpl[KT, VT]):
50
+ """
51
+ A thread-safe, memory-efficient hashmap-like cache with configurable maximum size.
52
+
53
+ Provides a flexible key-value storage mechanism with:
54
+ - Configurable maximum size (zero means unlimited)
55
+ - Lower memory usage compared to standard dict
56
+ - Thread-safe operations
57
+ - Useful memory management methods
58
+
59
+ Differs from standard dict by:
60
+ - Being thread-safe
61
+ - Unordered storage
62
+ - Size limitation
63
+ - Memory efficiency
64
+ - Additional cache management methods
65
+
66
+ Supports initialization with optional initial data and capacity,
67
+ and provides dictionary-like access with additional cache-specific operations.
68
+ """
69
+
70
+ __slots__ = ("_raw",)
71
+
72
+ def __init__(
73
+ self,
74
+ maxsize: int,
75
+ iterable: typing.Union[dict, typing.Iterable[tuple], None] = None,
76
+ *,
77
+ capacity: int = 0,
78
+ maxmemory: int = 0,
79
+ ) -> None:
80
+ """
81
+ Initialize a new Cache instance.
82
+
83
+ Args:
84
+ maxsize (int): Maximum number of elements the cache can hold. Zero means unlimited.
85
+ iterable (Union[Cache, dict, tuple, Generator, None], optional): Initial data to populate the cache. Defaults to None.
86
+ capacity (int, optional): Pre-allocate hash table capacity to minimize reallocations. Defaults to 0.
87
+ maxmemory (int, optional): Maximum memory (bytes) allowed for cached entries. Zero means unlimited.
88
+ On PyPy, it works same as `maxsize` if objects do not support `__sizeof__`
89
+ method.
90
+
91
+ Creates a new cache with specified size constraints and optional initial data. The cache can be pre-sized
92
+ to improve performance when the number of expected elements is known in advance.
93
+ """
94
+ self._raw = _core.Cache(maxsize, capacity=capacity, maxmemory=maxmemory)
95
+
96
+ if iterable is not None:
97
+ self.update(iterable)
98
+
99
+ @property
100
+ def maxsize(self) -> int:
101
+ return self._raw.maxsize()
102
+
103
+ @property
104
+ def maxmemory(self) -> int:
105
+ return self._raw.maxmemory()
106
+
107
+ def capacity(self) -> int:
108
+ """Returns the number of elements the map can hold without reallocating."""
109
+ return self._raw.capacity()
110
+
111
+ def memory(self) -> int:
112
+ """Returns the total estimated memory usage of cached entries in bytes."""
113
+ return self._raw.memory()
114
+
115
+ def __len__(self) -> int:
116
+ return len(self._raw)
117
+
118
+ def __sizeof__(self): # pragma: no cover
119
+ return self._raw.__sizeof__()
120
+
121
+ def __contains__(self, key: KT) -> bool:
122
+ return key in self._raw
123
+
124
+ def __bool__(self) -> bool:
125
+ return not self.is_empty()
126
+
127
+ def is_empty(self) -> bool:
128
+ return self._raw.is_empty()
129
+
130
+ def is_full(self) -> bool:
131
+ return self._raw.is_full()
132
+
133
+ def insert(self, key: KT, value: VT) -> typing.Optional[VT]:
134
+ """
135
+ Equals to `self[key] = value`, but returns a value:
136
+
137
+ - If the cache did not have this key present, None is returned.
138
+ - If the cache did have this key present, the value is updated,
139
+ and the old value is returned. The key is not updated, though;
140
+
141
+ Note: raises `OverflowError` if the cache reached the maxsize limit,
142
+ because this class does not have any algorithm.
143
+ """
144
+ return self._raw.insert(key, value)
145
+
146
+ def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
147
+ """
148
+ Retrieves the value for a given key from the cache.
149
+
150
+ Returns the value associated with the key if present, otherwise returns the specified default value.
151
+ Equivalent to `self[key]`, but provides a fallback default if the key is not found.
152
+
153
+ Args:
154
+ key: The key to look up in the cache.
155
+ default: The value to return if the key is not present in the cache. Defaults to None.
156
+
157
+ Returns:
158
+ The value associated with the key, or the default value if the key is not found.
159
+ """
160
+ try:
161
+ return self._raw.get(key)
162
+ except _core.CoreKeyError:
163
+ return default # type: ignore[return-value]
164
+
165
+ def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
166
+ """
167
+ Removes specified key and return the corresponding value. If the key is not found, returns the `default`.
168
+ """
169
+ try:
170
+ return self._raw.remove(key)
171
+ except _core.CoreKeyError:
172
+ return default # type: ignore[return-value]
173
+
174
+ def setdefault(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
175
+ """
176
+ Inserts key with a value of default if key is not in the cache. Return the value for key if key is
177
+ in the cache, else `default`.
178
+ """
179
+ return self._raw.setdefault(key, default)
180
+
181
+ def popitem(self) -> typing.NoReturn: # pragma: no cover
182
+ raise NotImplementedError()
183
+
184
+ def drain(self, n: int) -> typing.NoReturn: # pragma: no cover
185
+ raise NotImplementedError()
186
+
187
+ def update(self, iterable: typing.Union[dict, typing.Iterable[tuple]]) -> None:
188
+ """
189
+ Updates the cache with elements from a dictionary or an iterable object of key/value pairs.
190
+
191
+ Note: raises `OverflowError` if the cache reached the maxsize limit.
192
+ """
193
+ if hasattr(iterable, "items"):
194
+ iterable = iterable.items()
195
+
196
+ self._raw.update(iterable)
197
+
198
+ def __setitem__(self, key: KT, value: VT) -> None:
199
+ self.insert(key, value)
200
+
201
+ def __getitem__(self, key: KT) -> VT:
202
+ try:
203
+ return self._raw.get(key)
204
+ except _core.CoreKeyError:
205
+ raise KeyError(key) from None
206
+
207
+ def __delitem__(self, key: KT) -> None:
208
+ try:
209
+ self._raw.remove(key)
210
+ except _core.CoreKeyError:
211
+ raise KeyError(key) from None
212
+
213
+ def __eq__(self, other) -> bool:
214
+ if not isinstance(other, Cache):
215
+ return False # pragma: no cover
216
+
217
+ return self._raw == other._raw
218
+
219
+ def __ne__(self, other) -> bool:
220
+ if not isinstance(other, Cache):
221
+ return False # pragma: no cover
222
+
223
+ return self._raw != other._raw
224
+
225
+ def shrink_to_fit(self) -> None:
226
+ """Shrinks the cache to fit len(self) elements."""
227
+ self._raw.shrink_to_fit()
228
+
229
+ def clear(self, *, reuse: bool = False) -> None:
230
+ """
231
+ Removes all items from cache.
232
+
233
+ If reuse is True, will not free the memory for reusing in the future.
234
+ """
235
+ self._raw.clear(reuse)
236
+
237
+ def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
238
+ """
239
+ Returns an iterable object of the cache's items (key-value pairs).
240
+
241
+ Notes:
242
+ - You should not make any changes in cache while using this iterable object.
243
+ - Items are not ordered.
244
+ """
245
+ return IteratorView(self._raw.items(), lambda x: x)
246
+
247
+ def keys(self) -> IteratorView[KT]:
248
+ """
249
+ Returns an iterable object of the cache's keys.
250
+
251
+ Notes:
252
+ - You should not make any changes in cache while using this iterable object.
253
+ - Keys are not ordered.
254
+ """
255
+ return IteratorView(self._raw.items(), lambda x: x[0])
256
+
257
+ def values(self) -> IteratorView[VT]:
258
+ """
259
+ Returns an iterable object of the cache's values.
260
+
261
+ Notes:
262
+ - You should not make any changes in cache while using this iterable object.
263
+ - Values are not ordered.
264
+ """
265
+ return IteratorView(self._raw.items(), lambda x: x[1])
266
+
267
+ def copy(self) -> "Cache[KT, VT]":
268
+ """Returns a shallow copy of the cache"""
269
+ return self.__copy__()
270
+
271
+ def __copy__(self) -> "Cache[KT, VT]":
272
+ cls = type(self)
273
+ copied = cls.__new__(cls)
274
+ copied._raw = _std_copy.copy(self._raw)
275
+ return copied
276
+
277
+ def __deepcopy__(self, memo) -> "Cache[KT, VT]":
278
+ cls = type(self)
279
+ copied = cls.__new__(cls)
280
+ copied._raw = _std_copy.deepcopy(self._raw, memo)
281
+ return copied
282
+
283
+ def __iter__(self) -> IteratorView[KT]:
284
+ return self.keys()
285
+
286
+ def __repr__(self) -> str:
287
+ cls = type(self)
288
+
289
+ return "%s.%s[%d/%d](%s)" % (
290
+ cls.__module__,
291
+ cls.__name__,
292
+ len(self._raw),
293
+ self._raw.maxsize(),
294
+ _items_to_str(self._raw.items(), len(self._raw)),
295
+ )
296
+
297
+
298
+ class FIFOCache(BaseCacheImpl[KT, VT]):
299
+ """
300
+ A First-In-First-Out (FIFO) cache implementation with configurable maximum size and optional initial capacity.
301
+
302
+ This cache provides a fixed-size container that automatically removes the oldest items when the maximum size is reached.
303
+ Supports various operations like insertion, retrieval, deletion, and iteration.
304
+
305
+ Attributes:
306
+ maxsize: The maximum number of items the cache can hold.
307
+ capacity: The initial capacity of the cache before resizing.
308
+
309
+ Key features:
310
+ - Deterministic item eviction order (oldest items removed first)
311
+ - Efficient key-value storage and retrieval
312
+ - Supports dictionary-like operations
313
+ - Allows optional initial data population
314
+ """
315
+
316
+ __slots__ = ("_raw",)
317
+
318
+ def __init__(
319
+ self,
320
+ maxsize: int,
321
+ iterable: typing.Union[typing.Union[dict, typing.Iterable[tuple]], None] = None,
322
+ *,
323
+ capacity: int = 0,
324
+ maxmemory: int = 0,
325
+ ) -> None:
326
+ """
327
+ Initialize a new FIFOCache instance.
328
+
329
+ Args:
330
+ maxsize: The maximum number of items the cache can hold.
331
+ iterable: Optional initial data to populate the cache. Can be another FIFOCache,
332
+ a dictionary, tuple, generator, or None.
333
+ capacity: Optional initial capacity of the cache before resizing. Defaults to 0.
334
+ maxmemory: Maximum memory (bytes) allowed for cached entries. Zero means unlimited.
335
+ When maxmemory is set, updating an existing key can evict the updated key
336
+ if it is the oldest entry.
337
+ """
338
+ self._raw = _core.FIFOCache(maxsize, capacity=capacity, maxmemory=maxmemory)
339
+
340
+ if iterable is not None:
341
+ self.update(iterable)
342
+
343
+ @property
344
+ def maxsize(self) -> int:
345
+ return self._raw.maxsize()
346
+
347
+ @property
348
+ def maxmemory(self) -> int:
349
+ return self._raw.maxmemory()
350
+
351
+ def capacity(self) -> int:
352
+ """Returns the number of elements the map can hold without reallocating."""
353
+ return self._raw.capacity()
354
+
355
+ def memory(self) -> int:
356
+ """Returns the total estimated memory usage of cached entries in bytes."""
357
+ return self._raw.memory()
358
+
359
+ def __len__(self) -> int:
360
+ return len(self._raw)
361
+
362
+ def __sizeof__(self): # pragma: no cover
363
+ return self._raw.__sizeof__()
364
+
365
+ def __contains__(self, key: KT) -> bool:
366
+ return key in self._raw
367
+
368
+ def __bool__(self) -> bool:
369
+ return not self.is_empty()
370
+
371
+ def is_empty(self) -> bool:
372
+ return self._raw.is_empty()
373
+
374
+ def is_full(self) -> bool:
375
+ return self._raw.is_full()
376
+
377
+ def insert(self, key: KT, value: VT) -> typing.Optional[VT]:
378
+ """
379
+ Inserts a key-value pair into the cache, returning the previous value if the key existed.
380
+
381
+ Equivalent to `self[key] = value`, but with additional return value semantics:
382
+
383
+ - If the key was not previously in the cache, returns None.
384
+ - If the key was already present, updates the value and returns the old value.
385
+ The key itself is not modified.
386
+
387
+ Args:
388
+ key: The key to insert.
389
+ value: The value to associate with the key.
390
+
391
+ Returns:
392
+ The previous value associated with the key, or None if the key was not present.
393
+ """
394
+ return self._raw.insert(key, value)
395
+
396
+ def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
397
+ """
398
+ Retrieves the value for a given key from the cache.
399
+
400
+ Returns the value associated with the key if present, otherwise returns the specified default value.
401
+ Equivalent to `self[key]`, but provides a fallback default if the key is not found.
402
+
403
+ Args:
404
+ key: The key to look up in the cache.
405
+ default: The value to return if the key is not present in the cache. Defaults to None.
406
+
407
+ Returns:
408
+ The value associated with the key, or the default value if the key is not found.
409
+ """
410
+ try:
411
+ return self._raw.get(key)
412
+ except _core.CoreKeyError:
413
+ return default # type: ignore[return-value]
414
+
415
+ def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
416
+ """
417
+ Removes specified key and return the corresponding value. If the key is not found, returns the `default`.
418
+ """
419
+ try:
420
+ return self._raw.remove(key)
421
+ except _core.CoreKeyError:
422
+ return default # type: ignore[return-value] # type: ignore[return-value]
423
+
424
+ def setdefault(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
425
+ """
426
+ Inserts key with a value of default if key is not in the cache.
427
+
428
+ Return the value for key if key is in the cache, else default.
429
+ """
430
+ return self._raw.setdefault(key, default)
431
+
432
+ def popitem(self) -> typing.Tuple[KT, VT]:
433
+ """Removes the element that has been in the cache the longest."""
434
+ try:
435
+ return self._raw.popitem()
436
+ except _core.CoreKeyError:
437
+ raise KeyError() from None
438
+
439
+ def drain(self, n: int) -> int: # pragma: no cover
440
+ """Does the `popitem()` `n` times and returns count of removed items."""
441
+ if n <= 0:
442
+ return 0
443
+
444
+ for i in range(n):
445
+ try:
446
+ self._raw.popitem()
447
+ except _core.CoreKeyError:
448
+ return i
449
+
450
+ return i
451
+
452
+ def update(self, iterable: typing.Union[dict, typing.Iterable[tuple]]) -> None:
453
+ """Updates the cache with elements from a dictionary or an iterable object of key/value pairs."""
454
+ if hasattr(iterable, "items"):
455
+ iterable = iterable.items()
456
+
457
+ self._raw.update(iterable)
458
+
459
+ def __setitem__(self, key: KT, value: VT) -> None:
460
+ self.insert(key, value)
461
+
462
+ def __getitem__(self, key: KT) -> VT:
463
+ try:
464
+ return self._raw.get(key)
465
+ except _core.CoreKeyError:
466
+ raise KeyError(key) from None
467
+
468
+ def __delitem__(self, key: KT) -> None:
469
+ try:
470
+ self._raw.remove(key)
471
+ except _core.CoreKeyError:
472
+ raise KeyError(key) from None
473
+
474
+ def __eq__(self, other) -> bool:
475
+ if not isinstance(other, FIFOCache):
476
+ return False # pragma: no cover
477
+
478
+ return self._raw == other._raw
479
+
480
+ def __ne__(self, other) -> bool:
481
+ if not isinstance(other, FIFOCache):
482
+ return False # pragma: no cover
483
+
484
+ return self._raw != other._raw
485
+
486
+ def shrink_to_fit(self) -> None:
487
+ """Shrinks the cache to fit len(self) elements."""
488
+ self._raw.shrink_to_fit()
489
+
490
+ def clear(self, *, reuse: bool = False) -> None:
491
+ """
492
+ Removes all items from cache.
493
+
494
+ If reuse is True, will not free the memory for reusing in the future.
495
+ """
496
+ self._raw.clear(reuse)
497
+
498
+ def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
499
+ """
500
+ Returns an iterable object of the cache's items (key-value pairs).
501
+
502
+ Notes:
503
+ - You should not make any changes in cache while using this iterable object.
504
+ """
505
+ return IteratorView(self._raw.items(), lambda x: x)
506
+
507
+ def keys(self) -> IteratorView[KT]:
508
+ """
509
+ Returns an iterable object of the cache's keys.
510
+
511
+ Notes:
512
+ - You should not make any changes in cache while using this iterable object.
513
+ """
514
+ return IteratorView(self._raw.items(), lambda x: x[0])
515
+
516
+ def values(self) -> IteratorView[VT]:
517
+ """
518
+ Returns an iterable object of the cache's values.
519
+
520
+ Notes:
521
+ - You should not make any changes in cache while using this iterable object.
522
+ """
523
+ return IteratorView(self._raw.items(), lambda x: x[1])
524
+
525
+ def first(self, n: int = 0) -> typing.Optional[KT]:
526
+ """
527
+ Returns the first key in cache; this is the one which will be removed by `popitem()` (if n == 0).
528
+
529
+ By using `n` parameter, you can browse order index by index.
530
+ """
531
+ if n < 0:
532
+ n = len(self._raw) + n
533
+
534
+ if n < 0:
535
+ return None
536
+
537
+ return self._raw.get_index(n)
538
+
539
+ def last(self) -> typing.Optional[KT]:
540
+ """
541
+ Returns the last key in cache. Equals to `self.first(-1)`.
542
+ """
543
+ return self._raw.get_index(len(self._raw) - 1)
544
+
545
+ def copy(self) -> "FIFOCache[KT, VT]":
546
+ """Returns a shallow copy of the cache"""
547
+ return self.__copy__()
548
+
549
+ def __copy__(self) -> "FIFOCache[KT, VT]":
550
+ cls = type(self)
551
+ copied = cls.__new__(cls)
552
+ copied._raw = _std_copy.copy(self._raw)
553
+ return copied
554
+
555
+ def __deepcopy__(self, memo) -> "FIFOCache[KT, VT]":
556
+ cls = type(self)
557
+ copied = cls.__new__(cls)
558
+ copied._raw = _std_copy.deepcopy(self._raw, memo)
559
+ return copied
560
+
561
+ def __iter__(self) -> IteratorView[KT]:
562
+ return self.keys()
563
+
564
+ def __repr__(self) -> str:
565
+ cls = type(self)
566
+
567
+ return "%s.%s[%d/%d](%s)" % (
568
+ cls.__module__,
569
+ cls.__name__,
570
+ len(self._raw),
571
+ self._raw.maxsize(),
572
+ _items_to_str(self._raw.items(), len(self._raw)),
573
+ )
574
+
575
+
576
+ class RRCache(BaseCacheImpl[KT, VT]):
577
+ """
578
+ A thread-safe cache implementation with Random Replacement (RR) policy.
579
+
580
+ This cache randomly selects and removes elements when the cache reaches its maximum size,
581
+ ensuring a simple and efficient caching mechanism with configurable capacity.
582
+
583
+ Supports operations like insertion, retrieval, deletion, and iteration.
584
+ """
585
+
586
+ __slots__ = ("_raw",)
587
+
588
+ def __init__(
589
+ self,
590
+ maxsize: int,
591
+ iterable: typing.Union[typing.Union[dict, typing.Iterable[tuple]], None] = None,
592
+ *,
593
+ capacity: int = 0,
594
+ maxmemory: int = 0,
595
+ ) -> None:
596
+ """
597
+ Initialize a new RRCache instance.
598
+
599
+ Args:
600
+ maxsize (int): Maximum size of the cache. A value of zero means unlimited capacity.
601
+ iterable (dict or Iterable[tuple], optional): Initial data to populate the cache. Defaults to None.
602
+ capacity (int, optional): Preallocated capacity for the cache to minimize reallocations. Defaults to 0.
603
+ maxmemory (int, optional): Maximum memory (bytes) allowed for cached entries. Zero means unlimited.
604
+ When maxmemory is set, updates can evict any key, including the updated key.
605
+ On PyPy. In PyPy, the size of each object is assumed to be 1 if the object
606
+ does not have a `__sizeof__` method.
607
+
608
+ Note:
609
+ - The cache size limit is immutable after initialization.
610
+ - If an iterable is provided, the cache will be populated using the update method.
611
+ """
612
+ self._raw = _core.RRCache(maxsize, capacity=capacity, maxmemory=maxmemory)
613
+
614
+ if iterable is not None:
615
+ self.update(iterable)
616
+
617
+ @property
618
+ def maxsize(self) -> int:
619
+ return self._raw.maxsize()
620
+
621
+ @property
622
+ def maxmemory(self) -> int:
623
+ return self._raw.maxmemory()
624
+
625
+ def capacity(self) -> int:
626
+ """Returns the number of elements the map can hold without reallocating."""
627
+ return self._raw.capacity()
628
+
629
+ def memory(self) -> int:
630
+ """Returns the total estimated memory usage of cached entries in bytes."""
631
+ return self._raw.memory()
632
+
633
+ def __len__(self) -> int:
634
+ return len(self._raw)
635
+
636
+ def __sizeof__(self): # pragma: no cover
637
+ return self._raw.__sizeof__()
638
+
639
+ def __contains__(self, key: KT) -> bool:
640
+ return key in self._raw
641
+
642
+ def __bool__(self) -> bool:
643
+ return not self.is_empty()
644
+
645
+ def is_empty(self) -> bool:
646
+ return self._raw.is_empty()
647
+
648
+ def is_full(self) -> bool:
649
+ return self._raw.is_full()
650
+
651
+ def insert(self, key: KT, value: VT) -> typing.Optional[VT]:
652
+ """
653
+ Inserts a key-value pair into the cache, returning the previous value if the key existed.
654
+
655
+ Equivalent to `self[key] = value`, but with additional return value semantics:
656
+
657
+ - If the key was not previously in the cache, returns None.
658
+ - If the key was already present, updates the value and returns the old value.
659
+ The key itself is not modified.
660
+
661
+ Args:
662
+ key: The key to insert.
663
+ value: The value to associate with the key.
664
+
665
+ Returns:
666
+ The previous value associated with the key, or None if the key was not present.
667
+ """
668
+ return self._raw.insert(key, value)
669
+
670
+ def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
671
+ """
672
+ Retrieves the value for a given key from the cache.
673
+
674
+ Returns the value associated with the key if present, otherwise returns the specified default value.
675
+ Equivalent to `self[key]`, but provides a fallback default if the key is not found.
676
+
677
+ Args:
678
+ key: The key to look up in the cache.
679
+ default: The value to return if the key is not present in the cache. Defaults to None.
680
+
681
+ Returns:
682
+ The value associated with the key, or the default value if the key is not found.
683
+ """
684
+ try:
685
+ return self._raw.get(key)
686
+ except _core.CoreKeyError:
687
+ return default # type: ignore[return-value]
688
+
689
+ def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
690
+ """
691
+ Removes specified key and return the corresponding value. If the key is not found, returns the `default`.
692
+ """
693
+ try:
694
+ return self._raw.remove(key)
695
+ except _core.CoreKeyError:
696
+ return default # type: ignore[return-value]
697
+
698
+ def setdefault(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
699
+ """
700
+ Inserts key with a value of default if key is not in the cache.
701
+
702
+ Return the value for key if key is in the cache, else default.
703
+ """
704
+ return self._raw.setdefault(key, default)
705
+
706
+ def popitem(self) -> typing.Tuple[KT, VT]:
707
+ """Randomly selects and removes a (key, value) pair from the cache."""
708
+ try:
709
+ return self._raw.popitem()
710
+ except _core.CoreKeyError:
711
+ raise KeyError() from None
712
+
713
+ def drain(self, n: int) -> int: # pragma: no cover
714
+ """Does the `popitem()` `n` times and returns count of removed items."""
715
+ if n <= 0:
716
+ return 0
717
+
718
+ for i in range(n):
719
+ try:
720
+ self._raw.popitem()
721
+ except _core.CoreKeyError:
722
+ return i
723
+
724
+ return i
725
+
726
+ def update(self, iterable: typing.Union[dict, typing.Iterable[tuple]]) -> None:
727
+ """Updates the cache with elements from a dictionary or an iterable object of key/value pairs."""
728
+ if hasattr(iterable, "items"):
729
+ iterable = iterable.items()
730
+
731
+ self._raw.update(iterable)
732
+
733
+ def random_key(self) -> KT:
734
+ """
735
+ Randomly selects and returns a key from the cache.
736
+ Raises `KeyError` If the cache is empty.
737
+ """
738
+ try:
739
+ return self._raw.random_key()
740
+ except _core.CoreKeyError:
741
+ raise KeyError() from None
742
+
743
+ def __setitem__(self, key: KT, value: VT) -> None:
744
+ self.insert(key, value)
745
+
746
+ def __getitem__(self, key: KT) -> VT:
747
+ try:
748
+ return self._raw.get(key)
749
+ except _core.CoreKeyError:
750
+ raise KeyError(key) from None
751
+
752
+ def __delitem__(self, key: KT) -> None:
753
+ try:
754
+ self._raw.remove(key)
755
+ except _core.CoreKeyError:
756
+ raise KeyError(key) from None
757
+
758
+ def __eq__(self, other) -> bool:
759
+ if not isinstance(other, RRCache):
760
+ return False # pragma: no cover
761
+
762
+ return self._raw == other._raw
763
+
764
+ def __ne__(self, other) -> bool:
765
+ if not isinstance(other, RRCache):
766
+ return False # pragma: no cover
767
+
768
+ return self._raw != other._raw
769
+
770
+ def shrink_to_fit(self) -> None:
771
+ """Shrinks the cache to fit len(self) elements."""
772
+ self._raw.shrink_to_fit()
773
+
774
+ def clear(self, *, reuse: bool = False) -> None:
775
+ """
776
+ Removes all items from cache.
777
+
778
+ If reuse is True, will not free the memory for reusing in the future.
779
+ """
780
+ self._raw.clear(reuse)
781
+
782
+ def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
783
+ """
784
+ Returns an iterable object of the cache's items (key-value pairs).
785
+
786
+ Notes:
787
+ - You should not make any changes in cache while using this iterable object.
788
+ - Items are not ordered.
789
+ """
790
+ return IteratorView(self._raw.items(), lambda x: x)
791
+
792
+ def keys(self) -> IteratorView[KT]:
793
+ """
794
+ Returns an iterable object of the cache's keys.
795
+
796
+ Notes:
797
+ - You should not make any changes in cache while using this iterable object.
798
+ - Keys are not ordered.
799
+ """
800
+ return IteratorView(self._raw.items(), lambda x: x[0])
801
+
802
+ def values(self) -> IteratorView[VT]:
803
+ """
804
+ Returns an iterable object of the cache's values.
805
+
806
+ Notes:
807
+ - You should not make any changes in cache while using this iterable object.
808
+ - Values are not ordered.
809
+ """
810
+ return IteratorView(self._raw.items(), lambda x: x[1])
811
+
812
+ def copy(self) -> "RRCache[KT, VT]":
813
+ """Returns a shallow copy of the cache"""
814
+ return self.__copy__()
815
+
816
+ def __copy__(self) -> "RRCache[KT, VT]":
817
+ cls = type(self)
818
+ copied = cls.__new__(cls)
819
+ copied._raw = _std_copy.copy(self._raw)
820
+ return copied
821
+
822
+ def __deepcopy__(self, memo) -> "RRCache[KT, VT]":
823
+ cls = type(self)
824
+ copied = cls.__new__(cls)
825
+ copied._raw = _std_copy.deepcopy(self._raw, memo)
826
+ return copied
827
+
828
+ def __iter__(self) -> IteratorView[KT]:
829
+ return self.keys()
830
+
831
+ def __repr__(self) -> str:
832
+ cls = type(self)
833
+
834
+ return "%s.%s[%d/%d](%s)" % (
835
+ cls.__module__,
836
+ cls.__name__,
837
+ len(self._raw),
838
+ self._raw.maxsize(),
839
+ _items_to_str(self._raw.items(), len(self._raw)),
840
+ )
841
+
842
+
843
+ class LRUCache(BaseCacheImpl[KT, VT]):
844
+ """
845
+ Thread-safe Least Recently Used (LRU) cache implementation.
846
+
847
+ Provides a cache that automatically removes the least recently used items when
848
+ the cache reaches its maximum size. Supports various operations like insertion,
849
+ retrieval, and management of cached items with configurable maximum size and
850
+ initial capacity.
851
+
852
+ Key features:
853
+ - Configurable maximum cache size
854
+ - Optional initial capacity allocation
855
+ - Thread-safe operations
856
+ - Efficient key-value pair management
857
+ - Supports initialization from dictionaries or iterables
858
+ """
859
+
860
+ __slots__ = ("_raw",)
861
+
862
+ def __init__(
863
+ self,
864
+ maxsize: int,
865
+ iterable: typing.Union[typing.Union[dict, typing.Iterable[tuple]], None] = None,
866
+ *,
867
+ capacity: int = 0,
868
+ maxmemory: int = 0,
869
+ ) -> None:
870
+ """
871
+ Initialize a new LRU Cache instance.
872
+
873
+ Args:
874
+ maxsize (int): Maximum size of the cache. Zero indicates unlimited size.
875
+ iterable (dict | Iterable[tuple], optional): Initial data to populate the cache.
876
+ capacity (int, optional): Pre-allocated capacity for the cache to minimize reallocations.
877
+ maxmemory (int, optional): Maximum memory (bytes) allowed for cached entries. Zero means unlimited.
878
+ On PyPy. In PyPy, the size of each object is assumed to be 1 if the object
879
+ does not have a `__sizeof__` method.
880
+
881
+ Notes:
882
+ - The cache size is immutable after initialization.
883
+ - If an iterable is provided, it will be used to populate the cache.
884
+ """
885
+ self._raw = _core.LRUCache(maxsize, capacity=capacity, maxmemory=maxmemory)
886
+
887
+ if iterable is not None:
888
+ self.update(iterable)
889
+
890
+ @property
891
+ def maxsize(self) -> int:
892
+ return self._raw.maxsize()
893
+
894
+ @property
895
+ def maxmemory(self) -> int:
896
+ return self._raw.maxmemory()
897
+
898
+ def capacity(self) -> int:
899
+ """Returns the number of elements the map can hold without reallocating."""
900
+ return self._raw.capacity()
901
+
902
+ def memory(self) -> int:
903
+ """Returns the total estimated memory usage of cached entries in bytes."""
904
+ return self._raw.memory()
905
+
906
+ def __len__(self) -> int:
907
+ return len(self._raw)
908
+
909
+ def __sizeof__(self): # pragma: no cover
910
+ return self._raw.__sizeof__()
911
+
912
+ def __contains__(self, key: KT) -> bool:
913
+ return key in self._raw
914
+
915
+ def __bool__(self) -> bool:
916
+ return not self.is_empty()
917
+
918
+ def is_empty(self) -> bool:
919
+ return self._raw.is_empty()
920
+
921
+ def is_full(self) -> bool:
922
+ return self._raw.is_full()
923
+
924
+ def insert(self, key: KT, value: VT) -> typing.Optional[VT]:
925
+ """
926
+ Inserts a key-value pair into the cache, returning the previous value if the key existed.
927
+
928
+ Equivalent to `self[key] = value`, but with additional return value semantics:
929
+
930
+ - If the key was not previously in the cache, returns None.
931
+ - If the key was already present, updates the value and returns the old value.
932
+ The key itself is not modified.
933
+
934
+ Args:
935
+ key: The key to insert.
936
+ value: The value to associate with the key.
937
+
938
+ Returns:
939
+ The previous value associated with the key, or None if the key was not present.
940
+ """
941
+ return self._raw.insert(key, value)
942
+
943
+ def peek(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
944
+ """
945
+ Searches for a key-value in the cache and returns it (without moving the key to recently used).
946
+ """
947
+ try:
948
+ return self._raw.peek(key)
949
+ except _core.CoreKeyError:
950
+ return default # type: ignore[return-value]
951
+
952
+ def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
953
+ """
954
+ Retrieves the value for a given key from the cache.
955
+
956
+ Returns the value associated with the key if present, otherwise returns the specified default value.
957
+ Equivalent to `self[key]`, but provides a fallback default if the key is not found.
958
+
959
+ Args:
960
+ key: The key to look up in the cache.
961
+ default: The value to return if the key is not present in the cache. Defaults to None.
962
+
963
+ Returns:
964
+ The value associated with the key, or the default value if the key is not found.
965
+ """
966
+ try:
967
+ return self._raw.get(key)
968
+ except _core.CoreKeyError:
969
+ return default # type: ignore[return-value]
970
+
971
+ def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
972
+ """
973
+ Removes specified key and return the corresponding value. If the key is not found, returns the `default`.
974
+ """
975
+ try:
976
+ return self._raw.remove(key)
977
+ except _core.CoreKeyError:
978
+ return default # type: ignore[return-value]
979
+
980
+ def setdefault(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
981
+ """
982
+ Inserts key with a value of default if key is not in the cache.
983
+
984
+ Return the value for key if key is in the cache, else default.
985
+ """
986
+ return self._raw.setdefault(key, default)
987
+
988
+ def popitem(self) -> typing.Tuple[KT, VT]:
989
+ """
990
+ Removes the least recently used item from the cache and returns it as a (key, value) tuple.
991
+ Raises KeyError if the cache is empty.
992
+ """
993
+ try:
994
+ return self._raw.popitem()
995
+ except _core.CoreKeyError: # pragma: no cover
996
+ raise KeyError() from None
997
+
998
+ def drain(self, n: int) -> int: # pragma: no cover
999
+ """Does the `popitem()` `n` times and returns count of removed items."""
1000
+ if n <= 0:
1001
+ return 0
1002
+
1003
+ for i in range(n):
1004
+ try:
1005
+ self._raw.popitem()
1006
+ except _core.CoreKeyError:
1007
+ return i
1008
+
1009
+ return i
1010
+
1011
+ def update(self, iterable: typing.Union[dict, typing.Iterable[tuple]]) -> None:
1012
+ """Updates the cache with elements from a dictionary or an iterable object of key/value pairs."""
1013
+ if hasattr(iterable, "items"):
1014
+ iterable = iterable.items()
1015
+
1016
+ self._raw.update(iterable)
1017
+
1018
+ def __setitem__(self, key: KT, value: VT) -> None:
1019
+ self.insert(key, value)
1020
+
1021
+ def __getitem__(self, key: KT) -> VT:
1022
+ try:
1023
+ return self._raw.get(key)
1024
+ except _core.CoreKeyError:
1025
+ raise KeyError(key) from None
1026
+
1027
+ def __delitem__(self, key: KT) -> None:
1028
+ try:
1029
+ self._raw.remove(key)
1030
+ except _core.CoreKeyError:
1031
+ raise KeyError(key) from None
1032
+
1033
+ def __eq__(self, other) -> bool:
1034
+ if not isinstance(other, LRUCache):
1035
+ return False # pragma: no cover
1036
+
1037
+ return self._raw == other._raw
1038
+
1039
+ def __ne__(self, other) -> bool:
1040
+ if not isinstance(other, LRUCache):
1041
+ return False # pragma: no cover
1042
+
1043
+ return self._raw != other._raw
1044
+
1045
+ def shrink_to_fit(self) -> None:
1046
+ """Shrinks the cache to fit len(self) elements."""
1047
+ self._raw.shrink_to_fit()
1048
+
1049
+ def clear(self, *, reuse: bool = False) -> None:
1050
+ """
1051
+ Removes all items from cache.
1052
+
1053
+ If reuse is True, will not free the memory for reusing in the future.
1054
+ """
1055
+ self._raw.clear(reuse)
1056
+
1057
+ def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
1058
+ """
1059
+ Returns an iterable object of the cache's items (key-value pairs).
1060
+
1061
+ Notes:
1062
+ - You should not make any changes in cache while using this iterable object.
1063
+ """
1064
+ return IteratorView(self._raw.items(), lambda x: x)
1065
+
1066
+ def keys(self) -> IteratorView[KT]:
1067
+ """
1068
+ Returns an iterable object of the cache's keys.
1069
+
1070
+ Notes:
1071
+ - You should not make any changes in cache while using this iterable object.
1072
+ """
1073
+ return IteratorView(self._raw.items(), lambda x: x[0])
1074
+
1075
+ def values(self) -> IteratorView[VT]:
1076
+ """
1077
+ Returns an iterable object of the cache's values.
1078
+
1079
+ Notes:
1080
+ - You should not make any changes in cache while using this iterable object.
1081
+ """
1082
+ return IteratorView(self._raw.items(), lambda x: x[1])
1083
+
1084
+ def least_recently_used(self) -> typing.Optional[KT]:
1085
+ """
1086
+ Returns the key in the cache that has not been accessed in the longest time.
1087
+ """
1088
+ return self._raw.least_recently_used()
1089
+
1090
+ def most_recently_used(self) -> typing.Optional[KT]:
1091
+ """
1092
+ Returns the key in the cache that has been accessed in the shortest time.
1093
+ """
1094
+ return self._raw.most_recently_used()
1095
+
1096
+ def copy(self) -> "LRUCache[KT, VT]":
1097
+ """Returns a shallow copy of the cache"""
1098
+ return self.__copy__()
1099
+
1100
+ def __copy__(self) -> "LRUCache[KT, VT]":
1101
+ cls = type(self)
1102
+ copied = cls.__new__(cls)
1103
+ copied._raw = _std_copy.copy(self._raw)
1104
+ return copied
1105
+
1106
+ def __deepcopy__(self, memo) -> "LRUCache[KT, VT]":
1107
+ cls = type(self)
1108
+ copied = cls.__new__(cls)
1109
+ copied._raw = _std_copy.deepcopy(self._raw, memo)
1110
+ return copied
1111
+
1112
+ def __iter__(self) -> IteratorView[KT]:
1113
+ return self.keys()
1114
+
1115
+ def __repr__(self) -> str:
1116
+ cls = type(self)
1117
+
1118
+ return "%s.%s[%d/%d](%s)" % (
1119
+ cls.__module__,
1120
+ cls.__name__,
1121
+ len(self._raw),
1122
+ self._raw.maxsize(),
1123
+ _items_to_str(self._raw.items(), len(self._raw)),
1124
+ )
1125
+
1126
+
1127
+ class LFUCache(BaseCacheImpl[KT, VT]):
1128
+ """
1129
+ A thread-safe Least Frequently Used (LFU) cache implementation.
1130
+
1131
+ This cache removes elements that have been accessed the least number of times,
1132
+ regardless of their access time. It provides methods for inserting, retrieving,
1133
+ and managing cache entries with configurable maximum size and initial capacity.
1134
+
1135
+ Key features:
1136
+ - Thread-safe cache with LFU eviction policy
1137
+ - Configurable maximum size and initial capacity
1138
+ - Supports initialization from dictionaries or iterables
1139
+ - Provides methods for key-value management similar to dict
1140
+ """
1141
+
1142
+ __slots__ = ("_raw",)
1143
+
1144
+ def __init__(
1145
+ self,
1146
+ maxsize: int,
1147
+ iterable: typing.Union[typing.Union[dict, typing.Iterable[tuple]], None] = None,
1148
+ *,
1149
+ capacity: int = 0,
1150
+ maxmemory: int = 0,
1151
+ ) -> None:
1152
+ """
1153
+ Initialize a new Least Frequently Used (LFU) cache.
1154
+
1155
+ Args:
1156
+ maxsize (int): Maximum size of the cache. A value of zero means unlimited size.
1157
+ iterable (dict or Iterable[tuple], optional): Initial data to populate the cache.
1158
+ capacity (int, optional): Initial hash table capacity to minimize reallocations. Defaults to 0.
1159
+ maxmemory (int, optional): Maximum memory (bytes) allowed for cached entries. Zero means unlimited.
1160
+ On PyPy. In PyPy, the size of each object is assumed to be 1 if the object
1161
+ does not have a `__sizeof__` method.
1162
+
1163
+ The cache uses a thread-safe LFU eviction policy, removing least frequently accessed items when the cache reaches its maximum size.
1164
+ """
1165
+ self._raw = _core.LFUCache(maxsize, capacity=capacity, maxmemory=maxmemory)
1166
+
1167
+ if iterable is not None:
1168
+ self.update(iterable)
1169
+
1170
+ @property
1171
+ def maxsize(self) -> int:
1172
+ return self._raw.maxsize()
1173
+
1174
+ @property
1175
+ def maxmemory(self) -> int:
1176
+ return self._raw.maxmemory()
1177
+
1178
+ def capacity(self) -> int:
1179
+ """Returns the number of elements the map can hold without reallocating."""
1180
+ return self._raw.capacity()
1181
+
1182
+ def memory(self) -> int:
1183
+ """Returns the total estimated memory usage of cached entries in bytes."""
1184
+ return self._raw.memory()
1185
+
1186
+ def __len__(self) -> int:
1187
+ return len(self._raw)
1188
+
1189
+ def __sizeof__(self): # pragma: no cover
1190
+ return self._raw.__sizeof__()
1191
+
1192
+ def __contains__(self, key: KT) -> bool:
1193
+ return key in self._raw
1194
+
1195
+ def __bool__(self) -> bool:
1196
+ return not self.is_empty()
1197
+
1198
+ def is_empty(self) -> bool:
1199
+ return self._raw.is_empty()
1200
+
1201
+ def is_full(self) -> bool:
1202
+ return self._raw.is_full()
1203
+
1204
+ def insert(self, key: KT, value: VT) -> typing.Optional[VT]:
1205
+ """
1206
+ Inserts a key-value pair into the cache, returning the previous value if the key existed.
1207
+
1208
+ Equivalent to `self[key] = value`, but with additional return value semantics:
1209
+
1210
+ - If the key was not previously in the cache, returns None.
1211
+ - If the key was already present, updates the value and returns the old value.
1212
+ The key itself is not modified.
1213
+
1214
+ Args:
1215
+ key: The key to insert.
1216
+ value: The value to associate with the key.
1217
+
1218
+ Returns:
1219
+ The previous value associated with the key, or None if the key was not present.
1220
+ """
1221
+ return self._raw.insert(key, value)
1222
+
1223
+ def peek(
1224
+ self, key: KT, default: typing.Optional[DT] = None
1225
+ ) -> typing.Union[VT, DT]: # pragma: no cover
1226
+ """
1227
+ Searches for a key-value in the cache and returns it (without moving the key to recently used).
1228
+ """
1229
+ try:
1230
+ return self._raw.peek(key)
1231
+ except _core.CoreKeyError:
1232
+ return default # type: ignore[return-value]
1233
+
1234
+ def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1235
+ """
1236
+ Retrieves the value for a given key from the cache.
1237
+
1238
+ Returns the value associated with the key if present, otherwise returns the specified default value.
1239
+ Equivalent to `self[key]`, but provides a fallback default if the key is not found.
1240
+
1241
+ Args:
1242
+ key: The key to look up in the cache.
1243
+ default: The value to return if the key is not present in the cache. Defaults to None.
1244
+
1245
+ Returns:
1246
+ The value associated with the key, or the default value if the key is not found.
1247
+ """
1248
+ try:
1249
+ return self._raw.get(key)
1250
+ except _core.CoreKeyError:
1251
+ return default # type: ignore[return-value]
1252
+
1253
+ def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1254
+ """
1255
+ Removes specified key and return the corresponding value. If the key is not found, returns the `default`.
1256
+ """
1257
+ try:
1258
+ return self._raw.remove(key)
1259
+ except _core.CoreKeyError:
1260
+ return default # type: ignore[return-value]
1261
+
1262
+ def setdefault(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1263
+ """
1264
+ Inserts key with a value of default if key is not in the cache.
1265
+
1266
+ Return the value for key if key is in the cache, else default.
1267
+ """
1268
+ return self._raw.setdefault(key, default)
1269
+
1270
+ def popitem(self) -> typing.Tuple[KT, VT]:
1271
+ """
1272
+ Removes and returns the least frequently used (LFU) item from the cache.
1273
+ """
1274
+ try:
1275
+ return self._raw.popitem()
1276
+ except _core.CoreKeyError: # pragma: no cover
1277
+ raise KeyError() from None
1278
+
1279
+ def drain(self, n: int) -> int: # pragma: no cover
1280
+ """Does the `popitem()` `n` times and returns count of removed items."""
1281
+ if n <= 0:
1282
+ return 0
1283
+
1284
+ for i in range(n):
1285
+ try:
1286
+ self._raw.popitem()
1287
+ except _core.CoreKeyError:
1288
+ return i
1289
+
1290
+ return i
1291
+
1292
+ def update(self, iterable: typing.Union[dict, typing.Iterable[tuple]]) -> None:
1293
+ """Updates the cache with elements from a dictionary or an iterable object of key/value pairs."""
1294
+ if hasattr(iterable, "items"):
1295
+ iterable = iterable.items()
1296
+
1297
+ self._raw.update(iterable)
1298
+
1299
+ def __setitem__(self, key: KT, value: VT) -> None:
1300
+ self.insert(key, value)
1301
+
1302
+ def __getitem__(self, key: KT) -> VT:
1303
+ try:
1304
+ return self._raw.get(key)
1305
+ except _core.CoreKeyError:
1306
+ raise KeyError(key) from None
1307
+
1308
+ def __delitem__(self, key: KT) -> None:
1309
+ try:
1310
+ self._raw.remove(key)
1311
+ except _core.CoreKeyError:
1312
+ raise KeyError(key) from None
1313
+
1314
+ def __eq__(self, other) -> bool:
1315
+ if not isinstance(other, LFUCache):
1316
+ return False # pragma: no cover
1317
+
1318
+ return self._raw == other._raw
1319
+
1320
+ def __ne__(self, other) -> bool:
1321
+ if not isinstance(other, LFUCache):
1322
+ return False # pragma: no cover
1323
+
1324
+ return self._raw != other._raw
1325
+
1326
+ def shrink_to_fit(self) -> None:
1327
+ """Shrinks the cache to fit len(self) elements."""
1328
+ self._raw.shrink_to_fit()
1329
+
1330
+ def clear(self, *, reuse: bool = False) -> None:
1331
+ """
1332
+ Removes all items from cache.
1333
+
1334
+ If reuse is True, will not free the memory for reusing in the future.
1335
+ """
1336
+ self._raw.clear(reuse)
1337
+
1338
+ def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
1339
+ """
1340
+ Returns an iterable object of the cache's items (key-value pairs).
1341
+
1342
+ Notes:
1343
+ - You should not make any changes in cache while using this iterable object.
1344
+ """
1345
+ return IteratorView(self._raw.items(), lambda x: (x[0], x[1]))
1346
+
1347
+ def items_with_frequency(self) -> IteratorView[typing.Tuple[KT, VT, int]]:
1348
+ """
1349
+ Returns an iterable view - containing tuples of `(key, value, frequency)` - of the cache's items along with their access frequency.
1350
+
1351
+ Notes:
1352
+ - The returned iterator should not be used to modify the cache.
1353
+ - Frequency represents how many times the item has been accessed.
1354
+ """
1355
+ return IteratorView(self._raw.items(), lambda x: x)
1356
+
1357
+ def keys(self) -> IteratorView[KT]:
1358
+ """
1359
+ Returns an iterable object of the cache's keys.
1360
+
1361
+ Notes:
1362
+ - You should not make any changes in cache while using this iterable object.
1363
+ """
1364
+ return IteratorView(self._raw.items(), lambda x: x[0])
1365
+
1366
+ def values(self) -> IteratorView[VT]:
1367
+ """
1368
+ Returns an iterable object of the cache's values.
1369
+
1370
+ Notes:
1371
+ - You should not make any changes in cache while using this iterable object.
1372
+ """
1373
+ return IteratorView(self._raw.items(), lambda x: x[1])
1374
+
1375
+ def least_frequently_used(self, n: int = 0) -> typing.Optional[KT]:
1376
+ """
1377
+ Returns the key in the cache that has been accessed the least, regardless of time.
1378
+
1379
+ If n is given, returns the nth least frequently used key.
1380
+
1381
+ Notes:
1382
+ - This method may re-sort the cache which can cause iterators to be stopped.
1383
+ - Do not use this method while using iterators.
1384
+ """
1385
+ if n < 0:
1386
+ n = len(self._raw) + n
1387
+
1388
+ if n < 0:
1389
+ return None
1390
+
1391
+ return self._raw.least_frequently_used(n)
1392
+
1393
+ def copy(self) -> "LFUCache[KT, VT]":
1394
+ """Returns a shallow copy of the cache"""
1395
+ return self.__copy__()
1396
+
1397
+ def __copy__(self) -> "LFUCache[KT, VT]":
1398
+ cls = type(self)
1399
+ copied = cls.__new__(cls)
1400
+ copied._raw = _std_copy.copy(self._raw)
1401
+ return copied
1402
+
1403
+ def __deepcopy__(self, memo) -> "LFUCache[KT, VT]":
1404
+ cls = type(self)
1405
+ copied = cls.__new__(cls)
1406
+ copied._raw = _std_copy.deepcopy(self._raw, memo)
1407
+ return copied
1408
+
1409
+ def __iter__(self) -> IteratorView[KT]:
1410
+ return self.keys()
1411
+
1412
+ def __repr__(self) -> str:
1413
+ cls = type(self)
1414
+
1415
+ return "%s.%s[%d/%d](%s)" % (
1416
+ cls.__module__,
1417
+ cls.__name__,
1418
+ len(self._raw),
1419
+ self._raw.maxsize(),
1420
+ # NOTE: we cannot use self._raw.items() here because iterables a tuples of (key, value, frequency)
1421
+ _items_to_str(self.items(), len(self._raw)),
1422
+ )
1423
+
1424
+
1425
+ class TTLCache(BaseCacheImpl[KT, VT]):
1426
+ """
1427
+ A thread-safe Time-To-Live (TTL) cache implementation with configurable maximum size and expiration.
1428
+
1429
+ This cache automatically removes elements that have expired based on their time-to-live setting.
1430
+ Supports various operations like insertion, retrieval, and iteration.
1431
+ """
1432
+
1433
+ __slots__ = ("_raw",)
1434
+
1435
+ def __init__(
1436
+ self,
1437
+ maxsize: int,
1438
+ ttl: typing.Union[float, timedelta],
1439
+ iterable: typing.Union[typing.Union[dict, typing.Iterable[tuple]], None] = None,
1440
+ *,
1441
+ capacity: int = 0,
1442
+ maxmemory: int = 0,
1443
+ ) -> None:
1444
+ """
1445
+ Initialize a new TTL cache instance.
1446
+
1447
+ Args:
1448
+ maxsize: Maximum number of elements the cache can hold.
1449
+ ttl: Time-to-live for cache entries, either as seconds or a timedelta.
1450
+ iterable: Optional initial items to populate the cache, can be a dict or iterable of tuples.
1451
+ capacity: Optional initial capacity for the underlying cache storage. Defaults to 0.
1452
+ maxmemory: Maximum memory (bytes) allowed for cached entries. Zero means unlimited.
1453
+ On PyPy. In PyPy, the size of each object is assumed to be 1 if the object
1454
+ does not have a `__sizeof__` method.
1455
+
1456
+ Raises:
1457
+ ValueError: If the time-to-live (ttl) is not a positive number.
1458
+ """
1459
+ if isinstance(ttl, timedelta):
1460
+ ttl = ttl.total_seconds()
1461
+
1462
+ if ttl <= 0:
1463
+ raise ValueError("ttl must be a positive number and non-zero")
1464
+
1465
+ self._raw = _core.TTLCache(maxsize, ttl, capacity=capacity, maxmemory=maxmemory)
1466
+
1467
+ if iterable is not None:
1468
+ self.update(iterable)
1469
+
1470
+ @property
1471
+ def maxsize(self) -> int:
1472
+ return self._raw.maxsize()
1473
+
1474
+ @property
1475
+ def maxmemory(self) -> int:
1476
+ return self._raw.maxmemory()
1477
+
1478
+ @property
1479
+ def ttl(self) -> float:
1480
+ return self._raw.ttl()
1481
+
1482
+ def capacity(self) -> int:
1483
+ """Returns the number of elements the map can hold without reallocating."""
1484
+ return self._raw.capacity()
1485
+
1486
+ def memory(self) -> int:
1487
+ """Returns the total estimated memory usage of cached entries in bytes."""
1488
+ return self._raw.memory()
1489
+
1490
+ def __len__(self) -> int:
1491
+ return len(self._raw)
1492
+
1493
+ def __sizeof__(self): # pragma: no cover
1494
+ return self._raw.__sizeof__()
1495
+
1496
+ def __contains__(self, key: KT) -> bool:
1497
+ return key in self._raw
1498
+
1499
+ def __bool__(self) -> bool:
1500
+ return not self.is_empty()
1501
+
1502
+ def is_empty(self) -> bool:
1503
+ return self._raw.is_empty()
1504
+
1505
+ def is_full(self) -> bool:
1506
+ return self._raw.is_full()
1507
+
1508
+ def insert(self, key: KT, value: VT) -> typing.Optional[VT]:
1509
+ """
1510
+ Inserts a key-value pair into the cache, returning the previous value if the key existed.
1511
+
1512
+ Equivalent to `self[key] = value`, but with additional return value semantics:
1513
+
1514
+ - If the key was not previously in the cache, returns None.
1515
+ - If the key was already present, updates the value and returns the old value.
1516
+ The key itself is not modified.
1517
+
1518
+ Args:
1519
+ key: The key to insert.
1520
+ value: The value to associate with the key.
1521
+
1522
+ Returns:
1523
+ The previous value associated with the key, or None if the key was not present.
1524
+ """
1525
+ return self._raw.insert(key, value)
1526
+
1527
+ def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1528
+ """
1529
+ Retrieves the value for a given key from the cache.
1530
+
1531
+ Returns the value associated with the key if present, otherwise returns the specified default value.
1532
+ Equivalent to `self[key]`, but provides a fallback default if the key is not found.
1533
+
1534
+ Args:
1535
+ key: The key to look up in the cache.
1536
+ default: The value to return if the key is not present in the cache. Defaults to None.
1537
+
1538
+ Returns:
1539
+ The value associated with the key, or the default value if the key is not found.
1540
+ """
1541
+ try:
1542
+ return self._raw.get(key).value()
1543
+ except _core.CoreKeyError:
1544
+ return default # type: ignore[return-value]
1545
+
1546
+ def get_with_expire(
1547
+ self, key: KT, default: typing.Optional[DT] = None
1548
+ ) -> typing.Tuple[typing.Union[VT, DT], float]:
1549
+ """
1550
+ Retrieves the value and expiration duration for a given key from the cache.
1551
+
1552
+ Returns a tuple containing the value associated with the key and its duration.
1553
+ If the key is not found, returns the default value and 0.0 duration.
1554
+
1555
+ Args:
1556
+ key: The key to look up in the cache.
1557
+ default: The value to return if the key is not present in the cache. Defaults to None.
1558
+
1559
+ Returns:
1560
+ A tuple of (value, duration), where value is the cached value or default,
1561
+ and duration is the time-to-live for the key (or 0.0 if not found).
1562
+ """
1563
+ try:
1564
+ pair = self._raw.get(key)
1565
+ except _core.CoreKeyError:
1566
+ return default, 0.0 # type: ignore[return-value]
1567
+ else:
1568
+ return (pair.value(), pair.duration())
1569
+
1570
+ def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1571
+ """
1572
+ Removes specified key and return the corresponding value. If the key is not found, returns the `default`.
1573
+ """
1574
+ try:
1575
+ return self._raw.remove(key).value()
1576
+ except _core.CoreKeyError:
1577
+ return default # type: ignore[return-value]
1578
+
1579
+ def pop_with_expire(
1580
+ self, key: KT, default: typing.Optional[DT] = None
1581
+ ) -> typing.Tuple[typing.Union[VT, DT], float]:
1582
+ """
1583
+ Removes the specified key from the cache and returns its value and expiration duration.
1584
+
1585
+ If the key is not found, returns the default value and 0.0 duration.
1586
+
1587
+ Args:
1588
+ key: The key to remove from the cache.
1589
+ default: The value to return if the key is not present in the cache. Defaults to None.
1590
+
1591
+ Returns:
1592
+ A tuple of (value, duration), where value is the cached value or default,
1593
+ and duration is the time-to-live for the key (or 0.0 if not found).
1594
+ """
1595
+ try:
1596
+ pair = self._raw.remove(key)
1597
+ except _core.CoreKeyError:
1598
+ return default, 0.0 # type: ignore[return-value]
1599
+ else:
1600
+ return (pair.value(), pair.duration())
1601
+
1602
+ def setdefault(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1603
+ """
1604
+ Inserts key with a value of default if key is not in the cache.
1605
+
1606
+ Return the value for key if key is in the cache, else default.
1607
+ """
1608
+ return self._raw.setdefault(key, default)
1609
+
1610
+ def popitem(self) -> typing.Tuple[KT, VT]:
1611
+ """Removes the element that has been in the cache the longest."""
1612
+ try:
1613
+ val = self._raw.popitem()
1614
+ except _core.CoreKeyError:
1615
+ raise KeyError() from None
1616
+ else:
1617
+ return val.pack2()
1618
+
1619
+ def popitem_with_expire(self) -> typing.Tuple[KT, VT, float]:
1620
+ """
1621
+ Removes and returns the element that has been in the cache the longest, along with its key and expiration duration.
1622
+
1623
+ If the cache is empty, raises a KeyError.
1624
+
1625
+ Returns:
1626
+ A tuple of (key, value, duration), where:
1627
+ - key is the key of the removed item
1628
+ - value is the value of the removed item
1629
+ - duration is the time-to-live for the removed item
1630
+ """
1631
+ try:
1632
+ val = self._raw.popitem()
1633
+ except _core.CoreKeyError:
1634
+ raise KeyError() from None
1635
+ else:
1636
+ return val.pack3()
1637
+
1638
+ def drain(self, n: int) -> int: # pragma: no cover
1639
+ """Does the `popitem()` `n` times and returns count of removed items."""
1640
+ if n <= 0:
1641
+ return 0
1642
+
1643
+ for i in range(n):
1644
+ try:
1645
+ self._raw.popitem()
1646
+ except _core.CoreKeyError:
1647
+ return i
1648
+
1649
+ return i
1650
+
1651
+ def update(self, iterable: typing.Union[dict, typing.Iterable[tuple]]) -> None:
1652
+ """Updates the cache with elements from a dictionary or an iterable object of key/value pairs."""
1653
+ if hasattr(iterable, "items"):
1654
+ iterable = iterable.items()
1655
+
1656
+ self._raw.update(iterable)
1657
+
1658
+ def __setitem__(self, key: KT, value: VT) -> None:
1659
+ self.insert(key, value)
1660
+
1661
+ def __getitem__(self, key: KT) -> VT:
1662
+ try:
1663
+ return self._raw.get(key).value()
1664
+ except _core.CoreKeyError:
1665
+ raise KeyError(key) from None
1666
+
1667
+ def __delitem__(self, key: KT) -> None:
1668
+ try:
1669
+ self._raw.remove(key)
1670
+ except _core.CoreKeyError:
1671
+ raise KeyError(key) from None
1672
+
1673
+ def __eq__(self, other) -> bool:
1674
+ if not isinstance(other, TTLCache):
1675
+ return False # pragma: no cover
1676
+
1677
+ return self._raw == other._raw
1678
+
1679
+ def __ne__(self, other) -> bool:
1680
+ if not isinstance(other, TTLCache):
1681
+ return False # pragma: no cover
1682
+
1683
+ return self._raw != other._raw
1684
+
1685
+ def shrink_to_fit(self) -> None:
1686
+ """Shrinks the cache to fit len(self) elements."""
1687
+ self._raw.shrink_to_fit()
1688
+
1689
+ def clear(self, *, reuse: bool = False) -> None:
1690
+ """
1691
+ Removes all items from cache.
1692
+
1693
+ If reuse is True, will not free the memory for reusing in the future.
1694
+ """
1695
+ self._raw.clear(reuse)
1696
+
1697
+ def items_with_expire(self) -> IteratorView[typing.Tuple[KT, VT, float]]:
1698
+ """
1699
+ Returns an iterable object of the cache's items (key-value pairs along with their expiration duration).
1700
+
1701
+ Notes:
1702
+ - You should not make any changes in cache while using this iterable object.
1703
+ """
1704
+ return IteratorView(self._raw.items(), lambda x: x.pack3())
1705
+
1706
+ def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
1707
+ """
1708
+ Returns an iterable object of the cache's items (key-value pairs).
1709
+
1710
+ Notes:
1711
+ - You should not make any changes in cache while using this iterable object.
1712
+ """
1713
+ return IteratorView(self._raw.items(), lambda x: x.pack2())
1714
+
1715
+ def keys(self) -> IteratorView[KT]:
1716
+ """
1717
+ Returns an iterable object of the cache's keys.
1718
+
1719
+ Notes:
1720
+ - You should not make any changes in cache while using this iterable object.
1721
+ """
1722
+ return IteratorView(self._raw.items(), lambda x: x.key())
1723
+
1724
+ def values(self) -> IteratorView[VT]:
1725
+ """
1726
+ Returns an iterable object of the cache's values.
1727
+
1728
+ Notes:
1729
+ - You should not make any changes in cache while using this iterable object.
1730
+ """
1731
+ return IteratorView(self._raw.items(), lambda x: x.value())
1732
+
1733
+ def first(self, n: int = 0) -> typing.Optional[KT]: # pragma: no cover
1734
+ """
1735
+ Returns the first key in cache; this is the one which will be removed by `popitem()` (if n == 0).
1736
+
1737
+ By using `n` parameter, you can browse order index by index.
1738
+ """
1739
+ if n < 0:
1740
+ n = len(self._raw) + n
1741
+
1742
+ if n < 0:
1743
+ return None
1744
+
1745
+ return self._raw.get_index(n)
1746
+
1747
+ def last(self) -> typing.Optional[KT]:
1748
+ """
1749
+ Returns the last key in cache. Equals to `self.first(-1)`.
1750
+ """
1751
+ return self._raw.get_index(len(self._raw) - 1)
1752
+
1753
+ def expire(self) -> None: # pragma: no cover
1754
+ """
1755
+ Manually removes expired key-value pairs from memory and releases their memory.
1756
+
1757
+ Notes:
1758
+ - This operation is typically automatic and does not require manual invocation.
1759
+ """
1760
+ self._raw.expire()
1761
+
1762
+ def copy(self) -> "TTLCache[KT, VT]":
1763
+ """Returns a shallow copy of the cache"""
1764
+ return self.__copy__()
1765
+
1766
+ def __copy__(self) -> "TTLCache[KT, VT]":
1767
+ cls = type(self)
1768
+ copied = cls.__new__(cls)
1769
+ copied._raw = _std_copy.copy(self._raw)
1770
+ return copied
1771
+
1772
+ def __deepcopy__(self, memo) -> "TTLCache[KT, VT]":
1773
+ cls = type(self)
1774
+ copied = cls.__new__(cls)
1775
+ copied._raw = _std_copy.deepcopy(self._raw, memo)
1776
+ return copied
1777
+
1778
+ def __iter__(self) -> IteratorView[KT]:
1779
+ return self.keys()
1780
+
1781
+ def __repr__(self) -> str:
1782
+ cls = type(self)
1783
+
1784
+ return "%s.%s[%d/%d, ttl=%f](%s)" % (
1785
+ cls.__module__,
1786
+ cls.__name__,
1787
+ len(self._raw),
1788
+ self._raw.maxsize(),
1789
+ self._raw.ttl(),
1790
+ _items_to_str(self.items(), len(self._raw)),
1791
+ )
1792
+
1793
+
1794
+ class VTTLCache(BaseCacheImpl[KT, VT]):
1795
+ """
1796
+ A thread-safe, time-to-live (TTL) cache implementation with per-key expiration policy.
1797
+
1798
+ This cache allows storing key-value pairs with optional expiration times. When an item expires,
1799
+ it is automatically removed from the cache. The cache supports a maximum size and provides
1800
+ various methods for inserting, retrieving, and managing cached items.
1801
+
1802
+ Key features:
1803
+ - Per-key time-to-live (TTL) support
1804
+ - Configurable maximum cache size
1805
+ - Thread-safe operations
1806
+ - Automatic expiration of items
1807
+
1808
+ Supports dictionary-like operations such as get, insert, update, and iteration.
1809
+ """
1810
+
1811
+ __slots__ = ("_raw",)
1812
+
1813
+ def __init__(
1814
+ self,
1815
+ maxsize: int,
1816
+ iterable: typing.Union[typing.Union[dict, typing.Iterable[tuple]], None] = None,
1817
+ ttl: typing.Union[float, timedelta, datetime, None] = None, # This is not a global TTL!
1818
+ *,
1819
+ capacity: int = 0,
1820
+ maxmemory: int = 0,
1821
+ ) -> None:
1822
+ """
1823
+ Initialize a new VTTLCache instance.
1824
+
1825
+ Args:
1826
+ maxsize (int): Maximum size of the cache. Zero indicates unlimited size.
1827
+ iterable (dict or Iterable[tuple], optional): Initial data to populate the cache.
1828
+ ttl (float or timedelta or datetime, optional): Time-to-live duration for `iterable` items.
1829
+ capacity (int, optional): Preallocated capacity for the cache to minimize reallocations.
1830
+ maxmemory (int, optional): Maximum memory (bytes) allowed for cached entries. Zero means unlimited.
1831
+ On PyPy. In PyPy, the size of each object is assumed to be 1 if the object
1832
+ does not have a `__sizeof__` method.
1833
+
1834
+ Raises:
1835
+ ValueError: If provided TTL is zero or negative.
1836
+ """
1837
+ self._raw = _core.VTTLCache(maxsize, capacity=capacity, maxmemory=maxmemory)
1838
+
1839
+ if iterable is not None:
1840
+ self.update(iterable, ttl)
1841
+
1842
+ @property
1843
+ def maxsize(self) -> int:
1844
+ return self._raw.maxsize()
1845
+
1846
+ @property
1847
+ def maxmemory(self) -> int:
1848
+ return self._raw.maxmemory()
1849
+
1850
+ def capacity(self) -> int:
1851
+ """Returns the number of elements the map can hold without reallocating."""
1852
+ return self._raw.capacity()
1853
+
1854
+ def memory(self) -> int:
1855
+ """Returns the total estimated memory usage of cached entries in bytes."""
1856
+ return self._raw.memory()
1857
+
1858
+ def __len__(self) -> int:
1859
+ return len(self._raw)
1860
+
1861
+ def __sizeof__(self): # pragma: no cover
1862
+ return self._raw.__sizeof__()
1863
+
1864
+ def __contains__(self, key: KT) -> bool:
1865
+ return key in self._raw
1866
+
1867
+ def __bool__(self) -> bool:
1868
+ return not self.is_empty()
1869
+
1870
+ def is_empty(self) -> bool:
1871
+ return self._raw.is_empty()
1872
+
1873
+ def is_full(self) -> bool:
1874
+ return self._raw.is_full()
1875
+
1876
+ def insert(
1877
+ self,
1878
+ key: KT,
1879
+ value: VT,
1880
+ ttl: typing.Union[float, timedelta, datetime, None] = None,
1881
+ ) -> typing.Optional[VT]:
1882
+ """
1883
+ Insert a key-value pair into the cache with an optional time-to-live (TTL).
1884
+ Returns the previous value associated with the key, if it existed.
1885
+
1886
+ Args:
1887
+ key (KT): The key to insert.
1888
+ value (VT): The value to associate with the key.
1889
+ ttl (float or timedelta or datetime, optional): Time-to-live duration for the item.
1890
+ If a timedelta or datetime is provided, it will be converted to seconds.
1891
+
1892
+ Raises:
1893
+ ValueError: If the provided TTL is zero or negative.
1894
+ """
1895
+ if ttl is not None: # pragma: no cover
1896
+ if isinstance(ttl, timedelta):
1897
+ ttl = ttl.total_seconds()
1898
+
1899
+ elif isinstance(ttl, datetime):
1900
+ ttl = (ttl - datetime.now()).total_seconds()
1901
+
1902
+ if ttl <= 0:
1903
+ raise ValueError("ttl must be positive and non-zero")
1904
+
1905
+ return self._raw.insert(key, value, ttl)
1906
+
1907
+ def get(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1908
+ """
1909
+ Retrieves the value for a given key from the cache.
1910
+
1911
+ Returns the value associated with the key if present, otherwise returns the specified default value.
1912
+ Equivalent to `self[key]`, but provides a fallback default if the key is not found.
1913
+
1914
+ Args:
1915
+ key: The key to look up in the cache.
1916
+ default: The value to return if the key is not present in the cache. Defaults to None.
1917
+
1918
+ Returns:
1919
+ The value associated with the key, or the default value if the key is not found.
1920
+ """
1921
+ try:
1922
+ return self._raw.get(key).value()
1923
+ except _core.CoreKeyError:
1924
+ return default # type: ignore[return-value]
1925
+
1926
+ def get_with_expire(
1927
+ self, key: KT, default: typing.Optional[DT] = None
1928
+ ) -> typing.Tuple[typing.Union[VT, DT], float]:
1929
+ """
1930
+ Retrieves the value and expiration duration for a given key from the cache.
1931
+
1932
+ Returns a tuple containing the value associated with the key and its duration.
1933
+ If the key is not found, returns the default value and 0.0 duration.
1934
+
1935
+ Args:
1936
+ key: The key to look up in the cache.
1937
+ default: The value to return if the key is not present in the cache. Defaults to None.
1938
+
1939
+ Returns:
1940
+ A tuple of (value, duration), where value is the cached value or default,
1941
+ and duration is the time-to-live for the key (or 0.0 if not found).
1942
+ """
1943
+ try:
1944
+ pair = self._raw.get(key)
1945
+ except _core.CoreKeyError:
1946
+ return default, 0.0 # type: ignore[return-value]
1947
+ else:
1948
+ return (pair.value(), pair.duration())
1949
+
1950
+ def pop(self, key: KT, default: typing.Optional[DT] = None) -> typing.Union[VT, DT]:
1951
+ """
1952
+ Removes specified key and return the corresponding value. If the key is not found, returns the `default`.
1953
+ """
1954
+ try:
1955
+ return self._raw.remove(key).value()
1956
+ except _core.CoreKeyError:
1957
+ return default # type: ignore[return-value]
1958
+
1959
+ def pop_with_expire(
1960
+ self, key: KT, default: typing.Optional[DT] = None
1961
+ ) -> typing.Tuple[typing.Union[VT, DT], float]:
1962
+ """
1963
+ Removes the specified key from the cache and returns its value and expiration duration.
1964
+
1965
+ If the key is not found, returns the default value and 0.0 duration.
1966
+
1967
+ Args:
1968
+ key: The key to remove from the cache.
1969
+ default: The value to return if the key is not present in the cache. Defaults to None.
1970
+
1971
+ Returns:
1972
+ A tuple of (value, duration), where value is the cached value or default,
1973
+ and duration is the time-to-live for the key (or 0.0 if not found).
1974
+ """
1975
+ try:
1976
+ pair = self._raw.remove(key)
1977
+ except _core.CoreKeyError:
1978
+ return default, 0.0 # type: ignore[return-value]
1979
+ else:
1980
+ return (pair.value(), pair.duration())
1981
+
1982
+ def setdefault(
1983
+ self,
1984
+ key: KT,
1985
+ default: typing.Optional[DT] = None,
1986
+ ttl: typing.Union[float, timedelta, datetime, None] = None,
1987
+ ) -> typing.Union[VT, DT]:
1988
+ """
1989
+ Inserts a key-value pair into the cache with an optional time-to-live (TTL).
1990
+
1991
+ If the key is not in the cache, it will be inserted with the default value.
1992
+ If the key already exists, its current value is returned.
1993
+
1994
+ Args:
1995
+ key: The key to insert or retrieve from the cache.
1996
+ default: The value to insert if the key is not present. Defaults to None.
1997
+ ttl: Optional time-to-live for the key. Can be a float (seconds), timedelta, or datetime.
1998
+ If not specified, the key will not expire.
1999
+
2000
+ Returns:
2001
+ The value associated with the key, either existing or the default value.
2002
+
2003
+ Raises:
2004
+ ValueError: If the provided TTL is not a positive value.
2005
+ """
2006
+ if ttl is not None: # pragma: no cover
2007
+ if isinstance(ttl, timedelta):
2008
+ ttl = ttl.total_seconds()
2009
+
2010
+ elif isinstance(ttl, datetime):
2011
+ ttl = (ttl - datetime.now()).total_seconds()
2012
+
2013
+ if ttl <= 0:
2014
+ raise ValueError("ttl must be positive and non-zero")
2015
+
2016
+ return self._raw.setdefault(key, default, ttl)
2017
+
2018
+ def popitem(self) -> typing.Tuple[KT, VT]:
2019
+ """
2020
+ Removes and returns the key-value pair that is closest to expiration.
2021
+
2022
+ Returns:
2023
+ A tuple containing the key and value of the removed item.
2024
+
2025
+ Raises:
2026
+ KeyError: If the cache is empty.
2027
+ """
2028
+ try:
2029
+ val = self._raw.popitem()
2030
+ except _core.CoreKeyError: # pragma: no cover
2031
+ raise KeyError() from None
2032
+ else:
2033
+ return val.pack2()
2034
+
2035
+ def popitem_with_expire(self) -> typing.Tuple[KT, VT, float]:
2036
+ """
2037
+ Removes and returns the key-value pair that is closest to expiration, along with its expiration duration.
2038
+
2039
+ Returns:
2040
+ A tuple containing the key, value, and expiration duration of the removed item.
2041
+
2042
+ Raises:
2043
+ KeyError: If the cache is empty.
2044
+ """
2045
+ try:
2046
+ val = self._raw.popitem()
2047
+ except _core.CoreKeyError:
2048
+ raise KeyError() from None
2049
+ else:
2050
+ return val.pack3()
2051
+
2052
+ def drain(self, n: int) -> int: # pragma: no cover
2053
+ """Does the `popitem()` `n` times and returns count of removed items."""
2054
+ if n <= 0:
2055
+ return 0
2056
+
2057
+ for i in range(n):
2058
+ try:
2059
+ self._raw.popitem()
2060
+ except _core.CoreKeyError:
2061
+ return i
2062
+
2063
+ return i
2064
+
2065
+ def update(
2066
+ self,
2067
+ iterable: typing.Union[dict, typing.Iterable[tuple]],
2068
+ ttl: typing.Union[float, timedelta, datetime, None] = None,
2069
+ ) -> None:
2070
+ """Updates the cache with elements from a dictionary or an iterable object of key/value pairs."""
2071
+ if hasattr(iterable, "items"):
2072
+ iterable = iterable.items()
2073
+
2074
+ if ttl is not None: # pragma: no cover
2075
+ if isinstance(ttl, timedelta):
2076
+ ttl = ttl.total_seconds()
2077
+
2078
+ elif isinstance(ttl, datetime):
2079
+ ttl = (ttl - datetime.now()).total_seconds()
2080
+
2081
+ if ttl <= 0:
2082
+ raise ValueError("ttl must be positive and non-zero")
2083
+
2084
+ self._raw.update(iterable, ttl)
2085
+
2086
+ def __setitem__(self, key: KT, value: VT) -> None:
2087
+ self.insert(key, value, None)
2088
+
2089
+ def __getitem__(self, key: KT) -> VT:
2090
+ try:
2091
+ return self._raw.get(key).value()
2092
+ except _core.CoreKeyError:
2093
+ raise KeyError(key) from None
2094
+
2095
+ def __delitem__(self, key: KT) -> None:
2096
+ try:
2097
+ self._raw.remove(key)
2098
+ except _core.CoreKeyError:
2099
+ raise KeyError(key) from None
2100
+
2101
+ def __eq__(self, other) -> bool:
2102
+ if not isinstance(other, VTTLCache):
2103
+ return False # pragma: no cover
2104
+
2105
+ return self._raw == other._raw
2106
+
2107
+ def __ne__(self, other) -> bool:
2108
+ if not isinstance(other, VTTLCache):
2109
+ return False # pragma: no cover
2110
+
2111
+ return self._raw != other._raw
2112
+
2113
+ def shrink_to_fit(self) -> None:
2114
+ """Shrinks the cache to fit len(self) elements."""
2115
+ self._raw.shrink_to_fit()
2116
+
2117
+ def clear(self, *, reuse: bool = False) -> None:
2118
+ """
2119
+ Removes all items from cache.
2120
+
2121
+ If reuse is True, will not free the memory for reusing in the future.
2122
+ """
2123
+ self._raw.clear(reuse)
2124
+
2125
+ def items_with_expire(self) -> IteratorView[typing.Tuple[KT, VT, float]]:
2126
+ """
2127
+ Returns an iterable object of the cache's items (key-value pairs along with their expiration duration).
2128
+
2129
+ Notes:
2130
+ - You should not make any changes in cache while using this iterable object.
2131
+ """
2132
+ return IteratorView(self._raw.items(), lambda x: x.pack3())
2133
+
2134
+ def items(self) -> IteratorView[typing.Tuple[KT, VT]]:
2135
+ """
2136
+ Returns an iterable object of the cache's items (key-value pairs).
2137
+
2138
+ Notes:
2139
+ - You should not make any changes in cache while using this iterable object.
2140
+ """
2141
+ return IteratorView(self._raw.items(), lambda x: x.pack2())
2142
+
2143
+ def keys(self) -> IteratorView[KT]:
2144
+ """
2145
+ Returns an iterable object of the cache's keys.
2146
+
2147
+ Notes:
2148
+ - You should not make any changes in cache while using this iterable object.
2149
+ """
2150
+ return IteratorView(self._raw.items(), lambda x: x.key())
2151
+
2152
+ def values(self) -> IteratorView[VT]:
2153
+ """
2154
+ Returns an iterable object of the cache's values.
2155
+
2156
+ Notes:
2157
+ - You should not make any changes in cache while using this iterable object.
2158
+ """
2159
+ return IteratorView(self._raw.items(), lambda x: x.value())
2160
+
2161
+ def expire(self) -> None: # pragma: no cover
2162
+ """
2163
+ Manually removes expired key-value pairs from memory and releases their memory.
2164
+
2165
+ Notes:
2166
+ - This operation is typically automatic and does not require manual invocation.
2167
+ """
2168
+ self._raw.expire()
2169
+
2170
+ def copy(self) -> "VTTLCache[KT, VT]":
2171
+ """Returns a shallow copy of the cache"""
2172
+ return self.__copy__()
2173
+
2174
+ def __copy__(self) -> "VTTLCache[KT, VT]":
2175
+ cls = type(self)
2176
+ copied = cls.__new__(cls)
2177
+ copied._raw = _std_copy.copy(self._raw)
2178
+ return copied
2179
+
2180
+ def __deepcopy__(self, memo) -> "VTTLCache[KT, VT]":
2181
+ cls = type(self)
2182
+ copied = cls.__new__(cls)
2183
+ copied._raw = _std_copy.deepcopy(self._raw, memo)
2184
+ return copied
2185
+
2186
+ def __iter__(self) -> IteratorView[KT]:
2187
+ return self.keys()
2188
+
2189
+ def __repr__(self) -> str:
2190
+ cls = type(self)
2191
+
2192
+ return "%s.%s[%d/%d](%s)" % (
2193
+ cls.__module__,
2194
+ cls.__name__,
2195
+ len(self._raw),
2196
+ self._raw.maxsize(),
2197
+ _items_to_str(self.items(), len(self._raw)),
2198
+ )