summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2006-08-21 23:36:26 (GMT)
committerGuido van Rossum <guido@python.org>2006-08-21 23:36:26 (GMT)
commit389381564cfa936c8e50bc51dc53b55cba857652 (patch)
tree7259a38e0d6c06e5519276336ed26a593166de1d
parent5431ee4a499e173c5638297de30a965ed2206c66 (diff)
downloadcpython-389381564cfa936c8e50bc51dc53b55cba857652.zip
cpython-389381564cfa936c8e50bc51dc53b55cba857652.tar.gz
cpython-389381564cfa936c8e50bc51dc53b55cba857652.tar.bz2
Change the way __hash__ is inherited; when __eq__ or __cmp__ is overridden
but __hash__ is not, set __hash__ explicitly to None (and tp_hash to NULL). All unit tests pass now!
-rw-r--r--BROKEN61
-rw-r--r--Include/object.h1
-rw-r--r--Lib/test/mapping_tests.py4
-rw-r--r--Lib/test/test_dict.py4
-rw-r--r--Objects/object.c9
-rw-r--r--Objects/typeobject.c58
6 files changed, 65 insertions, 72 deletions
diff --git a/BROKEN b/BROKEN
index 53e9d43..4747794 100644
--- 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.)
diff --git a/Include/object.h b/Include/object.h
index 1f1aeaa..ab42f8a 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -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 *);
diff --git a/Lib/test/mapping_tests.py b/Lib/test/mapping_tests.py
index 1c570b6..b917540 100644
--- a/Lib/test/mapping_tests.py
+++ b/Lib/test/mapping_tests.py
@@ -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})
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index f168846..7295f41 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -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}
diff --git a/Objects/object.c b/Objects/object.c
index cb60320..80111b4 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -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");
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 1578801..f30a826 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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)
{