Skip to content

Commit 970d194

Browse files
committed
Implement pop and popitem
1 parent 378cbfc commit 970d194

File tree

4 files changed

+96
-12
lines changed

4 files changed

+96
-12
lines changed

aiohttp/_multidict.pyx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,13 +292,32 @@ cdef class MultiDict(_Base):
292292
self._add((key, default))
293293
return default
294294

295-
def pop(self, key, default=None):
295+
def pop(self, key, default=_marker):
296+
cdef int found
297+
cdef object value
298+
value = None
299+
found = False
300+
for i in range(len(self._items) - 1, -1, -1):
301+
if self._items[i][0] == key:
302+
value = self._items[i][1]
303+
del self._items[i]
304+
found = True
305+
if not found:
306+
if default is _marker:
307+
raise KeyError(key)
308+
else:
309+
return default
310+
else:
311+
return value
312+
296313
"""Method not allowed."""
297314
raise NotImplementedError
298315

299316
def popitem(self):
300-
"""Method not allowed."""
301-
raise NotImplementedError
317+
if self._items:
318+
return self._items.pop(0)
319+
else:
320+
raise KeyError("empty multidict")
302321

303322
def update(self, *args, **kw):
304323
"""Method not allowed."""

aiohttp/multidict.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,27 @@ def setdefault(self, key, default=None):
240240
self._items.append((key, default))
241241
return default
242242

243-
def pop(self, key, default=None):
244-
"""Method not allowed."""
245-
raise NotImplementedError
243+
def pop(self, key, default=_marker):
244+
value = None
245+
found = False
246+
for i in range(len(self._items) - 1, -1, -1):
247+
if self._items[i][0] == key:
248+
value = self._items[i][1]
249+
del self._items[i]
250+
found = True
251+
if not found:
252+
if default is _marker:
253+
raise KeyError(key)
254+
else:
255+
return default
256+
else:
257+
return value
246258

247259
def popitem(self):
248-
"""Method not allowed."""
249-
raise NotImplementedError
260+
if self._items:
261+
return self._items.pop(0)
262+
else:
263+
raise KeyError("empty multidict")
250264

251265
def update(self, *args, **kw):
252266
"""Method not allowed."""

docs/multidict.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,25 @@ MutableMultiDict
162162
View contains all values if *getall* is ``True`` (default) or
163163
only first key occurrences otherwise.
164164

165+
.. method:: pop(key[, default])
166+
167+
If *key* is in the dictionary, remove it and return its the
168+
**first** value, else return *default*.
169+
170+
If *default* is not given and *key* is not in the dictionary, a
171+
:exc:`KeyError` is raised.
172+
173+
174+
.. method:: popitem()
175+
176+
Remove and retun an arbitrary ``(key, value)`` pair from the dictionary.
177+
178+
:meth:`popitem` is useful to destructively iterate over a
179+
dictionary, as often used in set algorithms.
180+
181+
If the dictionary is empty, calling :meth:`popitem` raises a
182+
:exc:`KeyError`.
183+
165184
.. method:: setdefault(key[, default])
166185

167186
If *key* is in the dictionary, return its the **first** value.

tests/test_multidict.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,13 +442,45 @@ def test_set_default(self):
442442
self.assertIn('otherkey', d)
443443
self.assertEqual('three', d['otherkey'])
444444

445-
def test_not_implemented_methods(self):
445+
def test_popitem(self):
446446
d = self.make_dict()
447+
d.add('key', 'val1')
448+
d.add('key', 'val2')
447449

448-
with self.assertRaises(NotImplementedError):
449-
d.pop('foo')
450-
with self.assertRaises(NotImplementedError):
450+
self.assertEqual(('key', 'val1'), d.popitem())
451+
self.assertEqual([('key', 'val2')], list(d.items()))
452+
453+
def test_popitem_empty_multidict(self):
454+
d = self.make_dict()
455+
456+
with self.assertRaises(KeyError):
451457
d.popitem()
458+
459+
def test_pop(self):
460+
d = self.make_dict()
461+
d.add('key', 'val1')
462+
d.add('key', 'val2')
463+
464+
self.assertEqual('val1', d.pop('key'))
465+
self.assertFalse(d)
466+
467+
def test_pop_default(self):
468+
d = self.make_dict(other='val')
469+
470+
self.assertEqual('default', d.pop('key', 'default'))
471+
self.assertIn('other', d)
472+
473+
def test_pop_raises(self):
474+
d = self.make_dict(other='val')
475+
476+
with self.assertRaises(KeyError):
477+
d.pop('key')
478+
479+
self.assertIn('other', d)
480+
481+
def test_not_implemented_methods(self):
482+
d = self.make_dict()
483+
452484
with self.assertRaises(NotImplementedError):
453485
d.update(bar='baz')
454486

0 commit comments

Comments
 (0)