CacheToolsUtils 8.6__py3-none-any.whl → 9.1__py3-none-any.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.
- {CacheToolsUtils-8.6.dist-info → CacheToolsUtils-9.1.dist-info}/METADATA +28 -26
- CacheToolsUtils-9.1.dist-info/RECORD +6 -0
- {CacheToolsUtils-8.6.dist-info → CacheToolsUtils-9.1.dist-info}/WHEEL +1 -1
- CacheToolsUtils.py +78 -7
- CacheToolsUtils-8.6.dist-info/RECORD +0 -6
- {CacheToolsUtils-8.6.dist-info → CacheToolsUtils-9.1.dist-info}/LICENSE +0 -0
- {CacheToolsUtils-8.6.dist-info → CacheToolsUtils-9.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: CacheToolsUtils
|
|
3
|
-
Version:
|
|
3
|
+
Version: 9.1
|
|
4
4
|
Summary: Cachetools Utilities
|
|
5
5
|
Author-email: Fabien Coelho <cachetools.utils@coelho.net>
|
|
6
6
|
License: CC0
|
|
@@ -18,29 +18,29 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: cachetools
|
|
20
20
|
Provides-Extra: dev
|
|
21
|
-
Requires-Dist: mypy
|
|
22
|
-
Requires-Dist: pyright
|
|
23
|
-
Requires-Dist: black
|
|
24
|
-
Requires-Dist: flake8
|
|
25
|
-
Requires-Dist: ruff
|
|
26
|
-
Requires-Dist: pytest
|
|
27
|
-
Requires-Dist: coverage
|
|
28
|
-
Requires-Dist: pymarkdownlnt
|
|
29
|
-
Requires-Dist: types-redis
|
|
30
|
-
Requires-Dist: types-cachetools
|
|
21
|
+
Requires-Dist: mypy; extra == "dev"
|
|
22
|
+
Requires-Dist: pyright; extra == "dev"
|
|
23
|
+
Requires-Dist: black; extra == "dev"
|
|
24
|
+
Requires-Dist: flake8; extra == "dev"
|
|
25
|
+
Requires-Dist: ruff; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest; extra == "dev"
|
|
27
|
+
Requires-Dist: coverage; extra == "dev"
|
|
28
|
+
Requires-Dist: pymarkdownlnt; extra == "dev"
|
|
29
|
+
Requires-Dist: types-redis; extra == "dev"
|
|
30
|
+
Requires-Dist: types-cachetools; extra == "dev"
|
|
31
31
|
Provides-Extra: doc
|
|
32
|
-
Requires-Dist: sphinx
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist: sphinx-autoapi
|
|
35
|
-
Requires-Dist: sphinx-lint
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Provides-Extra: pub
|
|
38
|
-
Requires-Dist: build ; extra == 'pub'
|
|
39
|
-
Requires-Dist: wheel ; extra == 'pub'
|
|
40
|
-
Requires-Dist: twine ; extra == 'pub'
|
|
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"
|
|
41
37
|
Provides-Extra: tests
|
|
42
|
-
Requires-Dist: pymemcache
|
|
43
|
-
Requires-Dist: redis
|
|
38
|
+
Requires-Dist: pymemcache; extra == "tests"
|
|
39
|
+
Requires-Dist: redis; extra == "tests"
|
|
40
|
+
Provides-Extra: pub
|
|
41
|
+
Requires-Dist: build; extra == "pub"
|
|
42
|
+
Requires-Dist: wheel; extra == "pub"
|
|
43
|
+
Requires-Dist: twine; extra == "pub"
|
|
44
44
|
|
|
45
45
|
# CacheToolsUtils
|
|
46
46
|
|
|
@@ -51,7 +51,7 @@ Classes to add key prefix and stats to
|
|
|
51
51
|
and other cache-related utils.
|
|
52
52
|
|
|
53
53
|

|
|
54
|
-

|
|
55
55
|

|
|
56
56
|

|
|
57
57
|

|
|
@@ -75,12 +75,13 @@ with `cachetools`.
|
|
|
75
75
|
class for Memcached.
|
|
76
76
|
- `DictCache` a very simple `dict` cache.
|
|
77
77
|
|
|
78
|
-
###
|
|
78
|
+
### Wrappers to extend cache capabilities
|
|
79
79
|
|
|
80
80
|
- `PrefixedCache`, `PrefixedMemCached` and `PrefixedRedisCache` add a prefix to
|
|
81
81
|
distinguish sources on a shared cache.
|
|
82
|
-
- `StatsCache`, `
|
|
83
|
-
to report the cache hit rate
|
|
82
|
+
- `StatsCache`, `MemCached` and `RedisCache` add a `hits` method
|
|
83
|
+
to report the cache hit rate, `stats` to report statistics and
|
|
84
|
+
`reset` to reset statistics.
|
|
84
85
|
- `LockedCache` use a (thread) lock to control cache accesses.
|
|
85
86
|
- `TwoLevelCache` allows to combine two caches.
|
|
86
87
|
- `DebugCache` to trace cache calls using `logging`.
|
|
@@ -90,6 +91,7 @@ with `cachetools`.
|
|
|
90
91
|
- `cached` decorator: a cachetools replacement which allows to test if a
|
|
91
92
|
function result is in cache, and to delete such an entry.
|
|
92
93
|
- `cacheFunctions` and `cacheMethods`: add caching to functions or methods.
|
|
94
|
+
- `json_key` and `hash_json_key`: JSON-based cache key serialization functions
|
|
93
95
|
|
|
94
96
|
## License
|
|
95
97
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
CacheToolsUtils.py,sha256=zLX8cK3Siev3cCx4yOYSEW4hzrfZ9p3ho86_TfXppSY,20248
|
|
2
|
+
CacheToolsUtils-9.1.dist-info/LICENSE,sha256=y_yFW1bmBAUFCF-ZDyDiq10Ykns_19If5pFnZUzYgeU,33
|
|
3
|
+
CacheToolsUtils-9.1.dist-info/METADATA,sha256=60_gOx6frAF770fHQb0RPmS9bBAX5fkKRHuZbb1e6iY,4483
|
|
4
|
+
CacheToolsUtils-9.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
5
|
+
CacheToolsUtils-9.1.dist-info/top_level.txt,sha256=qCyE4jGot5mOmvIQrZueCnPtbYtRe2hqke4sGojm3zI,16
|
|
6
|
+
CacheToolsUtils-9.1.dist-info/RECORD,,
|
CacheToolsUtils.py
CHANGED
|
@@ -25,7 +25,8 @@ _NO_DEFAULT = object()
|
|
|
25
25
|
class _MutMapMix:
|
|
26
26
|
"""Convenient MutableMapping Mixin, forward to _cache."""
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
# FIXME coverage reports this as missing with Python 3.14
|
|
29
|
+
_cache: MutableMapping # pragma: no cover
|
|
29
30
|
|
|
30
31
|
def __contains__(self, key):
|
|
31
32
|
return self._cache.__contains__(key)
|
|
@@ -69,9 +70,12 @@ class _KeyMutMapMix(_MutMapMix):
|
|
|
69
70
|
class _StatsMix:
|
|
70
71
|
"""Convenient Mixin to forward stats methods to _cache."""
|
|
71
72
|
|
|
72
|
-
def hits(self):
|
|
73
|
+
def hits(self) -> float|None:
|
|
73
74
|
return self._cache.hits() # type: ignore
|
|
74
75
|
|
|
76
|
+
def stats(self) -> dict[str, Any]:
|
|
77
|
+
return self._cache.stats() # type: ignore
|
|
78
|
+
|
|
75
79
|
def reset(self):
|
|
76
80
|
return self._cache.reset() # type: ignore
|
|
77
81
|
|
|
@@ -102,7 +106,7 @@ class _RedisMix: # pragma: no cover
|
|
|
102
106
|
# CACHETOOLS EXTENSIONS
|
|
103
107
|
#
|
|
104
108
|
|
|
105
|
-
class DebugCache(MutableMapping):
|
|
109
|
+
class DebugCache(_StatsMix, MutableMapping):
|
|
106
110
|
"""Debug class.
|
|
107
111
|
|
|
108
112
|
:param cache: actual cache
|
|
@@ -241,14 +245,25 @@ class StatsCache(_MutMapMix, MutableMapping):
|
|
|
241
245
|
self._cache = cache
|
|
242
246
|
self.reset()
|
|
243
247
|
|
|
244
|
-
def hits(self):
|
|
248
|
+
def hits(self) -> float:
|
|
245
249
|
"""Return the cache hit ratio."""
|
|
246
|
-
return float(self._hits) /
|
|
250
|
+
return float(self._hits) / max(self._reads, 1)
|
|
247
251
|
|
|
248
252
|
def reset(self):
|
|
249
253
|
"""Reset internal stats data."""
|
|
250
254
|
self._reads, self._writes, self._dels, self._hits = 0, 0, 0, 0
|
|
251
255
|
|
|
256
|
+
def stats(self) -> dict[str, Any]:
|
|
257
|
+
"""Return available stats data as dict."""
|
|
258
|
+
return {
|
|
259
|
+
"type": 1,
|
|
260
|
+
"reads": self._reads,
|
|
261
|
+
"writes": self._writes,
|
|
262
|
+
"dels": self._dels,
|
|
263
|
+
"hits": self.hits(),
|
|
264
|
+
"size": self._cache.__len__()
|
|
265
|
+
}
|
|
266
|
+
|
|
252
267
|
def __getitem__(self, key):
|
|
253
268
|
self._reads += 1
|
|
254
269
|
res = self._cache.__getitem__(key)
|
|
@@ -324,6 +339,27 @@ class TwoLevelCache(_MutMapMix, MutableMapping):
|
|
|
324
339
|
# NOTE not passed on cache2…
|
|
325
340
|
return self._cache.clear()
|
|
326
341
|
|
|
342
|
+
def stats(self) -> dict[str, Any]:
|
|
343
|
+
data = {"type": 2}
|
|
344
|
+
try:
|
|
345
|
+
data["cache1"] = self._cache.stats() # type: ignore
|
|
346
|
+
except Exception:
|
|
347
|
+
data["cache1"] = {} # type: ignore
|
|
348
|
+
try:
|
|
349
|
+
data["cache2"] = self._cache2.stats() # type: ignore
|
|
350
|
+
except Exception:
|
|
351
|
+
data["cache2"] = {} # type: ignore
|
|
352
|
+
return data
|
|
353
|
+
|
|
354
|
+
def hits(self) -> float|None:
|
|
355
|
+
data = self.stats()
|
|
356
|
+
c1, c2 = data["cache1"], data["cache2"]
|
|
357
|
+
if (c1 and "type" in c1 and c1["type"] == 1 and
|
|
358
|
+
c2 and "type" in c2 and c2["type"] == 1):
|
|
359
|
+
return float(c1["hits"] + c2["hits"]) / max(c1["reads"] + c2["reads"], 1)
|
|
360
|
+
# else
|
|
361
|
+
return None
|
|
362
|
+
|
|
327
363
|
def reset(self): # pragma: no cover
|
|
328
364
|
self._cache.reset() # type: ignore
|
|
329
365
|
self._cache2.reset() # type: ignore
|
|
@@ -415,6 +451,38 @@ def cacheFunctions(
|
|
|
415
451
|
globs[fun] = cached(cache=gen(cache, prefix))(f)
|
|
416
452
|
|
|
417
453
|
|
|
454
|
+
# JSON-based key function
|
|
455
|
+
# NOTE obviously this only works if parameters are json-serializable…
|
|
456
|
+
def json_key(*args, **kwargs):
|
|
457
|
+
if kwargs:
|
|
458
|
+
return json.dumps({"*": args, "**": kwargs}, sort_keys=True)
|
|
459
|
+
else: # array
|
|
460
|
+
return json.dumps(args)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
# Hmmm…
|
|
464
|
+
class _HashJsonKey:
|
|
465
|
+
"""A cache key with a persistant hash value."""
|
|
466
|
+
|
|
467
|
+
def __init__(self, *args, **kwargs):
|
|
468
|
+
self._hashed = None
|
|
469
|
+
self._args = args
|
|
470
|
+
self._kwargs = kwargs
|
|
471
|
+
self._key = json_key(*args, **kwargs)
|
|
472
|
+
|
|
473
|
+
def __hash__(self):
|
|
474
|
+
if self._hashed is None:
|
|
475
|
+
self._hashed = self._key.__hash__()
|
|
476
|
+
return self._hashed
|
|
477
|
+
|
|
478
|
+
def __str__(self):
|
|
479
|
+
return self._key
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def hash_json_key(*args, **kwargs):
|
|
483
|
+
return _HashJsonKey(*args, **kwargs)
|
|
484
|
+
|
|
485
|
+
|
|
418
486
|
#
|
|
419
487
|
# MEMCACHED
|
|
420
488
|
#
|
|
@@ -586,6 +654,9 @@ class RedisCache(MutableMapping):
|
|
|
586
654
|
"""Return redis informations."""
|
|
587
655
|
return self._cache.info(*args, **kwargs)
|
|
588
656
|
|
|
657
|
+
def stats(self) -> dict[str, Any]:
|
|
658
|
+
return self.info(section="stats")
|
|
659
|
+
|
|
589
660
|
def dbsize(self, *args, **kwargs):
|
|
590
661
|
"""Return redis database size."""
|
|
591
662
|
return self._cache.dbsize(*args, **kwargs)
|
|
@@ -606,9 +677,9 @@ class RedisCache(MutableMapping):
|
|
|
606
677
|
del self[index]
|
|
607
678
|
|
|
608
679
|
# stats
|
|
609
|
-
def hits(self):
|
|
680
|
+
def hits(self) -> float:
|
|
610
681
|
"""Return cache hits."""
|
|
611
|
-
stats = self.
|
|
682
|
+
stats = self.stats()
|
|
612
683
|
return float(stats["keyspace_hits"]) / (
|
|
613
684
|
stats["keyspace_hits"] + stats["keyspace_misses"]
|
|
614
685
|
)
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
CacheToolsUtils.py,sha256=kqZNczVcgP_F8oioXBDCqEA5Ux9FovbO_VhlJ67pjiE,18135
|
|
2
|
-
CacheToolsUtils-8.6.dist-info/LICENSE,sha256=y_yFW1bmBAUFCF-ZDyDiq10Ykns_19If5pFnZUzYgeU,33
|
|
3
|
-
CacheToolsUtils-8.6.dist-info/METADATA,sha256=8L90Zezt3-y8LPTq4zHB8TIbjKvV0urezYd5LroZAJ4,4378
|
|
4
|
-
CacheToolsUtils-8.6.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
5
|
-
CacheToolsUtils-8.6.dist-info/top_level.txt,sha256=qCyE4jGot5mOmvIQrZueCnPtbYtRe2hqke4sGojm3zI,16
|
|
6
|
-
CacheToolsUtils-8.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|