Django

Code

root/django/branches/newforms-admin/django/utils/datastructures.py

Revision 7815, 12.8 kB (checked in by brosner, 5 months ago)

newforms-admin: Merged from trunk up to [7814].

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