123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- from __future__ import annotations
- import collections.abc as cabc
- import typing as t
- from functools import update_wrapper
- from itertools import repeat
- from .._internal import _missing
- if t.TYPE_CHECKING:
- import typing_extensions as te
- K = t.TypeVar("K")
- V = t.TypeVar("V")
- T = t.TypeVar("T")
- F = t.TypeVar("F", bound=cabc.Callable[..., t.Any])
- def _immutable_error(self: t.Any) -> t.NoReturn:
- raise TypeError(f"{type(self).__name__!r} objects are immutable")
- class ImmutableListMixin:
- """Makes a :class:`list` immutable.
- .. versionadded:: 0.5
- :private:
- """
- _hash_cache: int | None = None
- def __hash__(self) -> int:
- if self._hash_cache is not None:
- return self._hash_cache
- rv = self._hash_cache = hash(tuple(self)) # type: ignore[arg-type]
- return rv
- def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any:
- return type(self), (list(self),) # type: ignore[call-overload]
- def __delitem__(self, key: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def __iadd__(self, other: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def __imul__(self, other: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def __setitem__(self, key: t.Any, value: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def append(self, item: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def remove(self, item: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def extend(self, iterable: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def insert(self, pos: t.Any, value: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def pop(self, index: t.Any = -1) -> t.NoReturn:
- _immutable_error(self)
- def reverse(self: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def sort(self, key: t.Any = None, reverse: t.Any = False) -> t.NoReturn:
- _immutable_error(self)
- class ImmutableDictMixin(t.Generic[K, V]):
- """Makes a :class:`dict` immutable.
- .. versionchanged:: 3.1
- Disallow ``|=`` operator.
- .. versionadded:: 0.5
- :private:
- """
- _hash_cache: int | None = None
- @classmethod
- @t.overload
- def fromkeys(
- cls, keys: cabc.Iterable[K], value: None
- ) -> ImmutableDictMixin[K, t.Any | None]: ...
- @classmethod
- @t.overload
- def fromkeys(cls, keys: cabc.Iterable[K], value: V) -> ImmutableDictMixin[K, V]: ...
- @classmethod
- def fromkeys(
- cls, keys: cabc.Iterable[K], value: V | None = None
- ) -> ImmutableDictMixin[K, t.Any | None] | ImmutableDictMixin[K, V]:
- instance = super().__new__(cls)
- instance.__init__(zip(keys, repeat(value))) # type: ignore[misc]
- return instance
- def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any:
- return type(self), (dict(self),) # type: ignore[call-overload]
- def _iter_hashitems(self) -> t.Iterable[t.Any]:
- return self.items() # type: ignore[attr-defined,no-any-return]
- def __hash__(self) -> int:
- if self._hash_cache is not None:
- return self._hash_cache
- rv = self._hash_cache = hash(frozenset(self._iter_hashitems()))
- return rv
- def setdefault(self, key: t.Any, default: t.Any = None) -> t.NoReturn:
- _immutable_error(self)
- def update(self, arg: t.Any, /, **kwargs: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def __ior__(self, other: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def pop(self, key: t.Any, default: t.Any = None) -> t.NoReturn:
- _immutable_error(self)
- def popitem(self) -> t.NoReturn:
- _immutable_error(self)
- def __setitem__(self, key: t.Any, value: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def __delitem__(self, key: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def clear(self) -> t.NoReturn:
- _immutable_error(self)
- class ImmutableMultiDictMixin(ImmutableDictMixin[K, V]):
- """Makes a :class:`MultiDict` immutable.
- .. versionadded:: 0.5
- :private:
- """
- def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any:
- return type(self), (list(self.items(multi=True)),) # type: ignore[attr-defined]
- def _iter_hashitems(self) -> t.Iterable[t.Any]:
- return self.items(multi=True) # type: ignore[attr-defined,no-any-return]
- def add(self, key: t.Any, value: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def popitemlist(self) -> t.NoReturn:
- _immutable_error(self)
- def poplist(self, key: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def setlist(self, key: t.Any, new_list: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def setlistdefault(self, key: t.Any, default_list: t.Any = None) -> t.NoReturn:
- _immutable_error(self)
- class ImmutableHeadersMixin:
- """Makes a :class:`Headers` immutable. We do not mark them as
- hashable though since the only usecase for this datastructure
- in Werkzeug is a view on a mutable structure.
- .. versionchanged:: 3.1
- Disallow ``|=`` operator.
- .. versionadded:: 0.5
- :private:
- """
- def __delitem__(self, key: t.Any, **kwargs: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def __setitem__(self, key: t.Any, value: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def set(self, key: t.Any, value: t.Any, /, **kwargs: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def setlist(self, key: t.Any, values: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def add(self, key: t.Any, value: t.Any, /, **kwargs: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def add_header(self, key: t.Any, value: t.Any, /, **kwargs: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def remove(self, key: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def extend(self, arg: t.Any, /, **kwargs: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def update(self, arg: t.Any, /, **kwargs: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def __ior__(self, other: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def insert(self, pos: t.Any, value: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def pop(self, key: t.Any = None, default: t.Any = _missing) -> t.NoReturn:
- _immutable_error(self)
- def popitem(self) -> t.NoReturn:
- _immutable_error(self)
- def setdefault(self, key: t.Any, default: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def setlistdefault(self, key: t.Any, default: t.Any) -> t.NoReturn:
- _immutable_error(self)
- def _always_update(f: F) -> F:
- def wrapper(
- self: UpdateDictMixin[t.Any, t.Any], /, *args: t.Any, **kwargs: t.Any
- ) -> t.Any:
- rv = f(self, *args, **kwargs)
- if self.on_update is not None:
- self.on_update(self)
- return rv
- return update_wrapper(wrapper, f) # type: ignore[return-value]
- class UpdateDictMixin(dict[K, V]):
- """Makes dicts call `self.on_update` on modifications.
- .. versionchanged:: 3.1
- Implement ``|=`` operator.
- .. versionadded:: 0.5
- :private:
- """
- on_update: cabc.Callable[[te.Self], None] | None = None
- def setdefault(self: te.Self, key: K, default: V | None = None) -> V:
- modified = key not in self
- rv = super().setdefault(key, default) # type: ignore[arg-type]
- if modified and self.on_update is not None:
- self.on_update(self)
- return rv
- @t.overload
- def pop(self: te.Self, key: K) -> V: ...
- @t.overload
- def pop(self: te.Self, key: K, default: V) -> V: ...
- @t.overload
- def pop(self: te.Self, key: K, default: T) -> T: ...
- def pop(
- self: te.Self,
- key: K,
- default: V | T = _missing, # type: ignore[assignment]
- ) -> V | T:
- modified = key in self
- if default is _missing:
- rv = super().pop(key)
- else:
- rv = super().pop(key, default) # type: ignore[arg-type]
- if modified and self.on_update is not None:
- self.on_update(self)
- return rv
- @_always_update
- def __setitem__(self, key: K, value: V) -> None:
- super().__setitem__(key, value)
- @_always_update
- def __delitem__(self, key: K) -> None:
- super().__delitem__(key)
- @_always_update
- def clear(self) -> None:
- super().clear()
- @_always_update
- def popitem(self) -> tuple[K, V]:
- return super().popitem()
- @_always_update
- def update( # type: ignore[override]
- self,
- arg: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] | None = None,
- /,
- **kwargs: V,
- ) -> None:
- if arg is None:
- super().update(**kwargs)
- else:
- super().update(arg, **kwargs)
- @_always_update
- def __ior__( # type: ignore[override]
- self, other: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]]
- ) -> te.Self:
- return super().__ior__(other)
|