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.
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/CacheToolsUtils.egg-info/PKG-INFO +39 -12
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/CacheToolsUtils.egg-info/requires.txt +0 -2
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/CacheToolsUtils.py +44 -2
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/PKG-INFO +39 -12
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/README.md +38 -9
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/pyproject.toml +8 -2
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/CacheToolsUtils.egg-info/SOURCES.txt +0 -0
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/CacheToolsUtils.egg-info/dependency_links.txt +0 -0
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/CacheToolsUtils.egg-info/top_level.txt +0 -0
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/LICENSE +0 -0
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/MANIFEST.in +0 -0
- {cachetoolsutils-10.2 → cachetoolsutils-10.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: CacheToolsUtils
|
|
3
|
-
Version: 10.
|
|
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
|
-
|
|
50
|
-
[cachetools](https://pypi.org/project/cachetools/)
|
|
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
|
-

|
|
52
|
+

|
|
57
53
|

|
|
58
54
|

|
|
59
55
|

|
|
@@ -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.
|
|
@@ -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.
|
|
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
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: CacheToolsUtils
|
|
3
|
-
Version: 10.
|
|
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
|
-
|
|
50
|
-
[cachetools](https://pypi.org/project/cachetools/)
|
|
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
|
-

|
|
52
|
+

|
|
57
53
|

|
|
58
54
|

|
|
59
55
|

|
|
@@ -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
|
-
|
|
4
|
-
[cachetools](https://pypi.org/project/cachetools/)
|
|
5
|
-
[redis](https://redis.io/) and
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-

|
|
10
|
-

|
|
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
|
+

|
|
8
|
+

|
|
11
9
|

|
|
12
10
|

|
|
13
11
|

|
|
@@ -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.
|
|
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 = [
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|