Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dbb53d9

Browse files
committed
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.
1 parent cb33165 commit dbb53d9

3 files changed

Lines changed: 41 additions & 3 deletions

File tree

Lib/test/test_descr.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2571,6 +2571,29 @@ def __del__(self):
25712571
del c
25722572
vereq(log, [1])
25732573

2574+
def hashinherit():
2575+
if verbose: print "Testing hash of mutable subclasses..."
2576+
2577+
class mydict(dict):
2578+
pass
2579+
d = mydict()
2580+
try:
2581+
hash(d)
2582+
except TypeError:
2583+
pass
2584+
else:
2585+
raise TestFailed, "hash() of dict subclass should fail"
2586+
2587+
class mylist(list):
2588+
pass
2589+
d = mylist()
2590+
try:
2591+
hash(d)
2592+
except TypeError:
2593+
pass
2594+
else:
2595+
raise TestFailed, "hash() of list subclass should fail"
2596+
25742597
def test_main():
25752598
class_docstrings()
25762599
lists()
@@ -2623,6 +2646,7 @@ def test_main():
26232646
str_of_str_subclass()
26242647
kwdargs()
26252648
delhook()
2649+
hashinherit()
26262650
if verbose: print "All OK"
26272651

26282652
if __name__ == "__main__":

Objects/dictobject.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,13 @@ dict_init(PyObject *self, PyObject *args, PyObject *kwds)
17971797
return result;
17981798
}
17991799

1800+
static long
1801+
dict_nohash(PyObject *self)
1802+
{
1803+
PyErr_SetString(PyExc_TypeError, "dict objects are unhashable");
1804+
return -1;
1805+
}
1806+
18001807
static PyObject *
18011808
dict_iter(dictobject *dict)
18021809
{
@@ -1827,7 +1834,7 @@ PyTypeObject PyDict_Type = {
18271834
0, /* tp_as_number */
18281835
&dict_as_sequence, /* tp_as_sequence */
18291836
&dict_as_mapping, /* tp_as_mapping */
1830-
0, /* tp_hash */
1837+
dict_nohash, /* tp_hash */
18311838
0, /* tp_call */
18321839
0, /* tp_str */
18331840
PyObject_GenericGetAttr, /* tp_getattro */

Objects/listobject.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,6 +1617,13 @@ list_init(PyListObject *self, PyObject *args, PyObject *kw)
16171617
return 0;
16181618
}
16191619

1620+
static long
1621+
list_nohash(PyObject *self)
1622+
{
1623+
PyErr_SetString(PyExc_TypeError, "list objects are unhashable");
1624+
return -1;
1625+
}
1626+
16201627
static char append_doc[] =
16211628
"L.append(object) -- append object to end";
16221629
static char extend_doc[] =
@@ -1681,7 +1688,7 @@ PyTypeObject PyList_Type = {
16811688
0, /* tp_as_number */
16821689
&list_as_sequence, /* tp_as_sequence */
16831690
0, /* tp_as_mapping */
1684-
0, /* tp_hash */
1691+
list_nohash, /* tp_hash */
16851692
0, /* tp_call */
16861693
0, /* tp_str */
16871694
PyObject_GenericGetAttr, /* tp_getattro */
@@ -1771,7 +1778,7 @@ static PyTypeObject immutable_list_type = {
17711778
0, /* tp_as_number */
17721779
&immutable_list_as_sequence, /* tp_as_sequence */
17731780
0, /* tp_as_mapping */
1774-
0, /* tp_hash */
1781+
list_nohash, /* tp_hash */
17751782
0, /* tp_call */
17761783
0, /* tp_str */
17771784
PyObject_GenericGetAttr, /* tp_getattro */

0 commit comments

Comments
 (0)