]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-113024: C API: Add PyObject_GenericHash() function (GH-113025)
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 22 Mar 2024 18:19:10 +0000 (20:19 +0200)
committerGitHub <noreply@github.com>
Fri, 22 Mar 2024 18:19:10 +0000 (20:19 +0200)
14 files changed:
Doc/c-api/hash.rst
Doc/c-api/typeobj.rst
Doc/whatsnew/3.13.rst
Include/cpython/pyhash.h
Lib/test/test_capi/test_abstract.py
Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst [new file with mode: 0644]
Modules/_decimal/_decimal.c
Modules/_testcapi/hash.c
Objects/classobject.c
Objects/descrobject.c
Objects/methodobject.c
Objects/typeobject.c
PC/winreg.c
Python/pyhash.c

index 1cf094cfcdca24e821ff2121d8951e5fb251c113..ddf0b3e15dbdbe2884c6602f4245367d0aa79819 100644 (file)
@@ -82,3 +82,14 @@ See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`.
    The function cannot fail: it cannot return ``-1``.
 
    .. versionadded:: 3.13
+
+.. c:function:: Py_hash_t PyObject_GenericHash(PyObject *obj)
+
+   Generic hashing function that is meant to be put into a type
+   object's ``tp_hash`` slot.
+   Its result only depends on the object's identity.
+
+   .. impl-detail::
+      In CPython, it is equivalent to :c:func:`Py_HashPointer`.
+
+   .. versionadded:: 3.13
index 8a26f237652d125bed27045f4dc1e94796e940ba..e66ab01878cac05cce909b414ec6b4d93f885b62 100644 (file)
@@ -883,6 +883,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
    :c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash`, when the subtype's
    :c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash` are both ``NULL``.
 
+   **Default:**
+
+   :c:data:`PyBaseObject_Type` uses :c:func:`PyObject_GenericHash`.
+
 
 .. c:member:: ternaryfunc PyTypeObject.tp_call
 
index bec788e7ed2b0e035254897c1c44af7fe3394675..c9a93d58056747424aa3ea9a3021596208cbe643 100644 (file)
@@ -1702,6 +1702,10 @@ New Features
 * Add :c:func:`Py_HashPointer` function to hash a pointer.
   (Contributed by Victor Stinner in :gh:`111545`.)
 
+* Add :c:func:`PyObject_GenericHash` function that implements the default
+  hashing function of a Python object.
+  (Contributed by Serhiy Storchaka in :gh:`113024`.)
+
 * Add PyTime C API:
 
   * :c:type:`PyTime_t` type.
index b476c3f357de9204eebb87f2423c470ecb8bff30..2f8e12c1423aa1d004a63a6df3037caca43654f5 100644 (file)
@@ -43,3 +43,4 @@ typedef struct {
 PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void);
 
 PyAPI_FUNC(Py_hash_t) Py_HashPointer(const void *ptr);
+PyAPI_FUNC(Py_hash_t) PyObject_GenericHash(PyObject *);
index 7e6cc9a2d0154b1209b49a55b2f73b3880d519be..bc39036e90bf8ba82ac6921959b623d4a4a22473 100644 (file)
@@ -1001,6 +1001,12 @@ class CAPITest(unittest.TestCase):
         self.assertTrue(number_check(0.5))
         self.assertFalse(number_check("1 + 1j"))
 
+    def test_object_generichash(self):
+        # Test PyObject_GenericHash()
+        generichash = _testcapi.object_generichash
+        for obj in object(), 1, 'string', []:
+            self.assertEqual(generichash(obj), object.__hash__(obj))
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst b/Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst
new file mode 100644 (file)
index 0000000..60ed6e6
--- /dev/null
@@ -0,0 +1 @@
+Add :c:func:`PyObject_GenericHash` function.
index 5b053c73e20bc95661c19a0292d3b0c50375b6c9..2481455ac0d1437f429fb2183dedf2bb357fd2cd 100644 (file)
@@ -4780,7 +4780,7 @@ _dec_hash(PyDecObject *v)
             return -1;
         }
         else if (mpd_isnan(MPD(v))) {
-            return _Py_HashPointer(v);
+            return PyObject_GenericHash((PyObject *)v);
         }
         else {
             return py_hash_inf * mpd_arith_sign(MPD(v));
index aee76787dcddb3b4e238d1e66778013115047231..809d537bfef0d3391a9cfd66b5d36d2f62b09324 100644 (file)
@@ -59,9 +59,20 @@ hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg)
 }
 
 
+static PyObject *
+object_generichash(PyObject *Py_UNUSED(module), PyObject *arg)
+{
+    NULLABLE(arg);
+    Py_hash_t hash = PyObject_GenericHash(arg);
+    Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash));
+    return PyLong_FromLongLong(hash);
+}
+
+
 static PyMethodDef test_methods[] = {
     {"hash_getfuncdef", hash_getfuncdef, METH_NOARGS},
     {"hash_pointer", hash_pointer, METH_O},
+    {"object_generichash", object_generichash, METH_O},
     {NULL},
 };
 
index d7e520f556d9a0ff91a9710351349146b52564da..9cbb9442c6059c532380c155ed31af9eb86fe248 100644 (file)
@@ -301,7 +301,7 @@ static Py_hash_t
 method_hash(PyMethodObject *a)
 {
     Py_hash_t x, y;
-    x = _Py_HashPointer(a->im_self);
+    x = PyObject_GenericHash(a->im_self);
     y = PyObject_Hash(a->im_func);
     if (y == -1)
         return -1;
index df546a090c28e4564e0843b3ed877c0a108c7af4..3423f152ce862ddf5d0938d856e0243a2bd6cef4 100644 (file)
@@ -1346,7 +1346,7 @@ wrapper_hash(PyObject *self)
 {
     wrapperobject *wp = (wrapperobject *)self;
     Py_hash_t x, y;
-    x = _Py_HashPointer(wp->self);
+    x = PyObject_GenericHash(wp->self);
     y = _Py_HashPointer(wp->descr);
     x = x ^ y;
     if (x == -1)
index 599fb05cb5874f2002701907d2f72a25d4ab83ab..d6773a264101dcecc4e3855e530cf3c8e3df4961 100644 (file)
@@ -320,7 +320,7 @@ static Py_hash_t
 meth_hash(PyCFunctionObject *a)
 {
     Py_hash_t x, y;
-    x = _Py_HashPointer(a->m_self);
+    x = PyObject_GenericHash(a->m_self);
     y = _Py_HashPointer((void*)(a->m_ml->ml_meth));
     x ^= y;
     if (x == -1)
index 06c2fc8e6ca0724bd2db2792933415568ab698f5..82822784aaf40721b7b485582d81e3b2a5bc872f 100644 (file)
@@ -6891,12 +6891,6 @@ PyDoc_STRVAR(object_doc,
 "When called, it accepts no arguments and returns a new featureless\n"
 "instance that has no instance attributes and cannot be given any.\n");
 
-static Py_hash_t
-object_hash(PyObject *obj)
-{
-    return _Py_HashPointer(obj);
-}
-
 PyTypeObject PyBaseObject_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "object",                                   /* tp_name */
@@ -6911,7 +6905,7 @@ PyTypeObject PyBaseObject_Type = {
     0,                                          /* tp_as_number */
     0,                                          /* tp_as_sequence */
     0,                                          /* tp_as_mapping */
-    object_hash,                                /* tp_hash */
+    PyObject_GenericHash,                       /* tp_hash */
     0,                                          /* tp_call */
     object_str,                                 /* tp_str */
     PyObject_GenericGetAttr,                    /* tp_getattro */
index 77b80217ac0ab1b44854bd7773ce36c75cb6ef6f..8096d17e43b7bce4bc114f60bee29eb72177a56f 100644 (file)
@@ -200,7 +200,7 @@ PyHKEY_hashFunc(PyObject *ob)
     /* Just use the address.
        XXX - should we use the handle value?
     */
-    return _Py_HashPointer(ob);
+    return PyObject_GenericHash(ob);
 }
 
 
index 141407c265677a147e866937282160d862c9f0c6..d508d78092a9e768682da00ec813c91d5bd5b045 100644 (file)
@@ -94,7 +94,7 @@ _Py_HashDouble(PyObject *inst, double v)
         if (Py_IS_INFINITY(v))
             return v > 0 ? _PyHASH_INF : -_PyHASH_INF;
         else
-            return _Py_HashPointer(inst);
+            return PyObject_GenericHash(inst);
     }
 
     m = frexp(v, &e);
@@ -139,6 +139,12 @@ Py_HashPointer(const void *ptr)
     return hash;
 }
 
+Py_hash_t
+PyObject_GenericHash(PyObject *obj)
+{
+    return Py_HashPointer(obj);
+}
+
 Py_hash_t
 _Py_HashBytes(const void *src, Py_ssize_t len)
 {