CacheToolsUtils 10.2__tar.gz → 10.4__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,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: CacheToolsUtils
3
- Version: 10.2
3
+ Version: 10.4
4
4
  Summary: Cachetools Utilities
5
5
  Author-email: Fabien Coelho <cachetools.utils@coelho.net>
6
6
  License: CC0
@@ -26,8 +26,6 @@ Requires-Dist: ruff; extra == "dev"
26
26
  Requires-Dist: pytest; extra == "dev"
27
27
  Requires-Dist: coverage; extra == "dev"
28
28
  Requires-Dist: pymarkdownlnt; extra == "dev"
29
- Requires-Dist: types-redis; extra == "dev"
30
- Requires-Dist: types-cachetools; extra == "dev"
31
29
  Provides-Extra: doc
32
30
  Requires-Dist: sphinx; extra == "doc"
33
31
  Requires-Dist: sphinx_rtd_theme; extra == "doc"
@@ -46,14 +44,12 @@ Requires-Dist: twine; extra == "pub"
46
44
 
47
45
  # CacheToolsUtils
48
46
 
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.
47
+ Stackable cache classes for sharing, encryption, statistics _and more_
48
+ on top of [cachetools](https://pypi.org/project/cachetools/),
49
+ [redis](https://redis.io/) and [memcached](https://memcached.org/).
54
50
 
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-22%20✓-success)
51
+ ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/ctu.yml/badge.svg?branch=main&style=flat)
52
+ ![Tests](https://img.shields.io/badge/tests-25%20✓-success)
57
53
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
58
54
  ![Issues](https://img.shields.io/github/issues/zx80/cachetools-utils?style=flat)
59
55
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -66,7 +62,37 @@ and other cache-related utils.
66
62
  For our purpose, a cache is a key-value store, aka a dictionary, possibly with
67
63
  some constraints on keys (type, size) and values (size, serialization).
68
64
  This module provides new caches, wrappers and other utilities suitable to use
69
- with `cachetools`.
65
+ with `cachetools`. Example:
66
+
67
+ ```python
68
+ import redis
69
+ import CacheToolsUtils as ctu
70
+ import config
71
+
72
+ redis_storage = redis.Redis(**config.REDIS_SERVER)
73
+ storage = ctu.RedisCache(redis_storage, raw=True, ttl=120)
74
+ crypto = ctu.EncryptedCache(storage, config.CACHE_KEY)
75
+ cache = ctu.ToBytesCache(crypto)
76
+
77
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
78
+ def repeat(s: str, n: int) -> str:
79
+ return s * n
80
+
81
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
82
+ def banged(s: str, n: int) -> str:
83
+ return repeat(s, n) + "!"
84
+
85
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
86
+ def question(s: str, n: int) -> str:
87
+ return repeat(s, n) + "?"
88
+
89
+ print(banged("aa", 3)) # add 2 cache entries
90
+ print(question("aa", 3)) # add 1 entry, 1 hit
91
+ print(repeat("aa", 3)) # cache hit!
92
+ print(banged("aa", 3)) # cache hit!
93
+
94
+ assert cache.hits() > 0
95
+ ```
70
96
 
71
97
  ### Cache classes
72
98
 
@@ -81,6 +107,7 @@ with `cachetools`.
81
107
 
82
108
  - `PrefixedCache`, `PrefixedMemCached` and `PrefixedRedisCache` add a prefix to
83
109
  distinguish sources on a shared cache.
110
+ - `AutoPrefixedCache` add a counter-based prefix.
84
111
  - `StatsCache`, `MemCached` and `RedisCache` add a `hits` method
85
112
  to report the cache hit rate, `stats` to report statistics and
86
113
  `reset` to reset statistics.
@@ -12,8 +12,6 @@ ruff
12
12
  pytest
13
13
  coverage
14
14
  pymarkdownlnt
15
- types-redis
16
- types-cachetools
17
15
 
18
16
  [doc]
19
17
  sphinx
@@ -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
 
@@ -226,6 +229,39 @@ class PrefixedCache(_KeyMutMapMix, _StatsMix, MutableMapping):
226
229
  return self._prefix + self._cast(key) # type: ignore
227
230
 
228
231
 
232
+ class AutoPrefixedCache(PrefixedCache):
233
+ """Cache class with an automatic counter-based string prefix.
234
+
235
+ :param cache: actual cache.
236
+ :param sep: prefix separator, default is ".".
237
+ :param method: encoding method in "b64", "b64u", "b32", "b32x", "b16",
238
+ "a85" and "b85". Default is "b64".
239
+ """
240
+
241
+ _COUNTER: int = 0
242
+
243
+ _METHODS: dict[str, Callable[[Any], bytes]] = {
244
+ "b64": base64.b64encode,
245
+ "b64u": base64.urlsafe_b64encode,
246
+ "b32": base64.b32encode,
247
+ "b32x": base64.b32hexencode,
248
+ "b16": base64.b16encode,
249
+ "a85": base64.a85encode,
250
+ "b85": base64.b85encode,
251
+ # "z85": base64.z85encode, # Python 3.13
252
+ }
253
+
254
+ def __init__(self, cache: MutableMapping, sep: str = ".", method: str = "b64"):
255
+ if method not in AutoPrefixedCache._METHODS:
256
+ raise Exception(f"invalid conversion method: {method}")
257
+ encoder = AutoPrefixedCache._METHODS[method]
258
+ length = max(1, (AutoPrefixedCache._COUNTER.bit_length() + 7) // 8)
259
+ # NOTE byteorder default added to 3.11
260
+ prefix = encoder(AutoPrefixedCache._COUNTER.to_bytes(length, byteorder="big")).decode("ASCII")
261
+ AutoPrefixedCache._COUNTER += 1
262
+ super().__init__(cache, prefix + sep)
263
+
264
+
229
265
  class StatsCache(_MutMapMix, MutableMapping):
230
266
  """Cache class to keep stats.
231
267
 
@@ -253,19 +289,25 @@ class StatsCache(_MutMapMix, MutableMapping):
253
289
 
254
290
  def reset(self):
255
291
  """Reset internal stats data."""
256
- self._reads, self._writes, self._dels, self._hits = 0, 0, 0, 0
292
+ self._tests, self._reads, self._writes, self._dels, self._hits = 0, 0, 0, 0, 0
257
293
 
258
294
  def stats(self) -> dict[str, Any]:
259
295
  """Return available stats data as dict."""
260
296
  return {
261
297
  "type": 1,
298
+ "tests": self._tests,
262
299
  "reads": self._reads,
263
300
  "writes": self._writes,
264
301
  "dels": self._dels,
265
- "hits": self.hits(),
302
+ "hits": self._hits,
303
+ "hit_rate": self.hits(),
266
304
  "size": self._cache.__len__()
267
305
  }
268
306
 
307
+ def __contains__(self, key):
308
+ self._tests += 1
309
+ return self._cache.__contains__(key)
310
+
269
311
  def __getitem__(self, key):
270
312
  self._reads += 1
271
313
  res = self._cache.__getitem__(key)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: CacheToolsUtils
3
- Version: 10.2
3
+ Version: 10.4
4
4
  Summary: Cachetools Utilities
5
5
  Author-email: Fabien Coelho <cachetools.utils@coelho.net>
6
6
  License: CC0
@@ -26,8 +26,6 @@ Requires-Dist: ruff; extra == "dev"
26
26
  Requires-Dist: pytest; extra == "dev"
27
27
  Requires-Dist: coverage; extra == "dev"
28
28
  Requires-Dist: pymarkdownlnt; extra == "dev"
29
- Requires-Dist: types-redis; extra == "dev"
30
- Requires-Dist: types-cachetools; extra == "dev"
31
29
  Provides-Extra: doc
32
30
  Requires-Dist: sphinx; extra == "doc"
33
31
  Requires-Dist: sphinx_rtd_theme; extra == "doc"
@@ -46,14 +44,12 @@ Requires-Dist: twine; extra == "pub"
46
44
 
47
45
  # CacheToolsUtils
48
46
 
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.
47
+ Stackable cache classes for sharing, encryption, statistics _and more_
48
+ on top of [cachetools](https://pypi.org/project/cachetools/),
49
+ [redis](https://redis.io/) and [memcached](https://memcached.org/).
54
50
 
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-22%20✓-success)
51
+ ![Status](https://github.com/zx80/cachetools-utils/actions/workflows/ctu.yml/badge.svg?branch=main&style=flat)
52
+ ![Tests](https://img.shields.io/badge/tests-25%20✓-success)
57
53
  ![Coverage](https://img.shields.io/badge/coverage-100%25-success)
58
54
  ![Issues](https://img.shields.io/github/issues/zx80/cachetools-utils?style=flat)
59
55
  ![Python](https://img.shields.io/badge/python-3-informational)
@@ -66,7 +62,37 @@ and other cache-related utils.
66
62
  For our purpose, a cache is a key-value store, aka a dictionary, possibly with
67
63
  some constraints on keys (type, size) and values (size, serialization).
68
64
  This module provides new caches, wrappers and other utilities suitable to use
69
- with `cachetools`.
65
+ with `cachetools`. Example:
66
+
67
+ ```python
68
+ import redis
69
+ import CacheToolsUtils as ctu
70
+ import config
71
+
72
+ redis_storage = redis.Redis(**config.REDIS_SERVER)
73
+ storage = ctu.RedisCache(redis_storage, raw=True, ttl=120)
74
+ crypto = ctu.EncryptedCache(storage, config.CACHE_KEY)
75
+ cache = ctu.ToBytesCache(crypto)
76
+
77
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
78
+ def repeat(s: str, n: int) -> str:
79
+ return s * n
80
+
81
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
82
+ def banged(s: str, n: int) -> str:
83
+ return repeat(s, n) + "!"
84
+
85
+ @ctu.cached(ctu.AutoPrefixedCache(cache), key=ctu.json_key)
86
+ def question(s: str, n: int) -> str:
87
+ return repeat(s, n) + "?"
88
+
89
+ print(banged("aa", 3)) # add 2 cache entries
90
+ print(question("aa", 3)) # add 1 entry, 1 hit
91
+ print(repeat("aa", 3)) # cache hit!
92
+ print(banged("aa", 3)) # cache hit!
93
+
94
+ assert cache.hits() > 0
95
+ ```
70
96
 
71
97
  ### Cache classes
72
98
 
@@ -81,6 +107,7 @@ with `cachetools`.
81
107
 
82
108
  - `PrefixedCache`, `PrefixedMemCached` and `PrefixedRedisCache` add a prefix to
83
109
  distinguish sources on a shared cache.
110
+ - `AutoPrefixedCache` add a counter-based prefix.
84
111
  - `StatsCache`, `MemCached` and `RedisCache` add a `hits` method
85
112
  to report the cache hit rate, `stats` to report statistics and
86
113
  `reset` to reset statistics.
@@ -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-22%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
 
@@ -35,6 +63,7 @@ with `cachetools`.
35
63
 
36
64
  - `PrefixedCache`, `PrefixedMemCached` and `PrefixedRedisCache` add a prefix to
37
65
  distinguish sources on a shared cache.
66
+ - `AutoPrefixedCache` add a counter-based prefix.
38
67
  - `StatsCache`, `MemCached` and `RedisCache` add a `hits` method
39
68
  to report the cache hit rate, `stats` to report statistics and
40
69
  `reset` to reset statistics.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "CacheToolsUtils"
7
- version = "10.2"
7
+ version = "10.4"
8
8
  authors = [ { name = "Fabien Coelho", email = "cachetools.utils@coelho.net" } ]
9
9
  description = "Cachetools Utilities"
10
10
  readme = "README.md"
@@ -30,7 +30,13 @@ package = "https://pypi.org/project/CacheToolsUtils/"
30
30
  py-modules = [ "CacheToolsUtils" ]
31
31
 
32
32
  [project.optional-dependencies]
33
- dev = [ "mypy", "pyright", "black", "flake8", "ruff", "pytest", "coverage", "pymarkdownlnt", "types-redis", "types-cachetools" ]
33
+ dev = [
34
+ "mypy", "pyright",
35
+ # "types-redis", "types-cachetools",
36
+ "black", "flake8", "ruff",
37
+ "pytest", "coverage",
38
+ "pymarkdownlnt",
39
+ ]
34
40
  doc = [ "sphinx", "sphinx_rtd_theme", "sphinx-autoapi", "sphinx-lint", "myst_parser" ]
35
41
  crypt = [ "pycryptodome" ]
36
42
  tests = [ "pymemcache", "redis" ]
File without changes
File without changes