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

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

184 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 

10K = t.TypeVar("K") 

11V = t.TypeVar("V") 

12T = t.TypeVar("T") 

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

14 

15 

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

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

18 

19 

20class _ImmutableListMixin: 

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

22 

23 .. deprecated:: 3.2 

24 Will be removed in Werkzeug 4.0. Use ``collections.abc.Sequence`` 

25 instead. 

26 

27 .. versionadded:: 0.5 

28 

29 :private: 

30 """ 

31 

32 _hash_cache: int | None = None 

33 

34 def __hash__(self) -> int: 

35 if self._hash_cache is not None: 

36 return self._hash_cache 

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

38 return rv 

39 

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

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

42 

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

44 _immutable_error(self) 

45 

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

47 _immutable_error(self) 

48 

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

50 _immutable_error(self) 

51 

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

53 _immutable_error(self) 

54 

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

56 _immutable_error(self) 

57 

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

59 _immutable_error(self) 

60 

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

62 _immutable_error(self) 

63 

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

65 _immutable_error(self) 

66 

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

68 _immutable_error(self) 

69 

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

71 _immutable_error(self) 

72 

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

74 _immutable_error(self) 

75 

76 

77class _ImmutableDictMixin(t.Generic[K, V]): 

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

79 

80 .. deprecated:: 3.2 

81 Will be removed in Werkzeug 4.0. Use ``collections.abc.Mapping`` 

82 instead. 

83 

84 .. versionchanged:: 3.1 

85 Disallow ``|=`` operator. 

86 

87 .. versionadded:: 0.5 

88 

89 :private: 

90 """ 

91 

92 _hash_cache: int | None = None 

93 

94 @classmethod 

95 @t.overload 

96 def fromkeys( 

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

98 ) -> _ImmutableDictMixin[K, t.Any | None]: ... 

99 @classmethod 

100 @t.overload 

101 def fromkeys( 

102 cls, keys: cabc.Iterable[K], value: V 

103 ) -> _ImmutableDictMixin[K, V]: ... 

104 @classmethod 

105 def fromkeys( 

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

107 ) -> _ImmutableDictMixin[K, t.Any | None] | _ImmutableDictMixin[K, V]: 

108 instance = super().__new__(cls) 

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

110 return instance 

111 

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

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

114 

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

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

117 

118 def __hash__(self) -> int: 

119 if self._hash_cache is not None: 

120 return self._hash_cache 

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

122 return rv 

123 

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

125 _immutable_error(self) 

126 

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

128 _immutable_error(self) 

129 

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

131 _immutable_error(self) 

132 

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

134 _immutable_error(self) 

135 

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

137 _immutable_error(self) 

138 

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

140 _immutable_error(self) 

141 

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

143 _immutable_error(self) 

144 

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

146 _immutable_error(self) 

147 

148 

149class ImmutableMultiDictMixin(_ImmutableDictMixin[K, V]): 

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

151 

152 .. versionadded:: 0.5 

153 

154 :private: 

155 """ 

156 

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

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

159 

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

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

162 

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

164 _immutable_error(self) 

165 

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

167 _immutable_error(self) 

168 

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

170 _immutable_error(self) 

171 

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

173 _immutable_error(self) 

174 

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

176 _immutable_error(self) 

177 

178 

179class ImmutableHeadersMixin: 

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

181 hashable though since the only usecase for this datastructure 

182 in Werkzeug is a view on a mutable structure. 

183 

184 .. versionchanged:: 3.1 

185 Disallow ``|=`` operator. 

186 

187 .. versionadded:: 0.5 

188 

189 :private: 

190 """ 

191 

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

193 _immutable_error(self) 

194 

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

196 _immutable_error(self) 

197 

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

199 _immutable_error(self) 

200 

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

202 _immutable_error(self) 

203 

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

205 _immutable_error(self) 

206 

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

208 _immutable_error(self) 

209 

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

211 _immutable_error(self) 

212 

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

214 _immutable_error(self) 

215 

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

217 _immutable_error(self) 

218 

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

220 _immutable_error(self) 

221 

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

223 _immutable_error(self) 

224 

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

226 _immutable_error(self) 

227 

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

229 _immutable_error(self) 

230 

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

232 _immutable_error(self) 

233 

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

235 _immutable_error(self) 

236 

237 

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

239 def wrapper( 

240 self: _UpdateDictMixin[t.Any, t.Any], /, *args: t.Any, **kwargs: t.Any 

241 ) -> t.Any: 

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

243 

244 if self.on_update is not None: 

245 self.on_update(self) 

246 

247 return rv 

248 

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

250 

251 

252class _UpdateDictMixin(dict[K, V]): 

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

254 

255 .. deprecated:: 3.2 

256 Will be removed in Werkzeug 4.0. Use ``CallbackDict`` instead. 

257 

258 .. versionchanged:: 3.1 

259 Implement ``|=`` operator. 

260 

261 .. versionadded:: 0.5 

262 

263 :private: 

264 """ 

265 

266 on_update: cabc.Callable[[t.Self], None] | None = None 

267 

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

269 modified = key not in self 

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

271 if modified and self.on_update is not None: 

272 self.on_update(self) 

273 return rv 

274 

275 @t.overload 

276 def pop(self: t.Self, key: K) -> V: ... 

277 @t.overload 

278 def pop(self: t.Self, key: K, default: V) -> V: ... 

279 @t.overload 

280 def pop(self: t.Self, key: K, default: T) -> T: ... 

281 def pop( 

282 self: t.Self, 

283 key: K, 

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

285 ) -> V | T: 

286 modified = key in self 

287 if default is _missing: 

288 rv: V | T = super().pop(key) 

289 else: 

290 rv = super().pop(key, default) 

291 if modified and self.on_update is not None: 

292 self.on_update(self) 

293 return rv 

294 

295 @_always_update 

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

297 super().__setitem__(key, value) 

298 

299 @_always_update 

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

301 super().__delitem__(key) 

302 

303 @_always_update 

304 def clear(self) -> None: 

305 super().clear() 

306 

307 @_always_update 

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

309 return super().popitem() 

310 

311 @_always_update 

312 def update( # type: ignore[override] 

313 self, 

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

315 /, 

316 **kwargs: V, 

317 ) -> None: 

318 if arg is None: 

319 super().update(**kwargs) # type: ignore[call-overload] 

320 else: 

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

322 

323 @_always_update 

324 def __ior__( # type: ignore[override] 

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

326 ) -> t.Self: 

327 return super().__ior__(other) 

328 

329 

330if not t.TYPE_CHECKING: 

331 

332 def __getattr__(name: str) -> t.Any: 

333 alts = { 

334 "ImmutableListMixin": "collections.abc.Sequence", 

335 "ImmutableDictMixin": "collections.abc.Mapping", 

336 "UpdateDictMixin": "CallbackDict", 

337 } 

338 

339 if name in alts: 

340 import warnings 

341 

342 warnings.warn( 

343 f"The '{name}' class is deprecated and will be removed in" 

344 f" Werkzeug 4.0. Use '{alts[name]}' instead.", 

345 DeprecationWarning, 

346 stacklevel=2, 

347 ) 

348 return globals()[f"_{name}"] 

349 

350 raise AttributeError(name)