Package restkit :: Module datastructures
[hide private]
[frames] | no frames]

Source Code for Module restkit.datastructures

  1  # -*- coding: utf-8 - 
  2  # 
  3  # This file is part of restkit released under the MIT license.  
  4  # See the NOTICE for more information. 
  5   
  6  from UserDict import DictMixin 
7 8 9 -class MultiDict(DictMixin):
10 """ 11 An ordered dictionary that can have multiple values for each key. 12 Adds the methods getall, getone, mixed and extend and add to the normal 13 dictionary interface. 14 """ 15
16 - def __init__(self, *args, **kw):
17 if len(args) > 1: 18 raise TypeError("MultiDict can only be called with one positional argument") 19 if args: 20 if isinstance(args[0], MultiDict): 21 items = args[0]._items 22 elif hasattr(args[0], 'iteritems'): 23 items = list(args[0].iteritems()) 24 elif hasattr(args[0], 'items'): 25 items = args[0].items() 26 else: 27 items = list(args[0]) 28 self._items = items 29 else: 30 self._items = [] 31 if kw: 32 self._items.extend(kw.iteritems())
33 34 @classmethod
35 - def from_fieldstorage(cls, fs):
36 """ 37 Create a dict from a cgi.FieldStorage instance 38 """ 39 obj = cls() 40 # fs.list can be None when there's nothing to parse 41 for field in fs.list or (): 42 if field.filename: 43 obj.add(field.name, field) 44 else: 45 obj.add(field.name, field.value) 46 return obj
47
48 - def __getitem__(self, key):
49 for k, v in reversed(self._items): 50 if k == key: 51 return v 52 raise KeyError(key)
53
54 - def __setitem__(self, key, value):
55 try: 56 del self[key] 57 except KeyError: 58 pass 59 self._items.append((key, value))
60
61 - def add(self, key, value):
62 """ 63 Add the key and value, not overwriting any previous value. 64 """ 65 self._items.append((key, value))
66
67 - def getall(self, key):
68 """ 69 Return a list of all values matching the key (may be an empty list) 70 """ 71 result = [] 72 for k, v in self._items: 73 if key == k: 74 result.append(v)
75
76 - def iget(self, key):
77 """like get but case insensitive """ 78 lkey = key.lower() 79 for k, v in self._items: 80 if k.lower() == lkey: 81 return v 82 return None
83
84 - def getone(self, key):
85 """ 86 Get one value matching the key, raising a KeyError if multiple 87 values were found. 88 """ 89 v = self.getall(key) 90 if not v: 91 raise KeyError('Key not found: %r' % key) 92 if len(v) > 1: 93 raise KeyError('Multiple values match %r: %r' % (key, v)) 94 return v[0]
95
96 - def mixed(self):
97 """ 98 Returns a dictionary where the values are either single 99 values, or a list of values when a key/value appears more than 100 once in this dictionary. This is similar to the kind of 101 dictionary often used to represent the variables in a web 102 request. 103 """ 104 result = {} 105 multi = {} 106 for key, value in self.iteritems(): 107 if key in result: 108 # We do this to not clobber any lists that are 109 # *actual* values in this dictionary: 110 if key in multi: 111 result[key].append(value) 112 else: 113 result[key] = [result[key], value] 114 multi[key] = None 115 else: 116 result[key] = value 117 return result
118
119 - def dict_of_lists(self):
120 """ 121 Returns a dictionary where each key is associated with a list of values. 122 """ 123 r = {} 124 for key, val in self.iteritems(): 125 r.setdefault(key, []).append(val) 126 return r
127
128 - def __delitem__(self, key):
129 items = self._items 130 found = False 131 for i in range(len(items)-1, -1, -1): 132 if items[i][0] == key: 133 del items[i] 134 found = True 135 if not found: 136 raise KeyError(key)
137
138 - def __contains__(self, key):
139 for k, v in self._items: 140 if k == key: 141 return True 142 return False
143 144 has_key = __contains__ 145
146 - def clear(self):
147 self._items = []
148
149 - def copy(self):
150 return self.__class__(self)
151
152 - def setdefault(self, key, default=None):
153 for k, v in self._items: 154 if key == k: 155 return v 156 self._items.append((key, default)) 157 return default
158
159 - def pop(self, key, *args):
160 if len(args) > 1: 161 raise TypeError, "pop expected at most 2 arguments, got "\ 162 + repr(1 + len(args)) 163 for i in range(len(self._items)): 164 if self._items[i][0] == key: 165 v = self._items[i][1] 166 del self._items[i] 167 return v 168 if args: 169 return args[0] 170 else: 171 raise KeyError(key)
172
173 - def ipop(self, key, *args):
174 """ like pop but case insensitive """ 175 if len(args) > 1: 176 raise TypeError, "pop expected at most 2 arguments, got "\ 177 + repr(1 + len(args)) 178 179 lkey = key.lower() 180 for i, item in enumerate(self._items): 181 if item[0].lower() == lkey: 182 v = self._items[i][1] 183 del self._items[i] 184 return v 185 if args: 186 return args[0] 187 else: 188 raise KeyError(key)
189
190 - def popitem(self):
191 return self._items.pop()
192
193 - def extend(self, other=None, **kwargs):
194 if other is None: 195 pass 196 elif hasattr(other, 'items'): 197 self._items.extend(other.items()) 198 elif hasattr(other, 'keys'): 199 for k in other.keys(): 200 self._items.append((k, other[k])) 201 else: 202 for k, v in other: 203 self._items.append((k, v)) 204 if kwargs: 205 self.update(kwargs)
206
207 - def __repr__(self):
208 items = ', '.join(['(%r, %r)' % v for v in self.iteritems()]) 209 return '%s([%s])' % (self.__class__.__name__, items)
210
211 - def __len__(self):
212 return len(self._items)
213 214 ## 215 ## All the iteration: 216 ## 217
218 - def keys(self):
219 return [k for k, v in self._items]
220
221 - def iterkeys(self):
222 for k, v in self._items: 223 yield k
224 225 __iter__ = iterkeys 226
227 - def items(self):
228 return self._items[:]
229
230 - def iteritems(self):
231 return iter(self._items)
232
233 - def values(self):
234 return [v for k, v in self._items]
235
236 - def itervalues(self):
237 for k, v in self._items: 238 yield v
239