123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239 |
- from __future__ import annotations
- import collections.abc as cabc
- import typing as t
- from copy import deepcopy
- from .. import exceptions
- from .._internal import _missing
- from .mixins import ImmutableDictMixin
- from .mixins import ImmutableListMixin
- from .mixins import ImmutableMultiDictMixin
- from .mixins import UpdateDictMixin
- if t.TYPE_CHECKING:
- import typing_extensions as te
- K = t.TypeVar("K")
- V = t.TypeVar("V")
- T = t.TypeVar("T")
- def iter_multi_items(
- mapping: (
- MultiDict[K, V]
- | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- | cabc.Iterable[tuple[K, V]]
- ),
- ) -> cabc.Iterator[tuple[K, V]]:
- """Iterates over the items of a mapping yielding keys and values
- without dropping any from more complex structures.
- """
- if isinstance(mapping, MultiDict):
- yield from mapping.items(multi=True)
- elif isinstance(mapping, cabc.Mapping):
- for key, value in mapping.items():
- if isinstance(value, (list, tuple, set)):
- for v in value:
- yield key, v
- else:
- yield key, value
- else:
- yield from mapping
- class ImmutableList(ImmutableListMixin, list[V]): # type: ignore[misc]
- """An immutable :class:`list`.
- .. versionadded:: 0.5
- :private:
- """
- def __repr__(self) -> str:
- return f"{type(self).__name__}({list.__repr__(self)})"
- class TypeConversionDict(dict[K, V]):
- """Works like a regular dict but the :meth:`get` method can perform
- type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
- are subclasses of this class and provide the same feature.
- .. versionadded:: 0.5
- """
- @t.overload # type: ignore[override]
- def get(self, key: K) -> V | None: ...
- @t.overload
- def get(self, key: K, default: V) -> V: ...
- @t.overload
- def get(self, key: K, default: T) -> V | T: ...
- @t.overload
- def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ...
- @t.overload
- def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ...
- def get( # type: ignore[misc]
- self,
- key: K,
- default: V | T | None = None,
- type: cabc.Callable[[V], T] | None = None,
- ) -> V | T | None:
- """Return the default value if the requested data doesn't exist.
- If `type` is provided and is a callable it should convert the value,
- return it or raise a :exc:`ValueError` if that is not possible. In
- this case the function will return the default as if the value was not
- found:
- >>> d = TypeConversionDict(foo='42', bar='blub')
- >>> d.get('foo', type=int)
- 42
- >>> d.get('bar', -1, type=int)
- -1
- :param key: The key to be looked up.
- :param default: The default value to be returned if the key can't
- be looked up. If not further specified `None` is
- returned.
- :param type: A callable that is used to cast the value in the
- :class:`MultiDict`. If a :exc:`ValueError` or a
- :exc:`TypeError` is raised by this callable the default
- value is returned.
- .. versionchanged:: 3.0.2
- Returns the default value on :exc:`TypeError`, too.
- """
- try:
- rv = self[key]
- except KeyError:
- return default
- if type is None:
- return rv
- try:
- return type(rv)
- except (ValueError, TypeError):
- return default
- class ImmutableTypeConversionDict(ImmutableDictMixin[K, V], TypeConversionDict[K, V]): # type: ignore[misc]
- """Works like a :class:`TypeConversionDict` but does not support
- modifications.
- .. versionadded:: 0.5
- """
- def copy(self) -> TypeConversionDict[K, V]:
- """Return a shallow mutable copy of this object. Keep in mind that
- the standard library's :func:`copy` function is a no-op for this class
- like for any other python immutable type (eg: :class:`tuple`).
- """
- return TypeConversionDict(self)
- def __copy__(self) -> te.Self:
- return self
- class MultiDict(TypeConversionDict[K, V]):
- """A :class:`MultiDict` is a dictionary subclass customized to deal with
- multiple values for the same key which is for example used by the parsing
- functions in the wrappers. This is necessary because some HTML form
- elements pass multiple values for the same key.
- :class:`MultiDict` implements all standard dictionary methods.
- Internally, it saves all values for a key as a list, but the standard dict
- access methods will only return the first value for a key. If you want to
- gain access to the other values, too, you have to use the `list` methods as
- explained below.
- Basic Usage:
- >>> d = MultiDict([('a', 'b'), ('a', 'c')])
- >>> d
- MultiDict([('a', 'b'), ('a', 'c')])
- >>> d['a']
- 'b'
- >>> d.getlist('a')
- ['b', 'c']
- >>> 'a' in d
- True
- It behaves like a normal dict thus all dict functions will only return the
- first value when multiple values for one key are found.
- From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
- subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
- render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
- exceptions.
- A :class:`MultiDict` can be constructed from an iterable of
- ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
- onwards some keyword parameters.
- :param mapping: the initial value for the :class:`MultiDict`. Either a
- regular dict, an iterable of ``(key, value)`` tuples
- or `None`.
- .. versionchanged:: 3.1
- Implement ``|`` and ``|=`` operators.
- """
- def __init__(
- self,
- mapping: (
- MultiDict[K, V]
- | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- | cabc.Iterable[tuple[K, V]]
- | None
- ) = None,
- ) -> None:
- if mapping is None:
- super().__init__()
- elif isinstance(mapping, MultiDict):
- super().__init__((k, vs[:]) for k, vs in mapping.lists())
- elif isinstance(mapping, cabc.Mapping):
- tmp = {}
- for key, value in mapping.items():
- if isinstance(value, (list, tuple, set)):
- value = list(value)
- if not value:
- continue
- else:
- value = [value]
- tmp[key] = value
- super().__init__(tmp) # type: ignore[arg-type]
- else:
- tmp = {}
- for key, value in mapping:
- tmp.setdefault(key, []).append(value)
- super().__init__(tmp) # type: ignore[arg-type]
- def __getstate__(self) -> t.Any:
- return dict(self.lists())
- def __setstate__(self, value: t.Any) -> None:
- super().clear()
- super().update(value)
- def __iter__(self) -> cabc.Iterator[K]:
- # https://github.com/python/cpython/issues/87412
- # If __iter__ is not overridden, Python uses a fast path for dict(md),
- # taking the data directly and getting lists of values, rather than
- # calling __getitem__ and getting only the first value.
- return super().__iter__()
- def __getitem__(self, key: K) -> V:
- """Return the first data value for this key;
- raises KeyError if not found.
- :param key: The key to be looked up.
- :raise KeyError: if the key does not exist.
- """
- if key in self:
- lst = super().__getitem__(key)
- if len(lst) > 0: # type: ignore[arg-type]
- return lst[0] # type: ignore[index,no-any-return]
- raise exceptions.BadRequestKeyError(key)
- def __setitem__(self, key: K, value: V) -> None:
- """Like :meth:`add` but removes an existing key first.
- :param key: the key for the value.
- :param value: the value to set.
- """
- super().__setitem__(key, [value]) # type: ignore[assignment]
- def add(self, key: K, value: V) -> None:
- """Adds a new value for the key.
- .. versionadded:: 0.6
- :param key: the key for the value.
- :param value: the value to add.
- """
- super().setdefault(key, []).append(value) # type: ignore[arg-type,attr-defined]
- @t.overload
- def getlist(self, key: K) -> list[V]: ...
- @t.overload
- def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ...
- def getlist(
- self, key: K, type: cabc.Callable[[V], T] | None = None
- ) -> list[V] | list[T]:
- """Return the list of items for a given key. If that key is not in the
- `MultiDict`, the return value will be an empty list. Just like `get`,
- `getlist` accepts a `type` parameter. All items will be converted
- with the callable defined there.
- :param key: The key to be looked up.
- :param type: Callable to convert each value. If a ``ValueError`` or
- ``TypeError`` is raised, the value is omitted.
- :return: a :class:`list` of all the values for the key.
- .. versionchanged:: 3.1
- Catches ``TypeError`` in addition to ``ValueError``.
- """
- try:
- rv: list[V] = super().__getitem__(key) # type: ignore[assignment]
- except KeyError:
- return []
- if type is None:
- return list(rv)
- result = []
- for item in rv:
- try:
- result.append(type(item))
- except (ValueError, TypeError):
- pass
- return result
- def setlist(self, key: K, new_list: cabc.Iterable[V]) -> None:
- """Remove the old values for a key and add new ones. Note that the list
- you pass the values in will be shallow-copied before it is inserted in
- the dictionary.
- >>> d = MultiDict()
- >>> d.setlist('foo', ['1', '2'])
- >>> d['foo']
- '1'
- >>> d.getlist('foo')
- ['1', '2']
- :param key: The key for which the values are set.
- :param new_list: An iterable with the new values for the key. Old values
- are removed first.
- """
- super().__setitem__(key, list(new_list)) # type: ignore[assignment]
- @t.overload
- def setdefault(self, key: K) -> None: ...
- @t.overload
- def setdefault(self, key: K, default: V) -> V: ...
- def setdefault(self, key: K, default: V | None = None) -> V | None:
- """Returns the value for the key if it is in the dict, otherwise it
- returns `default` and sets that value for `key`.
- :param key: The key to be looked up.
- :param default: The default value to be returned if the key is not
- in the dict. If not further specified it's `None`.
- """
- if key not in self:
- self[key] = default # type: ignore[assignment]
- return self[key]
- def setlistdefault(
- self, key: K, default_list: cabc.Iterable[V] | None = None
- ) -> list[V]:
- """Like `setdefault` but sets multiple values. The list returned
- is not a copy, but the list that is actually used internally. This
- means that you can put new values into the dict by appending items
- to the list:
- >>> d = MultiDict({"foo": 1})
- >>> d.setlistdefault("foo").extend([2, 3])
- >>> d.getlist("foo")
- [1, 2, 3]
- :param key: The key to be looked up.
- :param default_list: An iterable of default values. It is either copied
- (in case it was a list) or converted into a list
- before returned.
- :return: a :class:`list`
- """
- if key not in self:
- super().__setitem__(key, list(default_list or ())) # type: ignore[assignment]
- return super().__getitem__(key) # type: ignore[return-value]
- def items(self, multi: bool = False) -> cabc.Iterable[tuple[K, V]]: # type: ignore[override]
- """Return an iterator of ``(key, value)`` pairs.
- :param multi: If set to `True` the iterator returned will have a pair
- for each value of each key. Otherwise it will only
- contain pairs for the first value of each key.
- """
- values: list[V]
- for key, values in super().items(): # type: ignore[assignment]
- if multi:
- for value in values:
- yield key, value
- else:
- yield key, values[0]
- def lists(self) -> cabc.Iterable[tuple[K, list[V]]]:
- """Return a iterator of ``(key, values)`` pairs, where values is the list
- of all values associated with the key."""
- values: list[V]
- for key, values in super().items(): # type: ignore[assignment]
- yield key, list(values)
- def values(self) -> cabc.Iterable[V]: # type: ignore[override]
- """Returns an iterator of the first value on every key's value list."""
- values: list[V]
- for values in super().values(): # type: ignore[assignment]
- yield values[0]
- def listvalues(self) -> cabc.Iterable[list[V]]:
- """Return an iterator of all values associated with a key. Zipping
- :meth:`keys` and this is the same as calling :meth:`lists`:
- >>> d = MultiDict({"foo": [1, 2, 3]})
- >>> zip(d.keys(), d.listvalues()) == d.lists()
- True
- """
- return super().values() # type: ignore[return-value]
- def copy(self) -> te.Self:
- """Return a shallow copy of this object."""
- return self.__class__(self)
- def deepcopy(self, memo: t.Any = None) -> te.Self:
- """Return a deep copy of this object."""
- return self.__class__(deepcopy(self.to_dict(flat=False), memo))
- @t.overload
- def to_dict(self) -> dict[K, V]: ...
- @t.overload
- def to_dict(self, flat: t.Literal[False]) -> dict[K, list[V]]: ...
- def to_dict(self, flat: bool = True) -> dict[K, V] | dict[K, list[V]]:
- """Return the contents as regular dict. If `flat` is `True` the
- returned dict will only have the first item present, if `flat` is
- `False` all values will be returned as lists.
- :param flat: If set to `False` the dict returned will have lists
- with all the values in it. Otherwise it will only
- contain the first value for each key.
- :return: a :class:`dict`
- """
- if flat:
- return dict(self.items())
- return dict(self.lists())
- def update( # type: ignore[override]
- self,
- mapping: (
- MultiDict[K, V]
- | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- | cabc.Iterable[tuple[K, V]]
- ),
- ) -> None:
- """update() extends rather than replaces existing key lists:
- >>> a = MultiDict({'x': 1})
- >>> b = MultiDict({'x': 2, 'y': 3})
- >>> a.update(b)
- >>> a
- MultiDict([('y', 3), ('x', 1), ('x', 2)])
- If the value list for a key in ``other_dict`` is empty, no new values
- will be added to the dict and the key will not be created:
- >>> x = {'empty_list': []}
- >>> y = MultiDict()
- >>> y.update(x)
- >>> y
- MultiDict([])
- """
- for key, value in iter_multi_items(mapping):
- self.add(key, value)
- def __or__( # type: ignore[override]
- self, other: cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- ) -> MultiDict[K, V]:
- if not isinstance(other, cabc.Mapping):
- return NotImplemented
- rv = self.copy()
- rv.update(other)
- return rv
- def __ior__( # type: ignore[override]
- self,
- other: (
- cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- | cabc.Iterable[tuple[K, V]]
- ),
- ) -> te.Self:
- if not isinstance(other, (cabc.Mapping, cabc.Iterable)):
- return NotImplemented
- self.update(other)
- return self
- @t.overload
- def pop(self, key: K) -> V: ...
- @t.overload
- def pop(self, key: K, default: V) -> V: ...
- @t.overload
- def pop(self, key: K, default: T) -> V | T: ...
- def pop(
- self,
- key: K,
- default: V | T = _missing, # type: ignore[assignment]
- ) -> V | T:
- """Pop the first item for a list on the dict. Afterwards the
- key is removed from the dict, so additional values are discarded:
- >>> d = MultiDict({"foo": [1, 2, 3]})
- >>> d.pop("foo")
- 1
- >>> "foo" in d
- False
- :param key: the key to pop.
- :param default: if provided the value to return if the key was
- not in the dictionary.
- """
- lst: list[V]
- try:
- lst = super().pop(key) # type: ignore[assignment]
- if len(lst) == 0:
- raise exceptions.BadRequestKeyError(key)
- return lst[0]
- except KeyError:
- if default is not _missing:
- return default
- raise exceptions.BadRequestKeyError(key) from None
- def popitem(self) -> tuple[K, V]:
- """Pop an item from the dict."""
- item: tuple[K, list[V]]
- try:
- item = super().popitem() # type: ignore[assignment]
- if len(item[1]) == 0:
- raise exceptions.BadRequestKeyError(item[0])
- return item[0], item[1][0]
- except KeyError as e:
- raise exceptions.BadRequestKeyError(e.args[0]) from None
- def poplist(self, key: K) -> list[V]:
- """Pop the list for a key from the dict. If the key is not in the dict
- an empty list is returned.
- .. versionchanged:: 0.5
- If the key does no longer exist a list is returned instead of
- raising an error.
- """
- return super().pop(key, []) # type: ignore[return-value]
- def popitemlist(self) -> tuple[K, list[V]]:
- """Pop a ``(key, list)`` tuple from the dict."""
- try:
- return super().popitem() # type: ignore[return-value]
- except KeyError as e:
- raise exceptions.BadRequestKeyError(e.args[0]) from None
- def __copy__(self) -> te.Self:
- return self.copy()
- def __deepcopy__(self, memo: t.Any) -> te.Self:
- return self.deepcopy(memo=memo)
- def __repr__(self) -> str:
- return f"{type(self).__name__}({list(self.items(multi=True))!r})"
- class _omd_bucket(t.Generic[K, V]):
- """Wraps values in the :class:`OrderedMultiDict`. This makes it
- possible to keep an order over multiple different keys. It requires
- a lot of extra memory and slows down access a lot, but makes it
- possible to access elements in O(1) and iterate in O(n).
- """
- __slots__ = ("prev", "key", "value", "next")
- def __init__(self, omd: _OrderedMultiDict[K, V], key: K, value: V) -> None:
- self.prev: _omd_bucket[K, V] | None = omd._last_bucket
- self.key: K = key
- self.value: V = value
- self.next: _omd_bucket[K, V] | None = None
- if omd._first_bucket is None:
- omd._first_bucket = self
- if omd._last_bucket is not None:
- omd._last_bucket.next = self
- omd._last_bucket = self
- def unlink(self, omd: _OrderedMultiDict[K, V]) -> None:
- if self.prev:
- self.prev.next = self.next
- if self.next:
- self.next.prev = self.prev
- if omd._first_bucket is self:
- omd._first_bucket = self.next
- if omd._last_bucket is self:
- omd._last_bucket = self.prev
- class _OrderedMultiDict(MultiDict[K, V]):
- """Works like a regular :class:`MultiDict` but preserves the
- order of the fields. To convert the ordered multi dict into a
- list you can use the :meth:`items` method and pass it ``multi=True``.
- In general an :class:`OrderedMultiDict` is an order of magnitude
- slower than a :class:`MultiDict`.
- .. admonition:: note
- Due to a limitation in Python you cannot convert an ordered
- multi dict into a regular dict by using ``dict(multidict)``.
- Instead you have to use the :meth:`to_dict` method, otherwise
- the internal bucket objects are exposed.
- .. deprecated:: 3.1
- Will be removed in Werkzeug 3.2. Use ``MultiDict`` instead.
- """
- def __init__(
- self,
- mapping: (
- MultiDict[K, V]
- | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- | cabc.Iterable[tuple[K, V]]
- | None
- ) = None,
- ) -> None:
- import warnings
- warnings.warn(
- "'OrderedMultiDict' is deprecated and will be removed in Werkzeug"
- " 3.2. Use 'MultiDict' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- super().__init__()
- self._first_bucket: _omd_bucket[K, V] | None = None
- self._last_bucket: _omd_bucket[K, V] | None = None
- if mapping is not None:
- self.update(mapping)
- def __eq__(self, other: object) -> bool:
- if not isinstance(other, MultiDict):
- return NotImplemented
- if isinstance(other, _OrderedMultiDict):
- iter1 = iter(self.items(multi=True))
- iter2 = iter(other.items(multi=True))
- try:
- for k1, v1 in iter1:
- k2, v2 = next(iter2)
- if k1 != k2 or v1 != v2:
- return False
- except StopIteration:
- return False
- try:
- next(iter2)
- except StopIteration:
- return True
- return False
- if len(self) != len(other):
- return False
- for key, values in self.lists():
- if other.getlist(key) != values:
- return False
- return True
- __hash__ = None # type: ignore[assignment]
- def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any:
- return type(self), (list(self.items(multi=True)),)
- def __getstate__(self) -> t.Any:
- return list(self.items(multi=True))
- def __setstate__(self, values: t.Any) -> None:
- self.clear()
- for key, value in values:
- self.add(key, value)
- def __getitem__(self, key: K) -> V:
- if key in self:
- return dict.__getitem__(self, key)[0].value # type: ignore[index,no-any-return]
- raise exceptions.BadRequestKeyError(key)
- def __setitem__(self, key: K, value: V) -> None:
- self.poplist(key)
- self.add(key, value)
- def __delitem__(self, key: K) -> None:
- self.pop(key)
- def keys(self) -> cabc.Iterable[K]: # type: ignore[override]
- return (key for key, _ in self.items())
- def __iter__(self) -> cabc.Iterator[K]:
- return iter(self.keys())
- def values(self) -> cabc.Iterable[V]: # type: ignore[override]
- return (value for key, value in self.items())
- def items(self, multi: bool = False) -> cabc.Iterable[tuple[K, V]]: # type: ignore[override]
- ptr = self._first_bucket
- if multi:
- while ptr is not None:
- yield ptr.key, ptr.value
- ptr = ptr.next
- else:
- returned_keys = set()
- while ptr is not None:
- if ptr.key not in returned_keys:
- returned_keys.add(ptr.key)
- yield ptr.key, ptr.value
- ptr = ptr.next
- def lists(self) -> cabc.Iterable[tuple[K, list[V]]]:
- returned_keys = set()
- ptr = self._first_bucket
- while ptr is not None:
- if ptr.key not in returned_keys:
- yield ptr.key, self.getlist(ptr.key)
- returned_keys.add(ptr.key)
- ptr = ptr.next
- def listvalues(self) -> cabc.Iterable[list[V]]:
- for _key, values in self.lists():
- yield values
- def add(self, key: K, value: V) -> None:
- dict.setdefault(self, key, []).append(_omd_bucket(self, key, value)) # type: ignore[arg-type,attr-defined]
- @t.overload
- def getlist(self, key: K) -> list[V]: ...
- @t.overload
- def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ...
- def getlist(
- self, key: K, type: cabc.Callable[[V], T] | None = None
- ) -> list[V] | list[T]:
- rv: list[_omd_bucket[K, V]]
- try:
- rv = dict.__getitem__(self, key) # type: ignore[index]
- except KeyError:
- return []
- if type is None:
- return [x.value for x in rv]
- result = []
- for item in rv:
- try:
- result.append(type(item.value))
- except (ValueError, TypeError):
- pass
- return result
- def setlist(self, key: K, new_list: cabc.Iterable[V]) -> None:
- self.poplist(key)
- for value in new_list:
- self.add(key, value)
- def setlistdefault(self, key: t.Any, default_list: t.Any = None) -> t.NoReturn:
- raise TypeError("setlistdefault is unsupported for ordered multi dicts")
- def update( # type: ignore[override]
- self,
- mapping: (
- MultiDict[K, V]
- | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- | cabc.Iterable[tuple[K, V]]
- ),
- ) -> None:
- for key, value in iter_multi_items(mapping):
- self.add(key, value)
- def poplist(self, key: K) -> list[V]:
- buckets: cabc.Iterable[_omd_bucket[K, V]] = dict.pop(self, key, ()) # type: ignore[arg-type]
- for bucket in buckets:
- bucket.unlink(self)
- return [x.value for x in buckets]
- @t.overload
- def pop(self, key: K) -> V: ...
- @t.overload
- def pop(self, key: K, default: V) -> V: ...
- @t.overload
- def pop(self, key: K, default: T) -> V | T: ...
- def pop(
- self,
- key: K,
- default: V | T = _missing, # type: ignore[assignment]
- ) -> V | T:
- buckets: list[_omd_bucket[K, V]]
- try:
- buckets = dict.pop(self, key) # type: ignore[arg-type]
- except KeyError:
- if default is not _missing:
- return default
- raise exceptions.BadRequestKeyError(key) from None
- for bucket in buckets:
- bucket.unlink(self)
- return buckets[0].value
- def popitem(self) -> tuple[K, V]:
- key: K
- buckets: list[_omd_bucket[K, V]]
- try:
- key, buckets = dict.popitem(self) # type: ignore[arg-type,assignment]
- except KeyError as e:
- raise exceptions.BadRequestKeyError(e.args[0]) from None
- for bucket in buckets:
- bucket.unlink(self)
- return key, buckets[0].value
- def popitemlist(self) -> tuple[K, list[V]]:
- key: K
- buckets: list[_omd_bucket[K, V]]
- try:
- key, buckets = dict.popitem(self) # type: ignore[arg-type,assignment]
- except KeyError as e:
- raise exceptions.BadRequestKeyError(e.args[0]) from None
- for bucket in buckets:
- bucket.unlink(self)
- return key, [x.value for x in buckets]
- class CombinedMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc]
- """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict`
- instances as sequence and it will combine the return values of all wrapped
- dicts:
- >>> from werkzeug.datastructures import CombinedMultiDict, MultiDict
- >>> post = MultiDict([('foo', 'bar')])
- >>> get = MultiDict([('blub', 'blah')])
- >>> combined = CombinedMultiDict([get, post])
- >>> combined['foo']
- 'bar'
- >>> combined['blub']
- 'blah'
- This works for all read operations and will raise a `TypeError` for
- methods that usually change data which isn't possible.
- From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
- subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
- render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
- exceptions.
- """
- def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any:
- return type(self), (self.dicts,)
- def __init__(self, dicts: cabc.Iterable[MultiDict[K, V]] | None = None) -> None:
- super().__init__()
- self.dicts: list[MultiDict[K, V]] = list(dicts or ())
- @classmethod
- def fromkeys(cls, keys: t.Any, value: t.Any = None) -> t.NoReturn:
- raise TypeError(f"cannot create {cls.__name__!r} instances by fromkeys")
- def __getitem__(self, key: K) -> V:
- for d in self.dicts:
- if key in d:
- return d[key]
- raise exceptions.BadRequestKeyError(key)
- @t.overload # type: ignore[override]
- def get(self, key: K) -> V | None: ...
- @t.overload
- def get(self, key: K, default: V) -> V: ...
- @t.overload
- def get(self, key: K, default: T) -> V | T: ...
- @t.overload
- def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ...
- @t.overload
- def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ...
- def get( # type: ignore[misc]
- self,
- key: K,
- default: V | T | None = None,
- type: cabc.Callable[[V], T] | None = None,
- ) -> V | T | None:
- for d in self.dicts:
- if key in d:
- if type is not None:
- try:
- return type(d[key])
- except (ValueError, TypeError):
- continue
- return d[key]
- return default
- @t.overload
- def getlist(self, key: K) -> list[V]: ...
- @t.overload
- def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ...
- def getlist(
- self, key: K, type: cabc.Callable[[V], T] | None = None
- ) -> list[V] | list[T]:
- rv = []
- for d in self.dicts:
- rv.extend(d.getlist(key, type)) # type: ignore[arg-type]
- return rv
- def _keys_impl(self) -> set[K]:
- """This function exists so __len__ can be implemented more efficiently,
- saving one list creation from an iterator.
- """
- return set(k for d in self.dicts for k in d)
- def keys(self) -> cabc.Iterable[K]: # type: ignore[override]
- return self._keys_impl()
- def __iter__(self) -> cabc.Iterator[K]:
- return iter(self._keys_impl())
- @t.overload # type: ignore[override]
- def items(self) -> cabc.Iterable[tuple[K, V]]: ...
- @t.overload
- def items(self, multi: t.Literal[True]) -> cabc.Iterable[tuple[K, list[V]]]: ...
- def items(
- self, multi: bool = False
- ) -> cabc.Iterable[tuple[K, V]] | cabc.Iterable[tuple[K, list[V]]]:
- found = set()
- for d in self.dicts:
- for key, value in d.items(multi):
- if multi:
- yield key, value
- elif key not in found:
- found.add(key)
- yield key, value
- def values(self) -> cabc.Iterable[V]: # type: ignore[override]
- for _, value in self.items():
- yield value
- def lists(self) -> cabc.Iterable[tuple[K, list[V]]]:
- rv: dict[K, list[V]] = {}
- for d in self.dicts:
- for key, values in d.lists():
- rv.setdefault(key, []).extend(values)
- return rv.items()
- def listvalues(self) -> cabc.Iterable[list[V]]:
- return (x[1] for x in self.lists())
- def copy(self) -> MultiDict[K, V]: # type: ignore[override]
- """Return a shallow mutable copy of this object.
- This returns a :class:`MultiDict` representing the data at the
- time of copying. The copy will no longer reflect changes to the
- wrapped dicts.
- .. versionchanged:: 0.15
- Return a mutable :class:`MultiDict`.
- """
- return MultiDict(self)
- def __len__(self) -> int:
- return len(self._keys_impl())
- def __contains__(self, key: K) -> bool: # type: ignore[override]
- for d in self.dicts:
- if key in d:
- return True
- return False
- def __repr__(self) -> str:
- return f"{type(self).__name__}({self.dicts!r})"
- class ImmutableDict(ImmutableDictMixin[K, V], dict[K, V]): # type: ignore[misc]
- """An immutable :class:`dict`.
- .. versionadded:: 0.5
- """
- def __repr__(self) -> str:
- return f"{type(self).__name__}({dict.__repr__(self)})"
- def copy(self) -> dict[K, V]:
- """Return a shallow mutable copy of this object. Keep in mind that
- the standard library's :func:`copy` function is a no-op for this class
- like for any other python immutable type (eg: :class:`tuple`).
- """
- return dict(self)
- def __copy__(self) -> te.Self:
- return self
- class ImmutableMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc]
- """An immutable :class:`MultiDict`.
- .. versionadded:: 0.5
- """
- def copy(self) -> MultiDict[K, V]: # type: ignore[override]
- """Return a shallow mutable copy of this object. Keep in mind that
- the standard library's :func:`copy` function is a no-op for this class
- like for any other python immutable type (eg: :class:`tuple`).
- """
- return MultiDict(self)
- def __copy__(self) -> te.Self:
- return self
- class _ImmutableOrderedMultiDict( # type: ignore[misc]
- ImmutableMultiDictMixin[K, V], _OrderedMultiDict[K, V]
- ):
- """An immutable :class:`OrderedMultiDict`.
- .. deprecated:: 3.1
- Will be removed in Werkzeug 3.2. Use ``ImmutableMultiDict`` instead.
- .. versionadded:: 0.6
- """
- def __init__(
- self,
- mapping: (
- MultiDict[K, V]
- | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]]
- | cabc.Iterable[tuple[K, V]]
- | None
- ) = None,
- ) -> None:
- super().__init__()
- if mapping is not None:
- for k, v in iter_multi_items(mapping):
- _OrderedMultiDict.add(self, k, v)
- def _iter_hashitems(self) -> cabc.Iterable[t.Any]:
- return enumerate(self.items(multi=True))
- def copy(self) -> _OrderedMultiDict[K, V]: # type: ignore[override]
- """Return a shallow mutable copy of this object. Keep in mind that
- the standard library's :func:`copy` function is a no-op for this class
- like for any other python immutable type (eg: :class:`tuple`).
- """
- return _OrderedMultiDict(self)
- def __copy__(self) -> te.Self:
- return self
- class CallbackDict(UpdateDictMixin[K, V], dict[K, V]):
- """A dict that calls a function passed every time something is changed.
- The function is passed the dict instance.
- """
- def __init__(
- self,
- initial: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] | None = None,
- on_update: cabc.Callable[[te.Self], None] | None = None,
- ) -> None:
- if initial is None:
- super().__init__()
- else:
- super().__init__(initial)
- self.on_update = on_update
- def __repr__(self) -> str:
- return f"<{type(self).__name__} {super().__repr__()}>"
- class HeaderSet(cabc.MutableSet[str]):
- """Similar to the :class:`ETags` class this implements a set-like structure.
- Unlike :class:`ETags` this is case insensitive and used for vary, allow, and
- content-language headers.
- If not constructed using the :func:`parse_set_header` function the
- instantiation works like this:
- >>> hs = HeaderSet(['foo', 'bar', 'baz'])
- >>> hs
- HeaderSet(['foo', 'bar', 'baz'])
- """
- def __init__(
- self,
- headers: cabc.Iterable[str] | None = None,
- on_update: cabc.Callable[[te.Self], None] | None = None,
- ) -> None:
- self._headers = list(headers or ())
- self._set = {x.lower() for x in self._headers}
- self.on_update = on_update
- def add(self, header: str) -> None:
- """Add a new header to the set."""
- self.update((header,))
- def remove(self: te.Self, header: str) -> None:
- """Remove a header from the set. This raises an :exc:`KeyError` if the
- header is not in the set.
- .. versionchanged:: 0.5
- In older versions a :exc:`IndexError` was raised instead of a
- :exc:`KeyError` if the object was missing.
- :param header: the header to be removed.
- """
- key = header.lower()
- if key not in self._set:
- raise KeyError(header)
- self._set.remove(key)
- for idx, key in enumerate(self._headers):
- if key.lower() == header:
- del self._headers[idx]
- break
- if self.on_update is not None:
- self.on_update(self)
- def update(self: te.Self, iterable: cabc.Iterable[str]) -> None:
- """Add all the headers from the iterable to the set.
- :param iterable: updates the set with the items from the iterable.
- """
- inserted_any = False
- for header in iterable:
- key = header.lower()
- if key not in self._set:
- self._headers.append(header)
- self._set.add(key)
- inserted_any = True
- if inserted_any and self.on_update is not None:
- self.on_update(self)
- def discard(self, header: str) -> None:
- """Like :meth:`remove` but ignores errors.
- :param header: the header to be discarded.
- """
- try:
- self.remove(header)
- except KeyError:
- pass
- def find(self, header: str) -> int:
- """Return the index of the header in the set or return -1 if not found.
- :param header: the header to be looked up.
- """
- header = header.lower()
- for idx, item in enumerate(self._headers):
- if item.lower() == header:
- return idx
- return -1
- def index(self, header: str) -> int:
- """Return the index of the header in the set or raise an
- :exc:`IndexError`.
- :param header: the header to be looked up.
- """
- rv = self.find(header)
- if rv < 0:
- raise IndexError(header)
- return rv
- def clear(self: te.Self) -> None:
- """Clear the set."""
- self._set.clear()
- self._headers.clear()
- if self.on_update is not None:
- self.on_update(self)
- def as_set(self, preserve_casing: bool = False) -> set[str]:
- """Return the set as real python set type. When calling this, all
- the items are converted to lowercase and the ordering is lost.
- :param preserve_casing: if set to `True` the items in the set returned
- will have the original case like in the
- :class:`HeaderSet`, otherwise they will
- be lowercase.
- """
- if preserve_casing:
- return set(self._headers)
- return set(self._set)
- def to_header(self) -> str:
- """Convert the header set into an HTTP header string."""
- return ", ".join(map(http.quote_header_value, self._headers))
- def __getitem__(self, idx: t.SupportsIndex) -> str:
- return self._headers[idx]
- def __delitem__(self: te.Self, idx: t.SupportsIndex) -> None:
- rv = self._headers.pop(idx)
- self._set.remove(rv.lower())
- if self.on_update is not None:
- self.on_update(self)
- def __setitem__(self: te.Self, idx: t.SupportsIndex, value: str) -> None:
- old = self._headers[idx]
- self._set.remove(old.lower())
- self._headers[idx] = value
- self._set.add(value.lower())
- if self.on_update is not None:
- self.on_update(self)
- def __contains__(self, header: str) -> bool: # type: ignore[override]
- return header.lower() in self._set
- def __len__(self) -> int:
- return len(self._set)
- def __iter__(self) -> cabc.Iterator[str]:
- return iter(self._headers)
- def __bool__(self) -> bool:
- return bool(self._set)
- def __str__(self) -> str:
- return self.to_header()
- def __repr__(self) -> str:
- return f"{type(self).__name__}({self._headers!r})"
- # circular dependencies
- from .. import http
- def __getattr__(name: str) -> t.Any:
- import warnings
- if name == "OrderedMultiDict":
- warnings.warn(
- "'OrderedMultiDict' is deprecated and will be removed in Werkzeug"
- " 3.2. Use 'MultiDict' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _OrderedMultiDict
- if name == "ImmutableOrderedMultiDict":
- warnings.warn(
- "'ImmutableOrderedMultiDict' is deprecated and will be removed in"
- " Werkzeug 3.2. Use 'ImmutableMultiDict' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _ImmutableOrderedMultiDict
- raise AttributeError(name)
|