Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cachetools/_decorator.py: 52%

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

164 statements  

1"""Mixin helpers. 

2 

3At least for now, the implementation prefers clarity and performance 

4over ease of maintenance, thus providing separate wrappers for all 

5valid combinations of parameters lock, condition and info. 

6 

7""" 

8 

9# pyright: reportAttributeAccessIssue=false 

10 

11__all__ = () 

12 

13 

14class _BasicMixin: 

15 def __init__(self, func): 

16 self.__func = func 

17 

18 def __call__(self, /, *args, **kwargs): 

19 cache = self.cache 

20 key = self.cache_key(*args, **kwargs) 

21 

22 try: 

23 return cache[key] 

24 except KeyError: 

25 pass # key not found 

26 val = self.__func(*args, **kwargs) 

27 try: 

28 cache[key] = val 

29 except ValueError: 

30 pass # value too large 

31 return val 

32 

33 def cache_clear(self): 

34 self.cache.clear() 

35 

36 

37class _LockedMixin: 

38 def __init__(self, func): 

39 self.__func = func 

40 

41 def __call__(self, /, *args, **kwargs): 

42 cache = self.cache 

43 key = self.cache_key(*args, **kwargs) 

44 lock = self.cache_lock 

45 

46 with lock: 

47 try: 

48 return cache[key] 

49 except KeyError: 

50 pass # key not found 

51 val = self.__func(*args, **kwargs) 

52 with lock: 

53 try: 

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

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

56 # self.__func(), prefer the cached value. 

57 return cache.setdefault(key, val) 

58 except ValueError: 

59 return val # value too large 

60 

61 def cache_clear(self): 

62 with self.cache_lock: 

63 self.cache.clear() 

64 

65 

66class _ConditionMixin: 

67 def __init__(self, func): 

68 self.__func = func 

69 self.__pending = set() 

70 

71 def __call__(self, /, *args, **kwargs): 

72 cache = self.cache 

73 key = self.cache_key(*args, **kwargs) 

74 lock = self.cache_lock 

75 cond = self.cache_condition 

76 

77 with lock: 

78 # apparently significantly faster than cond.wait_for(...) 

79 while key in self.__pending: 

80 cond.wait() 

81 try: 

82 return cache[key] 

83 except KeyError: 

84 self.__pending.add(key) 

85 try: 

86 val = self.__func(*args, **kwargs) 

87 with lock: 

88 try: 

89 cache[key] = val 

90 except ValueError: 

91 pass # value too large 

92 return val 

93 finally: 

94 with lock: 

95 self.__pending.remove(key) 

96 cond.notify_all() 

97 

98 def cache_clear(self): 

99 with self.cache_lock: 

100 self.cache.clear() 

101 

102 

103class _InfoMixin: 

104 def __init__(self, func, info): 

105 self.__func = func 

106 self.__info = info 

107 self.__hits = self.__misses = 0 

108 

109 def __call__(self, /, *args, **kwargs): 

110 cache = self.cache 

111 key = self.cache_key(*args, **kwargs) 

112 

113 try: 

114 result = cache[key] 

115 self.__hits += 1 

116 return result 

117 except KeyError: 

118 self.__misses += 1 

119 val = self.__func(*args, **kwargs) 

120 try: 

121 cache[key] = val 

122 except ValueError: 

123 pass # value too large 

124 return val 

125 

126 def cache_clear(self): 

127 self.cache.clear() 

128 self.__hits = self.__misses = 0 

129 

130 def cache_info(self): 

131 return self.__info(self.cache, self.__hits, self.__misses) 

132 

133 

134class _LockedInfoMixin: 

135 def __init__(self, func, info): 

136 self.__func = func 

137 self.__info = info 

138 self.__hits = self.__misses = 0 

139 

140 def __call__(self, /, *args, **kwargs): 

141 cache = self.cache 

142 key = self.cache_key(*args, **kwargs) 

143 lock = self.cache_lock 

144 

145 with lock: 

146 try: 

147 result = cache[key] 

148 self.__hits += 1 

149 return result 

150 except KeyError: 

151 self.__misses += 1 

152 val = self.__func(*args, **kwargs) 

153 with lock: 

154 try: 

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

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

157 # self.__func(), prefer the cached value. 

158 return cache.setdefault(key, val) 

159 except ValueError: 

160 return val # value too large 

161 

162 def cache_clear(self): 

163 with self.cache_lock: 

164 self.cache.clear() 

165 self.__hits = self.__misses = 0 

166 

167 def cache_info(self): 

168 with self.cache_lock: 

169 return self.__info(self.cache, self.__hits, self.__misses) 

170 

171 

172class _ConditionInfoMixin: 

173 def __init__(self, func, info): 

174 self.__func = func 

175 self.__info = info 

176 self.__hits = self.__misses = 0 

177 self.__pending = set() 

178 

179 def __call__(self, /, *args, **kwargs): 

180 cache = self.cache 

181 key = self.cache_key(*args, **kwargs) 

182 lock = self.cache_lock 

183 cond = self.cache_condition 

184 

185 with lock: 

186 # apparently significantly faster than cond.wait_for(...) 

187 while key in self.__pending: 

188 cond.wait() 

189 try: 

190 result = cache[key] 

191 self.__hits += 1 

192 return result 

193 except KeyError: 

194 self.__pending.add(key) 

195 self.__misses += 1 

196 try: 

197 val = self.__func(*args, **kwargs) 

198 with lock: 

199 try: 

200 cache[key] = val 

201 except ValueError: 

202 pass # value too large 

203 return val 

204 finally: 

205 with lock: 

206 self.__pending.remove(key) 

207 cond.notify_all() 

208 

209 def cache_clear(self): 

210 with self.cache_lock: 

211 self.cache.clear() 

212 self.__hits = self.__misses = 0 

213 

214 def cache_info(self): 

215 with self.cache_lock: 

216 return self.__info(self.cache, self.__hits, self.__misses)