Skip to content

Commit ed05ae7

Browse files
committed
Merge branch 'mind1master-multidict'
Conflicts: aiohttp/multidict.py
2 parents 3c31b38 + 5b551a8 commit ed05ae7

File tree

2 files changed

+168
-137
lines changed

2 files changed

+168
-137
lines changed

aiohttp/multidict.py

Lines changed: 122 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
import pprint
2-
from itertools import chain
3-
from collections import OrderedDict, abc
2+
from itertools import chain, filterfalse
3+
from collections import abc
44

55
_marker = object()
66

77

8+
def _unique_everseen(iterable):
9+
"""List unique elements, preserving order.
10+
Remember all elements ever seen.
11+
Recipe from
12+
https://docs.python.org/3/library/itertools.html#itertools-recipes"""
13+
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
14+
# unique_everseen('ABBCcAD', str.lower) --> A B C D
15+
seen = set()
16+
seen_add = seen.add
17+
for element in filterfalse(seen.__contains__, iterable):
18+
seen_add(element)
19+
yield element
20+
21+
822
class MultiDict(abc.Mapping):
923
"""Read-only ordered dictionary that can have multiple values for each key.
1024
@@ -16,46 +30,45 @@ class MultiDict(abc.Mapping):
1630
def __init__(self, *args, **kwargs):
1731
if len(args) > 1:
1832
raise TypeError("MultiDict takes at most 1 positional "
19-
"arguments ({} given)".format(len(args)))
20-
self._items = OrderedDict()
33+
"argument ({} given)".format(len(args)))
2134

35+
self._items = []
2236
if args:
2337
if hasattr(args[0], 'items'):
2438
args = list(args[0].items())
2539
else:
2640
args = list(args[0])
41+
for arg in args:
42+
if not len(arg) == 2:
43+
raise TypeError("MultiDict takes either dict "
44+
"or list of (key, value) tuples")
2745

2846
self._fill(chain(args, kwargs.items()))
2947

3048
def _fill(self, ipairs):
31-
for key, value in ipairs:
32-
if key in self._items:
33-
self._items[key].append(value)
34-
else:
35-
self._items[key] = [value]
36-
37-
def get(self, key, default=None):
38-
"""Return first value stored at key."""
39-
if key in self._items and self._items[key]:
40-
return self._items[key][0]
41-
else:
42-
return default
49+
self._items.extend(ipairs)
4350

4451
def getall(self, key, default=_marker):
45-
"""Returns all values stored at key as a tuple.
46-
47-
Raises KeyError if key doesn't exist."""
48-
if key in self._items:
49-
return tuple(self._items[key])
50-
else:
51-
if default is not _marker:
52-
return default
53-
else:
54-
raise KeyError(key)
52+
"""
53+
Return a list of all values matching the key (may be an empty list)
54+
"""
55+
res = tuple([v for k, v in self._items if k == key])
56+
if res:
57+
return res
58+
if not res and default is not _marker:
59+
return default
60+
raise KeyError('Key not found: %r' % key)
5561

56-
def getone(self, key):
57-
"""Return first value stored at key."""
58-
return self._items[key][0]
62+
def getone(self, key, default=_marker):
63+
"""
64+
Get first value matching the key
65+
"""
66+
for k, v in self._items:
67+
if k == key:
68+
return v
69+
if default is not _marker:
70+
return default
71+
raise KeyError('Key not found: %r' % key)
5972

6073
# extra methods #
6174

@@ -67,14 +80,20 @@ def copy(self):
6780
# Mapping interface #
6881

6982
def __getitem__(self, key):
70-
return self._items[key][0]
83+
for k, v in self._items:
84+
if k == key:
85+
return v
86+
raise KeyError(key)
7187

7288
def __iter__(self):
7389
return iter(self._items)
7490

7591
def __len__(self):
7692
return len(self._items)
7793

94+
def keys(self, *, getall=False):
95+
return _KeysView(self._items, getall=getall)
96+
7897
def items(self, *, getall=False):
7998
return _ItemsView(self._items, getall=getall)
8099

@@ -89,12 +108,16 @@ def __eq__(self, other):
89108
return dict(self.items()) == dict(other.items())
90109

91110
def __contains__(self, key):
92-
return key in self._items
111+
for k, v in self._items:
112+
if k == key:
113+
return True
114+
return False
93115

94116
def __repr__(self):
95117
return '<{}>\n{}'.format(
96118
self.__class__.__name__, pprint.pformat(
97-
list(self.items(getall=True))))
119+
list(self.items(getall=True)))
120+
)
98121

99122

100123
class CaseInsensitiveMultiDict(MultiDict):
@@ -109,51 +132,29 @@ def _from_uppercase_multidict(cls, dct):
109132

110133
def _fill(self, ipairs):
111134
for key, value in ipairs:
112-
key = key.upper()
113-
if key in self._items:
114-
self._items[key].append(value)
115-
else:
116-
self._items[key] = [value]
135+
uppkey = key.upper()
136+
self._items.append((uppkey, value))
117137

118138
def getall(self, key, default=_marker):
119139
return super().getall(key.upper(), default)
120140

121-
def get(self, key, default=None):
122-
key = key.upper()
123-
if key in self._items and self._items[key]:
124-
return self._items[key][0]
125-
else:
126-
return default
127-
128-
def getone(self, key):
129-
return self._items[key.upper()][0]
141+
def getone(self, key, default=_marker):
142+
return super().getone(key.upper(), default)
130143

131144
def __getitem__(self, key):
132-
return self._items[key.upper()][0]
145+
return super().__getitem__(key.upper())
133146

134147
def __contains__(self, key):
135-
return key.upper() in self._items
148+
return super().__contains__(key.upper())
136149

137150

138-
class BaseMutableMultiDict(abc.MutableMapping):
139-
140-
def getall(self, key, default=_marker):
141-
"""Returns all values stored at key as list.
142-
143-
Raises KeyError if key doesn't exist.
144-
"""
145-
result = super().getall(key, default)
146-
if result is not default:
147-
return list(result)
148-
else:
149-
return result
151+
class MutableMultiDictMixin(abc.MutableMapping):
150152

151153
def add(self, key, value):
152-
"""Adds value to a key."""
153-
if key in self._items:
154-
self._items[key].append(value)
155-
else:
156-
self._items[key] = [value]
154+
"""
155+
Add the key and value, not overwriting any previous value.
156+
"""
157+
self._items.append((key, value))
157158

158159
def extend(self, *args, **kwargs):
159160
"""Extends current MutableMultiDict with more values.
@@ -182,10 +183,21 @@ def clear(self):
182183
# MutableMapping interface #
183184

184185
def __setitem__(self, key, value):
185-
self._items[key] = [value]
186+
try:
187+
del self[key]
188+
except KeyError:
189+
pass
190+
self._items.append((key, value))
186191

187192
def __delitem__(self, key):
188-
del self._items[key]
193+
items = self._items
194+
found = False
195+
for i in range(len(items)-1, -1, -1):
196+
if items[i][0] == key:
197+
del items[i]
198+
found = True
199+
if not found:
200+
raise KeyError(key)
189201

190202
def pop(self, key, default=None):
191203
"""Method not allowed."""
@@ -200,71 +212,74 @@ def update(self, *args, **kw):
200212
raise NotImplementedError("Use extend method instead")
201213

202214

203-
class MutableMultiDict(BaseMutableMultiDict, MultiDict):
215+
class MutableMultiDict(MutableMultiDictMixin, MultiDict):
204216
"""An ordered dictionary that can have multiple values for each key."""
205217

206218

207219
class CaseInsensitiveMutableMultiDict(
208-
BaseMutableMultiDict, CaseInsensitiveMultiDict):
220+
MutableMultiDictMixin, CaseInsensitiveMultiDict):
209221
"""An ordered dictionary that can have multiple values for each key."""
210222

211-
def getall(self, key, default=_marker):
212-
return super().getall(key.upper(), default)
213-
214223
def add(self, key, value):
215224
super().add(key.upper(), value)
216225

217226
def __setitem__(self, key, value):
218-
self._items[key.upper()] = [value]
227+
super().__setitem__(key.upper(), value)
219228

220229
def __delitem__(self, key):
221-
del self._items[key.upper()]
230+
super().__delitem__(key.upper())
222231

223232

224-
class _ItemsView(abc.ItemsView):
233+
class _ViewBase:
225234

226-
def __init__(self, mapping, *, getall=False):
227-
super().__init__(mapping)
235+
def __init__(self, items, *, getall=False):
228236
self._getall = getall
237+
self._keys = [item[0] for item in items]
238+
if not getall:
239+
self._keys = list(_unique_everseen(self._keys))
229240

230-
def __contains__(self, item):
231-
key, value = item
232-
try:
233-
values = self._mapping[key]
234-
except KeyError:
235-
return False
241+
items_to_use = []
242+
if getall:
243+
items_to_use = items
236244
else:
237-
if self._getall:
238-
return value in values
239-
else:
240-
return value == values[0]
245+
for key in self._keys:
246+
for k, v in items:
247+
if k == key:
248+
items_to_use.append((k, v))
249+
break
250+
assert len(items_to_use) == len(self._keys)
241251

242-
def __iter__(self):
243-
for key, values in self._mapping.items():
244-
if self._getall:
245-
for value in values:
246-
yield key, value
247-
else:
248-
yield key, values[0]
252+
super().__init__(items_to_use)
249253

250254

251-
class _ValuesView(abc.KeysView):
255+
class _ItemsView(_ViewBase, abc.ItemsView):
252256

253-
def __init__(self, mapping, *, getall=False):
254-
super().__init__(mapping)
255-
self._getall = getall
257+
def __contains__(self, item):
258+
assert isinstance(item, tuple) or isinstance(item, list)
259+
assert len(item) == 2
260+
return item in self._mapping
261+
262+
def __iter__(self):
263+
yield from self._mapping
264+
265+
266+
class _ValuesView(_ViewBase, abc.ValuesView):
256267

257268
def __contains__(self, value):
258-
for values in self._mapping.values():
259-
if self._getall and value in values:
260-
return True
261-
elif value == values[0]:
269+
for item in self._mapping:
270+
if item[1] == value:
262271
return True
263272
return False
264273

265274
def __iter__(self):
266-
for values in self._mapping.values():
267-
if self._getall:
268-
yield from iter(values)
269-
else:
270-
yield values[0]
275+
for item in self._mapping:
276+
yield item[1]
277+
278+
279+
class _KeysView(_ViewBase, abc.KeysView):
280+
281+
def __contains__(self, key):
282+
return key in self._keys
283+
284+
def __iter__(self):
285+
yield from self._keys

0 commit comments

Comments
 (0)