| LEFT | RIGHT |
| 1 # test the invariant that | 1 # test the invariant that |
| 2 # iff a==b then hash(a)==hash(b) | 2 # iff a==b then hash(a)==hash(b) |
| 3 # | 3 # |
| 4 # Also test that hash implementations are inherited as expected | 4 # Also test that hash implementations are inherited as expected |
| 5 | 5 |
| 6 import datetime |
| 6 import os | 7 import os |
| 7 import struct | 8 import struct |
| 8 import subprocess | |
| 9 import sys | |
| 10 import unittest | 9 import unittest |
| 11 from test import test_support | 10 from test import support |
| 11 from test.script_helper import assert_python_ok |
| 12 from collections import Hashable | 12 from collections import Hashable |
| 13 | 13 |
| 14 IS_64BIT = (struct.calcsize('l') == 8) | 14 IS_64BIT = (struct.calcsize('l') == 8) |
| 15 |
| 15 | 16 |
| 16 class HashEqualityTestCase(unittest.TestCase): | 17 class HashEqualityTestCase(unittest.TestCase): |
| 17 | 18 |
| 18 def same_hash(self, *objlist): | 19 def same_hash(self, *objlist): |
| 19 # Hash each object given and fail if | 20 # Hash each object given and fail if |
| 20 # the hash values are not all the same. | 21 # the hash values are not all the same. |
| 21 hashed = map(hash, objlist) | 22 hashed = list(map(hash, objlist)) |
| 22 for h in hashed[1:]: | 23 for h in hashed[1:]: |
| 23 if h != hashed[0]: | 24 if h != hashed[0]: |
| 24 self.fail("hashed values differ: %r" % (objlist,)) | 25 self.fail("hashed values differ: %r" % (objlist,)) |
| 25 | 26 |
| 26 def test_numeric_literals(self): | 27 def test_numeric_literals(self): |
| 27 self.same_hash(1, 1L, 1.0, 1.0+0.0j) | 28 self.same_hash(1, 1, 1.0, 1.0+0.0j) |
| 28 self.same_hash(0, 0L, 0.0, 0.0+0.0j) | 29 self.same_hash(0, 0.0, 0.0+0.0j) |
| 29 self.same_hash(-1, -1L, -1.0, -1.0+0.0j) | 30 self.same_hash(-1, -1.0, -1.0+0.0j) |
| 30 self.same_hash(-2, -2L, -2.0, -2.0+0.0j) | 31 self.same_hash(-2, -2.0, -2.0+0.0j) |
| 31 | 32 |
| 32 def test_coerced_integers(self): | 33 def test_coerced_integers(self): |
| 33 self.same_hash(int(1), long(1), float(1), complex(1), | 34 self.same_hash(int(1), int(1), float(1), complex(1), |
| 34 int('1'), float('1.0')) | 35 int('1'), float('1.0')) |
| 35 self.same_hash(int(-2**31), long(-2**31), float(-2**31)) | 36 self.same_hash(int(-2**31), float(-2**31)) |
| 36 self.same_hash(int(1-2**31), long(1-2**31), float(1-2**31)) | 37 self.same_hash(int(1-2**31), float(1-2**31)) |
| 37 self.same_hash(int(2**31-1), long(2**31-1), float(2**31-1)) | 38 self.same_hash(int(2**31-1), float(2**31-1)) |
| 38 # for 64-bit platforms | 39 # for 64-bit platforms |
| 39 self.same_hash(int(2**31), long(2**31), float(2**31)) | 40 self.same_hash(int(2**31), float(2**31)) |
| 40 self.same_hash(int(-2**63), long(-2**63), float(-2**63)) | 41 self.same_hash(int(-2**63), float(-2**63)) |
| 41 self.same_hash(int(1-2**63), long(1-2**63)) | 42 self.same_hash(int(2**63), float(2**63)) |
| 42 self.same_hash(int(2**63-1), long(2**63-1)) | |
| 43 | 43 |
| 44 def test_coerced_floats(self): | 44 def test_coerced_floats(self): |
| 45 self.same_hash(long(1.23e300), float(1.23e300)) | 45 self.same_hash(int(1.23e300), float(1.23e300)) |
| 46 self.same_hash(float(0.5), complex(0.5, 0.0)) | 46 self.same_hash(float(0.5), complex(0.5, 0.0)) |
| 47 | 47 |
| 48 | 48 |
| 49 _default_hash = object.__hash__ | 49 _default_hash = object.__hash__ |
| 50 class DefaultHash(object): pass | 50 class DefaultHash(object): pass |
| 51 | 51 |
| 52 _FIXED_HASH_VALUE = 42 | 52 _FIXED_HASH_VALUE = 42 |
| 53 class FixedHash(object): | 53 class FixedHash(object): |
| 54 def __hash__(self): | 54 def __hash__(self): |
| 55 return _FIXED_HASH_VALUE | 55 return _FIXED_HASH_VALUE |
| 56 | 56 |
| 57 class OnlyEquality(object): | 57 class OnlyEquality(object): |
| 58 def __eq__(self, other): | 58 def __eq__(self, other): |
| 59 return self is other | 59 return self is other |
| 60 # Trick to suppress Py3k warning in 2.x | |
| 61 __hash__ = None | |
| 62 del OnlyEquality.__hash__ | |
| 63 | 60 |
| 64 class OnlyInequality(object): | 61 class OnlyInequality(object): |
| 65 def __ne__(self, other): | 62 def __ne__(self, other): |
| 66 return self is not other | 63 return self is not other |
| 67 | 64 |
| 68 class OnlyCmp(object): | |
| 69 def __cmp__(self, other): | |
| 70 return cmp(id(self), id(other)) | |
| 71 # Trick to suppress Py3k warning in 2.x | |
| 72 __hash__ = None | |
| 73 del OnlyCmp.__hash__ | |
| 74 | |
| 75 class InheritedHashWithEquality(FixedHash, OnlyEquality): pass | 65 class InheritedHashWithEquality(FixedHash, OnlyEquality): pass |
| 76 class InheritedHashWithInequality(FixedHash, OnlyInequality): pass | 66 class InheritedHashWithInequality(FixedHash, OnlyInequality): pass |
| 77 class InheritedHashWithCmp(FixedHash, OnlyCmp): pass | |
| 78 | 67 |
| 79 class NoHash(object): | 68 class NoHash(object): |
| 80 __hash__ = None | 69 __hash__ = None |
| 81 | 70 |
| 82 class HashInheritanceTestCase(unittest.TestCase): | 71 class HashInheritanceTestCase(unittest.TestCase): |
| 83 default_expected = [object(), | 72 default_expected = [object(), |
| 84 DefaultHash(), | 73 DefaultHash(), |
| 85 OnlyEquality(), | |
| 86 OnlyInequality(), | 74 OnlyInequality(), |
| 87 OnlyCmp(), | |
| 88 ] | 75 ] |
| 89 fixed_expected = [FixedHash(), | 76 fixed_expected = [FixedHash(), |
| 90 InheritedHashWithEquality(), | 77 InheritedHashWithEquality(), |
| 91 InheritedHashWithInequality(), | 78 InheritedHashWithInequality(), |
| 92 InheritedHashWithCmp(), | |
| 93 ] | 79 ] |
| 94 error_expected = [NoHash()] | 80 error_expected = [NoHash(), |
| 81 OnlyEquality(), |
| 82 ] |
| 95 | 83 |
| 96 def test_default_hash(self): | 84 def test_default_hash(self): |
| 97 for obj in self.default_expected: | 85 for obj in self.default_expected: |
| 98 self.assertEqual(hash(obj), _default_hash(obj)) | 86 self.assertEqual(hash(obj), _default_hash(obj)) |
| 99 | 87 |
| 100 def test_fixed_hash(self): | 88 def test_fixed_hash(self): |
| 101 for obj in self.fixed_expected: | 89 for obj in self.fixed_expected: |
| 102 self.assertEqual(hash(obj), _FIXED_HASH_VALUE) | 90 self.assertEqual(hash(obj), _FIXED_HASH_VALUE) |
| 103 | 91 |
| 104 def test_error_hash(self): | 92 def test_error_hash(self): |
| 105 for obj in self.error_expected: | 93 for obj in self.error_expected: |
| 106 self.assertRaises(TypeError, hash, obj) | 94 self.assertRaises(TypeError, hash, obj) |
| 107 | 95 |
| 108 def test_hashable(self): | 96 def test_hashable(self): |
| 109 objects = (self.default_expected + | 97 objects = (self.default_expected + |
| 110 self.fixed_expected) | 98 self.fixed_expected) |
| 111 for obj in objects: | 99 for obj in objects: |
| 112 self.assert_(isinstance(obj, Hashable), repr(obj)) | 100 self.assertTrue(isinstance(obj, Hashable), repr(obj)) |
| 113 | 101 |
| 114 def test_not_hashable(self): | 102 def test_not_hashable(self): |
| 115 for obj in self.error_expected: | 103 for obj in self.error_expected: |
| 116 self.assertFalse(isinstance(obj, Hashable), repr(obj)) | 104 self.assertFalse(isinstance(obj, Hashable), repr(obj)) |
| 117 | 105 |
| 118 | 106 |
| 119 # Issue #4701: Check that some builtin types are correctly hashable | 107 # Issue #4701: Check that some builtin types are correctly hashable |
| 120 # (This test only used to fail in Python 3.0, but has been included | |
| 121 # in 2.x along with the lazy call to PyType_Ready in PyObject_Hash) | |
| 122 class DefaultIterSeq(object): | 108 class DefaultIterSeq(object): |
| 123 seq = range(10) | 109 seq = range(10) |
| 124 def __len__(self): | 110 def __len__(self): |
| 125 return len(self.seq) | 111 return len(self.seq) |
| 126 def __getitem__(self, index): | 112 def __getitem__(self, index): |
| 127 return self.seq[index] | 113 return self.seq[index] |
| 128 | 114 |
| 129 class HashBuiltinsTestCase(unittest.TestCase): | 115 class HashBuiltinsTestCase(unittest.TestCase): |
| 130 hashes_to_check = [xrange(10), | 116 hashes_to_check = [range(10), |
| 131 enumerate(xrange(10)), | 117 enumerate(range(10)), |
| 132 iter(DefaultIterSeq()), | 118 iter(DefaultIterSeq()), |
| 133 iter(lambda: 0, 0), | 119 iter(lambda: 0, 0), |
| 134 ] | 120 ] |
| 135 | 121 |
| 136 def test_hashes(self): | 122 def test_hashes(self): |
| 137 _default_hash = object.__hash__ | 123 _default_hash = object.__hash__ |
| 138 for obj in self.hashes_to_check: | 124 for obj in self.hashes_to_check: |
| 139 self.assertEqual(hash(obj), _default_hash(obj)) | 125 self.assertEqual(hash(obj), _default_hash(obj)) |
| 140 | 126 |
| 141 class HashRandomizationTests(unittest.TestCase): | 127 class HashRandomizationTests(unittest.TestCase): |
| 142 | 128 |
| 143 # Each subclass should define a field "repr_", containing the repr() of | 129 # Each subclass should define a field "repr_", containing the repr() of |
| 144 # an object to be tested | 130 # an object to be tested |
| 145 | 131 |
| 146 def get_hash_command(self, repr_): | 132 def get_hash_command(self, repr_): |
| 147 return 'print(hash(%s))' % repr_ | 133 return 'print(hash(%s))' % repr_ |
| 148 | 134 |
| 149 def get_hash(self, repr_, randomization=None, seed=None): | 135 def get_hash(self, repr_, seed=None): |
| 150 env = os.environ.copy() | 136 env = os.environ.copy() |
| 151 if randomization is not None: | 137 env['__cleanenv'] = True # signal to assert_python not to do a copy |
| 152 env['PYTHONHASHRANDOMIZATION'] = str(randomization) | 138 # of os.environ on its own |
| 153 if seed is not None: | 139 if seed is not None: |
| 154 env['PYTHONHASHSEED'] = str(seed) | 140 env['PYTHONHASHSEED'] = str(seed) |
| 155 cmd_line = [sys.executable, | 141 else: |
| 156 '-c', self.get_hash_command(repr_)] | 142 env.pop('PYTHONHASHSEED', None) |
| 157 p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE, | 143 out = assert_python_ok( |
| 158 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, | 144 '-c', self.get_hash_command(repr_), |
| 159 env=env) | 145 **env) |
| 160 out, err = p.communicate() | 146 stdout = out[1].strip() |
| 161 out = test_support.strip_python_stderr(out) | 147 return int(stdout) |
| 162 return int(out.strip()) | |
| 163 | 148 |
| 164 def test_randomized_hash(self): | 149 def test_randomized_hash(self): |
| 165 # two runs should return different hashes | 150 # two runs should return different hashes |
| 166 run1 = self.get_hash(self.repr_, randomization=1) | 151 run1 = self.get_hash(self.repr_, seed='random') |
| 167 run2 = self.get_hash(self.repr_, randomization=1) | 152 run2 = self.get_hash(self.repr_, seed='random') |
| 168 self.assertNotEqual(run1, run2) | 153 self.assertNotEqual(run1, run2) |
| 169 | 154 |
| 170 class StringlikeHashRandomizationTests(HashRandomizationTests): | 155 class StringlikeHashRandomizationTests(HashRandomizationTests): |
| 171 def test_null_hash(self): | 156 def test_null_hash(self): |
| 172 # PYTHONHASHSEED=0 disables the randomized hash | 157 # PYTHONHASHSEED=0 disables the randomized hash |
| 173 if IS_64BIT: | 158 if IS_64BIT: |
| 174 known_hash_of_obj = 1453079729188098211 | 159 known_hash_of_obj = 1453079729188098211 |
| 175 else: | 160 else: |
| 176 known_hash_of_obj = -1600925533 | 161 known_hash_of_obj = -1600925533 |
| 177 | 162 |
| 178 # Randomization is disabled by default: | 163 # Randomization is disabled by default: |
| 179 self.assertEqual(self.get_hash(self.repr_), known_hash_of_obj) | 164 self.assertEqual(self.get_hash(self.repr_), known_hash_of_obj) |
| 180 | 165 |
| 181 # If enabled, it can still be disabled by setting the seed to 0: | 166 # It can also be disabled by setting the seed to 0: |
| 182 self.assertEqual(self.get_hash(self.repr_, randomization=1, seed=0), | 167 self.assertEqual(self.get_hash(self.repr_, seed=0), known_hash_of_obj) |
| 183 known_hash_of_obj) | |
| 184 | 168 |
| 185 def test_fixed_hash(self): | 169 def test_fixed_hash(self): |
| 186 # test a fixed seed for the randomized hash | 170 # test a fixed seed for the randomized hash |
| 187 # Note that all types share the same values: | 171 # Note that all types share the same values: |
| 188 if IS_64BIT: | 172 if IS_64BIT: |
| 189 h = -4410911502303878509 | 173 h = -4410911502303878509 |
| 190 else: | 174 else: |
| 191 h = -206076799 | 175 h = -206076799 |
| 192 self.assertEqual(self.get_hash(self.repr_, randomization=1, seed=42), | 176 self.assertEqual(self.get_hash(self.repr_, seed=42), h) |
| 193 h) | |
| 194 | 177 |
| 195 class StrHashRandomizationTests(StringlikeHashRandomizationTests): | 178 class StrHashRandomizationTests(StringlikeHashRandomizationTests): |
| 196 repr_ = repr('abc') | 179 repr_ = repr('abc') |
| 197 | 180 |
| 198 def test_empty_string(self): | 181 def test_empty_string(self): |
| 199 self.assertEqual(hash(""), 0) | 182 self.assertEqual(hash(""), 0) |
| 200 | 183 |
| 201 class UnicodeHashRandomizationTests(StringlikeHashRandomizationTests): | 184 class BytesHashRandomizationTests(StringlikeHashRandomizationTests): |
| 202 repr_ = repr(u'abc') | 185 repr_ = repr(b'abc') |
| 203 | 186 |
| 204 def test_empty_string(self): | 187 def test_empty_string(self): |
| 205 self.assertEqual(hash(u""), 0) | 188 self.assertEqual(hash(b""), 0) |
| 206 | 189 |
| 207 class BufferHashRandomizationTests(StringlikeHashRandomizationTests): | 190 class DatetimeTests(HashRandomizationTests): |
| 208 repr_ = 'buffer("abc")' | 191 def get_hash_command(self, repr_): |
| 209 | 192 return 'import datetime; print(hash(%s))' % repr_ |
| 210 def test_empty_string(self): | 193 |
| 211 self.assertEqual(hash(buffer("")), 0) | 194 class DatetimeDateTests(DatetimeTests): |
| 195 repr_ = repr(datetime.date(1066, 10, 14)) |
| 196 |
| 197 class DatetimeDatetimeTests(DatetimeTests): |
| 198 repr_ = repr(datetime.datetime(1, 2, 3, 4, 5, 6, 7)) |
| 199 |
| 200 class DatetimeTimeTests(DatetimeTests): |
| 201 repr_ = repr(datetime.time(0)) |
| 202 |
| 212 | 203 |
| 213 def test_main(): | 204 def test_main(): |
| 214 test_support.run_unittest(HashEqualityTestCase, | 205 support.run_unittest(HashEqualityTestCase, |
| 215 HashInheritanceTestCase, | 206 HashInheritanceTestCase, |
| 216 HashBuiltinsTestCase, | 207 HashBuiltinsTestCase, |
| 217 StrHashRandomizationTests, | 208 StrHashRandomizationTests, |
| 218 UnicodeHashRandomizationTests, | 209 BytesHashRandomizationTests, |
| 219 BufferHashRandomizationTests) | 210 DatetimeDateTests, |
| 211 DatetimeDatetimeTests, |
| 212 DatetimeTimeTests) |
| 220 | 213 |
| 221 | 214 |
| 222 if __name__ == "__main__": | 215 if __name__ == "__main__": |
| 223 test_main() | 216 test_main() |
| LEFT | RIGHT |