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!
This commit is contained in:
parent
5431ee4a49
commit
389381564c
61
BROKEN
61
BROKEN
|
@ -1,60 +1 @@
|
||||||
////////////////////////////////////////////////////////////////////////
|
(Nothing is broken at the moment AFAIK.)
|
||||||
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
|
|
||||||
|
|
|
@ -368,6 +368,7 @@ PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
|
||||||
|
|
||||||
/* Generic operations on objects */
|
/* Generic operations on objects */
|
||||||
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
|
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
|
||||||
|
PyAPI_FUNC(void) _Py_Break(void);
|
||||||
PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
|
PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
|
PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) _PyObject_Str(PyObject *);
|
PyAPI_FUNC(PyObject *) _PyObject_Str(PyObject *);
|
||||||
|
|
|
@ -545,6 +545,8 @@ class TestHashMappingProtocol(TestMappingProtocol):
|
||||||
class BadEq(object):
|
class BadEq(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
raise Exc()
|
raise Exc()
|
||||||
|
def __hash__(self):
|
||||||
|
return 24
|
||||||
|
|
||||||
d = self._empty_mapping()
|
d = self._empty_mapping()
|
||||||
d[BadEq()] = 42
|
d[BadEq()] = 42
|
||||||
|
@ -630,6 +632,8 @@ class TestHashMappingProtocol(TestMappingProtocol):
|
||||||
class BadCmp(object):
|
class BadCmp(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
raise Exc()
|
raise Exc()
|
||||||
|
def __hash__(self):
|
||||||
|
return 42
|
||||||
|
|
||||||
d1 = self._full_mapping({BadCmp(): 1})
|
d1 = self._full_mapping({BadCmp(): 1})
|
||||||
d2 = self._full_mapping({1: 1})
|
d2 = self._full_mapping({1: 1})
|
||||||
|
|
|
@ -76,6 +76,8 @@ class DictTest(unittest.TestCase):
|
||||||
class BadEq(object):
|
class BadEq(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
raise Exc()
|
raise Exc()
|
||||||
|
def __hash__(self):
|
||||||
|
return 24
|
||||||
|
|
||||||
d = {}
|
d = {}
|
||||||
d[BadEq()] = 42
|
d[BadEq()] = 42
|
||||||
|
@ -375,6 +377,8 @@ class DictTest(unittest.TestCase):
|
||||||
class BadCmp(object):
|
class BadCmp(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
raise Exc()
|
raise Exc()
|
||||||
|
def __hash__(self):
|
||||||
|
return 42
|
||||||
|
|
||||||
d1 = {BadCmp(): 1}
|
d1 = {BadCmp(): 1}
|
||||||
d2 = {1: 1}
|
d2 = {1: 1}
|
||||||
|
|
|
@ -320,9 +320,16 @@ PyObject_Print(PyObject *op, FILE *fp, int flags)
|
||||||
return internal_print(op, fp, flags, 0);
|
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 */
|
/* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */
|
||||||
void _PyObject_Dump(PyObject* op)
|
void
|
||||||
|
_PyObject_Dump(PyObject* op)
|
||||||
{
|
{
|
||||||
if (op == NULL)
|
if (op == NULL)
|
||||||
fprintf(stderr, "NULL\n");
|
fprintf(stderr, "NULL\n");
|
||||||
|
|
|
@ -2847,6 +2847,33 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)
|
||||||
COPYVAL(tp_dictoffset);
|
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
|
static void
|
||||||
inherit_slots(PyTypeObject *type, PyTypeObject *base)
|
inherit_slots(PyTypeObject *type, PyTypeObject *base)
|
||||||
{
|
{
|
||||||
|
@ -2970,9 +2997,12 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
|
||||||
COPYSLOT(tp_call);
|
COPYSLOT(tp_call);
|
||||||
COPYSLOT(tp_str);
|
COPYSLOT(tp_str);
|
||||||
{
|
{
|
||||||
|
/* Copy comparison-related slots only when
|
||||||
|
not overriding them anywhere */
|
||||||
if (type->tp_compare == NULL &&
|
if (type->tp_compare == NULL &&
|
||||||
type->tp_richcompare == 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_compare = base->tp_compare;
|
||||||
type->tp_richcompare = base->tp_richcompare;
|
type->tp_richcompare = base->tp_richcompare;
|
||||||
|
@ -3020,6 +3050,10 @@ PyType_Ready(PyTypeObject *type)
|
||||||
PyTypeObject *base;
|
PyTypeObject *base;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i, n;
|
||||||
|
|
||||||
|
if (strcmp(type->tp_name, "C") == 0) {
|
||||||
|
_Py_Break();
|
||||||
|
}
|
||||||
|
|
||||||
if (type->tp_flags & Py_TPFLAGS_READY) {
|
if (type->tp_flags & Py_TPFLAGS_READY) {
|
||||||
assert(type->tp_dict != NULL);
|
assert(type->tp_dict != NULL);
|
||||||
return 0;
|
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 */
|
/* Some more special stuff */
|
||||||
base = type->tp_base;
|
base = type->tp_base;
|
||||||
if (base != NULL) {
|
if (base != NULL) {
|
||||||
|
@ -4450,16 +4496,6 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Map rich comparison operators to their __xx__ namesakes */
|
|
||||||
static char *name_op[] = {
|
|
||||||
"__lt__",
|
|
||||||
"__le__",
|
|
||||||
"__eq__",
|
|
||||||
"__ne__",
|
|
||||||
"__gt__",
|
|
||||||
"__ge__",
|
|
||||||
};
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
half_richcompare(PyObject *self, PyObject *other, int op)
|
half_richcompare(PyObject *self, PyObject *other, int op)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue