kasse-py/venv/lib/python3.12/site-packages/werkzeug/datastructures/mixins.py

318 lines
8.8 KiB
Python

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)