test_dict_version.py 6.1 KB

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