Package restkit :: Module datastructures
[hide private]

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