mixins.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. from __future__ import annotations
  2. from itertools import repeat
  3. from .._internal import _missing
  4. def is_immutable(self):
  5. raise TypeError(f"{type(self).__name__!r} objects are immutable")
  6. class ImmutableListMixin:
  7. """Makes a :class:`list` immutable.
  8. .. versionadded:: 0.5
  9. :private:
  10. """
  11. _hash_cache = None
  12. def __hash__(self):
  13. if self._hash_cache is not None:
  14. return self._hash_cache
  15. rv = self._hash_cache = hash(tuple(self))
  16. return rv
  17. def __reduce_ex__(self, protocol):
  18. return type(self), (list(self),)
  19. def __delitem__(self, key):
  20. is_immutable(self)
  21. def __iadd__(self, other):
  22. is_immutable(self)
  23. def __imul__(self, other):
  24. is_immutable(self)
  25. def __setitem__(self, key, value):
  26. is_immutable(self)
  27. def append(self, item):
  28. is_immutable(self)
  29. def remove(self, item):
  30. is_immutable(self)
  31. def extend(self, iterable):
  32. is_immutable(self)
  33. def insert(self, pos, value):
  34. is_immutable(self)
  35. def pop(self, index=-1):
  36. is_immutable(self)
  37. def reverse(self):
  38. is_immutable(self)
  39. def sort(self, key=None, reverse=False):
  40. is_immutable(self)
  41. class ImmutableDictMixin:
  42. """Makes a :class:`dict` immutable.
  43. .. versionadded:: 0.5
  44. :private:
  45. """
  46. _hash_cache = None
  47. @classmethod
  48. def fromkeys(cls, keys, value=None):
  49. instance = super().__new__(cls)
  50. instance.__init__(zip(keys, repeat(value)))
  51. return instance
  52. def __reduce_ex__(self, protocol):
  53. return type(self), (dict(self),)
  54. def _iter_hashitems(self):
  55. return self.items()
  56. def __hash__(self):
  57. if self._hash_cache is not None:
  58. return self._hash_cache
  59. rv = self._hash_cache = hash(frozenset(self._iter_hashitems()))
  60. return rv
  61. def setdefault(self, key, default=None):
  62. is_immutable(self)
  63. def update(self, *args, **kwargs):
  64. is_immutable(self)
  65. def pop(self, key, default=None):
  66. is_immutable(self)
  67. def popitem(self):
  68. is_immutable(self)
  69. def __setitem__(self, key, value):
  70. is_immutable(self)
  71. def __delitem__(self, key):
  72. is_immutable(self)
  73. def clear(self):
  74. is_immutable(self)
  75. class ImmutableMultiDictMixin(ImmutableDictMixin):
  76. """Makes a :class:`MultiDict` immutable.
  77. .. versionadded:: 0.5
  78. :private:
  79. """
  80. def __reduce_ex__(self, protocol):
  81. return type(self), (list(self.items(multi=True)),)
  82. def _iter_hashitems(self):
  83. return self.items(multi=True)
  84. def add(self, key, value):
  85. is_immutable(self)
  86. def popitemlist(self):
  87. is_immutable(self)
  88. def poplist(self, key):
  89. is_immutable(self)
  90. def setlist(self, key, new_list):
  91. is_immutable(self)
  92. def setlistdefault(self, key, default_list=None):
  93. is_immutable(self)
  94. class ImmutableHeadersMixin:
  95. """Makes a :class:`Headers` immutable. We do not mark them as
  96. hashable though since the only usecase for this datastructure
  97. in Werkzeug is a view on a mutable structure.
  98. .. versionadded:: 0.5
  99. :private:
  100. """
  101. def __delitem__(self, key, **kwargs):
  102. is_immutable(self)
  103. def __setitem__(self, key, value):
  104. is_immutable(self)
  105. def set(self, _key, _value, **kwargs):
  106. is_immutable(self)
  107. def setlist(self, key, values):
  108. is_immutable(self)
  109. def add(self, _key, _value, **kwargs):
  110. is_immutable(self)
  111. def add_header(self, _key, _value, **_kwargs):
  112. is_immutable(self)
  113. def remove(self, key):
  114. is_immutable(self)
  115. def extend(self, *args, **kwargs):
  116. is_immutable(self)
  117. def update(self, *args, **kwargs):
  118. is_immutable(self)
  119. def insert(self, pos, value):
  120. is_immutable(self)
  121. def pop(self, key=None, default=_missing):
  122. is_immutable(self)
  123. def popitem(self):
  124. is_immutable(self)
  125. def setdefault(self, key, default):
  126. is_immutable(self)
  127. def setlistdefault(self, key, default):
  128. is_immutable(self)
  129. def _calls_update(name):
  130. def oncall(self, *args, **kw):
  131. rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw)
  132. if self.on_update is not None:
  133. self.on_update(self)
  134. return rv
  135. oncall.__name__ = name
  136. return oncall
  137. class UpdateDictMixin(dict):
  138. """Makes dicts call `self.on_update` on modifications.
  139. .. versionadded:: 0.5
  140. :private:
  141. """
  142. on_update = None
  143. def setdefault(self, key, default=None):
  144. modified = key not in self
  145. rv = super().setdefault(key, default)
  146. if modified and self.on_update is not None:
  147. self.on_update(self)
  148. return rv
  149. def pop(self, key, default=_missing):
  150. modified = key in self
  151. if default is _missing:
  152. rv = super().pop(key)
  153. else:
  154. rv = super().pop(key, default)
  155. if modified and self.on_update is not None:
  156. self.on_update(self)
  157. return rv
  158. __setitem__ = _calls_update("__setitem__")
  159. __delitem__ = _calls_update("__delitem__")
  160. clear = _calls_update("clear")
  161. popitem = _calls_update("popitem")
  162. update = _calls_update("update")