mirror of
https://github.com/python/cpython.git
synced 2025-07-30 06:34:15 +00:00
Issue 2235: __hash__ is once again inherited by default, but inheritance can be blocked explicitly so that collections.Hashable remains meaningful
This commit is contained in:
parent
9ace15ca25
commit
53663a695e
14 changed files with 134 additions and 134 deletions
|
@ -3648,27 +3648,6 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)
|
|||
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)
|
||||
{
|
||||
|
@ -3802,8 +3781,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
|
|||
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;
|
||||
|
@ -3984,18 +3962,6 @@ 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 &&
|
||||
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) {
|
||||
|
@ -5280,10 +5246,8 @@ slot_tp_hash(PyObject *self)
|
|||
func = lookup_method(self, "__cmp__", &cmp_str);
|
||||
}
|
||||
if (func != NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "unhashable type: '%.200s'",
|
||||
self->ob_type->tp_name);
|
||||
Py_DECREF(func);
|
||||
return -1;
|
||||
return PyObject_HashNotImplemented(self);
|
||||
}
|
||||
PyErr_Clear();
|
||||
h = _Py_HashPointer((void *)self);
|
||||
|
@ -6034,6 +5998,13 @@ update_one_slot(PyTypeObject *type, slotdef *p)
|
|||
sanity checks. I'll buy the first person to
|
||||
point out a bug in this reasoning a beer. */
|
||||
}
|
||||
else if (descr == Py_None &&
|
||||
strcmp(p->name, "__hash__") == 0) {
|
||||
/* We specifically allow __hash__ to be set to None
|
||||
to prevent inheritance of the default
|
||||
implementation from object.__hash__ */
|
||||
specific = PyObject_HashNotImplemented;
|
||||
}
|
||||
else {
|
||||
use_generic = 1;
|
||||
generic = p->function;
|
||||
|
@ -6247,12 +6218,21 @@ add_operators(PyTypeObject *type)
|
|||
continue;
|
||||
if (PyDict_GetItem(dict, p->name_strobj))
|
||||
continue;
|
||||
descr = PyDescr_NewWrapper(type, p, *ptr);
|
||||
if (descr == NULL)
|
||||
return -1;
|
||||
if (PyDict_SetItem(dict, p->name_strobj, descr) < 0)
|
||||
return -1;
|
||||
Py_DECREF(descr);
|
||||
if (*ptr == PyObject_HashNotImplemented) {
|
||||
/* Classes may prevent the inheritance of the tp_hash
|
||||
slot by storing PyObject_HashNotImplemented in it. Make it
|
||||
visible as a None value for the __hash__ attribute. */
|
||||
if (PyDict_SetItem(dict, p->name_strobj, Py_None) < 0)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
descr = PyDescr_NewWrapper(type, p, *ptr);
|
||||
if (descr == NULL)
|
||||
return -1;
|
||||
if (PyDict_SetItem(dict, p->name_strobj, descr) < 0)
|
||||
return -1;
|
||||
Py_DECREF(descr);
|
||||
}
|
||||
}
|
||||
if (type->tp_new != NULL) {
|
||||
if (add_tp_new_wrapper(type) < 0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue