mirror of https://github.com/python/cpython
Issue #4701: implicitly call PyType_Ready from PyObject_Hash
This commit is contained in:
parent
c13acb18bc
commit
180e400766
|
@ -111,9 +111,32 @@ class HashInheritanceTestCase(unittest.TestCase):
|
||||||
self.assertFalse(isinstance(obj, Hashable), repr(obj))
|
self.assertFalse(isinstance(obj, Hashable), repr(obj))
|
||||||
|
|
||||||
|
|
||||||
|
# Issue #4701: Check that some builtin types are correctly hashable
|
||||||
|
# (This test only used to fail in Python 3.0, but has been included
|
||||||
|
# in 2.x along with the lazy call to PyType_Ready in PyObject_Hash)
|
||||||
|
class DefaultIterSeq(object):
|
||||||
|
seq = range(10)
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.seq)
|
||||||
|
def __getitem__(self, index):
|
||||||
|
return self.seq[index]
|
||||||
|
|
||||||
|
class HashBuiltinsTestCase(unittest.TestCase):
|
||||||
|
hashes_to_check = [xrange(10),
|
||||||
|
enumerate(xrange(10)),
|
||||||
|
iter(DefaultIterSeq()),
|
||||||
|
iter(lambda: 0, 0),
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_hashes(self):
|
||||||
|
_default_hash = object.__hash__
|
||||||
|
for obj in self.hashes_to_check:
|
||||||
|
self.assertEqual(hash(obj), _default_hash(obj))
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
test_support.run_unittest(HashEqualityTestCase,
|
test_support.run_unittest(HashEqualityTestCase,
|
||||||
HashInheritanceTestCase)
|
HashInheritanceTestCase,
|
||||||
|
HashBuiltinsTestCase)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 1
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #4701: PyObject_Hash now implicitly calls PyType_Ready on types
|
||||||
|
where the tp_hash and tp_dict slots are both NULL.
|
||||||
|
|
||||||
- Issue #4764: With io.open, IOError.filename is set when trying to open a
|
- Issue #4764: With io.open, IOError.filename is set when trying to open a
|
||||||
directory on POSIX systems.
|
directory on POSIX systems.
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,106 @@ test_dict_iteration(PyObject* self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Issue #4701: Check that PyObject_Hash implicitly calls
|
||||||
|
* PyType_Ready if it hasn't already been called
|
||||||
|
*/
|
||||||
|
static PyTypeObject _HashInheritanceTester_Type = {
|
||||||
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
|
0, /* Number of items for varobject */
|
||||||
|
"hashinheritancetester", /* Name of this type */
|
||||||
|
sizeof(PyObject), /* Basic object size */
|
||||||
|
0, /* Item size for varobject */
|
||||||
|
(destructor)PyObject_Del, /* tp_dealloc */
|
||||||
|
0, /* tp_print */
|
||||||
|
0, /* tp_getattr */
|
||||||
|
0, /* tp_setattr */
|
||||||
|
0, /* tp_compare */
|
||||||
|
0, /* tp_repr */
|
||||||
|
0, /* tp_as_number */
|
||||||
|
0, /* tp_as_sequence */
|
||||||
|
0, /* tp_as_mapping */
|
||||||
|
0, /* tp_hash */
|
||||||
|
0, /* tp_call */
|
||||||
|
0, /* tp_str */
|
||||||
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
|
0, /* tp_setattro */
|
||||||
|
0, /* tp_as_buffer */
|
||||||
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||||
|
0, /* tp_doc */
|
||||||
|
0, /* tp_traverse */
|
||||||
|
0, /* tp_clear */
|
||||||
|
0, /* tp_richcompare */
|
||||||
|
0, /* tp_weaklistoffset */
|
||||||
|
0, /* tp_iter */
|
||||||
|
0, /* tp_iternext */
|
||||||
|
0, /* tp_methods */
|
||||||
|
0, /* tp_members */
|
||||||
|
0, /* tp_getset */
|
||||||
|
0, /* tp_base */
|
||||||
|
0, /* tp_dict */
|
||||||
|
0, /* tp_descr_get */
|
||||||
|
0, /* tp_descr_set */
|
||||||
|
0, /* tp_dictoffset */
|
||||||
|
0, /* tp_init */
|
||||||
|
0, /* tp_alloc */
|
||||||
|
PyType_GenericNew, /* tp_new */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
test_lazy_hash_inheritance(PyObject* self)
|
||||||
|
{
|
||||||
|
PyTypeObject *type;
|
||||||
|
PyObject *obj;
|
||||||
|
long hash;
|
||||||
|
|
||||||
|
type = &_HashInheritanceTester_Type;
|
||||||
|
obj = PyObject_New(PyObject, type);
|
||||||
|
if (obj == NULL) {
|
||||||
|
PyErr_Clear();
|
||||||
|
PyErr_SetString(
|
||||||
|
TestError,
|
||||||
|
"test_lazy_hash_inheritance: failed to create object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->tp_dict != NULL) {
|
||||||
|
PyErr_SetString(
|
||||||
|
TestError,
|
||||||
|
"test_lazy_hash_inheritance: type initialised too soon");
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = PyObject_Hash(obj);
|
||||||
|
if ((hash == -1) && PyErr_Occurred()) {
|
||||||
|
PyErr_Clear();
|
||||||
|
PyErr_SetString(
|
||||||
|
TestError,
|
||||||
|
"test_lazy_hash_inheritance: could not hash object");
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->tp_dict == NULL) {
|
||||||
|
PyErr_SetString(
|
||||||
|
TestError,
|
||||||
|
"test_lazy_hash_inheritance: type not initialised by hash()");
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->tp_hash != PyType_Type.tp_hash) {
|
||||||
|
PyErr_SetString(
|
||||||
|
TestError,
|
||||||
|
"test_lazy_hash_inheritance: unexpected hash function");
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Tests of PyLong_{As, From}{Unsigned,}Long(), and (#ifdef HAVE_LONG_LONG)
|
/* Tests of PyLong_{As, From}{Unsigned,}Long(), and (#ifdef HAVE_LONG_LONG)
|
||||||
PyLong_{As, From}{Unsigned,}LongLong().
|
PyLong_{As, From}{Unsigned,}LongLong().
|
||||||
|
|
||||||
|
@ -805,6 +905,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"test_config", (PyCFunction)test_config, METH_NOARGS},
|
{"test_config", (PyCFunction)test_config, METH_NOARGS},
|
||||||
{"test_list_api", (PyCFunction)test_list_api, METH_NOARGS},
|
{"test_list_api", (PyCFunction)test_list_api, METH_NOARGS},
|
||||||
{"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS},
|
{"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS},
|
||||||
|
{"test_lazy_hash_inheritance", (PyCFunction)test_lazy_hash_inheritance,METH_NOARGS},
|
||||||
{"test_long_api", (PyCFunction)test_long_api, METH_NOARGS},
|
{"test_long_api", (PyCFunction)test_long_api, METH_NOARGS},
|
||||||
{"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS},
|
{"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS},
|
||||||
{"test_k_code", (PyCFunction)test_k_code, METH_NOARGS},
|
{"test_k_code", (PyCFunction)test_k_code, METH_NOARGS},
|
||||||
|
|
|
@ -1100,6 +1100,17 @@ PyObject_Hash(PyObject *v)
|
||||||
PyTypeObject *tp = v->ob_type;
|
PyTypeObject *tp = v->ob_type;
|
||||||
if (tp->tp_hash != NULL)
|
if (tp->tp_hash != NULL)
|
||||||
return (*tp->tp_hash)(v);
|
return (*tp->tp_hash)(v);
|
||||||
|
/* To keep to the general practice that inheriting
|
||||||
|
* solely from object in C code should work without
|
||||||
|
* an explicit call to PyType_Ready, we implicitly call
|
||||||
|
* PyType_Ready here and then check the tp_hash slot again
|
||||||
|
*/
|
||||||
|
if (tp->tp_dict == NULL) {
|
||||||
|
if (PyType_Ready(tp) < 0)
|
||||||
|
return -1;
|
||||||
|
if (tp->tp_hash != NULL)
|
||||||
|
return (*tp->tp_hash)(v);
|
||||||
|
}
|
||||||
if (tp->tp_compare == NULL && RICHCOMPARE(tp) == NULL) {
|
if (tp->tp_compare == NULL && RICHCOMPARE(tp) == NULL) {
|
||||||
return _Py_HashPointer(v); /* Use address as hash value */
|
return _Py_HashPointer(v); /* Use address as hash value */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue