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