2016-09-09 03:51:24 +08:00
|
|
|
"""
|
|
|
|
Test implementation of the PEP 509: dictionary versionning.
|
|
|
|
"""
|
|
|
|
import unittest
|
2020-06-30 21:46:31 +08:00
|
|
|
from test.support import import_helper
|
2016-09-09 03:51:24 +08:00
|
|
|
|
|
|
|
# PEP 509 is implemented in CPython but other Python implementations
|
|
|
|
# don't require to implement it
|
2020-06-30 21:46:31 +08:00
|
|
|
_testcapi = import_helper.import_module('_testcapi')
|
2016-09-09 03:51:24 +08:00
|
|
|
|
|
|
|
|
|
|
|
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 20:30:58 +08:00
|
|
|
self.check_version_dont_change(d, d.__setitem__, 'key', value)
|
2016-09-09 03:51:24 +08:00
|
|
|
|
|
|
|
# setting a key to the same value with dict.update
|
|
|
|
# must change the version
|
2019-06-03 20:30:58 +08:00
|
|
|
self.check_version_dont_change(d, d.update, key=value)
|
2016-09-09 03:51:24 +08:00
|
|
|
|
|
|
|
d2 = self.new_dict(key=value)
|
2019-06-03 20:30:58 +08:00
|
|
|
self.check_version_dont_change(d, d.update, d2)
|
2016-09-09 03:51:24 +08: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 13:43:18 +08:00
|
|
|
self.assertIsNot(value1, value2)
|
2016-09-09 03:51:24 +08:00
|
|
|
|
|
|
|
d = self.new_dict()
|
|
|
|
self.check_version_changed(d, d.__setitem__, 'key', value1)
|
2019-08-08 13:43:18 +08:00
|
|
|
self.assertIs(d['key'], value1)
|
2016-09-09 03:51:24 +08: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 13:43:18 +08:00
|
|
|
self.assertIs(d['key'], value2)
|
2016-09-09 03:51:24 +08: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 13:43:18 +08:00
|
|
|
self.assertIs(d['key'], value1)
|
2016-09-09 03:51:24 +08:00
|
|
|
|
|
|
|
d2 = self.new_dict(key=value2)
|
|
|
|
self.check_version_changed(d, d.update, d2)
|
2019-08-08 13:43:18 +08:00
|
|
|
self.assertIs(d['key'], value2)
|
2016-09-09 03:51:24 +08: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()
|