1""" 2Test implementation of the PEP 509: dictionary versioning. 3""" 4import unittest 5from test.support import import_helper 6 7# PEP 509 is implemented in CPython but other Python implementations 8# don't require to implement it 9_testcapi = import_helper.import_module('_testcapi') 10 11 12class 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_dont_change(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_dont_change(d, d.update, key=value) 88 89 d2 = self.new_dict(key=value) 90 self.check_version_dont_change(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 self.assertIsNot(value1, value2) 102 103 d = self.new_dict() 104 self.check_version_changed(d, d.__setitem__, 'key', value1) 105 self.assertIs(d['key'], value1) 106 107 # setting a key to a value equal to the current value 108 # with dict.__setitem__() must change the version 109 self.check_version_changed(d, d.__setitem__, 'key', value2) 110 self.assertIs(d['key'], value2) 111 112 # setting a key to a value equal to the current value 113 # with dict.update() must change the version 114 self.check_version_changed(d, d.update, key=value1) 115 self.assertIs(d['key'], value1) 116 117 d2 = self.new_dict(key=value2) 118 self.check_version_changed(d, d.update, d2) 119 self.assertIs(d['key'], value2) 120 121 def test_setdefault(self): 122 d = self.new_dict() 123 124 # setting a key with dict.setdefault() must change the version 125 self.check_version_changed(d, d.setdefault, 'key', 'value1') 126 127 # don't change the version if the key already exists 128 self.check_version_dont_change(d, d.setdefault, 'key', 'value2') 129 130 def test_delitem(self): 131 d = self.new_dict(key='value') 132 133 # deleting a key with dict.__delitem__() must change the version 134 self.check_version_changed(d, d.__delitem__, 'key') 135 136 # don't change the version if the key doesn't exist 137 self.check_version_dont_change(d, self.assertRaises, KeyError, 138 d.__delitem__, 'key') 139 140 def test_pop(self): 141 d = self.new_dict(key='value') 142 143 # pop() must change the version if the key exists 144 self.check_version_changed(d, d.pop, 'key') 145 146 # pop() must not change the version if the key does not exist 147 self.check_version_dont_change(d, self.assertRaises, KeyError, 148 d.pop, 'key') 149 150 def test_popitem(self): 151 d = self.new_dict(key='value') 152 153 # popitem() must change the version if the dict is not empty 154 self.check_version_changed(d, d.popitem) 155 156 # popitem() must not change the version if the dict is empty 157 self.check_version_dont_change(d, self.assertRaises, KeyError, 158 d.popitem) 159 160 def test_update(self): 161 d = self.new_dict(key='value') 162 163 # update() calling with no argument must not change the version 164 self.check_version_dont_change(d, d.update) 165 166 # update() must change the version 167 self.check_version_changed(d, d.update, key='new value') 168 169 d2 = self.new_dict(key='value 3') 170 self.check_version_changed(d, d.update, d2) 171 172 def test_clear(self): 173 d = self.new_dict(key='value') 174 175 # clear() must change the version if the dict is not empty 176 self.check_version_changed(d, d.clear) 177 178 # clear() must not change the version if the dict is empty 179 self.check_version_dont_change(d, d.clear) 180 181 182class Dict(dict): 183 pass 184 185 186class DictSubtypeVersionTests(DictVersionTests): 187 type2test = Dict 188 189 190if __name__ == "__main__": 191 unittest.main() 192