From 8ebebd8f7e0d4f9e11a76687ce797bb78a219c34 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 2 Jan 2011 01:03:26 +0000 Subject: [PATCH] Raymond-Hettingers-MacBook-Pro:py27 raymondhettinger$ cat svn-commit.tmp Backport r87594 r87611 and r87612 so that OrderedDict subclassing behavior better matches dict subclassing (i.e. adding __missing__ works and extending/overriding the update() methods doesn't break __init__()). --- Lib/collections.py | 26 +++++++++++++++++++++----- Lib/test/test_collections.py | 28 ++++++++++++++++++++++++++++ Misc/NEWS | 2 ++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/Lib/collections.py b/Lib/collections.py index a49ecc7719e..17fc0126b2f 100644 --- a/Lib/collections.py +++ b/Lib/collections.py @@ -43,7 +43,7 @@ def _recursive_repr(user_function): ### OrderedDict ################################################################################ -class OrderedDict(dict, MutableMapping): +class OrderedDict(dict): 'Dictionary that remembers insertion order' # An inherited dict maps keys to values. # The inherited dict provides __getitem__, __len__, __contains__, and get. @@ -71,7 +71,7 @@ class OrderedDict(dict, MutableMapping): NEXT = 1 root[PREV] = root[NEXT] = root self.__map = {} - self.update(*args, **kwds) + self.__update(*args, **kwds) def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 'od.__setitem__(i, y) <==> od[i]=y' @@ -134,9 +134,7 @@ class OrderedDict(dict, MutableMapping): pass dict.clear(self) - setdefault = MutableMapping.setdefault - update = MutableMapping.update - pop = MutableMapping.pop + update = __update = MutableMapping.update keys = MutableMapping.keys values = MutableMapping.values items = MutableMapping.items @@ -157,6 +155,24 @@ class OrderedDict(dict, MutableMapping): "od.viewitems() -> a set-like object providing a view on od's items" return ItemsView(self) + __marker = object() + + def pop(self, key, default=__marker): + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + def popitem(self, last=True): '''od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 2d2319fe269..f6a43fcb1fc 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -812,6 +812,10 @@ class TestOrderedDict(unittest.TestCase): self.assertEqual(list(d.items()), [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)]) + def test_abc(self): + self.assertIsInstance(OrderedDict(), MutableMapping) + self.assertTrue(issubclass(OrderedDict, MutableMapping)) + def test_clear(self): pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] shuffle(pairs) @@ -873,6 +877,17 @@ class TestOrderedDict(unittest.TestCase): self.assertEqual(len(od), 0) self.assertEqual(od.pop(k, 12345), 12345) + # make sure pop still works when __missing__ is defined + class Missing(OrderedDict): + def __missing__(self, key): + return 0 + m = Missing(a=1) + self.assertEqual(m.pop('b', 5), 5) + self.assertEqual(m.pop('a', 6), 1) + self.assertEqual(m.pop('a', 6), 6) + with self.assertRaises(KeyError): + m.pop('a') + def test_equality(self): pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] shuffle(pairs) @@ -956,6 +971,12 @@ class TestOrderedDict(unittest.TestCase): # make sure 'x' is added to the end self.assertEqual(list(od.items())[-1], ('x', 10)) + # make sure setdefault still works when __missing__ is defined + class Missing(OrderedDict): + def __missing__(self, key): + return 0 + self.assertEqual(Missing().setdefault(5, 9), 9) + def test_reinsert(self): # Given insert a, insert b, delete a, re-insert a, # verify that a is now later than b. @@ -973,6 +994,13 @@ class TestOrderedDict(unittest.TestCase): self.assertEqual(list(od.viewvalues()), [None for k in s]) self.assertEqual(list(od.viewitems()), [(k, None) for k in s]) + def test_override_update(self): + # Verify that subclasses can override update() without breaking __init__() + class MyOD(OrderedDict): + def update(self, *args, **kwds): + raise Exception() + items = [('a', 1), ('c', 3), ('b', 2)] + self.assertEqual(list(MyOD(items).items()), items) class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = OrderedDict diff --git a/Misc/NEWS b/Misc/NEWS index 7b32d670618..12cee4067a7 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,8 @@ Core and Builtins Library ------- +- Subclasses of collections.OrderedDict now work correctly with __missing__. + - Issue 10753 - Characters ';','=' and ',' in the PATH_INFO environment variable won't be quoted when the URI is constructed by the wsgiref.util 's request_uri method. According to RFC 3986, these characters can be a part of