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