Skip to content

Commit 584e9c4

Browse files
author
Fabien Coelho
committed
add experimental json key stuff
1 parent 6ce0c72 commit 584e9c4

File tree

4 files changed

+77
-11
lines changed

4 files changed

+77
-11
lines changed

CacheToolsUtils.py

+32
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,38 @@ def cacheFunctions(
451451
globs[fun] = cached(cache=gen(cache, prefix))(f)
452452

453453

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+
454486
#
455487
# MEMCACHED
456488
#

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Classes to add key prefix and stats to
77
and other cache-related utils.
88

99
![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-19%20✓-success)
10+
![Tests](https://img.shields.io/badge/tests-20%20✓-success)
1111
![Coverage](https://img.shields.io/badge/coverage-100%25-success)
1212
![Issues](https://img.shields.io/github/issues/zx80/cachetools-utils?style=flat)
1313
![Python](https://img.shields.io/badge/python-3-informational)

docs/VERSIONS.md

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ Install [package](https://pypi.org/project/CacheToolsUtils/) from
1919
Maybe the existing client can do that with appropriate options?
2020
- `cached`: add `contains` and `delete` parameters to change names?
2121

22+
## ? on ?
23+
24+
Improve documentation.
25+
Add experimental `json_key` and `hash_json_key` cache key functions.
26+
2227
## 9.0 on 2024-10-27
2328

2429
Add `stats` method to return a convenient `dict` of statistics.

test.py

+39-10
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,50 @@ def has_service(host="localhost", port=22):
2525
tcp_ip.close()
2626

2727

28-
def cached_fun(cache, cached=ct.cached):
28+
def cached_fun(cache, cached=ct.cached, key=ct.keys.hashkey):
2929
"""return a cached function with basic types."""
3030

31-
@cached(cache=cache)
31+
@cached(cache=cache, key=key)
3232
def fun(i: int, s: str|None, b: bool) -> int:
3333
return i + (10 * len(s) if s is not None else -20) + (100 if b else 0)
3434

3535
return fun
3636

37+
# reset cache contents and stats
38+
def reset_cache(cache):
39+
try:
40+
cache.clear()
41+
except: # fails on redis
42+
pass
43+
if hasattr(cache, "_cache") and hasattr(cache._cache, "reset"):
44+
cache._cache.reset()
45+
if hasattr(cache, "_cache2") and hasattr(cache._cache2, "reset"):
46+
cache._cache2.reset()
47+
48+
49+
def run_cached_keys(cache):
50+
51+
for cached in (ct.cached, ctu.cached):
52+
for keyfun in (ct.keys.hashkey, ct.keys.typedkey, ctu.hash_json_key, ctu.json_key):
53+
reset_cache(cache)
54+
fun = cached_fun(cache, cached, key=keyfun)
55+
x = 0
56+
for n in range(10):
57+
for i in range(5):
58+
for s in ["a", "bb", "ccc", "", None]:
59+
for b in [False, True]:
60+
v = fun(i, s, b)
61+
# log.debug(f"fun{(i, s, b)} = {v} {type(v)}")
62+
x += v
63+
assert x == 30000
64+
3765

3866
def run_cached(cache):
3967
"""run something on a cached function."""
4068

4169
for cached in (ct.cached, ctu.cached):
4270
# reset cache contents and stats
43-
try:
44-
cache.clear()
45-
except: # fails on redis
46-
pass
47-
if hasattr(cache._cache, "reset"):
48-
cache._cache.reset()
49-
if hasattr(cache, "_cache2") and hasattr(cache._cache2, "reset"):
50-
cache._cache2.reset()
71+
reset_cache(cache)
5172
fun = cached_fun(cache, cached)
5273
x = 0
5374
for n in range(10):
@@ -145,6 +166,14 @@ def test_stats_ct():
145166
setgetdel(cache)
146167

147168

169+
def test_caches():
170+
n = 0
171+
for Cache in (ct.LRUCache, ct.FIFOCache, ct.LFUCache, ct.RRCache):
172+
n += 1
173+
cache = Cache(maxsize=1024)
174+
run_cached_keys(cache)
175+
assert n == 4
176+
148177
@pytest.mark.skipif(
149178
not has_service(port=11211),
150179
reason="no local memcached service available for testing",

0 commit comments

Comments
 (0)