Updated ez_setup.py from 0.6a7 to 0.6a9
[fdr-django.git] / django / utils / datastructures.py
blob8716c9cc3fee2da8bc56ce30acee3120d5262df3
1 class MergeDict:
2 """
3 A simple class for creating new "virtual" dictionaries that actualy look
4 up values in more than one dictionary, passed in the constructor.
5 """
6 def __init__(self, *dicts):
7 self.dicts = dicts
9 def __getitem__(self, key):
10 for dict in self.dicts:
11 try:
12 return dict[key]
13 except KeyError:
14 pass
15 raise KeyError
17 def get(self, key, default):
18 try:
19 return self[key]
20 except KeyError:
21 return default
23 def getlist(self, key):
24 for dict in self.dicts:
25 try:
26 return dict.getlist(key)
27 except KeyError:
28 pass
29 raise KeyError
31 def items(self):
32 item_list = []
33 for dict in self.dicts:
34 item_list.extend(dict.items())
35 return item_list
37 def has_key(self, key):
38 for dict in self.dicts:
39 if dict.has_key(key):
40 return True
41 return False
43 class MultiValueDictKeyError(KeyError):
44 pass
46 class MultiValueDict(dict):
47 """
48 A subclass of dictionary customized to handle multiple values for the same key.
50 >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
51 >>> d['name']
52 'Simon'
53 >>> d.getlist('name')
54 ['Adrian', 'Simon']
55 >>> d.get('lastname', 'nonexistent')
56 'nonexistent'
57 >>> d.setlist('lastname', ['Holovaty', 'Willison'])
59 This class exists to solve the irritating problem raised by cgi.parse_qs,
60 which returns a list for every key, even though most Web forms submit
61 single name-value pairs.
62 """
63 def __init__(self, key_to_list_mapping=()):
64 dict.__init__(self, key_to_list_mapping)
66 def __repr__(self):
67 return "<MultiValueDict: %s>" % dict.__repr__(self)
69 def __getitem__(self, key):
70 """
71 Returns the last data value for this key, or [] if it's an empty list;
72 raises KeyError if not found.
73 """
74 try:
75 list_ = dict.__getitem__(self, key)
76 except KeyError:
77 raise MultiValueDictKeyError, "Key %r not found in %r" % (key, self)
78 try:
79 return list_[-1]
80 except IndexError:
81 return []
83 def _setitem_list(self, key, value):
84 dict.__setitem__(self, key, [value])
85 __setitem__ = _setitem_list
87 def get(self, key, default=None):
88 "Returns the default value if the requested data doesn't exist"
89 try:
90 val = self[key]
91 except KeyError:
92 return default
93 if val == []:
94 return default
95 return val
97 def getlist(self, key):
98 "Returns an empty list if the requested data doesn't exist"
99 try:
100 return dict.__getitem__(self, key)
101 except KeyError:
102 return []
104 def setlist(self, key, list_):
105 dict.__setitem__(self, key, list_)
107 def setdefault(self, key, default=None):
108 if key not in self:
109 self[key] = default
110 return self[key]
112 def setlistdefault(self, key, default_list=()):
113 if key not in self:
114 self.setlist(key, default_list)
115 return self.getlist(key)
117 def appendlist(self, key, value):
118 "Appends an item to the internal list associated with key"
119 self.setlistdefault(key, [])
120 dict.__setitem__(self, key, self.getlist(key) + [value])
122 def items(self):
124 Returns a list of (key, value) pairs, where value is the last item in
125 the list associated with the key.
127 return [(key, self[key]) for key in self.keys()]
129 def lists(self):
130 "Returns a list of (key, list) pairs."
131 return dict.items(self)
133 def values(self):
134 "Returns a list of the last value on every key list."
135 return [self[key] for key in self.keys()]
137 def copy(self):
138 "Returns a copy of this object."
139 import copy
140 # Our custom __setitem__ must be disabled for copying machinery.
141 MultiValueDict.__setitem__ = dict.__setitem__
142 cp = copy.deepcopy(self)
143 MultiValueDict.__setitem__ = MultiValueDict._setitem_list
144 return cp
146 def update(self, other_dict):
147 "update() extends rather than replaces existing key lists."
148 if isinstance(other_dict, MultiValueDict):
149 for key, value_list in other_dict.lists():
150 self.setlistdefault(key, []).extend(value_list)
151 else:
152 try:
153 for key, value in other_dict.items():
154 self.setlistdefault(key, []).append(value)
155 except TypeError:
156 raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
158 class DotExpandedDict(dict):
160 A special dictionary constructor that takes a dictionary in which the keys
161 may contain dots to specify inner dictionaries. It's confusing, but this
162 example should make sense.
164 >>> d = DotExpandedDict({'person.1.firstname': ['Simon'],
165 'person.1.lastname': ['Willison'],
166 'person.2.firstname': ['Adrian'],
167 'person.2.lastname': ['Holovaty']})
168 >>> d
169 {'person': {'1': {'lastname': ['Willison'], 'firstname': ['Simon']},
170 '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}}
171 >>> d['person']
172 {'1': {'firstname': ['Simon'], 'lastname': ['Willison'],
173 '2': {'firstname': ['Adrian'], 'lastname': ['Holovaty']}
174 >>> d['person']['1']
175 {'firstname': ['Simon'], 'lastname': ['Willison']}
177 # Gotcha: Results are unpredictable if the dots are "uneven":
178 >>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1})
179 >>> {'c': 1}
181 def __init__(self, key_to_list_mapping):
182 for k, v in key_to_list_mapping.items():
183 current = self
184 bits = k.split('.')
185 for bit in bits[:-1]:
186 current = current.setdefault(bit, {})
187 # Now assign value to current position
188 try:
189 current[bits[-1]] = v
190 except TypeError: # Special-case if current isn't a dict.
191 current = {bits[-1]: v}