]> granicus.if.org Git - python/commitdiff
Merged revisions 68051 via svnmerge from
authorNick Coghlan <ncoghlan@gmail.com>
Tue, 30 Dec 2008 01:36:00 +0000 (01:36 +0000)
committerNick Coghlan <ncoghlan@gmail.com>
Tue, 30 Dec 2008 01:36:00 +0000 (01:36 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r68051 | nick.coghlan | 2008-12-30 11:18:48 +1000 (Tue, 30 Dec 2008) | 1 line

  Issue #4701: implicitly call PyType_Ready from PyObject_Hash
........

Lib/test/test_hash.py
Misc/NEWS
Modules/_testcapimodule.c
Objects/object.c

index 47c66d1d89dca6f68bfe356224d888c2e156a764..7ce40b95b7c76d551d62f26927e9d786ba67b41c 100644 (file)
@@ -111,9 +111,32 @@ class HashInheritanceTestCase(unittest.TestCase):
             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():
     test_support.run_unittest(HashEqualityTestCase,
-                              HashInheritanceTestCase)
+                              HashInheritanceTestCase,
+                              HashBuiltinsTestCase)
 
 
 if __name__ == "__main__":
index f1502e6bedc9c4d78ce0b5e001326bfc845fa483..c804f298044c877e16bac28a1a8d37270d518929 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.6.2
 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 #4759: fix a segfault for bytearray.translate(x, None).
 
 - Added test case to ensure attempts to read from a file opened for writing
index d640c39131a8d5be89c59fa0131007ceaecdff6d..bd2f2119e79d9b2d25ff176cec143037c2f10750 100644 (file)
@@ -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)
    PyLong_{As, From}{Unsigned,}LongLong().
 
@@ -805,6 +905,7 @@ static PyMethodDef TestMethods[] = {
        {"test_config",         (PyCFunction)test_config,        METH_NOARGS},
        {"test_list_api",       (PyCFunction)test_list_api,      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_numbits",   (PyCFunction)test_long_numbits,  METH_NOARGS},
        {"test_k_code",         (PyCFunction)test_k_code,        METH_NOARGS},
index c882cf25424f4129faea4cf82480b95c87a8f8a6..7b82db9d43b4fbe1d42f9ecd6407c856a35ec708 100644 (file)
@@ -1100,6 +1100,17 @@ PyObject_Hash(PyObject *v)
        PyTypeObject *tp = v->ob_type;
        if (tp->tp_hash != NULL)
                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) {
                return _Py_HashPointer(v); /* Use address as hash value */
        }