1from __future__ import annotations
2
3import collections.abc as cabc
4import typing as t
5from copy import deepcopy
6
7from .. import exceptions
8from .._internal import _missing
9from .mixins import _ImmutableDictMixin
10from .mixins import _ImmutableListMixin
11from .mixins import ImmutableMultiDictMixin
12
13K = t.TypeVar("K")
14V = t.TypeVar("V")
15T = t.TypeVar("T")
16
17
18def iter_multi_items(
19 mapping: (
20 MultiDict[K, V]
21 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
22 | cabc.Iterable[tuple[K, V]]
23 ),
24) -> cabc.Iterator[tuple[K, V]]:
25 """Iterates over the items of a mapping yielding keys and values
26 without dropping any from more complex structures.
27 """
28 if isinstance(mapping, MultiDict):
29 yield from mapping.items(multi=True)
30 elif isinstance(mapping, cabc.Mapping):
31 for key, value in mapping.items():
32 if isinstance(value, (list, tuple, set)):
33 for v in value:
34 yield key, v
35 else:
36 yield key, value
37 else:
38 yield from mapping
39
40
41class _ImmutableList(_ImmutableListMixin, list[V]): # type: ignore[misc]
42 """An immutable :class:`list`.
43
44 .. deprecated:: 3.2
45 Will be removed in Werkzeug 4.0. Use ``collections.abc.Sequence``
46 instead.
47
48 .. versionadded:: 0.5
49
50 :private:
51 """
52
53 def __repr__(self) -> str:
54 return f"{type(self).__name__}({list.__repr__(self)})"
55
56
57class _TypeConversionDict(dict[K, V]):
58 """Works like a regular dict but the :meth:`get` method can perform
59 type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
60 are subclasses of this class and provide the same feature.
61
62 .. versionadded:: 0.5
63 """
64
65 @t.overload # type: ignore[override]
66 def get(self, key: K) -> V | None: ...
67 @t.overload
68 def get(self, key: K, default: V) -> V: ...
69 @t.overload
70 def get(self, key: K, default: T) -> V | T: ...
71 @t.overload
72 def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ...
73 @t.overload
74 def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ...
75 def get( # type: ignore[misc]
76 self,
77 key: K,
78 default: V | T | None = None,
79 type: cabc.Callable[[V], T] | None = None,
80 ) -> V | T | None:
81 """Return the default value if the requested data doesn't exist.
82 If `type` is provided and is a callable it should convert the value,
83 return it or raise a :exc:`ValueError` if that is not possible. In
84 this case the function will return the default as if the value was not
85 found:
86
87 >>> d = TypeConversionDict(foo='42', bar='blub')
88 >>> d.get('foo', type=int)
89 42
90 >>> d.get('bar', -1, type=int)
91 -1
92
93 :param key: The key to be looked up.
94 :param default: The default value to be returned if the key can't
95 be looked up. If not further specified `None` is
96 returned.
97 :param type: A callable that is used to cast the value in the
98 :class:`MultiDict`. If a :exc:`ValueError` or a
99 :exc:`TypeError` is raised by this callable the default
100 value is returned.
101
102 .. versionchanged:: 3.0.2
103 Returns the default value on :exc:`TypeError`, too.
104 """
105 try:
106 rv = self[key]
107 except KeyError:
108 return default
109
110 if type is None:
111 return rv
112
113 try:
114 return type(rv)
115 except (ValueError, TypeError):
116 return default
117
118
119class _ImmutableTypeConversionDict( # type: ignore[misc]
120 _ImmutableDictMixin[K, V], _TypeConversionDict[K, V]
121):
122 """Works like a :class:`TypeConversionDict` but does not support
123 modifications.
124
125 .. versionadded:: 0.5
126 """
127
128 def copy(self) -> _TypeConversionDict[K, V]:
129 """Return a shallow mutable copy of this object. Keep in mind that
130 the standard library's :func:`copy` function is a no-op for this class
131 like for any other python immutable type (eg: :class:`tuple`).
132 """
133 return _TypeConversionDict(self)
134
135 def __copy__(self) -> t.Self:
136 return self
137
138
139class MultiDict(dict[K, V]):
140 """A :class:`MultiDict` is a dictionary subclass customized to deal with
141 multiple values for the same key which is for example used by the parsing
142 functions in the wrappers. This is necessary because some HTML form
143 elements pass multiple values for the same key.
144
145 :class:`MultiDict` implements all standard dictionary methods.
146 Internally, it saves all values for a key as a list, but the standard dict
147 access methods will only return the first value for a key. If you want to
148 gain access to the other values, too, you have to use the `list` methods as
149 explained below.
150
151 Basic Usage:
152
153 >>> d = MultiDict([('a', 'b'), ('a', 'c')])
154 >>> d
155 MultiDict([('a', 'b'), ('a', 'c')])
156 >>> d['a']
157 'b'
158 >>> d.getlist('a')
159 ['b', 'c']
160 >>> 'a' in d
161 True
162
163 It behaves like a normal dict thus all dict functions will only return the
164 first value when multiple values for one key are found.
165
166 From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
167 subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
168 render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
169 exceptions.
170
171 A :class:`MultiDict` can be constructed from an iterable of
172 ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
173 onwards some keyword parameters.
174
175 :param mapping: the initial value for the :class:`MultiDict`. Either a
176 regular dict, an iterable of ``(key, value)`` tuples
177 or `None`.
178
179 .. versionchanged:: 3.1
180 Implement ``|`` and ``|=`` operators.
181 """
182
183 def __init__(
184 self,
185 mapping: (
186 MultiDict[K, V]
187 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
188 | cabc.Iterable[tuple[K, V]]
189 | None
190 ) = None,
191 ) -> None:
192 if mapping is None:
193 super().__init__()
194 elif isinstance(mapping, MultiDict):
195 super().__init__((k, vs[:]) for k, vs in mapping.lists()) # type: ignore[misc]
196 elif isinstance(mapping, cabc.Mapping):
197 tmp = {}
198 for key, value in mapping.items():
199 if isinstance(value, (list, tuple, set)):
200 value = list(value)
201
202 if not value:
203 continue
204 else:
205 value = [value]
206 tmp[key] = value
207 super().__init__(tmp) # type: ignore[arg-type]
208 else:
209 tmp = {}
210 for key, value in mapping:
211 tmp.setdefault(key, []).append(value)
212 super().__init__(tmp) # type: ignore[arg-type]
213
214 def __getstate__(self) -> t.Any:
215 return dict(self.lists())
216
217 def __setstate__(self, value: t.Any) -> None:
218 super().clear()
219 super().update(value)
220
221 def __iter__(self) -> cabc.Iterator[K]:
222 # https://github.com/python/cpython/issues/87412
223 # If __iter__ is not overridden, Python uses a fast path for dict(md),
224 # taking the data directly and getting lists of values, rather than
225 # calling __getitem__ and getting only the first value.
226 return super().__iter__()
227
228 def __getitem__(self, key: K) -> V:
229 """Return the first data value for this key;
230 raises KeyError if not found.
231
232 :param key: The key to be looked up.
233 :raise KeyError: if the key does not exist.
234 """
235
236 if key in self:
237 lst = super().__getitem__(key)
238 if len(lst) > 0: # type: ignore[arg-type]
239 return lst[0] # type: ignore[index,no-any-return]
240 raise exceptions.BadRequestKeyError(key)
241
242 def __setitem__(self, key: K, value: V) -> None:
243 """Like :meth:`add` but removes an existing key first.
244
245 :param key: the key for the value.
246 :param value: the value to set.
247 """
248 super().__setitem__(key, [value]) # type: ignore[assignment]
249
250 def add(self, key: K, value: V) -> None:
251 """Adds a new value for the key.
252
253 .. versionadded:: 0.6
254
255 :param key: the key for the value.
256 :param value: the value to add.
257 """
258 super().setdefault(key, []).append(value) # type: ignore[arg-type,attr-defined]
259
260 @t.overload # type: ignore[override]
261 def get(self, key: K) -> V | None: ...
262 @t.overload
263 def get(self, key: K, default: V) -> V: ...
264 @t.overload
265 def get(self, key: K, default: T) -> V | T: ...
266 @t.overload
267 def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ...
268 @t.overload
269 def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ...
270 def get( # type: ignore[misc]
271 self,
272 key: K,
273 default: V | T | None = None,
274 type: cabc.Callable[[V], T] | None = None,
275 ) -> V | T | None:
276 """Get the first value for the key, or a default if it's not set.
277
278 :param key: The key to get.
279 :param default: The value to return if the key is not set.
280 :param type: Convert the value using this function. If it raises a
281 ``TypeError`` or ``ValueError``, return ``default``.
282
283 .. versionchanged:: 3.0.2
284 Returns the default value on :exc:`TypeError`, too.
285 """
286 try:
287 rv = self[key]
288 except KeyError:
289 return default
290
291 if type is None:
292 return rv
293
294 try:
295 return type(rv)
296 except (ValueError, TypeError):
297 return default
298
299 @t.overload
300 def getlist(self, key: K) -> list[V]: ...
301 @t.overload
302 def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ...
303 def getlist(
304 self, key: K, type: cabc.Callable[[V], T] | None = None
305 ) -> list[V] | list[T]:
306 """Return the list of items for a given key. If that key is not in the
307 `MultiDict`, the return value will be an empty list. Just like `get`,
308 `getlist` accepts a `type` parameter. All items will be converted
309 with the callable defined there.
310
311 :param key: The key to be looked up.
312 :param type: Callable to convert each value. If a ``ValueError`` or
313 ``TypeError`` is raised, the value is omitted.
314 :return: a :class:`list` of all the values for the key.
315
316 .. versionchanged:: 3.1
317 Catches ``TypeError`` in addition to ``ValueError``.
318 """
319 try:
320 rv: list[V] = super().__getitem__(key) # type: ignore[assignment]
321 except KeyError:
322 return []
323 if type is None:
324 return list(rv)
325 result = []
326 for item in rv:
327 try:
328 result.append(type(item))
329 except (ValueError, TypeError):
330 pass
331 return result
332
333 def setlist(self, key: K, new_list: cabc.Iterable[V]) -> None:
334 """Remove the old values for a key and add new ones. Note that the list
335 you pass the values in will be shallow-copied before it is inserted in
336 the dictionary.
337
338 >>> d = MultiDict()
339 >>> d.setlist('foo', ['1', '2'])
340 >>> d['foo']
341 '1'
342 >>> d.getlist('foo')
343 ['1', '2']
344
345 :param key: The key for which the values are set.
346 :param new_list: An iterable with the new values for the key. Old values
347 are removed first.
348 """
349 super().__setitem__(key, list(new_list)) # type: ignore[assignment]
350
351 @t.overload
352 def setdefault(self, key: K) -> None: ...
353 @t.overload
354 def setdefault(self, key: K, default: V) -> V: ...
355 def setdefault(self, key: K, default: V | None = None) -> V | None:
356 """Returns the value for the key if it is in the dict, otherwise it
357 returns `default` and sets that value for `key`.
358
359 :param key: The key to be looked up.
360 :param default: The default value to be returned if the key is not
361 in the dict. If not further specified it's `None`.
362 """
363 if key not in self:
364 self[key] = default # type: ignore[assignment]
365
366 return self[key]
367
368 def setlistdefault(
369 self, key: K, default_list: cabc.Iterable[V] | None = None
370 ) -> list[V]:
371 """Like `setdefault` but sets multiple values. The list returned
372 is not a copy, but the list that is actually used internally. This
373 means that you can put new values into the dict by appending items
374 to the list:
375
376 >>> d = MultiDict({"foo": 1})
377 >>> d.setlistdefault("foo").extend([2, 3])
378 >>> d.getlist("foo")
379 [1, 2, 3]
380
381 :param key: The key to be looked up.
382 :param default_list: An iterable of default values. It is either copied
383 (in case it was a list) or converted into a list
384 before returned.
385 :return: a :class:`list`
386 """
387 if key not in self:
388 super().__setitem__(key, list(default_list or ())) # type: ignore[assignment]
389
390 return super().__getitem__(key) # type: ignore[return-value]
391
392 def items(self, multi: bool = False) -> cabc.Iterable[tuple[K, V]]: # type: ignore[override]
393 """Return an iterator of ``(key, value)`` pairs.
394
395 :param multi: If set to `True` the iterator returned will have a pair
396 for each value of each key. Otherwise it will only
397 contain pairs for the first value of each key.
398 """
399 values: list[V]
400
401 for key, values in super().items(): # type: ignore[assignment]
402 if multi:
403 for value in values:
404 yield key, value
405 else:
406 yield key, values[0]
407
408 def lists(self) -> cabc.Iterable[tuple[K, list[V]]]:
409 """Return a iterator of ``(key, values)`` pairs, where values is the list
410 of all values associated with the key."""
411 values: list[V]
412
413 for key, values in super().items(): # type: ignore[assignment]
414 yield key, list(values)
415
416 def values(self) -> cabc.Iterable[V]: # type: ignore[override]
417 """Returns an iterator of the first value on every key's value list."""
418 values: list[V]
419
420 for values in super().values(): # type: ignore[assignment]
421 yield values[0]
422
423 def listvalues(self) -> cabc.Iterable[list[V]]:
424 """Return an iterator of all values associated with a key. Zipping
425 :meth:`keys` and this is the same as calling :meth:`lists`:
426
427 >>> d = MultiDict({"foo": [1, 2, 3]})
428 >>> zip(d.keys(), d.listvalues()) == d.lists()
429 True
430 """
431 return super().values() # type: ignore[return-value]
432
433 def copy(self) -> t.Self:
434 """Return a shallow copy of this object."""
435 return self.__class__(self)
436
437 def deepcopy(self, memo: t.Any = None) -> t.Self:
438 """Return a deep copy of this object."""
439 return self.__class__(deepcopy(self.to_dict(flat=False), memo))
440
441 @t.overload
442 def to_dict(self, flat: t.Literal[True] = ...) -> dict[K, V]: ...
443 @t.overload
444 def to_dict(self, flat: t.Literal[False]) -> dict[K, list[V]]: ...
445 def to_dict(self, flat: bool = True) -> dict[K, V] | dict[K, list[V]]:
446 """Return the contents as regular dict. If `flat` is `True` the
447 returned dict will only have the first item present, if `flat` is
448 `False` all values will be returned as lists.
449
450 :param flat: If set to `False` the dict returned will have lists
451 with all the values in it. Otherwise it will only
452 contain the first value for each key.
453 :return: a :class:`dict`
454 """
455 if flat:
456 return dict(self.items())
457 return dict(self.lists())
458
459 def update( # type: ignore[override]
460 self,
461 mapping: (
462 MultiDict[K, V]
463 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
464 | cabc.Iterable[tuple[K, V]]
465 ),
466 ) -> None:
467 """update() extends rather than replaces existing key lists:
468
469 >>> a = MultiDict({'x': 1})
470 >>> b = MultiDict({'x': 2, 'y': 3})
471 >>> a.update(b)
472 >>> a
473 MultiDict([('y', 3), ('x', 1), ('x', 2)])
474
475 If the value list for a key in ``other_dict`` is empty, no new values
476 will be added to the dict and the key will not be created:
477
478 >>> x = {'empty_list': []}
479 >>> y = MultiDict()
480 >>> y.update(x)
481 >>> y
482 MultiDict([])
483 """
484 for key, value in iter_multi_items(mapping):
485 self.add(key, value)
486
487 def __or__( # type: ignore[override]
488 self, other: cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
489 ) -> MultiDict[K, V]:
490 if not isinstance(other, cabc.Mapping):
491 return NotImplemented
492
493 rv = self.copy()
494 rv.update(other)
495 return rv
496
497 def __ior__( # type: ignore[override]
498 self,
499 other: (
500 cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
501 | cabc.Iterable[tuple[K, V]]
502 ),
503 ) -> t.Self:
504 if not isinstance(other, (cabc.Mapping, cabc.Iterable)):
505 return NotImplemented
506
507 self.update(other)
508 return self
509
510 @t.overload
511 def pop(self, key: K) -> V: ...
512 @t.overload
513 def pop(self, key: K, default: V) -> V: ...
514 @t.overload
515 def pop(self, key: K, default: T) -> V | T: ...
516 def pop(
517 self,
518 key: K,
519 default: V | T = _missing, # type: ignore[assignment]
520 ) -> V | T:
521 """Pop the first item for a list on the dict. Afterwards the
522 key is removed from the dict, so additional values are discarded:
523
524 >>> d = MultiDict({"foo": [1, 2, 3]})
525 >>> d.pop("foo")
526 1
527 >>> "foo" in d
528 False
529
530 :param key: the key to pop.
531 :param default: if provided the value to return if the key was
532 not in the dictionary.
533 """
534 lst: list[V]
535
536 try:
537 lst = super().pop(key) # type: ignore[assignment]
538
539 if len(lst) == 0:
540 raise exceptions.BadRequestKeyError(key)
541
542 return lst[0]
543 except KeyError:
544 if default is not _missing:
545 return default
546
547 raise exceptions.BadRequestKeyError(key) from None
548
549 def popitem(self) -> tuple[K, V]:
550 """Pop an item from the dict."""
551 item: tuple[K, list[V]]
552
553 try:
554 item = super().popitem() # type: ignore[assignment]
555
556 if len(item[1]) == 0:
557 raise exceptions.BadRequestKeyError(item[0])
558
559 return item[0], item[1][0]
560 except KeyError as e:
561 raise exceptions.BadRequestKeyError(e.args[0]) from None
562
563 def poplist(self, key: K) -> list[V]:
564 """Pop the list for a key from the dict. If the key is not in the dict
565 an empty list is returned.
566
567 .. versionchanged:: 0.5
568 If the key does no longer exist a list is returned instead of
569 raising an error.
570 """
571 return super().pop(key, []) # type: ignore[return-value]
572
573 def popitemlist(self) -> tuple[K, list[V]]:
574 """Pop a ``(key, list)`` tuple from the dict."""
575 try:
576 return super().popitem() # type: ignore[return-value]
577 except KeyError as e:
578 raise exceptions.BadRequestKeyError(e.args[0]) from None
579
580 def __copy__(self) -> t.Self:
581 return self.copy()
582
583 def __deepcopy__(self, memo: t.Any) -> t.Self:
584 return self.deepcopy(memo=memo)
585
586 def __repr__(self) -> str:
587 return f"{type(self).__name__}({list(self.items(multi=True))!r})"
588
589
590class CombinedMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc]
591 """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict`
592 instances as sequence and it will combine the return values of all wrapped
593 dicts:
594
595 >>> from werkzeug.datastructures import CombinedMultiDict, MultiDict
596 >>> post = MultiDict([('foo', 'bar')])
597 >>> get = MultiDict([('blub', 'blah')])
598 >>> combined = CombinedMultiDict([get, post])
599 >>> combined['foo']
600 'bar'
601 >>> combined['blub']
602 'blah'
603
604 This works for all read operations and will raise a `TypeError` for
605 methods that usually change data which isn't possible.
606
607 From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
608 subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
609 render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
610 exceptions.
611 """
612
613 def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any:
614 return type(self), (self.dicts,)
615
616 def __init__(self, dicts: cabc.Iterable[MultiDict[K, V]] | None = None) -> None:
617 super().__init__()
618 self.dicts: list[MultiDict[K, V]] = list(dicts or ())
619
620 @classmethod
621 def fromkeys(cls, keys: t.Any, value: t.Any = None) -> t.NoReturn:
622 raise TypeError(f"cannot create {cls.__name__!r} instances by fromkeys")
623
624 def __getitem__(self, key: K) -> V:
625 for d in self.dicts:
626 if key in d:
627 return d[key]
628 raise exceptions.BadRequestKeyError(key)
629
630 @t.overload # type: ignore[override]
631 def get(self, key: K) -> V | None: ...
632 @t.overload
633 def get(self, key: K, default: V) -> V: ...
634 @t.overload
635 def get(self, key: K, default: T) -> V | T: ...
636 @t.overload
637 def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ...
638 @t.overload
639 def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ...
640 def get( # type: ignore[misc]
641 self,
642 key: K,
643 default: V | T | None = None,
644 type: cabc.Callable[[V], T] | None = None,
645 ) -> V | T | None:
646 for d in self.dicts:
647 if key in d:
648 if type is not None:
649 try:
650 return type(d[key])
651 except (ValueError, TypeError):
652 continue
653 return d[key]
654 return default
655
656 @t.overload
657 def getlist(self, key: K) -> list[V]: ...
658 @t.overload
659 def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ...
660 def getlist(
661 self, key: K, type: cabc.Callable[[V], T] | None = None
662 ) -> list[V] | list[T]:
663 rv = []
664 for d in self.dicts:
665 rv.extend(d.getlist(key, type)) # type: ignore[arg-type]
666 return rv
667
668 def _keys_impl(self) -> set[K]:
669 """This function exists so __len__ can be implemented more efficiently,
670 saving one list creation from an iterator.
671 """
672 return set(k for d in self.dicts for k in d)
673
674 def keys(self) -> cabc.Iterable[K]: # type: ignore[override]
675 return self._keys_impl()
676
677 def __iter__(self) -> cabc.Iterator[K]:
678 return iter(self._keys_impl())
679
680 @t.overload # type: ignore[override]
681 def items(self) -> cabc.Iterable[tuple[K, V]]: ...
682 @t.overload
683 def items(self, multi: t.Literal[True]) -> cabc.Iterable[tuple[K, list[V]]]: ...
684 def items(
685 self, multi: bool = False
686 ) -> cabc.Iterable[tuple[K, V]] | cabc.Iterable[tuple[K, list[V]]]:
687 found = set()
688 for d in self.dicts:
689 for key, value in d.items(multi):
690 if multi:
691 yield key, value
692 elif key not in found:
693 found.add(key)
694 yield key, value
695
696 def values(self) -> cabc.Iterable[V]: # type: ignore[override]
697 for _, value in self.items():
698 yield value
699
700 def lists(self) -> cabc.Iterable[tuple[K, list[V]]]:
701 rv: dict[K, list[V]] = {}
702 for d in self.dicts:
703 for key, values in d.lists():
704 rv.setdefault(key, []).extend(values)
705 return rv.items()
706
707 def listvalues(self) -> cabc.Iterable[list[V]]:
708 return (x[1] for x in self.lists())
709
710 def copy(self) -> MultiDict[K, V]: # type: ignore[override]
711 """Return a shallow mutable copy of this object.
712
713 This returns a :class:`MultiDict` representing the data at the
714 time of copying. The copy will no longer reflect changes to the
715 wrapped dicts.
716
717 .. versionchanged:: 0.15
718 Return a mutable :class:`MultiDict`.
719 """
720 return MultiDict(self)
721
722 def __len__(self) -> int:
723 return len(self._keys_impl())
724
725 def __contains__(self, key: K) -> bool: # type: ignore[override]
726 for d in self.dicts:
727 if key in d:
728 return True
729 return False
730
731 def __repr__(self) -> str:
732 return f"{type(self).__name__}({self.dicts!r})"
733
734
735class _ImmutableDict(_ImmutableDictMixin[K, V], dict[K, V]): # type: ignore[misc]
736 """An immutable :class:`dict`.
737
738 .. deprecated:: 3.2
739 Will be removed in Werkzeug 4.0. Use ``collections.abc.Mapping``
740 instead.
741
742 .. versionadded:: 0.5
743 """
744
745 def __repr__(self) -> str:
746 return f"{type(self).__name__}({dict.__repr__(self)})"
747
748 def copy(self) -> dict[K, V]:
749 """Return a shallow mutable copy of this object. Keep in mind that
750 the standard library's :func:`copy` function is a no-op for this class
751 like for any other python immutable type (eg: :class:`tuple`).
752 """
753 return dict(self)
754
755 def __copy__(self) -> t.Self:
756 return self
757
758
759class ImmutableMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc]
760 """An immutable :class:`MultiDict`.
761
762 .. versionadded:: 0.5
763 """
764
765 def copy(self) -> MultiDict[K, V]: # type: ignore[override]
766 """Return a shallow mutable copy of this object. Keep in mind that
767 the standard library's :func:`copy` function is a no-op for this class
768 like for any other python immutable type (eg: :class:`tuple`).
769 """
770 return MultiDict(self)
771
772 def __copy__(self) -> t.Self:
773 return self
774
775
776class CallbackDict(dict[K, V]):
777 """A dict that calls a function every time it is mutated.
778
779 :param initial: Initial data.
780 :param on_update: A function to call every time this dict is mutated. The
781 dict is passed as the first argument.
782 """
783
784 def __init__(
785 self: t.Self,
786 initial: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] | None = None,
787 /,
788 on_update: cabc.Callable[[t.Self], None] | None = None,
789 ) -> None:
790 if initial is None:
791 super().__init__()
792 else:
793 super().__init__(initial)
794
795 self.on_update = on_update
796
797 @property
798 def _on_update(self) -> cabc.Callable[[t.Self], None] | None:
799 # compatibility with header datastructures
800 return self.on_update
801
802 @_on_update.setter
803 def _on_update(self, value: cabc.Callable[[t.Self], None] | None) -> None:
804 self.on_update = value
805
806 def __repr__(self) -> str:
807 return f"<{type(self).__name__} {super().__repr__()}>"
808
809 def _trigger_on_update(self: t.Self) -> None:
810 if self.on_update is not None:
811 self.on_update(self)
812
813 def setdefault(
814 self,
815 key: K,
816 default: V = None, # type: ignore[assignment]
817 /,
818 ) -> V:
819 modified = key not in self
820 rv = super().setdefault(key, default)
821
822 if modified:
823 self._trigger_on_update()
824
825 return rv
826
827 @t.overload
828 def pop(self, key: K, /) -> V: ...
829 @t.overload
830 def pop(self, key: K, default: V, /) -> V: ...
831 @t.overload
832 def pop(self, key: K, default: T, /) -> T: ...
833 def pop(
834 self: t.Self,
835 key: K,
836 default: V | T = _missing, # type: ignore[assignment]
837 /,
838 ) -> V | T:
839 modified = key in self
840
841 if default is _missing:
842 rv: V | T = super().pop(key)
843 else:
844 rv = super().pop(key, default)
845
846 if modified:
847 self._trigger_on_update()
848
849 return rv
850
851 def __setitem__(self, key: K, value: V) -> None:
852 super().__setitem__(key, value)
853 self._trigger_on_update()
854
855 def __delitem__(self, key: K) -> None:
856 super().__delitem__(key)
857 self._trigger_on_update()
858
859 def clear(self) -> None:
860 super().clear()
861 self._trigger_on_update()
862
863 def popitem(self) -> tuple[K, V]:
864 rv = super().popitem()
865 self._trigger_on_update()
866 return rv
867
868 def update( # type: ignore[override]
869 self,
870 arg: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] | None = None,
871 /,
872 **kwargs: V,
873 ) -> None:
874 if arg is None:
875 super().update(**kwargs) # type: ignore[call-overload]
876 else:
877 super().update(arg, **kwargs)
878
879 self._trigger_on_update()
880
881 def __ior__( # type: ignore[override, misc]
882 self, other: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]]
883 ) -> t.Self:
884 rv = super().__ior__(other)
885 self._trigger_on_update()
886 return rv
887
888
889if not t.TYPE_CHECKING:
890
891 def __getattr__(name: str) -> t.Any:
892 if name == "HeaderSet":
893 import warnings
894
895 from .set import HeaderSet
896
897 warnings.warn(
898 "'structures.HeaderSet' has moved to 'set.HeaderSet'. The old"
899 " location is deprecated and will be removed in Werkzeug 4.0.",
900 DeprecationWarning,
901 stacklevel=2,
902 )
903 return HeaderSet
904
905 alts = {
906 "ImmutableList": "collections.abc.Sequence",
907 "ImmutableDict": "collections.abc.Mapping",
908 "ImmutableTypeConversionDict": "ImmutableMultiDict",
909 "TypeConversionDict": "MultiDict",
910 }
911
912 if name in alts:
913 import warnings
914
915 warnings.warn(
916 f"The '{name}' class is deprecated and will be removed in"
917 f" Werkzeug 4.0. Use '{alts[name]}' instead.",
918 DeprecationWarning,
919 stacklevel=2,
920 )
921 return globals()[f"_{name}"]
922
923 raise AttributeError(name)