Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/werkzeug/datastructures/mixins.py: 54%

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

178 statements  

1from __future__ import annotations 

2 

3import collections.abc as cabc 

4import typing as t 

5from functools import update_wrapper 

6from itertools import repeat 

7 

8from .._internal import _missing 

9 

10if t.TYPE_CHECKING: 

11 import typing_extensions as te 

12 

13K = t.TypeVar("K") 

14V = t.TypeVar("V") 

15T = t.TypeVar("T") 

16F = t.TypeVar("F", bound=cabc.Callable[..., t.Any]) 

17 

18 

19def _immutable_error(self: t.Any) -> t.NoReturn: 

20 raise TypeError(f"{type(self).__name__!r} objects are immutable") 

21 

22 

23class ImmutableListMixin: 

24 """Makes a :class:`list` immutable. 

25 

26 .. versionadded:: 0.5 

27 

28 :private: 

29 """ 

30 

31 _hash_cache: int | None = None 

32 

33 def __hash__(self) -> int: 

34 if self._hash_cache is not None: 

35 return self._hash_cache 

36 rv = self._hash_cache = hash(tuple(self)) # type: ignore[arg-type] 

37 return rv 

38 

39 def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any: 

40 return type(self), (list(self),) # type: ignore[call-overload] 

41 

42 def __delitem__(self, key: t.Any) -> t.NoReturn: 

43 _immutable_error(self) 

44 

45 def __iadd__(self, other: t.Any) -> t.NoReturn: 

46 _immutable_error(self) 

47 

48 def __imul__(self, other: t.Any) -> t.NoReturn: 

49 _immutable_error(self) 

50 

51 def __setitem__(self, key: t.Any, value: t.Any) -> t.NoReturn: 

52 _immutable_error(self) 

53 

54 def append(self, item: t.Any) -> t.NoReturn: 

55 _immutable_error(self) 

56 

57 def remove(self, item: t.Any) -> t.NoReturn: 

58 _immutable_error(self) 

59 

60 def extend(self, iterable: t.Any) -> t.NoReturn: 

61 _immutable_error(self) 

62 

63 def insert(self, pos: t.Any, value: t.Any) -> t.NoReturn: 

64 _immutable_error(self) 

65 

66 def pop(self, index: t.Any = -1) -> t.NoReturn: 

67 _immutable_error(self) 

68 

69 def reverse(self: t.Any) -> t.NoReturn: 

70 _immutable_error(self) 

71 

72 def sort(self, key: t.Any = None, reverse: t.Any = False) -> t.NoReturn: 

73 _immutable_error(self) 

74 

75 

76class ImmutableDictMixin(t.Generic[K, V]): 

77 """Makes a :class:`dict` immutable. 

78 

79 .. versionchanged:: 3.1 

80 Disallow ``|=`` operator. 

81 

82 .. versionadded:: 0.5 

83 

84 :private: 

85 """ 

86 

87 _hash_cache: int | None = None 

88 

89 @classmethod 

90 @t.overload 

91 def fromkeys( 

92 cls, keys: cabc.Iterable[K], value: None 

93 ) -> ImmutableDictMixin[K, t.Any | None]: ... 

94 @classmethod 

95 @t.overload 

96 def fromkeys(cls, keys: cabc.Iterable[K], value: V) -> ImmutableDictMixin[K, V]: ... 

97 @classmethod 

98 def fromkeys( 

99 cls, keys: cabc.Iterable[K], value: V | None = None 

100 ) -> ImmutableDictMixin[K, t.Any | None] | ImmutableDictMixin[K, V]: 

101 instance = super().__new__(cls) 

102 instance.__init__(zip(keys, repeat(value))) # type: ignore[misc] 

103 return instance 

104 

105 def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any: 

106 return type(self), (dict(self),) # type: ignore[call-overload] 

107 

108 def _iter_hashitems(self) -> t.Iterable[t.Any]: 

109 return self.items() # type: ignore[attr-defined,no-any-return] 

110 

111 def __hash__(self) -> int: 

112 if self._hash_cache is not None: 

113 return self._hash_cache 

114 rv = self._hash_cache = hash(frozenset(self._iter_hashitems())) 

115 return rv 

116 

117 def setdefault(self, key: t.Any, default: t.Any = None) -> t.NoReturn: 

118 _immutable_error(self) 

119 

120 def update(self, arg: t.Any, /, **kwargs: t.Any) -> t.NoReturn: 

121 _immutable_error(self) 

122 

123 def __ior__(self, other: t.Any) -> t.NoReturn: 

124 _immutable_error(self) 

125 

126 def pop(self, key: t.Any, default: t.Any = None) -> t.NoReturn: 

127 _immutable_error(self) 

128 

129 def popitem(self) -> t.NoReturn: 

130 _immutable_error(self) 

131 

132 def __setitem__(self, key: t.Any, value: t.Any) -> t.NoReturn: 

133 _immutable_error(self) 

134 

135 def __delitem__(self, key: t.Any) -> t.NoReturn: 

136 _immutable_error(self) 

137 

138 def clear(self) -> t.NoReturn: 

139 _immutable_error(self) 

140 

141 

142class ImmutableMultiDictMixin(ImmutableDictMixin[K, V]): 

143 """Makes a :class:`MultiDict` immutable. 

144 

145 .. versionadded:: 0.5 

146 

147 :private: 

148 """ 

149 

150 def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any: 

151 return type(self), (list(self.items(multi=True)),) # type: ignore[attr-defined] 

152 

153 def _iter_hashitems(self) -> t.Iterable[t.Any]: 

154 return self.items(multi=True) # type: ignore[attr-defined,no-any-return] 

155 

156 def add(self, key: t.Any, value: t.Any) -> t.NoReturn: 

157 _immutable_error(self) 

158 

159 def popitemlist(self) -> t.NoReturn: 

160 _immutable_error(self) 

161 

162 def poplist(self, key: t.Any) -> t.NoReturn: 

163 _immutable_error(self) 

164 

165 def setlist(self, key: t.Any, new_list: t.Any) -> t.NoReturn: 

166 _immutable_error(self) 

167 

168 def setlistdefault(self, key: t.Any, default_list: t.Any = None) -> t.NoReturn: 

169 _immutable_error(self) 

170 

171 

172class ImmutableHeadersMixin: 

173 """Makes a :class:`Headers` immutable. We do not mark them as 

174 hashable though since the only usecase for this datastructure 

175 in Werkzeug is a view on a mutable structure. 

176 

177 .. versionchanged:: 3.1 

178 Disallow ``|=`` operator. 

179 

180 .. versionadded:: 0.5 

181 

182 :private: 

183 """ 

184 

185 def __delitem__(self, key: t.Any, **kwargs: t.Any) -> t.NoReturn: 

186 _immutable_error(self) 

187 

188 def __setitem__(self, key: t.Any, value: t.Any) -> t.NoReturn: 

189 _immutable_error(self) 

190 

191 def set(self, key: t.Any, value: t.Any, /, **kwargs: t.Any) -> t.NoReturn: 

192 _immutable_error(self) 

193 

194 def setlist(self, key: t.Any, values: t.Any) -> t.NoReturn: 

195 _immutable_error(self) 

196 

197 def add(self, key: t.Any, value: t.Any, /, **kwargs: t.Any) -> t.NoReturn: 

198 _immutable_error(self) 

199 

200 def add_header(self, key: t.Any, value: t.Any, /, **kwargs: t.Any) -> t.NoReturn: 

201 _immutable_error(self) 

202 

203 def remove(self, key: t.Any) -> t.NoReturn: 

204 _immutable_error(self) 

205 

206 def extend(self, arg: t.Any, /, **kwargs: t.Any) -> t.NoReturn: 

207 _immutable_error(self) 

208 

209 def update(self, arg: t.Any, /, **kwargs: t.Any) -> t.NoReturn: 

210 _immutable_error(self) 

211 

212 def __ior__(self, other: t.Any) -> t.NoReturn: 

213 _immutable_error(self) 

214 

215 def insert(self, pos: t.Any, value: t.Any) -> t.NoReturn: 

216 _immutable_error(self) 

217 

218 def pop(self, key: t.Any = None, default: t.Any = _missing) -> t.NoReturn: 

219 _immutable_error(self) 

220 

221 def popitem(self) -> t.NoReturn: 

222 _immutable_error(self) 

223 

224 def setdefault(self, key: t.Any, default: t.Any) -> t.NoReturn: 

225 _immutable_error(self) 

226 

227 def setlistdefault(self, key: t.Any, default: t.Any) -> t.NoReturn: 

228 _immutable_error(self) 

229 

230 

231def _always_update(f: F) -> F: 

232 def wrapper( 

233 self: UpdateDictMixin[t.Any, t.Any], /, *args: t.Any, **kwargs: t.Any 

234 ) -> t.Any: 

235 rv = f(self, *args, **kwargs) 

236 

237 if self.on_update is not None: 

238 self.on_update(self) 

239 

240 return rv 

241 

242 return update_wrapper(wrapper, f) # type: ignore[return-value] 

243 

244 

245class UpdateDictMixin(dict[K, V]): 

246 """Makes dicts call `self.on_update` on modifications. 

247 

248 .. versionchanged:: 3.1 

249 Implement ``|=`` operator. 

250 

251 .. versionadded:: 0.5 

252 

253 :private: 

254 """ 

255 

256 on_update: cabc.Callable[[te.Self], None] | None = None 

257 

258 def setdefault(self: te.Self, key: K, default: V | None = None) -> V: 

259 modified = key not in self 

260 rv = super().setdefault(key, default) # type: ignore[arg-type] 

261 if modified and self.on_update is not None: 

262 self.on_update(self) 

263 return rv 

264 

265 @t.overload 

266 def pop(self: te.Self, key: K) -> V: ... 

267 @t.overload 

268 def pop(self: te.Self, key: K, default: V) -> V: ... 

269 @t.overload 

270 def pop(self: te.Self, key: K, default: T) -> T: ... 

271 def pop( 

272 self: te.Self, 

273 key: K, 

274 default: V | T = _missing, # type: ignore[assignment] 

275 ) -> V | T: 

276 modified = key in self 

277 if default is _missing: 

278 rv = super().pop(key) 

279 else: 

280 rv = super().pop(key, default) # type: ignore[arg-type] 

281 if modified and self.on_update is not None: 

282 self.on_update(self) 

283 return rv 

284 

285 @_always_update 

286 def __setitem__(self, key: K, value: V) -> None: 

287 super().__setitem__(key, value) 

288 

289 @_always_update 

290 def __delitem__(self, key: K) -> None: 

291 super().__delitem__(key) 

292 

293 @_always_update 

294 def clear(self) -> None: 

295 super().clear() 

296 

297 @_always_update 

298 def popitem(self) -> tuple[K, V]: 

299 return super().popitem() 

300 

301 @_always_update 

302 def update( # type: ignore[override] 

303 self, 

304 arg: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] | None = None, 

305 /, 

306 **kwargs: V, 

307 ) -> None: 

308 if arg is None: 

309 super().update(**kwargs) 

310 else: 

311 super().update(arg, **kwargs) 

312 

313 @_always_update 

314 def __ior__( # type: ignore[override] 

315 self, other: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] 

316 ) -> te.Self: 

317 return super().__ior__(other)