|
1 class MergeDict(object): |
|
2 """ |
|
3 A simple class for creating new "virtual" dictionaries that actually look |
|
4 up values in more than one dictionary, passed in the constructor. |
|
5 |
|
6 If a key appears in more than one of the given dictionaries, only the |
|
7 first occurrence will be used. |
|
8 """ |
|
9 def __init__(self, *dicts): |
|
10 self.dicts = dicts |
|
11 |
|
12 def __getitem__(self, key): |
|
13 for dict_ in self.dicts: |
|
14 try: |
|
15 return dict_[key] |
|
16 except KeyError: |
|
17 pass |
|
18 raise KeyError |
|
19 |
|
20 def __copy__(self): |
|
21 return self.__class__(*self.dicts) |
|
22 |
|
23 def get(self, key, default=None): |
|
24 try: |
|
25 return self[key] |
|
26 except KeyError: |
|
27 return default |
|
28 |
|
29 def getlist(self, key): |
|
30 for dict_ in self.dicts: |
|
31 if key in dict_.keys(): |
|
32 return dict_.getlist(key) |
|
33 return [] |
|
34 |
|
35 def items(self): |
|
36 item_list = [] |
|
37 for dict_ in self.dicts: |
|
38 item_list.extend(dict_.items()) |
|
39 return item_list |
|
40 |
|
41 def has_key(self, key): |
|
42 for dict_ in self.dicts: |
|
43 if key in dict_: |
|
44 return True |
|
45 return False |
|
46 |
|
47 __contains__ = has_key |
|
48 |
|
49 def copy(self): |
|
50 """Returns a copy of this object.""" |
|
51 return self.__copy__() |
|
52 |
|
53 class SortedDict(dict): |
|
54 """ |
|
55 A dictionary that keeps its keys in the order in which they're inserted. |
|
56 """ |
|
57 def __new__(cls, *args, **kwargs): |
|
58 instance = super(SortedDict, cls).__new__(cls, *args, **kwargs) |
|
59 instance.keyOrder = [] |
|
60 return instance |
|
61 |
|
62 def __init__(self, data=None): |
|
63 if data is None: |
|
64 data = {} |
|
65 super(SortedDict, self).__init__(data) |
|
66 if isinstance(data, dict): |
|
67 self.keyOrder = data.keys() |
|
68 else: |
|
69 self.keyOrder = [] |
|
70 for key, value in data: |
|
71 if key not in self.keyOrder: |
|
72 self.keyOrder.append(key) |
|
73 |
|
74 def __deepcopy__(self, memo): |
|
75 from copy import deepcopy |
|
76 return self.__class__([(key, deepcopy(value, memo)) |
|
77 for key, value in self.iteritems()]) |
|
78 |
|
79 def __setitem__(self, key, value): |
|
80 super(SortedDict, self).__setitem__(key, value) |
|
81 if key not in self.keyOrder: |
|
82 self.keyOrder.append(key) |
|
83 |
|
84 def __delitem__(self, key): |
|
85 super(SortedDict, self).__delitem__(key) |
|
86 self.keyOrder.remove(key) |
|
87 |
|
88 def __iter__(self): |
|
89 for k in self.keyOrder: |
|
90 yield k |
|
91 |
|
92 def pop(self, k, *args): |
|
93 result = super(SortedDict, self).pop(k, *args) |
|
94 try: |
|
95 self.keyOrder.remove(k) |
|
96 except ValueError: |
|
97 # Key wasn't in the dictionary in the first place. No problem. |
|
98 pass |
|
99 return result |
|
100 |
|
101 def popitem(self): |
|
102 result = super(SortedDict, self).popitem() |
|
103 self.keyOrder.remove(result[0]) |
|
104 return result |
|
105 |
|
106 def items(self): |
|
107 return zip(self.keyOrder, self.values()) |
|
108 |
|
109 def iteritems(self): |
|
110 for key in self.keyOrder: |
|
111 yield key, super(SortedDict, self).__getitem__(key) |
|
112 |
|
113 def keys(self): |
|
114 return self.keyOrder[:] |
|
115 |
|
116 def iterkeys(self): |
|
117 return iter(self.keyOrder) |
|
118 |
|
119 def values(self): |
|
120 return map(super(SortedDict, self).__getitem__, self.keyOrder) |
|
121 |
|
122 def itervalues(self): |
|
123 for key in self.keyOrder: |
|
124 yield super(SortedDict, self).__getitem__(key) |
|
125 |
|
126 def update(self, dict_): |
|
127 for k, v in dict_.items(): |
|
128 self.__setitem__(k, v) |
|
129 |
|
130 def setdefault(self, key, default): |
|
131 if key not in self.keyOrder: |
|
132 self.keyOrder.append(key) |
|
133 return super(SortedDict, self).setdefault(key, default) |
|
134 |
|
135 def value_for_index(self, index): |
|
136 """Returns the value of the item at the given zero-based index.""" |
|
137 return self[self.keyOrder[index]] |
|
138 |
|
139 def insert(self, index, key, value): |
|
140 """Inserts the key, value pair before the item with the given index.""" |
|
141 if key in self.keyOrder: |
|
142 n = self.keyOrder.index(key) |
|
143 del self.keyOrder[n] |
|
144 if n < index: |
|
145 index -= 1 |
|
146 self.keyOrder.insert(index, key) |
|
147 super(SortedDict, self).__setitem__(key, value) |
|
148 |
|
149 def copy(self): |
|
150 """Returns a copy of this object.""" |
|
151 # This way of initializing the copy means it works for subclasses, too. |
|
152 obj = self.__class__(self) |
|
153 obj.keyOrder = self.keyOrder[:] |
|
154 return obj |
|
155 |
|
156 def __repr__(self): |
|
157 """ |
|
158 Replaces the normal dict.__repr__ with a version that returns the keys |
|
159 in their sorted order. |
|
160 """ |
|
161 return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()]) |
|
162 |
|
163 def clear(self): |
|
164 super(SortedDict, self).clear() |
|
165 self.keyOrder = [] |
|
166 |
|
167 class MultiValueDictKeyError(KeyError): |
|
168 pass |
|
169 |
|
170 class MultiValueDict(dict): |
|
171 """ |
|
172 A subclass of dictionary customized to handle multiple values for the |
|
173 same key. |
|
174 |
|
175 >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']}) |
|
176 >>> d['name'] |
|
177 'Simon' |
|
178 >>> d.getlist('name') |
|
179 ['Adrian', 'Simon'] |
|
180 >>> d.get('lastname', 'nonexistent') |
|
181 'nonexistent' |
|
182 >>> d.setlist('lastname', ['Holovaty', 'Willison']) |
|
183 |
|
184 This class exists to solve the irritating problem raised by cgi.parse_qs, |
|
185 which returns a list for every key, even though most Web forms submit |
|
186 single name-value pairs. |
|
187 """ |
|
188 def __init__(self, key_to_list_mapping=()): |
|
189 super(MultiValueDict, self).__init__(key_to_list_mapping) |
|
190 |
|
191 def __repr__(self): |
|
192 return "<%s: %s>" % (self.__class__.__name__, |
|
193 super(MultiValueDict, self).__repr__()) |
|
194 |
|
195 def __getitem__(self, key): |
|
196 """ |
|
197 Returns the last data value for this key, or [] if it's an empty list; |
|
198 raises KeyError if not found. |
|
199 """ |
|
200 try: |
|
201 list_ = super(MultiValueDict, self).__getitem__(key) |
|
202 except KeyError: |
|
203 raise MultiValueDictKeyError, "Key %r not found in %r" % (key, self) |
|
204 try: |
|
205 return list_[-1] |
|
206 except IndexError: |
|
207 return [] |
|
208 |
|
209 def __setitem__(self, key, value): |
|
210 super(MultiValueDict, self).__setitem__(key, [value]) |
|
211 |
|
212 def __copy__(self): |
|
213 return self.__class__(super(MultiValueDict, self).items()) |
|
214 |
|
215 def __deepcopy__(self, memo=None): |
|
216 import copy |
|
217 if memo is None: |
|
218 memo = {} |
|
219 result = self.__class__() |
|
220 memo[id(self)] = result |
|
221 for key, value in dict.items(self): |
|
222 dict.__setitem__(result, copy.deepcopy(key, memo), |
|
223 copy.deepcopy(value, memo)) |
|
224 return result |
|
225 |
|
226 def __getstate__(self): |
|
227 obj_dict = self.__dict__.copy() |
|
228 obj_dict['_data'] = dict([(k, self.getlist(k)) for k in self]) |
|
229 return obj_dict |
|
230 |
|
231 def __setstate__(self, obj_dict): |
|
232 data = obj_dict.pop('_data', {}) |
|
233 for k, v in data.items(): |
|
234 self.setlist(k, v) |
|
235 self.__dict__.update(obj_dict) |
|
236 |
|
237 def get(self, key, default=None): |
|
238 """ |
|
239 Returns the last data value for the passed key. If key doesn't exist |
|
240 or value is an empty list, then default is returned. |
|
241 """ |
|
242 try: |
|
243 val = self[key] |
|
244 except KeyError: |
|
245 return default |
|
246 if val == []: |
|
247 return default |
|
248 return val |
|
249 |
|
250 def getlist(self, key): |
|
251 """ |
|
252 Returns the list of values for the passed key. If key doesn't exist, |
|
253 then an empty list is returned. |
|
254 """ |
|
255 try: |
|
256 return super(MultiValueDict, self).__getitem__(key) |
|
257 except KeyError: |
|
258 return [] |
|
259 |
|
260 def setlist(self, key, list_): |
|
261 super(MultiValueDict, self).__setitem__(key, list_) |
|
262 |
|
263 def setdefault(self, key, default=None): |
|
264 if key not in self: |
|
265 self[key] = default |
|
266 return self[key] |
|
267 |
|
268 def setlistdefault(self, key, default_list=()): |
|
269 if key not in self: |
|
270 self.setlist(key, default_list) |
|
271 return self.getlist(key) |
|
272 |
|
273 def appendlist(self, key, value): |
|
274 """Appends an item to the internal list associated with key.""" |
|
275 self.setlistdefault(key, []) |
|
276 super(MultiValueDict, self).__setitem__(key, self.getlist(key) + [value]) |
|
277 |
|
278 def items(self): |
|
279 """ |
|
280 Returns a list of (key, value) pairs, where value is the last item in |
|
281 the list associated with the key. |
|
282 """ |
|
283 return [(key, self[key]) for key in self.keys()] |
|
284 |
|
285 def iteritems(self): |
|
286 """ |
|
287 Yields (key, value) pairs, where value is the last item in the list |
|
288 associated with the key. |
|
289 """ |
|
290 for key in self.keys(): |
|
291 yield (key, self[key]) |
|
292 |
|
293 def lists(self): |
|
294 """Returns a list of (key, list) pairs.""" |
|
295 return super(MultiValueDict, self).items() |
|
296 |
|
297 def iterlists(self): |
|
298 """Yields (key, list) pairs.""" |
|
299 return super(MultiValueDict, self).iteritems() |
|
300 |
|
301 def values(self): |
|
302 """Returns a list of the last value on every key list.""" |
|
303 return [self[key] for key in self.keys()] |
|
304 |
|
305 def itervalues(self): |
|
306 """Yield the last value on every key list.""" |
|
307 for key in self.iterkeys(): |
|
308 yield self[key] |
|
309 |
|
310 def copy(self): |
|
311 """Returns a copy of this object.""" |
|
312 return self.__deepcopy__() |
|
313 |
|
314 def update(self, *args, **kwargs): |
|
315 """ |
|
316 update() extends rather than replaces existing key lists. |
|
317 Also accepts keyword args. |
|
318 """ |
|
319 if len(args) > 1: |
|
320 raise TypeError, "update expected at most 1 arguments, got %d" % len(args) |
|
321 if args: |
|
322 other_dict = args[0] |
|
323 if isinstance(other_dict, MultiValueDict): |
|
324 for key, value_list in other_dict.lists(): |
|
325 self.setlistdefault(key, []).extend(value_list) |
|
326 else: |
|
327 try: |
|
328 for key, value in other_dict.items(): |
|
329 self.setlistdefault(key, []).append(value) |
|
330 except TypeError: |
|
331 raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary" |
|
332 for key, value in kwargs.iteritems(): |
|
333 self.setlistdefault(key, []).append(value) |
|
334 |
|
335 class DotExpandedDict(dict): |
|
336 """ |
|
337 A special dictionary constructor that takes a dictionary in which the keys |
|
338 may contain dots to specify inner dictionaries. It's confusing, but this |
|
339 example should make sense. |
|
340 |
|
341 >>> d = DotExpandedDict({'person.1.firstname': ['Simon'], \ |
|
342 'person.1.lastname': ['Willison'], \ |
|
343 'person.2.firstname': ['Adrian'], \ |
|
344 'person.2.lastname': ['Holovaty']}) |
|
345 >>> d |
|
346 {'person': {'1': {'lastname': ['Willison'], 'firstname': ['Simon']}, '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}} |
|
347 >>> d['person'] |
|
348 {'1': {'lastname': ['Willison'], 'firstname': ['Simon']}, '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}} |
|
349 >>> d['person']['1'] |
|
350 {'lastname': ['Willison'], 'firstname': ['Simon']} |
|
351 |
|
352 # Gotcha: Results are unpredictable if the dots are "uneven": |
|
353 >>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1}) |
|
354 {'c': 1} |
|
355 """ |
|
356 def __init__(self, key_to_list_mapping): |
|
357 for k, v in key_to_list_mapping.items(): |
|
358 current = self |
|
359 bits = k.split('.') |
|
360 for bit in bits[:-1]: |
|
361 current = current.setdefault(bit, {}) |
|
362 # Now assign value to current position |
|
363 try: |
|
364 current[bits[-1]] = v |
|
365 except TypeError: # Special-case if current isn't a dict. |
|
366 current = {bits[-1]: v} |
|
367 |
|
368 class ImmutableList(tuple): |
|
369 """ |
|
370 A tuple-like object that raises useful errors when it is asked to mutate. |
|
371 |
|
372 Example:: |
|
373 |
|
374 >>> a = ImmutableList(range(5), warning="You cannot mutate this.") |
|
375 >>> a[3] = '4' |
|
376 Traceback (most recent call last): |
|
377 ... |
|
378 AttributeError: You cannot mutate this. |
|
379 """ |
|
380 |
|
381 def __new__(cls, *args, **kwargs): |
|
382 if 'warning' in kwargs: |
|
383 warning = kwargs['warning'] |
|
384 del kwargs['warning'] |
|
385 else: |
|
386 warning = 'ImmutableList object is immutable.' |
|
387 self = tuple.__new__(cls, *args, **kwargs) |
|
388 self.warning = warning |
|
389 return self |
|
390 |
|
391 def complain(self, *wargs, **kwargs): |
|
392 if isinstance(self.warning, Exception): |
|
393 raise self.warning |
|
394 else: |
|
395 raise AttributeError, self.warning |
|
396 |
|
397 # All list mutation functions complain. |
|
398 __delitem__ = complain |
|
399 __delslice__ = complain |
|
400 __iadd__ = complain |
|
401 __imul__ = complain |
|
402 __setitem__ = complain |
|
403 __setslice__ = complain |
|
404 append = complain |
|
405 extend = complain |
|
406 insert = complain |
|
407 pop = complain |
|
408 remove = complain |
|
409 sort = complain |
|
410 reverse = complain |
|
411 |
|
412 class DictWrapper(dict): |
|
413 """ |
|
414 Wraps accesses to a dictionary so that certain values (those starting with |
|
415 the specified prefix) are passed through a function before being returned. |
|
416 The prefix is removed before looking up the real value. |
|
417 |
|
418 Used by the SQL construction code to ensure that values are correctly |
|
419 quoted before being used. |
|
420 """ |
|
421 def __init__(self, data, func, prefix): |
|
422 super(DictWrapper, self).__init__(data) |
|
423 self.func = func |
|
424 self.prefix = prefix |
|
425 |
|
426 def __getitem__(self, key): |
|
427 """ |
|
428 Retrieves the real value after stripping the prefix string (if |
|
429 present). If the prefix is present, pass the value through self.func |
|
430 before returning, otherwise return the raw value. |
|
431 """ |
|
432 if key.startswith(self.prefix): |
|
433 use_func = True |
|
434 key = key[len(self.prefix):] |
|
435 else: |
|
436 use_func = False |
|
437 value = super(DictWrapper, self).__getitem__(key) |
|
438 if use_func: |
|
439 return self.func(value) |
|
440 return value |
|
441 |