CacheToolsUtils 10.3__tar.gz → 11.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.
@@ -1,16 +1,15 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: CacheToolsUtils
3
- Version: 10.3
3
+ Version: 11.0
4
4
  Summary: Cachetools Utilities
5
5
  Author-email: Fabien Coelho <cachetools.utils@coelho.net>
6
- License: CC0
6
+ License-Expression: CC0-1.0
7
7
  Project-URL: repository, https://github.com/zx80/cachetools-utils
8
8
  Project-URL: documentation, https://zx80.github.io/cachetools-utils/
9
9
  Project-URL: issues, https://github.com/zx80/cachetools-utils/issues
10
10
  Project-URL: package, https://pypi.org/project/CacheToolsUtils/
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
- Classifier: License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
14
13
  Classifier: Programming Language :: Python
15
14
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
15
  Requires-Python: >=3.10
@@ -26,14 +25,10 @@ Requires-Dist: ruff; extra == "dev"
26
25
  Requires-Dist: pytest; extra == "dev"
27
26
  Requires-Dist: coverage; extra == "dev"
28
27
  Requires-Dist: pymarkdownlnt; extra == "dev"
29
- Requires-Dist: types-redis; extra == "dev"
30
- Requires-Dist: types-cachetools; extra == "dev"
31
28
  Provides-Extra: doc
32
- Requires-Dist: sphinx; extra == "doc"
33
- Requires-Dist: sphinx_rtd_theme; extra == "doc"
34
- Requires-Dist: sphinx-autoapi; extra == "doc"
35
- Requires-Dist: sphinx-lint; extra == "doc"
36
- Requires-Dist: myst_parser; extra == "doc"
29
+ Requires-Dist: mkdocs; extra == "doc"
30
+ Requires-Dist: mkdocs-material; extra == "doc"
31
+ Requires-Dist: mkdocstrings[python]; extra == "doc"
37
32
  Provides-Extra: crypt
38
33
  Requires-Dist: pycryptodome; extra == "crypt"
39
34
  Provides-Extra: tests
@@ -43,17 +38,16 @@ Provides-Extra: pub
43
38
  Requires-Dist: build; extra == "pub"
44
39
  Requires-Dist: wheel; extra == "pub"
45
40
  Requires-Dist: twine; extra == "pub"
41
+ Dynamic: license-file
46
42
 
47
43
  # CacheToolsUtils
48
44
 
49
- Classes to add key prefix and stats to
50
- [cachetools](https://pypi.org/project/cachetools/) classes and use
51
- [redis](https://redis.io/) and
52
- [memcached](https://memcached.org/) as storage backends,
53
- and other cache-related utils.
45
+ Stackable cache classes for sharing, encryption, statistics _and more_
46
+ on top of [cachetools](https://pypi.org/project/cachetools/),
47
+ [redis](https://redis.io/) and [memcached](https://memcached.org/).
54
48
 
55
- ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/cachetools-utils.yml/badge.svg?branch=main&style=flat)
56
- ![Tests](https://img.shields.io/badge/tests-23%20✓-success)
49
+ ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/ctu.yml/badge.svg?branch=main&style=flat)
50
+ ![Tests](https://img.shields.io/badge/tests-25%20✓-success)
57
51
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
58
52
  ![Issues](https://img.shields.io/github/issues/zx80/cachetools-utils?style=flat)
59
53
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -66,7 +60,37 @@ and other cache-related utils.
66
60
  For our purpose, a cache is a key-value store, aka a dictionary, possibly with
67
61
  some constraints on keys (type, size) and values (size, serialization).
68
62
  This module provides new caches, wrappers and other utilities suitable to use
69
- with `cachetools`.
63
+ with `cachetools`. Example:
64
+
65
+ ```python
66
+ import redis
67
+ import CacheToolsUtils as ctu
68
+ import config
69
+
70
+ redis_storage = redis.Redis(**config.REDIS_SERVER)
71
+ storage = ctu.RedisCache(redis_storage, raw=True, ttl=120)
72
+ crypto = ctu.EncryptedCache(storage, config.CACHE_KEY)
73
+ cache = ctu.ToBytesCache(crypto)
74
+
75
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
76
+ def repeat(s: str, n: int) -> str:
77
+ return s * n
78
+
79
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
80
+ def banged(s: str, n: int) -> str:
81
+ return repeat(s, n) + "!"
82
+
83
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
84
+ def question(s: str, n: int) -> str:
85
+ return repeat(s, n) + "?"
86
+
87
+ print(banged("aa", 3)) # add 2 cache entries
88
+ print(question("aa", 3)) # add 1 entry, 1 hit
89
+ print(repeat("aa", 3)) # cache hit!
90
+ print(banged("aa", 3)) # cache hit!
91
+
92
+ assert cache.hits() > 0
93
+ ```
70
94
 
71
95
  ### Cache classes
72
96
 
@@ -12,15 +12,11 @@ ruff
12
12
  pytest
13
13
  coverage
14
14
  pymarkdownlnt
15
- types-redis
16
- types-cachetools
17
15
 
18
16
  [doc]
19
- sphinx
20
- sphinx_rtd_theme
21
- sphinx-autoapi
22
- sphinx-lint
23
- myst_parser
17
+ mkdocs
18
+ mkdocs-material
19
+ mkdocstrings[python]
24
20
 
25
21
  [pub]
26
22
  build
@@ -2,6 +2,9 @@
2
2
  CacheTools Utilities. This code is public domain.
3
3
  """
4
4
 
5
+ # FIXME 3.13t checks without Cipher package
6
+ # pyright: reportMissingImports=false
7
+
5
8
  from typing import Any, Callable, MutableMapping
6
9
  import abc
7
10
 
@@ -25,7 +28,7 @@ _NO_DEFAULT = object()
25
28
 
26
29
 
27
30
  class _MutMapMix:
28
- """Convenient MutableMapping Mixin, forward to _cache."""
31
+ """Convenient MutableMapping Mixin, forward to `_cache`."""
29
32
 
30
33
  # FIXME coverage reports this as missing with Python 3.14
31
34
  _cache: MutableMapping # pragma: no cover
@@ -50,7 +53,7 @@ class _MutMapMix:
50
53
 
51
54
 
52
55
  class _KeyMutMapMix(_MutMapMix):
53
- """Convenient MutableMapping Mixin with a key filter, forward to _cache."""
56
+ """Convenient MutableMapping Mixin with a key filter, forward to `_cache`."""
54
57
 
55
58
  @abc.abstractmethod
56
59
  def _key(self, key: Any) -> Any: # pragma: no cover
@@ -70,7 +73,7 @@ class _KeyMutMapMix(_MutMapMix):
70
73
 
71
74
 
72
75
  class _StatsMix:
73
- """Convenient Mixin to forward stats methods to _cache."""
76
+ """Convenient Mixin to forward stats methods to `_cache`."""
74
77
 
75
78
  def hits(self) -> float|None:
76
79
  return self._cache.hits() # type: ignore
@@ -111,9 +114,11 @@ class _RedisMix: # pragma: no cover
111
114
  class DebugCache(_StatsMix, MutableMapping):
112
115
  """Debug class.
113
116
 
114
- :param cache: actual cache
115
- :param log: logging instance
116
- :param name: name of instance for output
117
+ Parameters:
118
+
119
+ - cache: actual cache
120
+ - log: logging instance
121
+ - name: name of instance for output
117
122
  """
118
123
 
119
124
  def __init__(self, cache: MutableMapping, log: logging.Logger, name="cache"):
@@ -159,7 +164,7 @@ class DebugCache(_StatsMix, MutableMapping):
159
164
 
160
165
 
161
166
  class DictCache(_MutMapMix, MutableMapping):
162
- """Cache class based on dict."""
167
+ """Cache class based on `dict`."""
163
168
 
164
169
  def __init__(self):
165
170
  self._cache = dict()
@@ -171,17 +176,19 @@ class DictCache(_MutMapMix, MutableMapping):
171
176
  class LockedCache(_MutMapMix, _StatsMix, _RedisMix, MutableMapping):
172
177
  """Cache class with a lock.
173
178
 
174
- :param cache: actual cache.
175
- :param lock: lock (context manager) to use
179
+ Parameters:
176
180
 
177
- The locked layer should be the last one before the actual cache.
181
+ - cache: actual cache.
182
+ - lock: lock (context manager) to use
178
183
 
179
- .. code-block: python
184
+ The locked layer should be the last one before the actual cache.
180
185
 
181
- import threading
182
- import cachetools as ct
183
- import CacheToolsUtils as ctu
184
- cache = ctu.LockedCache(ct.LFUCache(), threading.RLock())
186
+ ```python
187
+ import threading
188
+ import cachetools as ct
189
+ import CacheToolsUtils as ctu
190
+ cache = ctu.LockedCache(ct.LFUCache(), threading.RLock())
191
+ ```
185
192
  """
186
193
 
187
194
  def __init__(self, cache: MutableMapping, lock):
@@ -208,8 +215,10 @@ class LockedCache(_MutMapMix, _StatsMix, _RedisMix, MutableMapping):
208
215
  class PrefixedCache(_KeyMutMapMix, _StatsMix, MutableMapping):
209
216
  """Cache class to add a key prefix.
210
217
 
211
- :param cache: actual cache.
212
- :param prefix: prefix to prepend to keys.
218
+ Parameters:
219
+
220
+ - cache: actual cache.
221
+ - prefix: prefix to prepend to keys.
213
222
  """
214
223
 
215
224
  def __init__(self, cache: MutableMapping, prefix: str|bytes = ""):
@@ -229,9 +238,11 @@ class PrefixedCache(_KeyMutMapMix, _StatsMix, MutableMapping):
229
238
  class AutoPrefixedCache(PrefixedCache):
230
239
  """Cache class with an automatic counter-based string prefix.
231
240
 
232
- :param cache: actual cache.
233
- :param sep: prefix separator, default is ".".
234
- :param method: encoding method in "b64", "b64u", "b32", "b32x", "b16",
241
+ Parameters:
242
+
243
+ - cache: actual cache.
244
+ - sep: prefix separator, default is ".".
245
+ - method: encoding method in "b64", "b64u", "b32", "b32x", "b16",
235
246
  "a85" and "b85". Default is "b64".
236
247
  """
237
248
 
@@ -262,16 +273,17 @@ class AutoPrefixedCache(PrefixedCache):
262
273
  class StatsCache(_MutMapMix, MutableMapping):
263
274
  """Cache class to keep stats.
264
275
 
265
- :param cache: actual cache.
276
+ Parameter:
266
277
 
267
- .. code-block: python
278
+ - cache: actual cache.
268
279
 
269
- import cachetools as ct
270
- import CacheToolsUtils as ctu
271
- cache = ctu.StatsCache(ct.LRUCache())
272
- ...
280
+ ```python
281
+ import cachetools as ct
282
+ import CacheToolsUtils as ctu
283
+ cache = ctu.StatsCache(ct.LRUCache())
284
+ ```
273
285
 
274
- Note that CacheTools ``cached`` decorator with ``info=True`` provides
286
+ Note that CacheTools `cached` decorator with `info=True` provides
275
287
  hits, misses, maxsize and currsize information.
276
288
  However, this only works for its own classes.
277
289
  """
@@ -286,19 +298,25 @@ class StatsCache(_MutMapMix, MutableMapping):
286
298
 
287
299
  def reset(self):
288
300
  """Reset internal stats data."""
289
- self._reads, self._writes, self._dels, self._hits = 0, 0, 0, 0
301
+ self._tests, self._reads, self._writes, self._dels, self._hits = 0, 0, 0, 0, 0
290
302
 
291
303
  def stats(self) -> dict[str, Any]:
292
304
  """Return available stats data as dict."""
293
305
  return {
294
306
  "type": 1,
307
+ "tests": self._tests,
295
308
  "reads": self._reads,
296
309
  "writes": self._writes,
297
310
  "dels": self._dels,
298
- "hits": self.hits(),
311
+ "hits": self._hits,
312
+ "hit_rate": self.hits(),
299
313
  "size": self._cache.__len__()
300
314
  }
301
315
 
316
+ def __contains__(self, key):
317
+ self._tests += 1
318
+ return self._cache.__contains__(key)
319
+
302
320
  def __getitem__(self, key):
303
321
  self._reads += 1
304
322
  res = self._cache.__getitem__(key)
@@ -320,9 +338,11 @@ class StatsCache(_MutMapMix, MutableMapping):
320
338
  class TwoLevelCache(_MutMapMix, MutableMapping):
321
339
  """Two-Level Cache class for CacheTools.
322
340
 
323
- :param cache: first (smaller, shorter TTL) cache
324
- :param cache2: second (larger, longer TTL) cache
325
- :param resilient: whether to ignore cache2 failures
341
+ Parameters:
342
+
343
+ - cache: first (smaller, shorter TTL) cache
344
+ - cache2: second (larger, longer TTL) cache
345
+ - resilient: whether to ignore cache2 failures
326
346
  """
327
347
 
328
348
  def __init__(self, cache: MutableMapping, cache2: MutableMapping, resilient=False):
@@ -448,10 +468,12 @@ class _Cipher:
448
468
  class EncryptedCache(_KeyMutMapMix, _StatsMix, MutableMapping):
449
469
  """Encrypted Bytes Key-Value Cache.
450
470
 
451
- :param secret: bytes of secret, at least 16 bytes.
452
- :param hsize: size of hashed key, default is *16*.
453
- :param csize: value checksum size, default is *0*.
454
- :param cipher: chose cipher from "Salsa20", "AES-128-CBC" or "ChaCha20".
471
+ Parameters:
472
+
473
+ - secret: bytes of secret, at least 16 bytes.
474
+ - hsize: size of hashed key, default is *16*.
475
+ - csize: value checksum size, default is *0*.
476
+ - cipher: chose cipher from "Salsa20", "AES-128-CBC" or "ChaCha20".
455
477
 
456
478
  The key is *not* encrypted but simply hashed, thus they are
457
479
  fixed size with a very low collision probability.
@@ -544,14 +566,16 @@ class ToBytesCache(_KeyMutMapMix, _StatsMix, MutableMapping):
544
566
  def cached(cache, *args, **kwargs):
545
567
  """Extended decorator with delete and exists.
546
568
 
547
- All parameters are forwarded to ``cachetools.cached``.
569
+ All parameters are forwarded to `cachetools.cached`.
570
+
571
+ Parameter:
548
572
 
549
- :param cache: actual cache.
573
+ - cache: actual cache.
550
574
 
551
- If *f(\\*args, \\*\\*kwargs)* is the ``cached`` function, then:
575
+ If *f(\\*args, \\*\\*kwargs)* is the _cached_ function, then:
552
576
 
553
- - ``f.cache_in(*args, **kwargs)`` tells whether the result is cached.
554
- - ``f.cache_del(*args, **kwargs)`` deletes (invalidates) the cached result.
577
+ - `f.cache_in(*args, **kwargs)` tells whether the result is cached.
578
+ - `f.cache_del(*args, **kwargs)` deletes (invalidates) the cached result.
555
579
  """
556
580
 
557
581
  def decorate(fun: Callable):
@@ -594,15 +618,17 @@ def cacheMethods(
594
618
  ):
595
619
  """Cache some object methods.
596
620
 
597
- :param cache: cache to use.
598
- :param obj: object instance to be cached.
599
- :param gen: generator of PrefixedCache.
600
- :param opts: additional parameters when calling `cached`.
601
- :param funs: name of methods and corresponding prefix
621
+ Parameters:
602
622
 
603
- .. code-block:: python
623
+ - cache: cache to use.
624
+ - obj: object instance to be cached.
625
+ - gen: generator of PrefixedCache.
626
+ - opts: additional parameters when calling `cached`.
627
+ - funs: name of methods and corresponding prefix
604
628
 
605
- cacheMethods(cache, item, PrefixedCache, compute1="c1.", compute2="c2.")
629
+ ```python
630
+ cacheMethods(cache, item, PrefixedCache, compute1="c1.", compute2="c2.")
631
+ ```
606
632
  """
607
633
  for fun, prefix in funs.items():
608
634
  assert hasattr(obj, fun), f"cannot cache missing method {fun} on {obj}"
@@ -621,15 +647,17 @@ def cacheFunctions(
621
647
  ):
622
648
  """Cache some global functions, with a prefix.
623
649
 
624
- :param cache: cache to use.
625
- :param globs: global object dictionary.
626
- :param gen: generator of PrefixedCache.
627
- :param opts: additional parameters when calling `cached`.
628
- :param funs: name of functions and corresponding prefix
650
+ Parameters:
629
651
 
630
- .. code-block:: python
652
+ - cache: cache to use.
653
+ - globs: global object dictionary.
654
+ - gen: generator of PrefixedCache.
655
+ - opts: additional parameters when calling `cached`.
656
+ - funs: name of functions and corresponding prefix
631
657
 
632
- cacheFunctions(cache, globals(), PrefixedCache, fun1="f1.", fun2="f2.")
658
+ ```python
659
+ cacheFunctions(cache, globals(), PrefixedCache, fun1="f1.", fun2="f2.")
660
+ ```
633
661
  """
634
662
  for fun, prefix in funs.items():
635
663
  assert fun in globs, "caching functions: {fun} not found"
@@ -688,13 +716,13 @@ def full_hash_key(*args, **kwargs) -> str:
688
716
  # MEMCACHED
689
717
  #
690
718
  class JsonSerde:
691
- """JSON serialize/deserialize class for MemCached (``pymemcache``).
719
+ """JSON serialize/deserialize class for MemCached (`pymemcache`).
692
720
 
693
- .. code-block:: python
694
-
695
- import pymemcache as pmc
696
- import CacheToolsUtils as ctu
697
- pmc_cache = pmc.Client(server="localhost", serde=ctu.JsonSerde())
721
+ ```python
722
+ import pymemcache as pmc
723
+ import CacheToolsUtils as ctu
724
+ pmc_cache = pmc.Client(server="localhost", serde=ctu.JsonSerde())
725
+ ```
698
726
  """
699
727
 
700
728
  # keep strings, else json
@@ -721,16 +749,19 @@ class JsonSerde:
721
749
  class MemCached(_KeyMutMapMix, MutableMapping):
722
750
  """MemCached-compatible wrapper class for cachetools with key encoding.
723
751
 
724
- :param cache: actual memcached cache.
752
+ Parameter:
725
753
 
726
- .. code-block:: python
754
+ - cache: actual memcached cache.
727
755
 
728
- import pymemcache as pmc
729
- import CacheToolsUtils as ctu
730
- cache = ctu.MemCached(pmc.Client(server="localhost", serde=ctu.JsonSerde()))
756
+ ```python
757
+ import pymemcache as pmc
758
+ import CacheToolsUtils as ctu
759
+ cache = ctu.MemCached(pmc.Client(server="localhost", serde=ctu.JsonSerde()))
731
760
 
732
- @ctu.cached(cache=cache)
733
- def whatever(...):
761
+ @ctu.cached(cache=cache)
762
+ def whatever(...):
763
+ ...
764
+ ```
734
765
  """
735
766
 
736
767
  def __init__(self, cache):
@@ -764,15 +795,17 @@ class MemCached(_KeyMutMapMix, MutableMapping):
764
795
  class PrefixedMemCached(MemCached):
765
796
  """MemCached-compatible wrapper class for cachetools with a key prefix.
766
797
 
767
- :param cache: actual memcached cache.
768
- :param prefix: post key-encoding prepended prefix.
798
+ Parameters:
769
799
 
770
- .. code-block:: python
800
+ - cache: actual memcached cache.
801
+ - prefix: post key-encoding prepended prefix.
771
802
 
772
- import pymemcache as pmc
773
- import CacheToolsUtils as ctu
774
- # add a "app." prefix to keys, after serialization
775
- cache = ctu.PrefixedMemCached(pmc.Client(server="localhost", serde=ctu.JsonSerde()), "app.")
803
+ ```python
804
+ import pymemcache as pmc
805
+ import CacheToolsUtils as ctu
806
+ # add a "app." prefix to keys, after serialization
807
+ cache = ctu.PrefixedMemCached(pmc.Client(server="localhost", serde=ctu.JsonSerde()), "app.")
808
+ ```
776
809
  """
777
810
 
778
811
  def __init__(self, cache, prefix: str = ""):
@@ -787,20 +820,22 @@ class PrefixedMemCached(MemCached):
787
820
  # REDIS
788
821
  #
789
822
  class RedisCache(MutableMapping):
790
- """Redis TTL-ed wrapper for cachetools (``redis``).
823
+ """Redis TTL-ed wrapper for cachetools (`redis`).
791
824
 
792
- :param cache: actual redis cache.
793
- :param ttl: time-to-live in seconds, used as default expiration (``ex``), default is 600.
794
- :param raw: whether to serialize keys and values, default is *False*.
825
+ Parameters:
795
826
 
796
- Keys and values are serialized in *JSON*.
827
+ - cache: actual redis cache.
828
+ - ttl: time-to-live in seconds, used as default expiration (`ex`), default is _600_.
829
+ - raw: whether to serialize keys and values, default is *False*.
797
830
 
798
- .. code-block:: python
831
+ Keys and values are serialized in *JSON*.
799
832
 
800
- import redis
801
- import CacheToolsUtils as ctu
802
- # redis with 1 hour expiration
803
- cache = ctu.RedisCache(redis.Redis(host="localhost"), 3600)
833
+ ```python
834
+ import redis
835
+ import CacheToolsUtils as ctu
836
+ # redis with 1 hour expiration
837
+ cache = ctu.RedisCache(redis.Redis(host="localhost"), 3600)
838
+ ```
804
839
  """
805
840
 
806
841
  def __init__(self, cache, ttl=600, raw=False):
@@ -880,16 +915,18 @@ class RedisCache(MutableMapping):
880
915
  class PrefixedRedisCache(RedisCache):
881
916
  """Prefixed Redis wrapper class for cachetools.
882
917
 
883
- :param cache: actual redis cache.
884
- :param prefix: post key encoding prefix, default is empty.
885
- :param ttl: time-to-live in seconds, used as default expiration (``ex``), default is 600.
918
+ Parameters:
886
919
 
887
- .. code-block:: python
920
+ - cache: actual redis cache.
921
+ - prefix: post key encoding prefix, default is empty.
922
+ - ttl: time-to-live in seconds, used as default expiration (`ex`), default is _600_.
888
923
 
889
- import redis
890
- import CacheToolsUtils as ctu
891
- # redis with "app." prefix and 1 hour expiration
892
- cache = ctu.PrefixedRedisCache(redis.Redis(host="localhost"), "app.", 3600)
924
+ ```python
925
+ import redis
926
+ import CacheToolsUtils as ctu
927
+ # redis with "app." prefix and 1 hour expiration
928
+ cache = ctu.PrefixedRedisCache(redis.Redis(host="localhost"), "app.", 3600)
929
+ ```
893
930
  """
894
931
 
895
932
  def __init__(self, cache, prefix: str = "", ttl=600):
@@ -1,16 +1,15 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: CacheToolsUtils
3
- Version: 10.3
3
+ Version: 11.0
4
4
  Summary: Cachetools Utilities
5
5
  Author-email: Fabien Coelho <cachetools.utils@coelho.net>
6
- License: CC0
6
+ License-Expression: CC0-1.0
7
7
  Project-URL: repository, https://github.com/zx80/cachetools-utils
8
8
  Project-URL: documentation, https://zx80.github.io/cachetools-utils/
9
9
  Project-URL: issues, https://github.com/zx80/cachetools-utils/issues
10
10
  Project-URL: package, https://pypi.org/project/CacheToolsUtils/
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
- Classifier: License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
14
13
  Classifier: Programming Language :: Python
15
14
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
15
  Requires-Python: >=3.10
@@ -26,14 +25,10 @@ Requires-Dist: ruff; extra == "dev"
26
25
  Requires-Dist: pytest; extra == "dev"
27
26
  Requires-Dist: coverage; extra == "dev"
28
27
  Requires-Dist: pymarkdownlnt; extra == "dev"
29
- Requires-Dist: types-redis; extra == "dev"
30
- Requires-Dist: types-cachetools; extra == "dev"
31
28
  Provides-Extra: doc
32
- Requires-Dist: sphinx; extra == "doc"
33
- Requires-Dist: sphinx_rtd_theme; extra == "doc"
34
- Requires-Dist: sphinx-autoapi; extra == "doc"
35
- Requires-Dist: sphinx-lint; extra == "doc"
36
- Requires-Dist: myst_parser; extra == "doc"
29
+ Requires-Dist: mkdocs; extra == "doc"
30
+ Requires-Dist: mkdocs-material; extra == "doc"
31
+ Requires-Dist: mkdocstrings[python]; extra == "doc"
37
32
  Provides-Extra: crypt
38
33
  Requires-Dist: pycryptodome; extra == "crypt"
39
34
  Provides-Extra: tests
@@ -43,17 +38,16 @@ Provides-Extra: pub
43
38
  Requires-Dist: build; extra == "pub"
44
39
  Requires-Dist: wheel; extra == "pub"
45
40
  Requires-Dist: twine; extra == "pub"
41
+ Dynamic: license-file
46
42
 
47
43
  # CacheToolsUtils
48
44
 
49
- Classes to add key prefix and stats to
50
- [cachetools](https://pypi.org/project/cachetools/) classes and use
51
- [redis](https://redis.io/) and
52
- [memcached](https://memcached.org/) as storage backends,
53
- and other cache-related utils.
45
+ Stackable cache classes for sharing, encryption, statistics _and more_
46
+ on top of [cachetools](https://pypi.org/project/cachetools/),
47
+ [redis](https://redis.io/) and [memcached](https://memcached.org/).
54
48
 
55
- ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/cachetools-utils.yml/badge.svg?branch=main&style=flat)
56
- ![Tests](https://img.shields.io/badge/tests-23%20✓-success)
49
+ ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/ctu.yml/badge.svg?branch=main&style=flat)
50
+ ![Tests](https://img.shields.io/badge/tests-25%20✓-success)
57
51
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
58
52
  ![Issues](https://img.shields.io/github/issues/zx80/cachetools-utils?style=flat)
59
53
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -66,7 +60,37 @@ and other cache-related utils.
66
60
  For our purpose, a cache is a key-value store, aka a dictionary, possibly with
67
61
  some constraints on keys (type, size) and values (size, serialization).
68
62
  This module provides new caches, wrappers and other utilities suitable to use
69
- with `cachetools`.
63
+ with `cachetools`. Example:
64
+
65
+ ```python
66
+ import redis
67
+ import CacheToolsUtils as ctu
68
+ import config
69
+
70
+ redis_storage = redis.Redis(**config.REDIS_SERVER)
71
+ storage = ctu.RedisCache(redis_storage, raw=True, ttl=120)
72
+ crypto = ctu.EncryptedCache(storage, config.CACHE_KEY)
73
+ cache = ctu.ToBytesCache(crypto)
74
+
75
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
76
+ def repeat(s: str, n: int) -> str:
77
+ return s * n
78
+
79
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
80
+ def banged(s: str, n: int) -> str:
81
+ return repeat(s, n) + "!"
82
+
83
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
84
+ def question(s: str, n: int) -> str:
85
+ return repeat(s, n) + "?"
86
+
87
+ print(banged("aa", 3)) # add 2 cache entries
88
+ print(question("aa", 3)) # add 1 entry, 1 hit
89
+ print(repeat("aa", 3)) # cache hit!
90
+ print(banged("aa", 3)) # cache hit!
91
+
92
+ assert cache.hits() > 0
93
+ ```
70
94
 
71
95
  ### Cache classes
72
96
 
@@ -1,13 +1,11 @@
1
1
  # CacheToolsUtils
2
2
 
3
- Classes to add key prefix and stats to
4
- [cachetools](https://pypi.org/project/cachetools/) classes and use
5
- [redis](https://redis.io/) and
6
- [memcached](https://memcached.org/) as storage backends,
7
- and other cache-related utils.
8
-
9
- ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/cachetools-utils.yml/badge.svg?branch=main&style=flat)
10
- ![Tests](https://img.shields.io/badge/tests-23%20✓-success)
3
+ Stackable cache classes for sharing, encryption, statistics _and more_
4
+ on top of [cachetools](https://pypi.org/project/cachetools/),
5
+ [redis](https://redis.io/) and [memcached](https://memcached.org/).
6
+
7
+ ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/ctu.yml/badge.svg?branch=main&style=flat)
8
+ ![Tests](https://img.shields.io/badge/tests-25%20✓-success)
11
9
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
12
10
  ![Issues](https://img.shields.io/github/issues/zx80/cachetools-utils?style=flat)
13
11
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -20,7 +18,37 @@ and other cache-related utils.
20
18
  For our purpose, a cache is a key-value store, aka a dictionary, possibly with
21
19
  some constraints on keys (type, size) and values (size, serialization).
22
20
  This module provides new caches, wrappers and other utilities suitable to use
23
- with `cachetools`.
21
+ with `cachetools`. Example:
22
+
23
+ ```python
24
+ import redis
25
+ import CacheToolsUtils as ctu
26
+ import config
27
+
28
+ redis_storage = redis.Redis(**config.REDIS_SERVER)
29
+ storage = ctu.RedisCache(redis_storage, raw=True, ttl=120)
30
+ crypto = ctu.EncryptedCache(storage, config.CACHE_KEY)
31
+ cache = ctu.ToBytesCache(crypto)
32
+
33
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
34
+ def repeat(s: str, n: int) -> str:
35
+ return s * n
36
+
37
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
38
+ def banged(s: str, n: int) -> str:
39
+ return repeat(s, n) + "!"
40
+
41
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
42
+ def question(s: str, n: int) -> str:
43
+ return repeat(s, n) + "?"
44
+
45
+ print(banged("aa", 3)) # add 2 cache entries
46
+ print(question("aa", 3)) # add 1 entry, 1 hit
47
+ print(repeat("aa", 3)) # cache hit!
48
+ print(banged("aa", 3)) # cache hit!
49
+
50
+ assert cache.hits() > 0
51
+ ```
24
52
 
25
53
  ### Cache classes
26
54
 
@@ -4,17 +4,16 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "CacheToolsUtils"
7
- version = "10.3"
7
+ version = "11.0"
8
8
  authors = [ { name = "Fabien Coelho", email = "cachetools.utils@coelho.net" } ]
9
9
  description = "Cachetools Utilities"
10
10
  readme = "README.md"
11
- license = { text = "CC0" }
11
+ license = "CC0-1.0"
12
12
  requires-python = ">= 3.10"
13
13
  dependencies = [ "cachetools" ]
14
14
  classifiers = [
15
15
  "Development Status :: 5 - Production/Stable",
16
16
  "Intended Audience :: Developers",
17
- "License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication",
18
17
  "Programming Language :: Python",
19
18
  "Topic :: Software Development :: Libraries :: Python Modules"
20
19
  ]
@@ -30,8 +29,14 @@ package = "https://pypi.org/project/CacheToolsUtils/"
30
29
  py-modules = [ "CacheToolsUtils" ]
31
30
 
32
31
  [project.optional-dependencies]
33
- dev = [ "mypy", "pyright", "black", "flake8", "ruff", "pytest", "coverage", "pymarkdownlnt", "types-redis", "types-cachetools" ]
34
- doc = [ "sphinx", "sphinx_rtd_theme", "sphinx-autoapi", "sphinx-lint", "myst_parser" ]
32
+ dev = [
33
+ "mypy", "pyright",
34
+ # "types-redis", "types-cachetools",
35
+ "black", "flake8", "ruff",
36
+ "pytest", "coverage",
37
+ "pymarkdownlnt",
38
+ ]
39
+ doc = [ "mkdocs", "mkdocs-material", "mkdocstrings[python]" ]
35
40
  crypt = [ "pycryptodome" ]
36
41
  tests = [ "pymemcache", "redis" ]
37
42
  pub = [ "build", "wheel", "twine" ]
File without changes
File without changes