summaryrefslogtreecommitdiff
path: root/Lib/test/test_dict.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_dict.py')
-rw-r--r--Lib/test/test_dict.py157
1 files changed, 149 insertions, 8 deletions
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index b85d333dcb..832bb9c8e2 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -1,10 +1,13 @@
-import unittest
-from test import support
-
-import collections, random, string
+import collections
import collections.abc
-import gc, weakref
+import gc
import pickle
+import random
+import string
+import sys
+import unittest
+import weakref
+from test import support
class DictTest(unittest.TestCase):
@@ -836,21 +839,127 @@ class DictTest(unittest.TestCase):
pass
self._tracked(MyDict())
+ def make_shared_key_dict(self, n):
+ class C:
+ pass
+
+ dicts = []
+ for i in range(n):
+ a = C()
+ a.x, a.y, a.z = 1, 2, 3
+ dicts.append(a.__dict__)
+
+ return dicts
+
+ @support.cpython_only
+ def test_splittable_setdefault(self):
+ """split table must be combined when setdefault()
+ breaks insertion order"""
+ a, b = self.make_shared_key_dict(2)
+
+ a['a'] = 1
+ size_a = sys.getsizeof(a)
+ a['b'] = 2
+ b.setdefault('b', 2)
+ size_b = sys.getsizeof(b)
+ b['a'] = 1
+
+ self.assertGreater(size_b, size_a)
+ self.assertEqual(list(a), ['x', 'y', 'z', 'a', 'b'])
+ self.assertEqual(list(b), ['x', 'y', 'z', 'b', 'a'])
+
+ @support.cpython_only
+ def test_splittable_del(self):
+ """split table must be combined when del d[k]"""
+ a, b = self.make_shared_key_dict(2)
+
+ orig_size = sys.getsizeof(a)
+
+ del a['y'] # split table is combined
+ with self.assertRaises(KeyError):
+ del a['y']
+
+ self.assertGreater(sys.getsizeof(a), orig_size)
+ self.assertEqual(list(a), ['x', 'z'])
+ self.assertEqual(list(b), ['x', 'y', 'z'])
+
+ # Two dicts have different insertion order.
+ a['y'] = 42
+ self.assertEqual(list(a), ['x', 'z', 'y'])
+ self.assertEqual(list(b), ['x', 'y', 'z'])
+
+ @support.cpython_only
+ def test_splittable_pop(self):
+ """split table must be combined when d.pop(k)"""
+ a, b = self.make_shared_key_dict(2)
+
+ orig_size = sys.getsizeof(a)
+
+ a.pop('y') # split table is combined
+ with self.assertRaises(KeyError):
+ a.pop('y')
+
+ self.assertGreater(sys.getsizeof(a), orig_size)
+ self.assertEqual(list(a), ['x', 'z'])
+ self.assertEqual(list(b), ['x', 'y', 'z'])
+
+ # Two dicts have different insertion order.
+ a['y'] = 42
+ self.assertEqual(list(a), ['x', 'z', 'y'])
+ self.assertEqual(list(b), ['x', 'y', 'z'])
+
+ @support.cpython_only
+ def test_splittable_pop_pending(self):
+ """pop a pending key in a splitted table should not crash"""
+ a, b = self.make_shared_key_dict(2)
+
+ a['a'] = 4
+ with self.assertRaises(KeyError):
+ b.pop('a')
+
+ @support.cpython_only
+ def test_splittable_popitem(self):
+ """split table must be combined when d.popitem()"""
+ a, b = self.make_shared_key_dict(2)
+
+ orig_size = sys.getsizeof(a)
+
+ item = a.popitem() # split table is combined
+ self.assertEqual(item, ('z', 3))
+ with self.assertRaises(KeyError):
+ del a['z']
+
+ self.assertGreater(sys.getsizeof(a), orig_size)
+ self.assertEqual(list(a), ['x', 'y'])
+ self.assertEqual(list(b), ['x', 'y', 'z'])
+
@support.cpython_only
def test_splittable_setattr_after_pop(self):
- """setattr must not convert combined table into split table"""
+ """setattr() must not convert combined table into split table."""
# Issue 28147
import _testcapi
class C:
pass
a = C()
+
+ a.a = 1
+ self.assertTrue(_testcapi.dict_hassplittable(a.__dict__))
+
+ # dict.pop() convert it to combined table
+ a.__dict__.pop('a')
+ self.assertFalse(_testcapi.dict_hassplittable(a.__dict__))
+
+ # But C should not convert a.__dict__ to split table again.
+ a.a = 1
+ self.assertFalse(_testcapi.dict_hassplittable(a.__dict__))
+
+ # Same for popitem()
+ a = C()
a.a = 2
self.assertTrue(_testcapi.dict_hassplittable(a.__dict__))
- # dict.popitem() convert it to combined table
a.__dict__.popitem()
self.assertFalse(_testcapi.dict_hassplittable(a.__dict__))
- # But C should not convert a.__dict__ to split table again.
a.a = 3
self.assertFalse(_testcapi.dict_hassplittable(a.__dict__))
@@ -976,6 +1085,37 @@ class DictTest(unittest.TestCase):
support.check_free_after_iterating(self, lambda d: iter(d.values()), dict)
support.check_free_after_iterating(self, lambda d: iter(d.items()), dict)
+
+class CAPITest(unittest.TestCase):
+
+ # Test _PyDict_GetItem_KnownHash()
+ @support.cpython_only
+ def test_getitem_knownhash(self):
+ from _testcapi import dict_getitem_knownhash
+
+ d = {'x': 1, 'y': 2, 'z': 3}
+ self.assertEqual(dict_getitem_knownhash(d, 'x', hash('x')), 1)
+ self.assertEqual(dict_getitem_knownhash(d, 'y', hash('y')), 2)
+ self.assertEqual(dict_getitem_knownhash(d, 'z', hash('z')), 3)
+
+ # not a dict
+ self.assertRaises(SystemError, dict_getitem_knownhash, [], 1, hash(1))
+ # key does not exist
+ self.assertRaises(KeyError, dict_getitem_knownhash, {}, 1, hash(1))
+
+ class Exc(Exception): pass
+ class BadEq:
+ def __eq__(self, other):
+ raise Exc
+ def __hash__(self):
+ return 7
+
+ k1, k2 = BadEq(), BadEq()
+ d = {k1: 1}
+ self.assertEqual(dict_getitem_knownhash(d, k1, hash(k1)), 1)
+ self.assertRaises(Exc, dict_getitem_knownhash, d, k2, hash(k2))
+
+
from test import mapping_tests
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
@@ -987,5 +1127,6 @@ class Dict(dict):
class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
type2test = Dict
+
if __name__ == "__main__":
unittest.main()