| """ |
| 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 |
| self.check_version_dont_change(d, d.__setitem__, 'key', value) |
| |
| # setting a key to the same value with dict.update |
| # must change the version |
| self.check_version_dont_change(d, d.update, key=value) |
| |
| d2 = self.new_dict(key=value) |
| self.check_version_dont_change(d, d.update, d2) |
| |
| 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) |
| self.assertIsNot(value1, value2) |
| |
| d = self.new_dict() |
| self.check_version_changed(d, d.__setitem__, 'key', value1) |
| self.assertIs(d['key'], value1) |
| |
| # 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) |
| self.assertIs(d['key'], value2) |
| |
| # 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) |
| self.assertIs(d['key'], value1) |
| |
| d2 = self.new_dict(key=value2) |
| self.check_version_changed(d, d.update, d2) |
| self.assertIs(d['key'], value2) |
| |
| 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() |