1 """ 2 Test implementation of the PEP 509: dictionary versionning. 3 """ 4 import unittest 5 from test import support 6 7 # PEP 509 is implemented in CPython but other Python implementations 8 # don't require to implement it 9 _testcapi = support.import_module('_testcapi') 10 11 12 class DictVersionTests(unittest.TestCase): 13 type2test = dict 14 15 def setUp(self): 16 self.seen_versions = set() 17 self.dict = None 18 19 def check_version_unique(self, mydict): 20 version = _testcapi.dict_get_version(mydict) 21 self.assertNotIn(version, self.seen_versions) 22 self.seen_versions.add(version) 23 24 def check_version_changed(self, mydict, method, *args, **kw): 25 result = method(*args, **kw) 26 self.check_version_unique(mydict) 27 return result 28 29 def check_version_dont_change(self, mydict, method, *args, **kw): 30 version1 = _testcapi.dict_get_version(mydict) 31 self.seen_versions.add(version1) 32 33 result = method(*args, **kw) 34 35 version2 = _testcapi.dict_get_version(mydict) 36 self.assertEqual(version2, version1, "version changed") 37 38 return result 39 40 def new_dict(self, *args, **kw): 41 d = self.type2test(*args, **kw) 42 self.check_version_unique(d) 43 return d 44 45 def test_constructor(self): 46 # new empty dictionaries must all have an unique version 47 empty1 = self.new_dict() 48 empty2 = self.new_dict() 49 empty3 = self.new_dict() 50 51 # non-empty dictionaries must also have an unique version 52 nonempty1 = self.new_dict(x='x') 53 nonempty2 = self.new_dict(x='x', y='y') 54 55 def test_copy(self): 56 d = self.new_dict(a=1, b=2) 57 58 d2 = self.check_version_dont_change(d, d.copy) 59 60 # dict.copy() must create a dictionary with a new unique version 61 self.check_version_unique(d2) 62 63 def test_setitem(self): 64 d = self.new_dict() 65 66 # creating new keys must change the version 67 self.check_version_changed(d, d.__setitem__, 'x', 'x') 68 self.check_version_changed(d, d.__setitem__, 'y', 'y') 69 70 # changing values must change the version 71 self.check_version_changed(d, d.__setitem__, 'x', 1) 72 self.check_version_changed(d, d.__setitem__, 'y', 2) 73 74 def test_setitem_same_value(self): 75 value = object() 76 d = self.new_dict() 77 78 # setting a key must change the version 79 self.check_version_changed(d, d.__setitem__, 'key', value) 80 81 # setting a key to the same value with dict.__setitem__ 82 # must change the version 83 self.check_version_changed(d, d.__setitem__, 'key', value) 84 85 # setting a key to the same value with dict.update 86 # must change the version 87 self.check_version_changed(d, d.update, key=value) 88 89 d2 = self.new_dict(key=value) 90 self.check_version_changed(d, d.update, d2) 91 92 def test_setitem_equal(self): 93 class AlwaysEqual: 94 def __eq__(self, other): 95 return True 96 97 value1 = AlwaysEqual() 98 value2 = AlwaysEqual() 99 self.assertTrue(value1 == value2) 100 self.assertFalse(value1 != value2) 101 102 d = self.new_dict() 103 self.check_version_changed(d, d.__setitem__, 'key', value1) 104 105 # setting a key to a value equal to the current value 106 # with dict.__setitem__() must change the version 107 self.check_version_changed(d, d.__setitem__, 'key', value2) 108 109 # setting a key to a value equal to the current value 110 # with dict.update() must change the version 111 self.check_version_changed(d, d.update, key=value1) 112 113 d2 = self.new_dict(key=value2) 114 self.check_version_changed(d, d.update, d2) 115 116 def test_setdefault(self): 117 d = self.new_dict() 118 119 # setting a key with dict.setdefault() must change the version 120 self.check_version_changed(d, d.setdefault, 'key', 'value1') 121 122 # don't change the version if the key already exists 123 self.check_version_dont_change(d, d.setdefault, 'key', 'value2') 124 125 def test_delitem(self): 126 d = self.new_dict(key='value') 127 128 # deleting a key with dict.__delitem__() must change the version 129 self.check_version_changed(d, d.__delitem__, 'key') 130 131 # don't change the version if the key doesn't exist 132 self.check_version_dont_change(d, self.assertRaises, KeyError, 133 d.__delitem__, 'key') 134 135 def test_pop(self): 136 d = self.new_dict(key='value') 137 138 # pop() must change the version if the key exists 139 self.check_version_changed(d, d.pop, 'key') 140 141 # pop() must not change the version if the key does not exist 142 self.check_version_dont_change(d, self.assertRaises, KeyError, 143 d.pop, 'key') 144 145 def test_popitem(self): 146 d = self.new_dict(key='value') 147 148 # popitem() must change the version if the dict is not empty 149 self.check_version_changed(d, d.popitem) 150 151 # popitem() must not change the version if the dict is empty 152 self.check_version_dont_change(d, self.assertRaises, KeyError, 153 d.popitem) 154 155 def test_update(self): 156 d = self.new_dict(key='value') 157 158 # update() calling with no argument must not change the version 159 self.check_version_dont_change(d, d.update) 160 161 # update() must change the version 162 self.check_version_changed(d, d.update, key='new value') 163 164 d2 = self.new_dict(key='value 3') 165 self.check_version_changed(d, d.update, d2) 166 167 def test_clear(self): 168 d = self.new_dict(key='value') 169 170 # clear() must change the version if the dict is not empty 171 self.check_version_changed(d, d.clear) 172 173 # clear() must not change the version if the dict is empty 174 self.check_version_dont_change(d, d.clear) 175 176 177 class Dict(dict): 178 pass 179 180 181 class DictSubtypeVersionTests(DictVersionTests): 182 type2test = Dict 183 184 185 if __name__ == "__main__": 186 unittest.main() 187