]> granicus.if.org Git - python/commitdiff
Change the way __hash__ is inherited; when __eq__ or __cmp__ is overridden
authorGuido van Rossum <guido@python.org>
Mon, 21 Aug 2006 23:36:26 +0000 (23:36 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 21 Aug 2006 23:36:26 +0000 (23:36 +0000)
but __hash__ is not, set __hash__ explicitly to None (and tp_hash to NULL).
All unit tests pass now!

BROKEN
Include/object.h
Lib/test/mapping_tests.py
Lib/test/test_dict.py
Objects/object.c
Objects/typeobject.c

diff --git a/BROKEN b/BROKEN
index 53e9d43f6f30b75cd0749006e2153871683e53db..47477942cd798482054dccdcacddef99c4562d0d 100644 (file)
--- a/BROKEN
+++ b/BROKEN
@@ -1,60 +1 @@
-////////////////////////////////////////////////////////////////////////
-test_class
-////////////////////////////////////////////////////////////////////////
-
-test test_class failed -- hash(C1()) should raise <class 'exceptions.TypeError'>
-Also hash(C2())
-Also stack blowout, recursing between
-#5921 0x0003868c in slot_tp_call (self=0x5b0c90, args=0x338030, kwds=0x0) at ../Objects/typeobject.c:4583
-#5922 0x00021124 in PyObject_Call (func=0x5b0c90, arg=0x3384c0, kw=0x134e10) at ../Objects/abstract.c:1791
-
-////////////////////////////////////////////////////////////////////////
-test_descr
-////////////////////////////////////////////////////////////////////////
-
-Testing hash of mutable subclasses...
-Traceback (most recent call last):
-  File "../Lib/test/test_descr.py", line 4096, in <module>
-    test_main()
-  File "../Lib/test/test_descr.py", line 4059, in test_main
-    hashinherit()
-  File "../Lib/test/test_descr.py", line 3108, in hashinherit
-    raise TestFailed, "hash() of dict subclass should fail"
-test.test_support.TestFailed: hash() of dict subclass should fail
-
-
-////////////////////////////////////////////////////////////////////////
-test_set
-////////////////////////////////////////////////////////////////////////
-
-======================================================================
-FAIL: test_contains (__main__.TestSetSubclass)
-----------------------------------------------------------------------
-Traceback (most recent call last):
-  File "../Lib/test/test_set.py", line 52, in test_contains
-    self.assert_(self.thetype(self.letters) in s)
-AssertionError
-
-======================================================================
-FAIL: test_discard (__main__.TestSetSubclass)
-----------------------------------------------------------------------
-Traceback (most recent call last):
-  File "../Lib/test/test_set.py", line 302, in test_discard
-    self.assert_(self.thetype(self.word) in s)
-AssertionError
-
-======================================================================
-FAIL: test_hash (__main__.TestSetSubclass)
-----------------------------------------------------------------------
-Traceback (most recent call last):
-  File "../Lib/test/test_set.py", line 265, in test_hash
-    self.assertRaises(TypeError, hash, self.s)
-AssertionError: TypeError not raised
-
-======================================================================
-FAIL: test_remove (__main__.TestSetSubclass)
-----------------------------------------------------------------------
-Traceback (most recent call last):
-  File "../Lib/test/test_set.py", line 291, in test_remove
-    self.assert_(self.thetype(self.word) in s)
-AssertionError
+(Nothing is broken at the moment AFAIK.)
index 1f1aeaaa9fed35d130e3ac99ea71fb9aaafddcfe..ab42f8a39fd98dc0684c57282f563d754394400b 100644 (file)
@@ -368,6 +368,7 @@ PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
 
 /* Generic operations on objects */
 PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
+PyAPI_FUNC(void) _Py_Break(void);
 PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
 PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
 PyAPI_FUNC(PyObject *) _PyObject_Str(PyObject *);
index 1c570b6d34919c8e244b1cb2ea1c67a48dda2c9e..b917540d49a70f834806295fda4ca62db2f9d874 100644 (file)
@@ -545,6 +545,8 @@ class TestHashMappingProtocol(TestMappingProtocol):
         class BadEq(object):
             def __eq__(self, other):
                 raise Exc()
+            def __hash__(self):
+                return 24
 
         d = self._empty_mapping()
         d[BadEq()] = 42
@@ -630,6 +632,8 @@ class TestHashMappingProtocol(TestMappingProtocol):
         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})
index f16884607edecbe930b812bfefa0d10c981004eb..7295f41b7864e3fa4f961b769b323af94b9d4bb1 100644 (file)
@@ -76,6 +76,8 @@ class DictTest(unittest.TestCase):
         class BadEq(object):
             def __eq__(self, other):
                 raise Exc()
+            def __hash__(self):
+                return 24
 
         d = {}
         d[BadEq()] = 42
@@ -375,6 +377,8 @@ class DictTest(unittest.TestCase):
         class BadCmp(object):
             def __eq__(self, other):
                 raise Exc()
+            def __hash__(self):
+                return 42
 
         d1 = {BadCmp(): 1}
         d2 = {1: 1}
index cb603201b5df3fb36659dbd432a6db9e6cb5c56e..80111b4ee1ac1aed48ab7253b1a6054adbefc5d8 100644 (file)
@@ -320,9 +320,16 @@ PyObject_Print(PyObject *op, FILE *fp, int flags)
        return internal_print(op, fp, flags, 0);
 }
 
+/* For debugging convenience.  Set a breakpoint here and call it from your DLL */
+void
+_Py_Break(void)
+{
+}
+
 
 /* For debugging convenience.  See Misc/gdbinit for some useful gdb hooks */
-void _PyObject_Dump(PyObject* op)
+void
+_PyObject_Dump(PyObject* op)
 {
        if (op == NULL)
                fprintf(stderr, "NULL\n");
index 1578801a5ff41de041ada80970f490288c37731b..f30a8268fc9744ff0e52a58f067e4c1ce45e372b 100644 (file)
@@ -2847,6 +2847,33 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)
        COPYVAL(tp_dictoffset);
 }
 
+/* Map rich comparison operators to their __xx__ namesakes */
+static char *name_op[] = {
+       "__lt__",
+       "__le__",
+       "__eq__",
+       "__ne__",
+       "__gt__",
+       "__ge__",
+       /* These are only for overrides_cmp_or_hash(): */ 
+       "__cmp__",
+       "__hash__",
+};
+
+static int
+overrides_cmp_or_hash(PyTypeObject *type)
+{
+       int i;
+       PyObject *dict = type->tp_dict;
+
+       assert(dict != NULL);
+       for (i = 0; i < 8; i++) {
+               if (PyDict_GetItemString(dict, name_op[i]) != NULL)
+                       return 1;
+       }
+       return 0;
+}
+
 static void
 inherit_slots(PyTypeObject *type, PyTypeObject *base)
 {
@@ -2970,9 +2997,12 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
        COPYSLOT(tp_call);
        COPYSLOT(tp_str);
        {
+               /* Copy comparison-related slots only when
+                  not overriding them anywhere */
                if (type->tp_compare == NULL &&
                    type->tp_richcompare == NULL &&
-                   type->tp_hash == NULL)
+                   type->tp_hash == NULL &&
+                   !overrides_cmp_or_hash(type))
                {
                        type->tp_compare = base->tp_compare;
                        type->tp_richcompare = base->tp_richcompare;
@@ -3020,6 +3050,10 @@ PyType_Ready(PyTypeObject *type)
        PyTypeObject *base;
        Py_ssize_t i, n;
 
+       if (strcmp(type->tp_name, "C") == 0) {
+               _Py_Break();
+       }
+
        if (type->tp_flags & Py_TPFLAGS_READY) {
                assert(type->tp_dict != NULL);
                return 0;
@@ -3150,6 +3184,18 @@ PyType_Ready(PyTypeObject *type)
                }
        }
 
+       /* 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) {
+               if (PyDict_GetItemString(type->tp_dict, "__hash__") == NULL) {
+                       if (PyDict_SetItemString(type->tp_dict, "__hash__", Py_None) < 0)
+                               goto error;
+               }
+       }
+
        /* Some more special stuff */
        base = type->tp_base;
        if (base != NULL) {
@@ -4450,16 +4496,6 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value)
        return 0;
 }
 
-/* Map rich comparison operators to their __xx__ namesakes */
-static char *name_op[] = {
-       "__lt__",
-       "__le__",
-       "__eq__",
-       "__ne__",
-       "__gt__",
-       "__ge__",
-};
-
 static PyObject *
 half_richcompare(PyObject *self, PyObject *other, int op)
 {