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