cache.py
3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import hashlib
import pickle
import time
import base64
from threading import currentThread, RLock
from django.core.cache import cache
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.core.cache.backends.locmem import LocMemCache
from django.utils import translation
from floraconcierge.errors import MiddlewareError
_request_cache = {}
_installed_middleware = False
def get_request_cache():
try:
return _request_cache[currentThread()]
except KeyError:
raise MiddlewareError('floraconcierge.middleware.RequestCacheMiddleware not loaded')
def new_request_cache(cachecls):
_request_cache[currentThread()] = cache = cachecls()
return cache
def cached(func=None, timeout=DEFAULT_TIMEOUT, *args, **kwargs):
def _make_key(func, args, kwargs):
lang = translation.get_language()
if args and hasattr(args[0], 'get') and hasattr(args[0], 'add'):
data = [lang, func.__name__, args[0].__class__.__name__, base64.b64encode(
pickle.dumps((args[1:], kwargs))
).decode()]
else:
data = [lang, func.__name__, base64.b64encode(
pickle.dumps((args, kwargs))
).decode()]
return hashlib.md5("%".join(data).encode()).hexdigest()
def inner(func):
def wrapped(*args, **kwargs):
key = _make_key(func, args, kwargs)
if args and hasattr(args[0], 'get') and hasattr(args[0], 'add'):
obj = args[0]
if key in obj:
return obj.get(key)
else:
result = func(*args, **kwargs)
obj.add(key, result, timeout=timeout)
return result
else:
result = cache.get(key)
if not result:
result = func(*args, **kwargs)
cache.set(key, result, timeout=timeout)
return result
return wrapped
if func is None:
# @cached(...)
return inner
elif callable(func):
# @cached
return inner(func)
else:
raise ValueError('Bad @cached decorator usage')
class RequestCache(LocMemCache):
def __init__(self):
name = 'requestcache@%i' % hash(currentThread())
super(RequestCache, self).__init__(name, {})
self._lock = RLock()
def add(self, key, value, timeout=None, version=None):
key = self.make_key(key, version=version)
self.validate_key(key)
with self._lock:
exp = self._expire_info.get(key)
if exp is None or exp <= time.time():
self._set(key, value, timeout)
return True
return False
def get(self, key, default=None, version=None):
key = self.make_key(key, version=version)
self.validate_key(key)
self._lock.acquire(blocking=False)
try:
exp = self._expire_info.get(key)
if exp is None:
return default
elif exp > time.time():
return self._cache[key]
finally:
self._lock.release()
with self._lock:
try:
del self._cache[key]
del self._expire_info[key]
except KeyError:
pass
return default
def set(self, key, value, timeout=None, version=None):
key = self.make_key(key, version=version)
self.validate_key(key)
with self._lock:
self._set(key, value, timeout)
def incr(self, key, delta=1, version=None):
value = self.get(key, version=version)
if value is None:
raise ValueError("Key '%s' not found" % key)
new_value = value + delta
key = self.make_key(key, version=version)
with self._lock:
self._cache[key] = new_value
return new_value