]> granicus.if.org Git - python/commitdiff
Fix of SF bug #475877 (Mutable subtype instances are hashable).
authorGuido van Rossum <guido@python.org>
Mon, 3 Dec 2001 16:32:18 +0000 (16:32 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 3 Dec 2001 16:32:18 +0000 (16:32 +0000)
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.

Lib/test/test_descr.py
Objects/dictobject.c
Objects/listobject.c

index f3b81ac57819cecd7ca43b879a80f0924e20b4f2..d76013e753dca73ee527d475825a979d5b00ef09 100644 (file)
@@ -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__":
index 019f1121b39690e5abcd71d0281640d3f32aeda7..3b922a9396c718a6c0933e33a189f315408eb7fa 100644 (file)
@@ -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 */
index b05fe27d602999acdc3b51bbbb5c68ff991c4556..dbbc4a99587a67ab1b7c105130e5e37a65385221 100644 (file)
@@ -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 */