From: Guido van Rossum Date: Mon, 3 Dec 2001 16:32:18 +0000 (+0000) Subject: Fix of SF bug #475877 (Mutable subtype instances are hashable). X-Git-Tag: v2.2.1c1~597 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dbb53d99189075522eecc87723292c85aee15388;p=python Fix of SF bug #475877 (Mutable subtype instances are hashable). Rather than tweaking the inheritance of type object slots (which turns out to be too messy to try), this fix adds a __hash__ to the list and dict types (the only mutable types I'm aware of) that explicitly raises an error. This has the advantage that list.__hash__([]) also raises an error (previously, this would invoke object.__hash__([]), returning the argument's address); ditto for dict.__hash__. The disadvantage for this fix is that 3rd party mutable types aren't automatically fixed. This should be added to the rules for creating subclassable extension types: if you don't want your object to be hashable, add a tp_hash function that raises an exception. Also, it's possible that I've forgotten about other mutable types for which this should be done. --- diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index f3b81ac578..d76013e753 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2571,6 +2571,29 @@ def delhook(): del c vereq(log, [1]) +def hashinherit(): + if verbose: print "Testing hash of mutable subclasses..." + + class mydict(dict): + pass + d = mydict() + try: + hash(d) + except TypeError: + pass + else: + raise TestFailed, "hash() of dict subclass should fail" + + class mylist(list): + pass + d = mylist() + try: + hash(d) + except TypeError: + pass + else: + raise TestFailed, "hash() of list subclass should fail" + def test_main(): class_docstrings() lists() @@ -2623,6 +2646,7 @@ def test_main(): str_of_str_subclass() kwdargs() delhook() + hashinherit() if verbose: print "All OK" if __name__ == "__main__": diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 019f1121b3..3b922a9396 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1797,6 +1797,13 @@ dict_init(PyObject *self, PyObject *args, PyObject *kwds) return result; } +static long +dict_nohash(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "dict objects are unhashable"); + return -1; +} + static PyObject * dict_iter(dictobject *dict) { @@ -1827,7 +1834,7 @@ PyTypeObject PyDict_Type = { 0, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ + dict_nohash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ diff --git a/Objects/listobject.c b/Objects/listobject.c index b05fe27d60..dbbc4a9958 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -1617,6 +1617,13 @@ list_init(PyListObject *self, PyObject *args, PyObject *kw) return 0; } +static long +list_nohash(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "list objects are unhashable"); + return -1; +} + static char append_doc[] = "L.append(object) -- append object to end"; static char extend_doc[] = @@ -1681,7 +1688,7 @@ PyTypeObject PyList_Type = { 0, /* tp_as_number */ &list_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ - 0, /* tp_hash */ + list_nohash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ @@ -1771,7 +1778,7 @@ static PyTypeObject immutable_list_type = { 0, /* tp_as_number */ &immutable_list_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ - 0, /* tp_hash */ + list_nohash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */