Index: Objects/typeobject.c =================================================================== --- Objects/typeobject.c (revision 61236) +++ Objects/typeobject.c (working copy) @@ -3590,27 +3590,6 @@ type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS; } -static char *hash_name_op[] = { - "__eq__", - "__cmp__", - "__hash__", - NULL -}; - -static int -overrides_hash(PyTypeObject *type) -{ - char **p; - PyObject *dict = type->tp_dict; - - assert(dict != NULL); - for (p = hash_name_op; *p; p++) { - if (PyDict_GetItemString(dict, *p) != NULL) - return 1; - } - return 0; -} - static void inherit_slots(PyTypeObject *type, PyTypeObject *base) { @@ -3742,8 +3721,7 @@ if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE) { if (type->tp_compare == NULL && type->tp_richcompare == NULL && - type->tp_hash == NULL && - !overrides_hash(type)) + type->tp_hash == NULL) { type->tp_compare = base->tp_compare; type->tp_richcompare = base->tp_richcompare; @@ -3924,18 +3902,6 @@ } } - /* Hack for tp_hash and __hash__. - If after all that, tp_hash is still NULL, and __hash__ is not in - tp_dict, set tp_dict['__hash__'] equal to None. - This signals that __hash__ is not inherited. - */ - if (type->tp_hash == NULL && - PyDict_GetItemString(type->tp_dict, "__hash__") == NULL && - PyDict_SetItemString(type->tp_dict, "__hash__", Py_None) < 0) - { - goto error; - } - /* Some more special stuff */ base = type->tp_base; if (base != NULL) { @@ -5198,6 +5164,22 @@ static PyObject *hash_str, *eq_str, *cmp_str; long h; + if (Py_Py3kWarningFlag && Py_TYPE(self)->tp_hash != NULL && + PyDict_GetItemString(Py_TYPE(self)->tp_dict, "__hash__") == NULL) { + if (PyDict_GetItemString(Py_TYPE(self)->tp_dict, + "__eq__") != NULL + && PyErr_Warn(PyExc_DeprecationWarning, + "type defines __eq__ but not __hash__, " + "and will not be hashable in 3.x") < 0) + return -1; + else if (PyDict_GetItemString(Py_TYPE(self)->tp_dict, + "__cmp__") != NULL + && PyErr_Warn(PyExc_DeprecationWarning, + "type defines __cmp__ but not __hash__, " + "and will not be hashable in 3.x") < 0) + return -1; + } + func = lookup_method(self, "__hash__", &hash_str); if (func != NULL && func != Py_None) { Index: Lib/test/test_dict.py =================================================================== --- Lib/test/test_dict.py (revision 61236) +++ Lib/test/test_dict.py (working copy) @@ -95,8 +95,6 @@ class BadEq(object): def __eq__(self, other): raise Exc() - def __hash__(self): - return 24 d = {} d[BadEq()] = 42 @@ -400,8 +398,6 @@ class BadCmp(object): def __eq__(self, other): raise Exc() - def __hash__(self): - return 42 d1 = {BadCmp(): 1} d2 = {1: 1} Index: Lib/test/test_richcmp.py =================================================================== --- Lib/test/test_richcmp.py (revision 61236) +++ Lib/test/test_richcmp.py (working copy) @@ -86,34 +86,6 @@ return other -class SimpleOrder(object): - """ - A simple class that defines order but not full comparison. - """ - - def __init__(self, value): - self.value = value - - def __lt__(self, other): - if not isinstance(other, SimpleOrder): - return True - return self.value < other.value - - def __gt__(self, other): - if not isinstance(other, SimpleOrder): - return False - return self.value > other.value - - -class DumbEqualityWithoutHash(object): - """ - A class that define __eq__, but no __hash__: it shouldn't be hashable. - """ - - def __eq__(self, other): - return False - - opmap = { "lt": (lambda a,b: a< b, operator.lt, operator.__lt__), "le": (lambda a,b: a<=b, operator.le, operator.__le__), @@ -360,38 +332,8 @@ self.assertIs(op(x, y), True) -class HashableTest(unittest.TestCase): - """ - Test hashability of classes with rich operators defined. - """ - - def test_simpleOrderHashable(self): - """ - A class that only defines __gt__ and/or __lt__ should be hashable. - """ - a = SimpleOrder(1) - b = SimpleOrder(2) - self.assert_(a < b) - self.assert_(b > a) - self.assert_(a.__hash__ is not None) - - def test_notHashableException(self): - """ - If a class is not hashable, it should raise a TypeError with an - understandable message. - """ - a = DumbEqualityWithoutHash() - try: - hash(a) - except TypeError, e: - self.assertEquals(str(e), - "unhashable type: 'DumbEqualityWithoutHash'") - else: - raise test_support.TestFailed("Should not be here") - - def test_main(): - test_support.run_unittest(VectorTest, NumberTest, MiscTest, DictTest, ListTest, HashableTest) + test_support.run_unittest(VectorTest, NumberTest, MiscTest, DictTest, ListTest) if __name__ == "__main__": test_main() Index: Lib/test/mapping_tests.py =================================================================== --- Lib/test/mapping_tests.py (revision 61236) +++ Lib/test/mapping_tests.py (working copy) @@ -557,8 +557,6 @@ class BadEq(object): def __eq__(self, other): raise Exc() - def __hash__(self): - return 24 d = self._empty_mapping() d[BadEq()] = 42 @@ -644,8 +642,6 @@ class BadCmp(object): def __eq__(self, other): raise Exc() - def __hash__(self): - return 42 d1 = self._full_mapping({BadCmp(): 1}) d2 = self._full_mapping({1: 1})