v0.158.0
  1from __future__ import annotations
  2
  3import builtins
  4import copy
  5from collections.abc import Callable, Iterable, Iterator, Mapping
  6from typing import Any, Self
  7
  8
  9class OrderedSet:
 10    """
 11    A set which keeps the ordering of the inserted items.
 12    """
 13
 14    def __init__(self, iterable: Iterable[Any] | None = None) -> None:
 15        self.dict: dict[Any, None] = dict.fromkeys(iterable or ())
 16
 17    def add(self, item: Any) -> None:
 18        self.dict[item] = None
 19
 20    def remove(self, item: Any) -> None:
 21        del self.dict[item]
 22
 23    def discard(self, item: Any) -> None:
 24        try:
 25            self.remove(item)
 26        except KeyError:
 27            pass
 28
 29    def __iter__(self) -> Iterator[Any]:
 30        return iter(self.dict)
 31
 32    def __reversed__(self) -> Iterator[Any]:
 33        return reversed(self.dict)
 34
 35    def __contains__(self, item: Any) -> bool:
 36        return item in self.dict
 37
 38    def __bool__(self) -> bool:
 39        return bool(self.dict)
 40
 41    def __len__(self) -> int:
 42        return len(self.dict)
 43
 44    def __repr__(self) -> str:
 45        data = repr(list(self.dict)) if self.dict else ""
 46        return f"{self.__class__.__qualname__}({data})"
 47
 48
 49class MultiValueDictKeyError(KeyError):
 50    pass
 51
 52
 53class MultiValueDict(dict[str, list[Any]]):
 54    """
 55    A subclass of dictionary customized to handle multiple values for the
 56    same key.
 57
 58    >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
 59    >>> d['name']
 60    'Simon'
 61    >>> d.getlist('name')
 62    ['Adrian', 'Simon']
 63    >>> d.getlist('doesnotexist')
 64    []
 65    >>> d.getlist('doesnotexist', ['Adrian', 'Simon'])
 66    ['Adrian', 'Simon']
 67    >>> d.get('lastname', 'nonexistent')
 68    'nonexistent'
 69    >>> d.setlist('lastname', ['Holovaty', 'Willison'])
 70
 71    This class exists to solve the irritating problem raised by cgi.parse_qs,
 72    which returns a list for every key, even though most web forms submit
 73    single name-value pairs.
 74    """
 75
 76    def __init__(
 77        self,
 78        key_to_list_mapping: Mapping[str, list[Any]]
 79        | Iterable[tuple[str, list[Any]]] = (),
 80    ) -> None:
 81        super().__init__(key_to_list_mapping)
 82
 83    def __repr__(self) -> str:
 84        return f"<{self.__class__.__name__}: {super().__repr__()}>"
 85
 86    def __getitem__(self, key: str) -> Any:
 87        """
 88        Return the last data value for this key, or [] if it's an empty list;
 89        raise KeyError if not found.
 90        """
 91        try:
 92            list_ = super().__getitem__(key)
 93        except KeyError:
 94            raise MultiValueDictKeyError(key)
 95        try:
 96            return list_[-1]
 97        except IndexError:
 98            return []
 99
100    def __setitem__(self, key: str, value: Any) -> None:
101        super().__setitem__(key, [value])
102
103    def __copy__(self) -> MultiValueDict:
104        return self.__class__([(k, v[:]) for k, v in self.lists()])
105
106    def __deepcopy__(self, memo: builtins.dict[int, Any]) -> MultiValueDict:
107        result = self.__class__()
108        memo[id(self)] = result
109        for key, value in dict.items(self):
110            dict.__setitem__(
111                result, copy.deepcopy(key, memo), copy.deepcopy(value, memo)
112            )
113        return result
114
115    def __getstate__(self) -> builtins.dict[str, Any]:
116        return {**self.__dict__, "_data": {k: self._getlist(k) for k in self}}
117
118    def __setstate__(self, obj_dict: builtins.dict[str, Any]) -> None:
119        data = obj_dict.pop("_data", {})
120        for k, v in data.items():
121            self.setlist(k, v)
122        self.__dict__.update(obj_dict)
123
124    def get(self, key: object, default: Any = None) -> Any:
125        """
126        Return the last data value for the passed key. If key doesn't exist
127        or value is an empty list, return `default`.
128        """
129        list_ = super().get(key)
130        if not list_:
131            return default
132        val = list_[-1]
133        if val == []:
134            return default
135        return val
136
137    def _getlist(
138        self, key: str, default: list[Any] | None = None, force_list: bool = False
139    ) -> list[Any] | None:
140        """
141        Return a list of values for the key.
142
143        Used internally to manipulate values list. If force_list is True,
144        return a new copy of values.
145        """
146        try:
147            values = super().__getitem__(key)
148        except KeyError:
149            if default is None:
150                return []
151            return default
152        else:
153            if force_list:
154                values = list(values) if values is not None else None
155            return values
156
157    def getlist(self, key: str, default: list[Any] | None = None) -> list[Any]:
158        """
159        Return the list of values for the key. If key doesn't exist, return a
160        default value.
161        """
162        return self._getlist(key, default, force_list=True)  # type: ignore
163
164    def setlist(self, key: str, list_: list[Any]) -> None:
165        super().__setitem__(key, list_)
166
167    def setdefault(self, key: str, default: Any = None) -> Any:
168        if key not in self:
169            self[key] = default
170            # Do not return default here because __setitem__() may store
171            # another value -- QueryDict.__setitem__() does. Look it up.
172        return self[key]
173
174    def setlistdefault(
175        self, key: str, default_list: list[Any] | None = None
176    ) -> list[Any]:
177        if key not in self:
178            if default_list is None:
179                default_list = []
180            self.setlist(key, default_list)
181            # Do not return default_list here because setlist() may store
182            # another value -- QueryDict.setlist() does. Look it up.
183        result = self._getlist(key)
184        assert result is not None
185        return result
186
187    def appendlist(self, key: str, value: Any) -> None:
188        """Append an item to the internal list associated with key."""
189        self.setlistdefault(key).append(value)
190
191    def items(self) -> Iterator[tuple[str, Any]]:  # ty: ignore[invalid-method-override]
192        """
193        Yield (key, value) pairs, where value is the last item in the list
194        associated with the key.
195        """
196        for key in self:
197            yield key, self[key]
198
199    def lists(self) -> Iterator[tuple[str, list[Any]]]:
200        """Yield (key, list) pairs."""
201        return iter(super().items())
202
203    def values(self) -> Iterator[Any]:  # ty: ignore[invalid-method-override]
204        """Yield the last value on every key list."""
205        for key in self:
206            yield self[key]
207
208    def copy(self) -> MultiValueDict:
209        """Return a shallow copy of this object."""
210        return copy.copy(self)
211
212    def update(self, *args: Any, **kwargs: Any) -> None:
213        """Extend rather than replace existing key lists."""
214        if len(args) > 1:
215            raise TypeError(f"update expected at most 1 argument, got {len(args)}")
216        if args:
217            arg = args[0]
218            if isinstance(arg, MultiValueDict):
219                for key, value_list in arg.lists():
220                    self.setlistdefault(key).extend(value_list)
221            else:
222                if isinstance(arg, Mapping):
223                    arg = arg.items()
224                for key, value in arg:
225                    self.setlistdefault(key).append(value)
226        for key, value in kwargs.items():
227            self.setlistdefault(key).append(value)
228
229    def dict(self) -> builtins.dict[str, Any]:
230        """Return current object as a dict with singular values."""
231        return {key: self[key] for key in self}
232
233
234class ImmutableList(tuple):
235    """
236    A tuple-like object that raises useful errors when it is asked to mutate.
237
238    Example::
239
240        >>> a = ImmutableList(range(5), warning="You cannot mutate this.")
241        >>> a[3] = '4'
242        Traceback (most recent call last):
243            ...
244        AttributeError: You cannot mutate this.
245    """
246
247    warning: str  # Set in __new__
248
249    def __new__(
250        cls,
251        *args: Any,
252        warning: str = "ImmutableList object is immutable.",
253        **kwargs: Any,
254    ) -> Self:
255        self = tuple.__new__(cls, *args, **kwargs)
256        self.warning = warning
257        return self
258
259    def complain(self, *args: Any, **kwargs: Any) -> None:
260        raise AttributeError(self.warning)
261
262    # All list mutation functions complain.
263    __delitem__ = complain
264    __delslice__ = complain
265    __iadd__ = complain
266    __imul__ = complain
267    __setitem__ = complain
268    __setslice__ = complain
269    append = complain
270    extend = complain
271    insert = complain
272    pop = complain
273    remove = complain
274    sort = complain
275    reverse = complain
276
277
278class DictWrapper(dict[str, Any]):
279    """
280    Wrap accesses to a dictionary so that certain values (those starting with
281    the specified prefix) are passed through a function before being returned.
282    The prefix is removed before looking up the real value.
283
284    Used by the SQL construction code to ensure that values are correctly
285    quoted before being used.
286    """
287
288    def __init__(
289        self, data: dict[str, Any], func: Callable[[Any], Any], prefix: str
290    ) -> None:
291        super().__init__(data)
292        self.func = func
293        self.prefix = prefix
294
295    def __getitem__(self, key: str) -> Any:
296        """
297        Retrieve the real value after stripping the prefix string (if
298        present). If the prefix is present, pass the value through self.func
299        before returning, otherwise return the raw value.
300        """
301        use_func = key.startswith(self.prefix)
302        key = key.removeprefix(self.prefix)
303        value = super().__getitem__(key)
304        if use_func:
305            return self.func(value)
306        return value
307
308
309class CaseInsensitiveMapping(Mapping[str, Any]):
310    """
311    Mapping allowing case-insensitive key lookups. Original case of keys is
312    preserved for iteration and string representation.
313
314    Example::
315
316        >>> ci_map = CaseInsensitiveMapping({'name': 'Jane'})
317        >>> ci_map['Name']
318        Jane
319        >>> ci_map['NAME']
320        Jane
321        >>> ci_map['name']
322        Jane
323        >>> ci_map  # original case preserved
324        {'name': 'Jane'}
325    """
326
327    def __init__(self, data: Mapping[str, Any] | Iterable[tuple[str, Any]]) -> None:
328        self._store: dict[str, tuple[str, Any]] = {
329            k.lower(): (k, v) for k, v in self._unpack_items(data)
330        }
331
332    def __getitem__(self, key: str) -> Any:
333        return self._store[key.lower()][1]
334
335    def __len__(self) -> int:
336        return len(self._store)
337
338    def __eq__(self, other: object) -> bool:
339        if not isinstance(other, Mapping):
340            return False
341        return {k.lower(): v for k, v in self.items()} == {
342            k.lower(): v for k, v in other.items() if isinstance(k, str)
343        }
344
345    def __iter__(self) -> Iterator[str]:
346        return (original_key for original_key, value in self._store.values())
347
348    def __repr__(self) -> str:
349        return repr(dict(self._store.values()))
350
351    def copy(self) -> CaseInsensitiveMapping:
352        return self
353
354    @staticmethod
355    def _unpack_items(
356        data: Mapping[str, Any] | Iterable[tuple[str, Any]],
357    ) -> Iterator[tuple[str, Any]]:
358        # Explicitly test for dict first as the common case for performance,
359        # avoiding abc's __instancecheck__ and _abc_instancecheck for the
360        # general Mapping case.
361        if isinstance(data, dict):
362            yield from data.items()  # ty: ignore[invalid-yield]
363            return
364        if isinstance(data, Mapping):
365            yield from data.items()  # ty: ignore[invalid-yield]
366            return
367        for i, elem in enumerate(data):
368            if len(elem) != 2:
369                raise ValueError(
370                    f"dictionary update sequence element #{i} has length {len(elem)}; "
371                    "2 is required."
372                )
373            if not isinstance(elem[0], str):
374                raise TypeError(
375                    f"Element key {elem[0]!r} invalid, only strings are allowed"
376                )
377            yield elem