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

191 statements  

1"""Function decorator helpers.""" 

2 

3__all__ = () 

4 

5import functools 

6 

7# At least for now, the implementation prefers clarity and performance 

8# over ease of maintenance, thus providing separate wrappers for 

9# all valid combinations of decorator parameters lock, condition and 

10# info. 

11 

12 

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

14 hits = misses = 0 

15 pending = set() 

16 

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

18 nonlocal hits, misses 

19 k = key(*args, **kwargs) 

20 with lock: 

21 cond.wait_for(lambda: k not in pending) 

22 try: 

23 result = cache[k] 

24 hits += 1 

25 return result 

26 except KeyError: 

27 pending.add(k) 

28 misses += 1 

29 try: 

30 v = func(*args, **kwargs) 

31 with lock: 

32 try: 

33 cache[k] = v 

34 except ValueError: 

35 pass # value too large 

36 return v 

37 finally: 

38 with lock: 

39 pending.remove(k) 

40 cond.notify_all() 

41 

42 def cache_clear(): 

43 nonlocal hits, misses 

44 with lock: 

45 cache.clear() 

46 hits = misses = 0 

47 

48 def cache_info(): 

49 with lock: 

50 return info(hits, misses) 

51 

52 wrapper.cache_clear = cache_clear 

53 wrapper.cache_info = cache_info 

54 return wrapper 

55 

56 

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

58 hits = misses = 0 

59 

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

61 nonlocal hits, misses 

62 k = key(*args, **kwargs) 

63 with lock: 

64 try: 

65 result = cache[k] 

66 hits += 1 

67 return result 

68 except KeyError: 

69 misses += 1 

70 v = func(*args, **kwargs) 

71 with lock: 

72 try: 

73 # In case of a race condition, i.e. if another thread 

74 # stored a value for this key while we were calling 

75 # func(), prefer the cached value. 

76 return cache.setdefault(k, v) 

77 except ValueError: 

78 return v # value too large 

79 

80 def cache_clear(): 

81 nonlocal hits, misses 

82 with lock: 

83 cache.clear() 

84 hits = misses = 0 

85 

86 def cache_info(): 

87 with lock: 

88 return info(hits, misses) 

89 

90 wrapper.cache_clear = cache_clear 

91 wrapper.cache_info = cache_info 

92 return wrapper 

93 

94 

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

96 hits = misses = 0 

97 

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

99 nonlocal hits, misses 

100 k = key(*args, **kwargs) 

101 try: 

102 result = cache[k] 

103 hits += 1 

104 return result 

105 except KeyError: 

106 misses += 1 

107 v = func(*args, **kwargs) 

108 try: 

109 cache[k] = v 

110 except ValueError: 

111 pass # value too large 

112 return v 

113 

114 def cache_clear(): 

115 nonlocal hits, misses 

116 cache.clear() 

117 hits = misses = 0 

118 

119 def cache_info(): 

120 return info(hits, misses) 

121 

122 wrapper.cache_clear = cache_clear 

123 wrapper.cache_info = cache_info 

124 return wrapper 

125 

126 

127def _uncached_info(func, info): 

128 misses = 0 

129 

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

131 nonlocal misses 

132 misses += 1 

133 return func(*args, **kwargs) 

134 

135 def cache_clear(): 

136 nonlocal misses 

137 misses = 0 

138 

139 wrapper.cache_clear = cache_clear 

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

141 return wrapper 

142 

143 

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

145 pending = set() 

146 

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

148 k = key(*args, **kwargs) 

149 with lock: 

150 cond.wait_for(lambda: k not in pending) 

151 try: 

152 result = cache[k] 

153 return result 

154 except KeyError: 

155 pending.add(k) 

156 try: 

157 v = func(*args, **kwargs) 

158 with lock: 

159 try: 

160 cache[k] = v 

161 except ValueError: 

162 pass # value too large 

163 return v 

164 finally: 

165 with lock: 

166 pending.remove(k) 

167 cond.notify_all() 

168 

169 def cache_clear(): 

170 with lock: 

171 cache.clear() 

172 

173 wrapper.cache_clear = cache_clear 

174 return wrapper 

175 

176 

177def _locked(func, cache, key, lock): 

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

179 k = key(*args, **kwargs) 

180 with lock: 

181 try: 

182 return cache[k] 

183 except KeyError: 

184 pass # key not found 

185 v = func(*args, **kwargs) 

186 with lock: 

187 try: 

188 # In case of a race condition, i.e. if another thread 

189 # stored a value for this key while we were calling 

190 # func(), prefer the cached value. 

191 return cache.setdefault(k, v) 

192 except ValueError: 

193 return v # value too large 

194 

195 def cache_clear(): 

196 with lock: 

197 cache.clear() 

198 

199 wrapper.cache_clear = cache_clear 

200 return wrapper 

201 

202 

203def _unlocked(func, cache, key): 

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

205 k = key(*args, **kwargs) 

206 try: 

207 return cache[k] 

208 except KeyError: 

209 pass # key not found 

210 v = func(*args, **kwargs) 

211 try: 

212 cache[k] = v 

213 except ValueError: 

214 pass # value too large 

215 return v 

216 

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

218 return wrapper 

219 

220 

221def _uncached(func): 

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

223 return func(*args, **kwargs) 

224 

225 wrapper.cache_clear = lambda: None 

226 return wrapper 

227 

228 

229def _wrapper(func, cache, key, lock=None, cond=None, info=None): 

230 if info is not None: 

231 if cache is None: 

232 wrapper = _uncached_info(func, info) 

233 elif cond is not None and lock is not None: 

234 wrapper = _condition_info(func, cache, key, lock, cond, info) 

235 elif cond is not None: 

236 wrapper = _condition_info(func, cache, key, cond, cond, info) 

237 elif lock is not None: 

238 wrapper = _locked_info(func, cache, key, lock, info) 

239 else: 

240 wrapper = _unlocked_info(func, cache, key, info) 

241 else: 

242 if cache is None: 

243 wrapper = _uncached(func) 

244 elif cond is not None and lock is not None: 

245 wrapper = _condition(func, cache, key, lock, cond) 

246 elif cond is not None: 

247 wrapper = _condition(func, cache, key, cond, cond) 

248 elif lock is not None: 

249 wrapper = _locked(func, cache, key, lock) 

250 else: 

251 wrapper = _unlocked(func, cache, key) 

252 wrapper.cache_info = None 

253 

254 wrapper.cache = cache 

255 wrapper.cache_key = key 

256 wrapper.cache_lock = lock if lock is not None else cond 

257 wrapper.cache_condition = cond 

258 

259 return functools.update_wrapper(wrapper, func)