| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | """
 | 
					
						
							|  |  |  | Test implementation of the PEP 509: dictionary versionning. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | from test import support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # PEP 509 is implemented in CPython but other Python implementations | 
					
						
							|  |  |  | # don't require to implement it | 
					
						
							|  |  |  | _testcapi = support.import_module('_testcapi') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DictVersionTests(unittest.TestCase): | 
					
						
							|  |  |  |     type2test = dict | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         self.seen_versions = set() | 
					
						
							|  |  |  |         self.dict = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_version_unique(self, mydict): | 
					
						
							|  |  |  |         version = _testcapi.dict_get_version(mydict) | 
					
						
							|  |  |  |         self.assertNotIn(version, self.seen_versions) | 
					
						
							|  |  |  |         self.seen_versions.add(version) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_version_changed(self, mydict, method, *args, **kw): | 
					
						
							|  |  |  |         result = method(*args, **kw) | 
					
						
							|  |  |  |         self.check_version_unique(mydict) | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_version_dont_change(self, mydict, method, *args, **kw): | 
					
						
							|  |  |  |         version1 = _testcapi.dict_get_version(mydict) | 
					
						
							|  |  |  |         self.seen_versions.add(version1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result = method(*args, **kw) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         version2 = _testcapi.dict_get_version(mydict) | 
					
						
							|  |  |  |         self.assertEqual(version2, version1, "version changed") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return  result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def new_dict(self, *args, **kw): | 
					
						
							|  |  |  |         d = self.type2test(*args, **kw) | 
					
						
							|  |  |  |         self.check_version_unique(d) | 
					
						
							|  |  |  |         return d | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_constructor(self): | 
					
						
							|  |  |  |         # new empty dictionaries must all have an unique version | 
					
						
							|  |  |  |         empty1 = self.new_dict() | 
					
						
							|  |  |  |         empty2 = self.new_dict() | 
					
						
							|  |  |  |         empty3 = self.new_dict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # non-empty dictionaries must also have an unique version | 
					
						
							|  |  |  |         nonempty1 = self.new_dict(x='x') | 
					
						
							|  |  |  |         nonempty2 = self.new_dict(x='x', y='y') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_copy(self): | 
					
						
							|  |  |  |         d = self.new_dict(a=1, b=2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         d2 = self.check_version_dont_change(d, d.copy) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # dict.copy() must create a dictionary with a new unique version | 
					
						
							|  |  |  |         self.check_version_unique(d2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_setitem(self): | 
					
						
							|  |  |  |         d = self.new_dict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # creating new keys must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__setitem__, 'x', 'x') | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__setitem__, 'y', 'y') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # changing values must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__setitem__, 'x', 1) | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__setitem__, 'y', 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_setitem_same_value(self): | 
					
						
							|  |  |  |         value = object() | 
					
						
							|  |  |  |         d = self.new_dict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # setting a key must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__setitem__, 'key', value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # setting a key to the same value with dict.__setitem__ | 
					
						
							|  |  |  |         # must change the version | 
					
						
							| 
									
										
										
										
											2019-06-03 21:30:58 +09:00
										 |  |  |         self.check_version_dont_change(d, d.__setitem__, 'key', value) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # setting a key to the same value with dict.update | 
					
						
							|  |  |  |         # must change the version | 
					
						
							| 
									
										
										
										
											2019-06-03 21:30:58 +09:00
										 |  |  |         self.check_version_dont_change(d, d.update, key=value) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         d2 = self.new_dict(key=value) | 
					
						
							| 
									
										
										
										
											2019-06-03 21:30:58 +09:00
										 |  |  |         self.check_version_dont_change(d, d.update, d2) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_setitem_equal(self): | 
					
						
							|  |  |  |         class AlwaysEqual: | 
					
						
							|  |  |  |             def __eq__(self, other): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         value1 = AlwaysEqual() | 
					
						
							|  |  |  |         value2 = AlwaysEqual() | 
					
						
							|  |  |  |         self.assertTrue(value1 == value2) | 
					
						
							|  |  |  |         self.assertFalse(value1 != value2) | 
					
						
							| 
									
										
										
										
											2019-08-08 08:43:18 +03:00
										 |  |  |         self.assertIsNot(value1, value2) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         d = self.new_dict() | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__setitem__, 'key', value1) | 
					
						
							| 
									
										
										
										
											2019-08-08 08:43:18 +03:00
										 |  |  |         self.assertIs(d['key'], value1) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # setting a key to a value equal to the current value | 
					
						
							|  |  |  |         # with dict.__setitem__() must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__setitem__, 'key', value2) | 
					
						
							| 
									
										
										
										
											2019-08-08 08:43:18 +03:00
										 |  |  |         self.assertIs(d['key'], value2) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # setting a key to a value equal to the current value | 
					
						
							|  |  |  |         # with dict.update() must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.update, key=value1) | 
					
						
							| 
									
										
										
										
											2019-08-08 08:43:18 +03:00
										 |  |  |         self.assertIs(d['key'], value1) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         d2 = self.new_dict(key=value2) | 
					
						
							|  |  |  |         self.check_version_changed(d, d.update, d2) | 
					
						
							| 
									
										
										
										
											2019-08-08 08:43:18 +03:00
										 |  |  |         self.assertIs(d['key'], value2) | 
					
						
							| 
									
										
										
										
											2016-09-08 12:51:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_setdefault(self): | 
					
						
							|  |  |  |         d = self.new_dict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # setting a key with dict.setdefault() must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.setdefault, 'key', 'value1') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # don't change the version if the key already exists | 
					
						
							|  |  |  |         self.check_version_dont_change(d, d.setdefault, 'key', 'value2') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_delitem(self): | 
					
						
							|  |  |  |         d = self.new_dict(key='value') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # deleting a key with dict.__delitem__() must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.__delitem__, 'key') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # don't change the version if the key doesn't exist | 
					
						
							|  |  |  |         self.check_version_dont_change(d, self.assertRaises, KeyError, | 
					
						
							|  |  |  |                                        d.__delitem__, 'key') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_pop(self): | 
					
						
							|  |  |  |         d = self.new_dict(key='value') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # pop() must change the version if the key exists | 
					
						
							|  |  |  |         self.check_version_changed(d, d.pop, 'key') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # pop() must not change the version if the key does not exist | 
					
						
							|  |  |  |         self.check_version_dont_change(d, self.assertRaises, KeyError, | 
					
						
							|  |  |  |                                        d.pop, 'key') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_popitem(self): | 
					
						
							|  |  |  |         d = self.new_dict(key='value') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # popitem() must change the version if the dict is not empty | 
					
						
							|  |  |  |         self.check_version_changed(d, d.popitem) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # popitem() must not change the version if the dict is empty | 
					
						
							|  |  |  |         self.check_version_dont_change(d, self.assertRaises, KeyError, | 
					
						
							|  |  |  |                                        d.popitem) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_update(self): | 
					
						
							|  |  |  |         d = self.new_dict(key='value') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # update() calling with no argument must not change the version | 
					
						
							|  |  |  |         self.check_version_dont_change(d, d.update) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # update() must change the version | 
					
						
							|  |  |  |         self.check_version_changed(d, d.update, key='new value') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         d2 = self.new_dict(key='value 3') | 
					
						
							|  |  |  |         self.check_version_changed(d, d.update, d2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_clear(self): | 
					
						
							|  |  |  |         d = self.new_dict(key='value') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # clear() must change the version if the dict is not empty | 
					
						
							|  |  |  |         self.check_version_changed(d, d.clear) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # clear() must not change the version if the dict is empty | 
					
						
							|  |  |  |         self.check_version_dont_change(d, d.clear) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Dict(dict): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DictSubtypeVersionTests(DictVersionTests): | 
					
						
							|  |  |  |     type2test = Dict | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     unittest.main() |