1from __future__ import annotations 
    2 
    3from itertools import repeat 
    4 
    5from .._internal import _missing 
    6 
    7 
    8def is_immutable(self): 
    9    raise TypeError(f"{type(self).__name__!r} objects are immutable") 
    10 
    11 
    12class ImmutableListMixin: 
    13    """Makes a :class:`list` immutable. 
    14 
    15    .. versionadded:: 0.5 
    16 
    17    :private: 
    18    """ 
    19 
    20    _hash_cache = None 
    21 
    22    def __hash__(self): 
    23        if self._hash_cache is not None: 
    24            return self._hash_cache 
    25        rv = self._hash_cache = hash(tuple(self)) 
    26        return rv 
    27 
    28    def __reduce_ex__(self, protocol): 
    29        return type(self), (list(self),) 
    30 
    31    def __delitem__(self, key): 
    32        is_immutable(self) 
    33 
    34    def __iadd__(self, other): 
    35        is_immutable(self) 
    36 
    37    def __imul__(self, other): 
    38        is_immutable(self) 
    39 
    40    def __setitem__(self, key, value): 
    41        is_immutable(self) 
    42 
    43    def append(self, item): 
    44        is_immutable(self) 
    45 
    46    def remove(self, item): 
    47        is_immutable(self) 
    48 
    49    def extend(self, iterable): 
    50        is_immutable(self) 
    51 
    52    def insert(self, pos, value): 
    53        is_immutable(self) 
    54 
    55    def pop(self, index=-1): 
    56        is_immutable(self) 
    57 
    58    def reverse(self): 
    59        is_immutable(self) 
    60 
    61    def sort(self, key=None, reverse=False): 
    62        is_immutable(self) 
    63 
    64 
    65class ImmutableDictMixin: 
    66    """Makes a :class:`dict` immutable. 
    67 
    68    .. versionadded:: 0.5 
    69 
    70    :private: 
    71    """ 
    72 
    73    _hash_cache = None 
    74 
    75    @classmethod 
    76    def fromkeys(cls, keys, value=None): 
    77        instance = super().__new__(cls) 
    78        instance.__init__(zip(keys, repeat(value))) 
    79        return instance 
    80 
    81    def __reduce_ex__(self, protocol): 
    82        return type(self), (dict(self),) 
    83 
    84    def _iter_hashitems(self): 
    85        return self.items() 
    86 
    87    def __hash__(self): 
    88        if self._hash_cache is not None: 
    89            return self._hash_cache 
    90        rv = self._hash_cache = hash(frozenset(self._iter_hashitems())) 
    91        return rv 
    92 
    93    def setdefault(self, key, default=None): 
    94        is_immutable(self) 
    95 
    96    def update(self, *args, **kwargs): 
    97        is_immutable(self) 
    98 
    99    def pop(self, key, default=None): 
    100        is_immutable(self) 
    101 
    102    def popitem(self): 
    103        is_immutable(self) 
    104 
    105    def __setitem__(self, key, value): 
    106        is_immutable(self) 
    107 
    108    def __delitem__(self, key): 
    109        is_immutable(self) 
    110 
    111    def clear(self): 
    112        is_immutable(self) 
    113 
    114 
    115class ImmutableMultiDictMixin(ImmutableDictMixin): 
    116    """Makes a :class:`MultiDict` immutable. 
    117 
    118    .. versionadded:: 0.5 
    119 
    120    :private: 
    121    """ 
    122 
    123    def __reduce_ex__(self, protocol): 
    124        return type(self), (list(self.items(multi=True)),) 
    125 
    126    def _iter_hashitems(self): 
    127        return self.items(multi=True) 
    128 
    129    def add(self, key, value): 
    130        is_immutable(self) 
    131 
    132    def popitemlist(self): 
    133        is_immutable(self) 
    134 
    135    def poplist(self, key): 
    136        is_immutable(self) 
    137 
    138    def setlist(self, key, new_list): 
    139        is_immutable(self) 
    140 
    141    def setlistdefault(self, key, default_list=None): 
    142        is_immutable(self) 
    143 
    144 
    145class ImmutableHeadersMixin: 
    146    """Makes a :class:`Headers` immutable.  We do not mark them as 
    147    hashable though since the only usecase for this datastructure 
    148    in Werkzeug is a view on a mutable structure. 
    149 
    150    .. versionadded:: 0.5 
    151 
    152    :private: 
    153    """ 
    154 
    155    def __delitem__(self, key, **kwargs): 
    156        is_immutable(self) 
    157 
    158    def __setitem__(self, key, value): 
    159        is_immutable(self) 
    160 
    161    def set(self, _key, _value, **kwargs): 
    162        is_immutable(self) 
    163 
    164    def setlist(self, key, values): 
    165        is_immutable(self) 
    166 
    167    def add(self, _key, _value, **kwargs): 
    168        is_immutable(self) 
    169 
    170    def add_header(self, _key, _value, **_kwargs): 
    171        is_immutable(self) 
    172 
    173    def remove(self, key): 
    174        is_immutable(self) 
    175 
    176    def extend(self, *args, **kwargs): 
    177        is_immutable(self) 
    178 
    179    def update(self, *args, **kwargs): 
    180        is_immutable(self) 
    181 
    182    def insert(self, pos, value): 
    183        is_immutable(self) 
    184 
    185    def pop(self, key=None, default=_missing): 
    186        is_immutable(self) 
    187 
    188    def popitem(self): 
    189        is_immutable(self) 
    190 
    191    def setdefault(self, key, default): 
    192        is_immutable(self) 
    193 
    194    def setlistdefault(self, key, default): 
    195        is_immutable(self) 
    196 
    197 
    198def _calls_update(name): 
    199    def oncall(self, *args, **kw): 
    200        rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw) 
    201 
    202        if self.on_update is not None: 
    203            self.on_update(self) 
    204 
    205        return rv 
    206 
    207    oncall.__name__ = name 
    208    return oncall 
    209 
    210 
    211class UpdateDictMixin(dict): 
    212    """Makes dicts call `self.on_update` on modifications. 
    213 
    214    .. versionadded:: 0.5 
    215 
    216    :private: 
    217    """ 
    218 
    219    on_update = None 
    220 
    221    def setdefault(self, key, default=None): 
    222        modified = key not in self 
    223        rv = super().setdefault(key, default) 
    224        if modified and self.on_update is not None: 
    225            self.on_update(self) 
    226        return rv 
    227 
    228    def pop(self, key, default=_missing): 
    229        modified = key in self 
    230        if default is _missing: 
    231            rv = super().pop(key) 
    232        else: 
    233            rv = super().pop(key, default) 
    234        if modified and self.on_update is not None: 
    235            self.on_update(self) 
    236        return rv 
    237 
    238    __setitem__ = _calls_update("__setitem__") 
    239    __delitem__ = _calls_update("__delitem__") 
    240    clear = _calls_update("clear") 
    241    popitem = _calls_update("popitem") 
    242    update = _calls_update("update")