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

194 statements  

1"""Function decorator helpers.""" 

2 

3__all__ = () 

4 

5import functools 

6import warnings 

7 

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. 

12 

13 

14def _condition_info(func, cache, key, lock, cond, info): 

15 hits = misses = 0 

16 pending = set() 

17 

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() 

42 

43 def cache_clear(): 

44 nonlocal hits, misses 

45 with lock: 

46 cache.clear() 

47 hits = misses = 0 

48 

49 def cache_info(): 

50 with lock: 

51 return info(hits, misses) 

52 

53 wrapper.cache_clear = cache_clear 

54 wrapper.cache_info = cache_info 

55 return wrapper 

56 

57 

58def _locked_info(func, cache, key, lock, info): 

59 hits = misses = 0 

60 

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 

80 

81 def cache_clear(): 

82 nonlocal hits, misses 

83 with lock: 

84 cache.clear() 

85 hits = misses = 0 

86 

87 def cache_info(): 

88 with lock: 

89 return info(hits, misses) 

90 

91 wrapper.cache_clear = cache_clear 

92 wrapper.cache_info = cache_info 

93 return wrapper 

94 

95 

96def _unlocked_info(func, cache, key, info): 

97 hits = misses = 0 

98 

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 

114 

115 def cache_clear(): 

116 nonlocal hits, misses 

117 cache.clear() 

118 hits = misses = 0 

119 

120 def cache_info(): 

121 return info(hits, misses) 

122 

123 wrapper.cache_clear = cache_clear 

124 wrapper.cache_info = cache_info 

125 return wrapper 

126 

127 

128def _uncached_info(func, info): 

129 misses = 0 

130 

131 def wrapper(*args, **kwargs): 

132 nonlocal misses 

133 misses += 1 

134 return func(*args, **kwargs) 

135 

136 def cache_clear(): 

137 nonlocal misses 

138 misses = 0 

139 

140 wrapper.cache_clear = cache_clear 

141 wrapper.cache_info = lambda: info(0, misses) 

142 return wrapper 

143 

144 

145def _condition(func, cache, key, lock, cond): 

146 pending = set() 

147 

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() 

169 

170 def cache_clear(): 

171 with lock: 

172 cache.clear() 

173 

174 wrapper.cache_clear = cache_clear 

175 return wrapper 

176 

177 

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 

195 

196 def cache_clear(): 

197 with lock: 

198 cache.clear() 

199 

200 wrapper.cache_clear = cache_clear 

201 return wrapper 

202 

203 

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 

217 

218 wrapper.cache_clear = lambda: cache.clear() 

219 return wrapper 

220 

221 

222def _uncached(func): 

223 def wrapper(*args, **kwargs): 

224 return func(*args, **kwargs) 

225 

226 wrapper.cache_clear = lambda: None 

227 return wrapper 

228 

229 

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 ) 

237 

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 

261 

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 

266 

267 return functools.update_wrapper(wrapper, func)