Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cachetools/_cached.py: 49%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Function decorator helpers."""
3__all__ = ()
5import functools
6import warnings
8# At least for now, the implementation prefers clarity and performance
9# over ease of maintenance, thus providing separate wrappers for
10# all valid combinations of decorator parameters lock, condition and
11# info.
14def _condition_info(func, cache, key, lock, cond, info):
15 hits = misses = 0
16 pending = set()
18 def wrapper(*args, **kwargs):
19 nonlocal hits, misses
20 k = key(*args, **kwargs)
21 with lock:
22 cond.wait_for(lambda: k not in pending)
23 try:
24 result = cache[k]
25 hits += 1
26 return result
27 except KeyError:
28 pending.add(k)
29 misses += 1
30 try:
31 v = func(*args, **kwargs)
32 with lock:
33 try:
34 cache[k] = v
35 except ValueError:
36 pass # value too large
37 return v
38 finally:
39 with lock:
40 pending.remove(k)
41 cond.notify_all()
43 def cache_clear():
44 nonlocal hits, misses
45 with lock:
46 cache.clear()
47 hits = misses = 0
49 def cache_info():
50 with lock:
51 return info(hits, misses)
53 wrapper.cache_clear = cache_clear
54 wrapper.cache_info = cache_info
55 return wrapper
58def _locked_info(func, cache, key, lock, info):
59 hits = misses = 0
61 def wrapper(*args, **kwargs):
62 nonlocal hits, misses
63 k = key(*args, **kwargs)
64 with lock:
65 try:
66 result = cache[k]
67 hits += 1
68 return result
69 except KeyError:
70 misses += 1
71 v = func(*args, **kwargs)
72 with lock:
73 try:
74 # In case of a race condition, i.e. if another thread
75 # stored a value for this key while we were calling
76 # func(), prefer the cached value.
77 return cache.setdefault(k, v)
78 except ValueError:
79 return v # value too large
81 def cache_clear():
82 nonlocal hits, misses
83 with lock:
84 cache.clear()
85 hits = misses = 0
87 def cache_info():
88 with lock:
89 return info(hits, misses)
91 wrapper.cache_clear = cache_clear
92 wrapper.cache_info = cache_info
93 return wrapper
96def _unlocked_info(func, cache, key, info):
97 hits = misses = 0
99 def wrapper(*args, **kwargs):
100 nonlocal hits, misses
101 k = key(*args, **kwargs)
102 try:
103 result = cache[k]
104 hits += 1
105 return result
106 except KeyError:
107 misses += 1
108 v = func(*args, **kwargs)
109 try:
110 cache[k] = v
111 except ValueError:
112 pass # value too large
113 return v
115 def cache_clear():
116 nonlocal hits, misses
117 cache.clear()
118 hits = misses = 0
120 def cache_info():
121 return info(hits, misses)
123 wrapper.cache_clear = cache_clear
124 wrapper.cache_info = cache_info
125 return wrapper
128def _uncached_info(func, info):
129 misses = 0
131 def wrapper(*args, **kwargs):
132 nonlocal misses
133 misses += 1
134 return func(*args, **kwargs)
136 def cache_clear():
137 nonlocal misses
138 misses = 0
140 wrapper.cache_clear = cache_clear
141 wrapper.cache_info = lambda: info(0, misses)
142 return wrapper
145def _condition(func, cache, key, lock, cond):
146 pending = set()
148 def wrapper(*args, **kwargs):
149 k = key(*args, **kwargs)
150 with lock:
151 cond.wait_for(lambda: k not in pending)
152 try:
153 result = cache[k]
154 return result
155 except KeyError:
156 pending.add(k)
157 try:
158 v = func(*args, **kwargs)
159 with lock:
160 try:
161 cache[k] = v
162 except ValueError:
163 pass # value too large
164 return v
165 finally:
166 with lock:
167 pending.remove(k)
168 cond.notify_all()
170 def cache_clear():
171 with lock:
172 cache.clear()
174 wrapper.cache_clear = cache_clear
175 return wrapper
178def _locked(func, cache, key, lock):
179 def wrapper(*args, **kwargs):
180 k = key(*args, **kwargs)
181 with lock:
182 try:
183 return cache[k]
184 except KeyError:
185 pass # key not found
186 v = func(*args, **kwargs)
187 with lock:
188 try:
189 # In case of a race condition, i.e. if another thread
190 # stored a value for this key while we were calling
191 # func(), prefer the cached value.
192 return cache.setdefault(k, v)
193 except ValueError:
194 return v # value too large
196 def cache_clear():
197 with lock:
198 cache.clear()
200 wrapper.cache_clear = cache_clear
201 return wrapper
204def _unlocked(func, cache, key):
205 def wrapper(*args, **kwargs):
206 k = key(*args, **kwargs)
207 try:
208 return cache[k]
209 except KeyError:
210 pass # key not found
211 v = func(*args, **kwargs)
212 try:
213 cache[k] = v
214 except ValueError:
215 pass # value too large
216 return v
218 wrapper.cache_clear = lambda: cache.clear()
219 return wrapper
222def _uncached(func):
223 def wrapper(*args, **kwargs):
224 return func(*args, **kwargs)
226 wrapper.cache_clear = lambda: None
227 return wrapper
230def _wrapper(func, cache, key, lock=None, cond=None, info=None):
231 if cache is None:
232 warnings.warn(
233 "@cachetools.cached(cache=None) is deprecated",
234 DeprecationWarning,
235 stacklevel=3,
236 )
238 if info is not None:
239 if cache is None:
240 wrapper = _uncached_info(func, info)
241 elif cond is not None and lock is not None:
242 wrapper = _condition_info(func, cache, key, lock, cond, info)
243 elif cond is not None:
244 wrapper = _condition_info(func, cache, key, cond, cond, info)
245 elif lock is not None:
246 wrapper = _locked_info(func, cache, key, lock, info)
247 else:
248 wrapper = _unlocked_info(func, cache, key, info)
249 else:
250 if cache is None:
251 wrapper = _uncached(func)
252 elif cond is not None and lock is not None:
253 wrapper = _condition(func, cache, key, lock, cond)
254 elif cond is not None:
255 wrapper = _condition(func, cache, key, cond, cond)
256 elif lock is not None:
257 wrapper = _locked(func, cache, key, lock)
258 else:
259 wrapper = _unlocked(func, cache, key)
260 wrapper.cache_info = None
262 wrapper.cache = cache
263 wrapper.cache_key = key
264 wrapper.cache_lock = lock if lock is not None else cond
265 wrapper.cache_condition = cond
267 return functools.update_wrapper(wrapper, func)