bpo-27275: Change popitem() and pop() methods of collections.OrderedDict (GH-27530)

* Unify the C and Python implementations of OrderedDict.popitem().

The C implementation no longer calls ``__getitem__`` and ``__delitem__``
methods of the OrderedDict subclasses.

* Change popitem() and pop() methods of collections.OrderedDict

For consistency with dict both implementations (pure Python and C)
of these methods in OrderedDict no longer call __getitem__ and
__delitem__ methods of the OrderedDict subclasses.

Previously only the Python implementation of popitem() did not
call them.
This commit is contained in:
Serhiy Storchaka 2021-08-03 14:00:55 +03:00 committed by GitHub
parent 83ca46b778
commit 8c9f847997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 130 additions and 77 deletions

View file

@ -896,5 +896,88 @@ class CPythonSubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
self.assertRaises(KeyError, d.popitem)
class SimpleLRUCache:
def __init__(self, size):
super().__init__()
self.size = size
self.counts = dict.fromkeys(('get', 'set', 'del'), 0)
def __getitem__(self, item):
self.counts['get'] += 1
value = super().__getitem__(item)
self.move_to_end(item)
return value
def __setitem__(self, key, value):
self.counts['set'] += 1
while key not in self and len(self) >= self.size:
self.popitem(last=False)
super().__setitem__(key, value)
self.move_to_end(key)
def __delitem__(self, key):
self.counts['del'] += 1
super().__delitem__(key)
class SimpleLRUCacheTests:
def test_add_after_full(self):
c = self.type2test(2)
c['t1'] = 1
c['t2'] = 2
c['t3'] = 3
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
self.assertEqual(list(c), ['t2', 't3'])
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
def test_popitem(self):
c = self.type2test(3)
for i in range(1, 4):
c[i] = i
self.assertEqual(c.popitem(last=False), (1, 1))
self.assertEqual(c.popitem(last=True), (3, 3))
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
def test_pop(self):
c = self.type2test(3)
for i in range(1, 4):
c[i] = i
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
self.assertEqual(c.pop(2), 2)
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
self.assertEqual(c.pop(4, 0), 0)
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
self.assertRaises(KeyError, c.pop, 4)
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
def test_change_order_on_get(self):
c = self.type2test(3)
for i in range(1, 4):
c[i] = i
self.assertEqual(list(c), list(range(1, 4)))
self.assertEqual(c.counts, {'get': 0, 'set': 3, 'del': 0})
self.assertEqual(c[2], 2)
self.assertEqual(c.counts, {'get': 1, 'set': 3, 'del': 0})
self.assertEqual(list(c), [1, 3, 2])
class PySimpleLRUCacheTests(SimpleLRUCacheTests, unittest.TestCase):
class type2test(SimpleLRUCache, py_coll.OrderedDict):
pass
@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
class CSimpleLRUCacheTests(SimpleLRUCacheTests, unittest.TestCase):
@classmethod
def setUpClass(cls):
class type2test(SimpleLRUCache, c_coll.OrderedDict):
pass
cls.type2test = type2test
if __name__ == "__main__":
unittest.main()