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 ..http import dump_header
10from ..http import parse_list_header
11from .mixins import ImmutableDictMixin
12from .mixins import ImmutableListMixin
13from .mixins import ImmutableMultiDictMixin
14from .mixins import UpdateDictMixin
15
16if t.TYPE_CHECKING:
17 import typing_extensions as te
18
19K = t.TypeVar("K")
20V = t.TypeVar("V")
21T = t.TypeVar("T")
22
23
24def iter_multi_items(
25 mapping: (
26 MultiDict[K, V]
27 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
28 | cabc.Iterable[tuple[K, V]]
29 ),
30) -> cabc.Iterator[tuple[K, V]]:
31 """Iterates over the items of a mapping yielding keys and values
32 without dropping any from more complex structures.
33 """
34 if isinstance(mapping, MultiDict):
35 yield from mapping.items(multi=True)
36 elif isinstance(mapping, cabc.Mapping):
37 for key, value in mapping.items():
38 if isinstance(value, (list, tuple, set)):
39 for v in value:
40 yield key, v
41 else:
42 yield key, value
43 else:
44 yield from mapping
45
46
47class ImmutableList(ImmutableListMixin, list[V]): # type: ignore[misc]
48 """An immutable :class:`list`.
49
50 .. versionadded:: 0.5
51
52 :private:
53 """
54
55 def __repr__(self) -> str:
56 return f"{type(self).__name__}({list.__repr__(self)})"
57
58
59class TypeConversionDict(dict[K, V]):
60 """Works like a regular dict but the :meth:`get` method can perform
61 type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
62 are subclasses of this class and provide the same feature.
63
64 .. versionadded:: 0.5
65 """
66
67 @t.overload # type: ignore[override]
68 def get(self, key: K) -> V | None: ...
69 @t.overload
70 def get(self, key: K, default: V) -> V: ...
71 @t.overload
72 def get(self, key: K, default: T) -> V | T: ...
73 @t.overload
74 def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ...
75 @t.overload
76 def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ...
77 def get( # type: ignore[misc]
78 self,
79 key: K,
80 default: V | T | None = None,
81 type: cabc.Callable[[V], T] | None = None,
82 ) -> V | T | None:
83 """Return the default value if the requested data doesn't exist.
84 If `type` is provided and is a callable it should convert the value,
85 return it or raise a :exc:`ValueError` if that is not possible. In
86 this case the function will return the default as if the value was not
87 found:
88
89 >>> d = TypeConversionDict(foo='42', bar='blub')
90 >>> d.get('foo', type=int)
91 42
92 >>> d.get('bar', -1, type=int)
93 -1
94
95 :param key: The key to be looked up.
96 :param default: The default value to be returned if the key can't
97 be looked up. If not further specified `None` is
98 returned.
99 :param type: A callable that is used to cast the value in the
100 :class:`MultiDict`. If a :exc:`ValueError` or a
101 :exc:`TypeError` is raised by this callable the default
102 value is returned.
103
104 .. versionchanged:: 3.0.2
105 Returns the default value on :exc:`TypeError`, too.
106 """
107 try:
108 rv = self[key]
109 except KeyError:
110 return default
111
112 if type is None:
113 return rv
114
115 try:
116 return type(rv)
117 except (ValueError, TypeError):
118 return default
119
120
121class ImmutableTypeConversionDict(ImmutableDictMixin[K, V], TypeConversionDict[K, V]): # type: ignore[misc]
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) -> te.Self:
136 return self
137
138
139class MultiDict(TypeConversionDict[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
261 def getlist(self, key: K) -> list[V]: ...
262 @t.overload
263 def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ...
264 def getlist(
265 self, key: K, type: cabc.Callable[[V], T] | None = None
266 ) -> list[V] | list[T]:
267 """Return the list of items for a given key. If that key is not in the
268 `MultiDict`, the return value will be an empty list. Just like `get`,
269 `getlist` accepts a `type` parameter. All items will be converted
270 with the callable defined there.
271
272 :param key: The key to be looked up.
273 :param type: Callable to convert each value. If a ``ValueError`` or
274 ``TypeError`` is raised, the value is omitted.
275 :return: a :class:`list` of all the values for the key.
276
277 .. versionchanged:: 3.1
278 Catches ``TypeError`` in addition to ``ValueError``.
279 """
280 try:
281 rv: list[V] = super().__getitem__(key) # type: ignore[assignment]
282 except KeyError:
283 return []
284 if type is None:
285 return list(rv)
286 result = []
287 for item in rv:
288 try:
289 result.append(type(item))
290 except (ValueError, TypeError):
291 pass
292 return result
293
294 def setlist(self, key: K, new_list: cabc.Iterable[V]) -> None:
295 """Remove the old values for a key and add new ones. Note that the list
296 you pass the values in will be shallow-copied before it is inserted in
297 the dictionary.
298
299 >>> d = MultiDict()
300 >>> d.setlist('foo', ['1', '2'])
301 >>> d['foo']
302 '1'
303 >>> d.getlist('foo')
304 ['1', '2']
305
306 :param key: The key for which the values are set.
307 :param new_list: An iterable with the new values for the key. Old values
308 are removed first.
309 """
310 super().__setitem__(key, list(new_list)) # type: ignore[assignment]
311
312 @t.overload
313 def setdefault(self, key: K) -> None: ...
314 @t.overload
315 def setdefault(self, key: K, default: V) -> V: ...
316 def setdefault(self, key: K, default: V | None = None) -> V | None:
317 """Returns the value for the key if it is in the dict, otherwise it
318 returns `default` and sets that value for `key`.
319
320 :param key: The key to be looked up.
321 :param default: The default value to be returned if the key is not
322 in the dict. If not further specified it's `None`.
323 """
324 if key not in self:
325 self[key] = default # type: ignore[assignment]
326
327 return self[key]
328
329 def setlistdefault(
330 self, key: K, default_list: cabc.Iterable[V] | None = None
331 ) -> list[V]:
332 """Like `setdefault` but sets multiple values. The list returned
333 is not a copy, but the list that is actually used internally. This
334 means that you can put new values into the dict by appending items
335 to the list:
336
337 >>> d = MultiDict({"foo": 1})
338 >>> d.setlistdefault("foo").extend([2, 3])
339 >>> d.getlist("foo")
340 [1, 2, 3]
341
342 :param key: The key to be looked up.
343 :param default_list: An iterable of default values. It is either copied
344 (in case it was a list) or converted into a list
345 before returned.
346 :return: a :class:`list`
347 """
348 if key not in self:
349 super().__setitem__(key, list(default_list or ())) # type: ignore[assignment]
350
351 return super().__getitem__(key) # type: ignore[return-value]
352
353 def items(self, multi: bool = False) -> cabc.Iterable[tuple[K, V]]: # type: ignore[override]
354 """Return an iterator of ``(key, value)`` pairs.
355
356 :param multi: If set to `True` the iterator returned will have a pair
357 for each value of each key. Otherwise it will only
358 contain pairs for the first value of each key.
359 """
360 values: list[V]
361
362 for key, values in super().items(): # type: ignore[assignment]
363 if multi:
364 for value in values:
365 yield key, value
366 else:
367 yield key, values[0]
368
369 def lists(self) -> cabc.Iterable[tuple[K, list[V]]]:
370 """Return a iterator of ``(key, values)`` pairs, where values is the list
371 of all values associated with the key."""
372 values: list[V]
373
374 for key, values in super().items(): # type: ignore[assignment]
375 yield key, list(values)
376
377 def values(self) -> cabc.Iterable[V]: # type: ignore[override]
378 """Returns an iterator of the first value on every key's value list."""
379 values: list[V]
380
381 for values in super().values(): # type: ignore[assignment]
382 yield values[0]
383
384 def listvalues(self) -> cabc.Iterable[list[V]]:
385 """Return an iterator of all values associated with a key. Zipping
386 :meth:`keys` and this is the same as calling :meth:`lists`:
387
388 >>> d = MultiDict({"foo": [1, 2, 3]})
389 >>> zip(d.keys(), d.listvalues()) == d.lists()
390 True
391 """
392 return super().values() # type: ignore[return-value]
393
394 def copy(self) -> te.Self:
395 """Return a shallow copy of this object."""
396 return self.__class__(self)
397
398 def deepcopy(self, memo: t.Any = None) -> te.Self:
399 """Return a deep copy of this object."""
400 return self.__class__(deepcopy(self.to_dict(flat=False), memo))
401
402 @t.overload
403 def to_dict(self, flat: t.Literal[True] = ...) -> dict[K, V]: ...
404 @t.overload
405 def to_dict(self, flat: t.Literal[False]) -> dict[K, list[V]]: ...
406 def to_dict(self, flat: bool = True) -> dict[K, V] | dict[K, list[V]]:
407 """Return the contents as regular dict. If `flat` is `True` the
408 returned dict will only have the first item present, if `flat` is
409 `False` all values will be returned as lists.
410
411 :param flat: If set to `False` the dict returned will have lists
412 with all the values in it. Otherwise it will only
413 contain the first value for each key.
414 :return: a :class:`dict`
415 """
416 if flat:
417 return dict(self.items())
418 return dict(self.lists())
419
420 def update( # type: ignore[override]
421 self,
422 mapping: (
423 MultiDict[K, V]
424 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
425 | cabc.Iterable[tuple[K, V]]
426 ),
427 ) -> None:
428 """update() extends rather than replaces existing key lists:
429
430 >>> a = MultiDict({'x': 1})
431 >>> b = MultiDict({'x': 2, 'y': 3})
432 >>> a.update(b)
433 >>> a
434 MultiDict([('y', 3), ('x', 1), ('x', 2)])
435
436 If the value list for a key in ``other_dict`` is empty, no new values
437 will be added to the dict and the key will not be created:
438
439 >>> x = {'empty_list': []}
440 >>> y = MultiDict()
441 >>> y.update(x)
442 >>> y
443 MultiDict([])
444 """
445 for key, value in iter_multi_items(mapping):
446 self.add(key, value)
447
448 def __or__( # type: ignore[override]
449 self, other: cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
450 ) -> MultiDict[K, V]:
451 if not isinstance(other, cabc.Mapping):
452 return NotImplemented
453
454 rv = self.copy()
455 rv.update(other)
456 return rv
457
458 def __ior__( # type: ignore[override]
459 self,
460 other: (
461 cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
462 | cabc.Iterable[tuple[K, V]]
463 ),
464 ) -> te.Self:
465 if not isinstance(other, (cabc.Mapping, cabc.Iterable)):
466 return NotImplemented
467
468 self.update(other)
469 return self
470
471 @t.overload
472 def pop(self, key: K) -> V: ...
473 @t.overload
474 def pop(self, key: K, default: V) -> V: ...
475 @t.overload
476 def pop(self, key: K, default: T) -> V | T: ...
477 def pop(
478 self,
479 key: K,
480 default: V | T = _missing, # type: ignore[assignment]
481 ) -> V | T:
482 """Pop the first item for a list on the dict. Afterwards the
483 key is removed from the dict, so additional values are discarded:
484
485 >>> d = MultiDict({"foo": [1, 2, 3]})
486 >>> d.pop("foo")
487 1
488 >>> "foo" in d
489 False
490
491 :param key: the key to pop.
492 :param default: if provided the value to return if the key was
493 not in the dictionary.
494 """
495 lst: list[V]
496
497 try:
498 lst = super().pop(key) # type: ignore[assignment]
499
500 if len(lst) == 0:
501 raise exceptions.BadRequestKeyError(key)
502
503 return lst[0]
504 except KeyError:
505 if default is not _missing:
506 return default
507
508 raise exceptions.BadRequestKeyError(key) from None
509
510 def popitem(self) -> tuple[K, V]:
511 """Pop an item from the dict."""
512 item: tuple[K, list[V]]
513
514 try:
515 item = super().popitem() # type: ignore[assignment]
516
517 if len(item[1]) == 0:
518 raise exceptions.BadRequestKeyError(item[0])
519
520 return item[0], item[1][0]
521 except KeyError as e:
522 raise exceptions.BadRequestKeyError(e.args[0]) from None
523
524 def poplist(self, key: K) -> list[V]:
525 """Pop the list for a key from the dict. If the key is not in the dict
526 an empty list is returned.
527
528 .. versionchanged:: 0.5
529 If the key does no longer exist a list is returned instead of
530 raising an error.
531 """
532 return super().pop(key, []) # type: ignore[return-value]
533
534 def popitemlist(self) -> tuple[K, list[V]]:
535 """Pop a ``(key, list)`` tuple from the dict."""
536 try:
537 return super().popitem() # type: ignore[return-value]
538 except KeyError as e:
539 raise exceptions.BadRequestKeyError(e.args[0]) from None
540
541 def __copy__(self) -> te.Self:
542 return self.copy()
543
544 def __deepcopy__(self, memo: t.Any) -> te.Self:
545 return self.deepcopy(memo=memo)
546
547 def __repr__(self) -> str:
548 return f"{type(self).__name__}({list(self.items(multi=True))!r})"
549
550
551class CombinedMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc]
552 """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict`
553 instances as sequence and it will combine the return values of all wrapped
554 dicts:
555
556 >>> from werkzeug.datastructures import CombinedMultiDict, MultiDict
557 >>> post = MultiDict([('foo', 'bar')])
558 >>> get = MultiDict([('blub', 'blah')])
559 >>> combined = CombinedMultiDict([get, post])
560 >>> combined['foo']
561 'bar'
562 >>> combined['blub']
563 'blah'
564
565 This works for all read operations and will raise a `TypeError` for
566 methods that usually change data which isn't possible.
567
568 From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
569 subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
570 render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
571 exceptions.
572 """
573
574 def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any:
575 return type(self), (self.dicts,)
576
577 def __init__(self, dicts: cabc.Iterable[MultiDict[K, V]] | None = None) -> None:
578 super().__init__()
579 self.dicts: list[MultiDict[K, V]] = list(dicts or ())
580
581 @classmethod
582 def fromkeys(cls, keys: t.Any, value: t.Any = None) -> t.NoReturn:
583 raise TypeError(f"cannot create {cls.__name__!r} instances by fromkeys")
584
585 def __getitem__(self, key: K) -> V:
586 for d in self.dicts:
587 if key in d:
588 return d[key]
589 raise exceptions.BadRequestKeyError(key)
590
591 @t.overload # type: ignore[override]
592 def get(self, key: K) -> V | None: ...
593 @t.overload
594 def get(self, key: K, default: V) -> V: ...
595 @t.overload
596 def get(self, key: K, default: T) -> V | T: ...
597 @t.overload
598 def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ...
599 @t.overload
600 def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ...
601 def get( # type: ignore[misc]
602 self,
603 key: K,
604 default: V | T | None = None,
605 type: cabc.Callable[[V], T] | None = None,
606 ) -> V | T | None:
607 for d in self.dicts:
608 if key in d:
609 if type is not None:
610 try:
611 return type(d[key])
612 except (ValueError, TypeError):
613 continue
614 return d[key]
615 return default
616
617 @t.overload
618 def getlist(self, key: K) -> list[V]: ...
619 @t.overload
620 def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ...
621 def getlist(
622 self, key: K, type: cabc.Callable[[V], T] | None = None
623 ) -> list[V] | list[T]:
624 rv = []
625 for d in self.dicts:
626 rv.extend(d.getlist(key, type)) # type: ignore[arg-type]
627 return rv
628
629 def _keys_impl(self) -> set[K]:
630 """This function exists so __len__ can be implemented more efficiently,
631 saving one list creation from an iterator.
632 """
633 return set(k for d in self.dicts for k in d)
634
635 def keys(self) -> cabc.Iterable[K]: # type: ignore[override]
636 return self._keys_impl()
637
638 def __iter__(self) -> cabc.Iterator[K]:
639 return iter(self._keys_impl())
640
641 @t.overload # type: ignore[override]
642 def items(self) -> cabc.Iterable[tuple[K, V]]: ...
643 @t.overload
644 def items(self, multi: t.Literal[True]) -> cabc.Iterable[tuple[K, list[V]]]: ...
645 def items(
646 self, multi: bool = False
647 ) -> cabc.Iterable[tuple[K, V]] | cabc.Iterable[tuple[K, list[V]]]:
648 found = set()
649 for d in self.dicts:
650 for key, value in d.items(multi):
651 if multi:
652 yield key, value
653 elif key not in found:
654 found.add(key)
655 yield key, value
656
657 def values(self) -> cabc.Iterable[V]: # type: ignore[override]
658 for _, value in self.items():
659 yield value
660
661 def lists(self) -> cabc.Iterable[tuple[K, list[V]]]:
662 rv: dict[K, list[V]] = {}
663 for d in self.dicts:
664 for key, values in d.lists():
665 rv.setdefault(key, []).extend(values)
666 return rv.items()
667
668 def listvalues(self) -> cabc.Iterable[list[V]]:
669 return (x[1] for x in self.lists())
670
671 def copy(self) -> MultiDict[K, V]: # type: ignore[override]
672 """Return a shallow mutable copy of this object.
673
674 This returns a :class:`MultiDict` representing the data at the
675 time of copying. The copy will no longer reflect changes to the
676 wrapped dicts.
677
678 .. versionchanged:: 0.15
679 Return a mutable :class:`MultiDict`.
680 """
681 return MultiDict(self)
682
683 def __len__(self) -> int:
684 return len(self._keys_impl())
685
686 def __contains__(self, key: K) -> bool: # type: ignore[override]
687 for d in self.dicts:
688 if key in d:
689 return True
690 return False
691
692 def __repr__(self) -> str:
693 return f"{type(self).__name__}({self.dicts!r})"
694
695
696class ImmutableDict(ImmutableDictMixin[K, V], dict[K, V]): # type: ignore[misc]
697 """An immutable :class:`dict`.
698
699 .. versionadded:: 0.5
700 """
701
702 def __repr__(self) -> str:
703 return f"{type(self).__name__}({dict.__repr__(self)})"
704
705 def copy(self) -> dict[K, V]:
706 """Return a shallow mutable copy of this object. Keep in mind that
707 the standard library's :func:`copy` function is a no-op for this class
708 like for any other python immutable type (eg: :class:`tuple`).
709 """
710 return dict(self)
711
712 def __copy__(self) -> te.Self:
713 return self
714
715
716class ImmutableMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc]
717 """An immutable :class:`MultiDict`.
718
719 .. versionadded:: 0.5
720 """
721
722 def copy(self) -> MultiDict[K, V]: # type: ignore[override]
723 """Return a shallow mutable copy of this object. Keep in mind that
724 the standard library's :func:`copy` function is a no-op for this class
725 like for any other python immutable type (eg: :class:`tuple`).
726 """
727 return MultiDict(self)
728
729 def __copy__(self) -> te.Self:
730 return self
731
732
733class CallbackDict(UpdateDictMixin[K, V], dict[K, V]):
734 """A dict that calls a function passed every time something is changed.
735 The function is passed the dict instance.
736 """
737
738 def __init__(
739 self,
740 initial: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] | None = None,
741 on_update: cabc.Callable[[te.Self], None] | None = None,
742 ) -> None:
743 if initial is None:
744 super().__init__()
745 else:
746 super().__init__(initial)
747
748 self.on_update = on_update
749
750 def __repr__(self) -> str:
751 return f"<{type(self).__name__} {super().__repr__()}>"
752
753
754class HeaderSet(cabc.MutableSet[str]):
755 """Similar to the :class:`ETags` class this implements a set-like structure.
756 Unlike :class:`ETags` this is case insensitive and used for vary, allow, and
757 content-language headers.
758
759 If not constructed using the :func:`parse_set_header` function the
760 instantiation works like this:
761
762 >>> hs = HeaderSet(['foo', 'bar', 'baz'])
763 >>> hs
764 HeaderSet(['foo', 'bar', 'baz'])
765
766 .. versionchanged:: 3.2
767 The ``on_update`` parameter was removed.
768 """
769
770 def __init__(self, headers: cabc.Iterable[str] | None = None) -> None:
771 self._headers = list(headers or ())
772 self._set = {x.lower() for x in self._headers}
773 self._on_update: cabc.Callable[[HeaderSet], None] | None = None
774
775 def add(self, header: str) -> None:
776 """Add a new header to the set."""
777 self.update((header,))
778
779 def remove(self: te.Self, header: str) -> None:
780 """Remove a header from the set. This raises an :exc:`KeyError` if the
781 header is not in the set.
782
783 .. versionchanged:: 0.5
784 In older versions a :exc:`IndexError` was raised instead of a
785 :exc:`KeyError` if the object was missing.
786
787 :param header: the header to be removed.
788 """
789 key = header.lower()
790 if key not in self._set:
791 raise KeyError(header)
792 self._set.remove(key)
793 for idx, key in enumerate(self._headers):
794 if key.lower() == header:
795 del self._headers[idx]
796 break
797 if self._on_update is not None:
798 self._on_update(self)
799
800 def update(self: te.Self, iterable: cabc.Iterable[str]) -> None:
801 """Add all the headers from the iterable to the set.
802
803 :param iterable: updates the set with the items from the iterable.
804 """
805 inserted_any = False
806 for header in iterable:
807 key = header.lower()
808 if key not in self._set:
809 self._headers.append(header)
810 self._set.add(key)
811 inserted_any = True
812 if inserted_any and self._on_update is not None:
813 self._on_update(self)
814
815 def discard(self, header: str) -> None:
816 """Like :meth:`remove` but ignores errors.
817
818 :param header: the header to be discarded.
819 """
820 try:
821 self.remove(header)
822 except KeyError:
823 pass
824
825 def find(self, header: str) -> int:
826 """Return the index of the header in the set or return -1 if not found.
827
828 :param header: the header to be looked up.
829 """
830 header = header.lower()
831 for idx, item in enumerate(self._headers):
832 if item.lower() == header:
833 return idx
834 return -1
835
836 def index(self, header: str) -> int:
837 """Return the index of the header in the set or raise an
838 :exc:`IndexError`.
839
840 :param header: the header to be looked up.
841 """
842 rv = self.find(header)
843 if rv < 0:
844 raise IndexError(header)
845 return rv
846
847 def clear(self: te.Self) -> None:
848 """Clear the set."""
849 self._set.clear()
850 self._headers.clear()
851
852 if self._on_update is not None:
853 self._on_update(self)
854
855 def as_set(self, preserve_casing: bool = False) -> set[str]:
856 """Return the set as real python set type. When calling this, all
857 the items are converted to lowercase and the ordering is lost.
858
859 :param preserve_casing: if set to `True` the items in the set returned
860 will have the original case like in the
861 :class:`HeaderSet`, otherwise they will
862 be lowercase.
863 """
864 if preserve_casing:
865 return set(self._headers)
866 return set(self._set)
867
868 @classmethod
869 def from_header(cls, value: str | None) -> te.Self:
870 """Parse a header value and create an instance of this class.
871
872 .. versionadded:: 3.2
873 """
874 if not value:
875 return cls()
876
877 return cls(parse_list_header(value))
878
879 def to_header(self) -> str:
880 """Convert to a header value."""
881 return dump_header(self._headers)
882
883 def __getitem__(self, idx: t.SupportsIndex) -> str:
884 return self._headers[idx]
885
886 def __delitem__(self: te.Self, idx: t.SupportsIndex) -> None:
887 rv = self._headers.pop(idx)
888 self._set.remove(rv.lower())
889 if self._on_update is not None:
890 self._on_update(self)
891
892 def __setitem__(self: te.Self, idx: t.SupportsIndex, value: str) -> None:
893 old = self._headers[idx]
894 self._set.remove(old.lower())
895 self._headers[idx] = value
896 self._set.add(value.lower())
897 if self._on_update is not None:
898 self._on_update(self)
899
900 def __contains__(self, header: str) -> bool: # type: ignore[override]
901 return header.lower() in self._set
902
903 def __len__(self) -> int:
904 return len(self._set)
905
906 def __iter__(self) -> cabc.Iterator[str]:
907 return iter(self._headers)
908
909 def __bool__(self) -> bool:
910 return bool(self._set)
911
912 def __str__(self) -> str:
913 return self.to_header()
914
915 def __repr__(self) -> str:
916 return f"{type(self).__name__}({self._headers!r})"