| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -475,6 +475,7 @@ PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); | |||
| 475 | 475 | PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, | |
| 476 | 476 | PyObject *, PyObject *); | |
| 477 | 477 | PyAPI_FUNC(long) PyObject_Hash(PyObject *); | |
| 478 | + PyAPI_FUNC(long) PyObject_HashNotImplemented(PyObject *); | ||
| 478 | 479 | PyAPI_FUNC(int) PyObject_IsTrue(PyObject *); | |
| 479 | 480 | PyAPI_FUNC(int) PyObject_Not(PyObject *); | |
| 480 | 481 | PyAPI_FUNC(int) PyCallable_Check(PyObject *); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -150,8 +150,10 @@ def __init__(self, string=""): | |||
| 150 | 150 | warnpy3k('the class UserString.MutableString has been removed in ' | |
| 151 | 151 | 'Python 3.0', stacklevel=2) | |
| 152 | 152 | self.data = string | |
| 153 | - def __hash__(self): | ||
| 154 | - raise TypeError, "unhashable type (it is mutable)" | ||
| 153 | + | ||
| 154 | + # We inherit object.__hash__, so we must deny this explicitly | ||
| 155 | + __hash__ = None | ||
| 156 | + | ||
| 155 | 157 | def __setitem__(self, index, sub): | |
| 156 | 158 | if isinstance(index, slice): | |
| 157 | 159 | if isinstance(sub, UserString): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3697,10 +3697,8 @@ def _regard_flags(self, *flags): | |||
| 3697 | 3697 | for flag in flags: | |
| 3698 | 3698 | self._ignored_flags.remove(flag) | |
| 3699 | 3699 | ||
| 3700 | - def __hash__(self): | ||
| 3701 | - """A Context cannot be hashed.""" | ||
| 3702 | - # We inherit object.__hash__, so we must deny this explicitly | ||
| 3703 | - raise TypeError("Cannot hash a Context.") | ||
| 3700 | + # We inherit object.__hash__, so we must deny this explicitly | ||
| 3701 | + __hash__ = None | ||
| 3704 | 3702 | ||
| 3705 | 3703 | def Etiny(self): | |
| 3706 | 3704 | """Returns Etiny (= Emin - prec + 1)""" | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -439,10 +439,8 @@ def __getstate__(self): | |||
| 439 | 439 | def __setstate__(self, data): | |
| 440 | 440 | self._data, = data | |
| 441 | 441 | ||
| 442 | - def __hash__(self): | ||
| 443 | - """A Set cannot be hashed.""" | ||
| 444 | - # We inherit object.__hash__, so we must deny this explicitly | ||
| 445 | - raise TypeError, "Can't hash a Set, only an ImmutableSet." | ||
| 442 | + # We inherit object.__hash__, so we must deny this explicitly | ||
| 443 | + __hash__ = None | ||
| 446 | 444 | ||
| 447 | 445 | # In-place union, intersection, differences. | |
| 448 | 446 | # Subtle: The xyz_update() functions deliberately return None, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -214,8 +214,7 @@ class AllEq: | |||
| 214 | 214 | # So instances of AllEq must be found in all non-empty sequences. | |
| 215 | 215 | def __eq__(self, other): | |
| 216 | 216 | return True | |
| 217 | - def __hash__(self): | ||
| 218 | - raise NotImplemented | ||
| 217 | + __hash__ = None # Can't meet hash invariant requirements | ||
| 219 | 218 | self.assert_(AllEq() not in self.type2test([])) | |
| 220 | 219 | self.assert_(AllEq() in self.type2test([1])) | |
| 221 | 220 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3283,12 +3283,20 @@ class D(B, C): | |||
| 3283 | 3283 | self.assertEqual(hash(d), 144) | |
| 3284 | 3284 | D.__hash__ = lambda self: 100 | |
| 3285 | 3285 | self.assertEqual(hash(d), 100) | |
| 3286 | + D.__hash__ = None | ||
| 3287 | + self.assertRaises(TypeError, hash, d) | ||
| 3286 | 3288 | del D.__hash__ | |
| 3287 | 3289 | self.assertEqual(hash(d), 144) | |
| 3290 | + B.__hash__ = None | ||
| 3291 | + self.assertRaises(TypeError, hash, d) | ||
| 3288 | 3292 | del B.__hash__ | |
| 3289 | 3293 | self.assertEqual(hash(d), 314) | |
| 3294 | + C.__hash__ = None | ||
| 3295 | + self.assertRaises(TypeError, hash, d) | ||
| 3290 | 3296 | del C.__hash__ | |
| 3291 | 3297 | self.assertEqual(hash(d), 42) | |
| 3298 | + A.__hash__ = None | ||
| 3299 | + self.assertRaises(TypeError, hash, d) | ||
| 3292 | 3300 | del A.__hash__ | |
| 3293 | 3301 | self.assertEqual(hash(d), orig_hash) | |
| 3294 | 3302 | d.foo = 42 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,9 +1,11 @@ | |||
| 1 | 1 | # test the invariant that | |
| 2 | 2 | # iff a==b then hash(a)==hash(b) | |
| 3 | 3 | # | |
| 4 | + # Also test that hash implementations are inherited as expected | ||
| 4 | 5 | ||
| 5 | 6 | import unittest | |
| 6 | 7 | from test import test_support | |
| 8 | + from collections import Hashable | ||
| 7 | 9 | ||
| 8 | 10 | ||
| 9 | 11 | class HashEqualityTestCase(unittest.TestCase): | |
@@ -39,8 +41,83 @@ def test_coerced_floats(self): | |||
| 39 | 41 | self.same_hash(float(0.5), complex(0.5, 0.0)) | |
| 40 | 42 | ||
| 41 | 43 | ||
| 44 | + _default_hash = object.__hash__ | ||
| 45 | + class DefaultHash(object): pass | ||
| 46 | + | ||
| 47 | + _FIXED_HASH_VALUE = 42 | ||
| 48 | + class FixedHash(object): | ||
| 49 | + def __hash__(self): | ||
| 50 | + return _FIXED_HASH_VALUE | ||
| 51 | + | ||
| 52 | + class OnlyEquality(object): | ||
| 53 | + def __eq__(self, other): | ||
| 54 | + return self is other | ||
| 55 | + | ||
| 56 | + class OnlyInequality(object): | ||
| 57 | + def __ne__(self, other): | ||
| 58 | + return self is not other | ||
| 59 | + | ||
| 60 | + class OnlyCmp(object): | ||
| 61 | + def __cmp__(self, other): | ||
| 62 | + return cmp(id(self), id(other)) | ||
| 63 | + | ||
| 64 | + class InheritedHashWithEquality(FixedHash, OnlyEquality): pass | ||
| 65 | + class InheritedHashWithInequality(FixedHash, OnlyInequality): pass | ||
| 66 | + class InheritedHashWithCmp(FixedHash, OnlyCmp): pass | ||
| 67 | + | ||
| 68 | + class NoHash(object): | ||
| 69 | + __hash__ = None | ||
| 70 | + | ||
| 71 | + class HashInheritanceTestCase(unittest.TestCase): | ||
| 72 | + default_expected = [object(), | ||
| 73 | + DefaultHash(), | ||
| 74 | + ] | ||
| 75 | + fixed_expected = [FixedHash(), | ||
| 76 | + InheritedHashWithEquality(), | ||
| 77 | + InheritedHashWithInequality(), | ||
| 78 | + InheritedHashWithCmp(), | ||
| 79 | + ] | ||
| 80 | + # TODO: Change these to expecting an exception | ||
| 81 | + # when forward porting to Py3k | ||
| 82 | + warning_expected = [OnlyEquality(), | ||
| 83 | + OnlyInequality(), | ||
| 84 | + OnlyCmp(), | ||
| 85 | + ] | ||
| 86 | + error_expected = [NoHash()] | ||
| 87 | + | ||
| 88 | + def test_default_hash(self): | ||
| 89 | + for obj in self.default_expected: | ||
| 90 | + self.assertEqual(hash(obj), _default_hash(obj)) | ||
| 91 | + | ||
| 92 | + def test_fixed_hash(self): | ||
| 93 | + for obj in self.fixed_expected: | ||
| 94 | + self.assertEqual(hash(obj), _FIXED_HASH_VALUE) | ||
| 95 | + | ||
| 96 | + def test_warning_hash(self): | ||
| 97 | + for obj in self.warning_expected: | ||
| 98 | + # TODO: Check for the expected Py3k warning here | ||
| 99 | + obj_hash = hash(obj) | ||
| 100 | + self.assertEqual(obj_hash, _default_hash(obj)) | ||
| 101 | + | ||
| 102 | + def test_error_hash(self): | ||
| 103 | + for obj in self.error_expected: | ||
| 104 | + self.assertRaises(TypeError, hash, obj) | ||
| 105 | + | ||
| 106 | + def test_hashable(self): | ||
| 107 | + objects = (self.default_expected + | ||
| 108 | + self.fixed_expected + | ||
| 109 | + self.warning_expected) | ||
| 110 | + for obj in objects: | ||
| 111 | + self.assert_(isinstance(obj, Hashable), repr(obj)) | ||
| 112 | + | ||
| 113 | + def test_not_hashable(self): | ||
| 114 | + for obj in self.error_expected: | ||
| 115 | + self.assertFalse(isinstance(obj, Hashable), repr(obj)) | ||
| 116 | + | ||
| 117 | + | ||
| 42 | 118 | def test_main(): | |
| 43 | - test_support.run_unittest(HashEqualityTestCase) | ||
| 119 | + test_support.run_unittest(HashEqualityTestCase, | ||
| 120 | + HashInheritanceTestCase) | ||
| 44 | 121 | ||
| 45 | 122 | ||
| 46 | 123 | if __name__ == "__main__": | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,8 +48,7 @@ def __getitem__(self, i): | |||
| 48 | 48 | def __setitem__(self, i, v): | |
| 49 | 49 | self.data[i] = v | |
| 50 | 50 | ||
| 51 | - def __hash__(self): | ||
| 52 | - raise TypeError, "Vectors cannot be hashed" | ||
| 51 | + __hash__ = None # Vectors cannot be hashed | ||
| 53 | 52 | ||
| 54 | 53 | def __nonzero__(self): | |
| 55 | 54 | raise TypeError, "Vectors cannot be used in Boolean contexts" | |
@@ -85,35 +84,6 @@ def __cast(self, other): | |||
| 85 | 84 | raise ValueError, "Cannot compare vectors of different length" | |
| 86 | 85 | return other | |
| 87 | 86 | ||
| 88 | - | ||
| 89 | - class SimpleOrder(object): | ||
| 90 | - """ | ||
| 91 | - A simple class that defines order but not full comparison. | ||
| 92 | - """ | ||
| 93 | - | ||
| 94 | - def __init__(self, value): | ||
| 95 | - self.value = value | ||
| 96 | - | ||
| 97 | - def __lt__(self, other): | ||
| 98 | - if not isinstance(other, SimpleOrder): | ||
| 99 | - return True | ||
| 100 | - return self.value < other.value | ||
| 101 | - | ||
| 102 | - def __gt__(self, other): | ||
| 103 | - if not isinstance(other, SimpleOrder): | ||
| 104 | - return False | ||
| 105 | - return self.value > other.value | ||
| 106 | - | ||
| 107 | - | ||
| 108 | - class DumbEqualityWithoutHash(object): | ||
| 109 | - """ | ||
| 110 | - A class that define __eq__, but no __hash__: it shouldn't be hashable. | ||
| 111 | - """ | ||
| 112 | - | ||
| 113 | - def __eq__(self, other): | ||
| 114 | - return False | ||
| 115 | - | ||
| 116 | - | ||
| 117 | 87 | opmap = { | |
| 118 | 88 | "lt": (lambda a,b: a< b, operator.lt, operator.__lt__), | |
| 119 | 89 | "le": (lambda a,b: a<=b, operator.le, operator.__le__), | |
@@ -359,39 +329,8 @@ def __lt__(self, other): | |||
| 359 | 329 | for op in opmap["lt"]: | |
| 360 | 330 | self.assertIs(op(x, y), True) | |
| 361 | 331 | ||
| 362 | - | ||
| 363 | - class HashableTest(unittest.TestCase): | ||
| 364 | - """ | ||
| 365 | - Test hashability of classes with rich operators defined. | ||
| 366 | - """ | ||
| 367 | - | ||
| 368 | - def test_simpleOrderHashable(self): | ||
| 369 | - """ | ||
| 370 | - A class that only defines __gt__ and/or __lt__ should be hashable. | ||
| 371 | - """ | ||
| 372 | - a = SimpleOrder(1) | ||
| 373 | - b = SimpleOrder(2) | ||
| 374 | - self.assert_(a < b) | ||
| 375 | - self.assert_(b > a) | ||
| 376 | - self.assert_(a.__hash__ is not None) | ||
| 377 | - | ||
| 378 | - def test_notHashableException(self): | ||
| 379 | - """ | ||
| 380 | - If a class is not hashable, it should raise a TypeError with an | ||
| 381 | - understandable message. | ||
| 382 | - """ | ||
| 383 | - a = DumbEqualityWithoutHash() | ||
| 384 | - try: | ||
| 385 | - hash(a) | ||
| 386 | - except TypeError, e: | ||
| 387 | - self.assertEquals(str(e), | ||
| 388 | - "unhashable type: 'DumbEqualityWithoutHash'") | ||
| 389 | - else: | ||
| 390 | - raise test_support.TestFailed("Should not be here") | ||
| 391 | - | ||
| 392 | - | ||
| 393 | 332 | def test_main(): | |
| 394 | - test_support.run_unittest(VectorTest, NumberTest, MiscTest, DictTest, ListTest, HashableTest) | ||
| 333 | + test_support.run_unittest(VectorTest, NumberTest, MiscTest, DictTest, ListTest) | ||
| 395 | 334 | ||
| 396 | 335 | if __name__ == "__main__": | |
| 397 | 336 | test_main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -608,13 +608,6 @@ deque_traverse(dequeobject *deque, visitproc visit, void *arg) | |||
| 608 | 608 | return 0; | |
| 609 | 609 | } | |
| 610 | 610 | ||
| 611 | - static long | ||
| 612 | - deque_nohash(PyObject *self) | ||
| 613 | - { | ||
| 614 | - PyErr_SetString(PyExc_TypeError, "deque objects are unhashable"); | ||
| 615 | - return -1; | ||
| 616 | - } | ||
| 617 | - | ||
| 618 | 611 | static PyObject * | |
| 619 | 612 | deque_copy(PyObject *deque) | |
| 620 | 613 | { | |
@@ -917,7 +910,7 @@ static PyTypeObject deque_type = { | |||
| 917 | 910 | 0, /* tp_as_number */ | |
| 918 | 911 | &deque_as_sequence, /* tp_as_sequence */ | |
| 919 | 912 | 0, /* tp_as_mapping */ | |
| 920 | - deque_nohash, /* tp_hash */ | ||
| 913 | + (hashfunc)PyObject_HashNotImplemented, /* tp_hash */ | ||
| 921 | 914 | 0, /* tp_call */ | |
| 922 | 915 | 0, /* tp_str */ | |
| 923 | 916 | PyObject_GenericGetAttr, /* tp_getattro */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2240,7 +2240,7 @@ PyTypeObject PyDict_Type = { | |||
| 2240 | 2240 | 0, /* tp_as_number */ | |
| 2241 | 2241 | &dict_as_sequence, /* tp_as_sequence */ | |
| 2242 | 2242 | &dict_as_mapping, /* tp_as_mapping */ | |
| 2243 | - 0, /* tp_hash */ | ||
| 2243 | + (hashfunc)PyObject_HashNotImplemented, /* tp_hash */ | ||
| 2244 | 2244 | 0, /* tp_call */ | |
| 2245 | 2245 | 0, /* tp_str */ | |
| 2246 | 2246 | PyObject_GenericGetAttr, /* tp_getattro */ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments