mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
cb33165ca2
commit
dbb53d9918
|
@ -2571,6 +2571,29 @@ def delhook():
|
||||||
del c
|
del c
|
||||||
vereq(log, [1])
|
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():
|
def test_main():
|
||||||
class_docstrings()
|
class_docstrings()
|
||||||
lists()
|
lists()
|
||||||
|
@ -2623,6 +2646,7 @@ def test_main():
|
||||||
str_of_str_subclass()
|
str_of_str_subclass()
|
||||||
kwdargs()
|
kwdargs()
|
||||||
delhook()
|
delhook()
|
||||||
|
hashinherit()
|
||||||
if verbose: print "All OK"
|
if verbose: print "All OK"
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1797,6 +1797,13 @@ dict_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
dict_nohash(PyObject *self)
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_TypeError, "dict objects are unhashable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dict_iter(dictobject *dict)
|
dict_iter(dictobject *dict)
|
||||||
{
|
{
|
||||||
|
@ -1827,7 +1834,7 @@ PyTypeObject PyDict_Type = {
|
||||||
0, /* tp_as_number */
|
0, /* tp_as_number */
|
||||||
&dict_as_sequence, /* tp_as_sequence */
|
&dict_as_sequence, /* tp_as_sequence */
|
||||||
&dict_as_mapping, /* tp_as_mapping */
|
&dict_as_mapping, /* tp_as_mapping */
|
||||||
0, /* tp_hash */
|
dict_nohash, /* tp_hash */
|
||||||
0, /* tp_call */
|
0, /* tp_call */
|
||||||
0, /* tp_str */
|
0, /* tp_str */
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
|
|
|
@ -1617,6 +1617,13 @@ list_init(PyListObject *self, PyObject *args, PyObject *kw)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
list_nohash(PyObject *self)
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_TypeError, "list objects are unhashable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static char append_doc[] =
|
static char append_doc[] =
|
||||||
"L.append(object) -- append object to end";
|
"L.append(object) -- append object to end";
|
||||||
static char extend_doc[] =
|
static char extend_doc[] =
|
||||||
|
@ -1681,7 +1688,7 @@ PyTypeObject PyList_Type = {
|
||||||
0, /* tp_as_number */
|
0, /* tp_as_number */
|
||||||
&list_as_sequence, /* tp_as_sequence */
|
&list_as_sequence, /* tp_as_sequence */
|
||||||
0, /* tp_as_mapping */
|
0, /* tp_as_mapping */
|
||||||
0, /* tp_hash */
|
list_nohash, /* tp_hash */
|
||||||
0, /* tp_call */
|
0, /* tp_call */
|
||||||
0, /* tp_str */
|
0, /* tp_str */
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
|
@ -1771,7 +1778,7 @@ static PyTypeObject immutable_list_type = {
|
||||||
0, /* tp_as_number */
|
0, /* tp_as_number */
|
||||||
&immutable_list_as_sequence, /* tp_as_sequence */
|
&immutable_list_as_sequence, /* tp_as_sequence */
|
||||||
0, /* tp_as_mapping */
|
0, /* tp_as_mapping */
|
||||||
0, /* tp_hash */
|
list_nohash, /* tp_hash */
|
||||||
0, /* tp_call */
|
0, /* tp_call */
|
||||||
0, /* tp_str */
|
0, /* tp_str */
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
|
|
Loading…
Reference in New Issue