Skip to content

Commit 6217d9c

Browse files
authored
feat: adds extra cache key components support (#62)
* feat: adds extra cache key components support * fix: docstring arg type --------- Co-authored-by: Antoni Martyniuk <a.martyniuk@convertiser.com>
1 parent d788fa4 commit 6217d9c

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

src/cache_memoize/__init__.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from functools import wraps
22
import itertools
3+
import json
4+
import inspect
35

46
import hashlib
57
from urllib.parse import quote
68

9+
from django.db import models
710
from django.core.cache import caches, DEFAULT_CACHE_ALIAS
811

912
from django.utils.encoding import force_bytes
@@ -14,6 +17,7 @@
1417
def cache_memoize(
1518
timeout,
1619
prefix=None,
20+
extra=None,
1721
args_rewrite=None,
1822
hit_callable=None,
1923
miss_callable=None,
@@ -27,6 +31,8 @@ def cache_memoize(
2731
2832
:arg int timeout: Number of seconds to store the result if not None
2933
:arg string prefix: If None becomes the function name.
34+
:arg extra: Optional callable or serializable structure of key
35+
components cache should vary on.
3036
:arg function args_rewrite: Callable that rewrites the args first useful
3137
if your function needs nontrivial types but you know a simple way to
3238
re-represent them for the sake of the cache key.
@@ -88,6 +94,17 @@ def callmeonce(arg1):
8894
callmeonce('peter') # nothing printed
8995
callmeonce('peter', _refresh=True) # will print 'peter'
9096
97+
If your cache depends on external state you can provide `extra` values::
98+
99+
@cache_memoize(100, extra={'version': 2})
100+
def callmeonce(arg1):
101+
print(arg1)
102+
103+
An `extra` argument can be callable or any serializable structure::
104+
105+
@cache_memoize(100, extra=lambda req: req.user.is_staff)
106+
def callmeonce(arg1):
107+
print(arg1)
91108
"""
92109

93110
if args_rewrite is None:
@@ -97,6 +114,17 @@ def noop(*args):
97114

98115
args_rewrite = noop
99116

117+
def obj_key(obj):
118+
if isinstance(obj, models.Model):
119+
return "%s.%s.%s" % (obj._meta.app_label, obj._meta.model_name, obj.pk)
120+
elif hasattr(obj, "build_absolute_uri"):
121+
return obj.build_absolute_uri()
122+
elif inspect.isfunction(obj):
123+
factors = [obj.__module__, obj.__name__]
124+
return factors
125+
else:
126+
return str(obj)
127+
100128
def decorator(func):
101129
def _default_make_cache_key(*args, **kwargs):
102130
cache_key = ":".join(
@@ -109,8 +137,13 @@ def _default_make_cache_key(*args, **kwargs):
109137
)
110138
)
111139
prefix_ = prefix or ".".join((func.__module__ or "", func.__qualname__))
140+
extra_val = json.dumps(
141+
extra(*args, **kwargs) if callable(extra) else extra,
142+
sort_keys=True,
143+
default=obj_key,
144+
)
112145
return hashlib.md5(
113-
force_bytes("cache_memoize" + prefix_ + cache_key)
146+
force_bytes("cache_memoize" + prefix_ + cache_key + extra_val)
114147
).hexdigest()
115148

116149
_make_cache_key = key_generator_callable or _default_make_cache_key

tests/test_cache_memoize.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,8 @@ def test_get_cache_key():
313313
def funky(argument):
314314
pass
315315

316-
assert funky.get_cache_key(100) == "d33e7fad5d1d04da8e588a9ee348644a"
317-
assert funky.get_cache_key(100, _refresh=True) == "d33e7fad5d1d04da8e588a9ee348644a"
316+
assert funky.get_cache_key(100) == "eb96668ba0d14dc7748161fb1d000239"
317+
assert funky.get_cache_key(100, _refresh=True) == "eb96668ba0d14dc7748161fb1d000239"
318318

319319

320320
def test_cache_memoize_custom_alias():
@@ -388,6 +388,21 @@ def funky(argument):
388388
assert funky.get_cache_key("1") == "1111111111"
389389

390390

391+
def test_get_cache_key_with_extra_components():
392+
def funky(argument):
393+
pass
394+
395+
fn1 = cache_memoize(10, extra={"version": 1})(funky)
396+
fn2 = cache_memoize(10, extra={"version": 1})(funky)
397+
fn3 = cache_memoize(10, extra={"version": 2})(funky)
398+
fn4 = cache_memoize(10, extra=lambda x: x * 2)(funky)
399+
fn5 = cache_memoize(10, extra=lambda x: x * 3)(funky)
400+
401+
assert fn1.get_cache_key(1) == fn2.get_cache_key(1)
402+
assert fn2.get_cache_key(1) != fn3.get_cache_key(1)
403+
assert fn4.get_cache_key(1) != fn5.get_cache_key(1)
404+
405+
391406
def test_cache_memoize_none_value():
392407
calls_made = []
393408

0 commit comments

Comments
 (0)