https://github.com/python/cpython/commit/e2e0b4b4b92694ba894e02b4a66fd87c166ed10f
commit: e2e0b4b4b92694ba894e02b4a66fd87c166ed10f
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-03-22T20:19:10+02:00
summary:

gh-113024: C API: Add PyObject_GenericHash() function (GH-113025)

files:
A Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst
M Doc/c-api/hash.rst
M Doc/c-api/typeobj.rst
M Doc/whatsnew/3.13.rst
M Include/cpython/pyhash.h
M Lib/test/test_capi/test_abstract.py
M Modules/_decimal/_decimal.c
M Modules/_testcapi/hash.c
M Objects/classobject.c
M Objects/descrobject.c
M Objects/methodobject.c
M Objects/typeobject.c
M PC/winreg.c
M Python/pyhash.c

diff --git a/Doc/c-api/hash.rst b/Doc/c-api/hash.rst
index 1cf094cfcdca24..ddf0b3e15dbdbe 100644
--- a/Doc/c-api/hash.rst
+++ b/Doc/c-api/hash.rst
@@ -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
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index 8a26f237652d12..e66ab01878cac0 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -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
 
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index bec788e7ed2b0e..c9a93d58056747 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -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.
diff --git a/Include/cpython/pyhash.h b/Include/cpython/pyhash.h
index b476c3f357de92..2f8e12c1423aa1 100644
--- a/Include/cpython/pyhash.h
+++ b/Include/cpython/pyhash.h
@@ -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 *);
diff --git a/Lib/test/test_capi/test_abstract.py 
b/Lib/test/test_capi/test_abstract.py
index 7e6cc9a2d0154b..bc39036e90bf8b 100644
--- a/Lib/test/test_capi/test_abstract.py
+++ b/Lib/test/test_capi/test_abstract.py
@@ -1001,6 +1001,12 @@ def test_number_check(self):
         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
index 00000000000000..60ed6e64c3b6b8
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-12-12-19-48-31.gh-issue-113024.rXcQs7.rst     
@@ -0,0 +1 @@
+Add :c:func:`PyObject_GenericHash` function.
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c
index 5b053c73e20bc9..2481455ac0d143 100644
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -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));
diff --git a/Modules/_testcapi/hash.c b/Modules/_testcapi/hash.c
index aee76787dcddb3..809d537bfef0d3 100644
--- a/Modules/_testcapi/hash.c
+++ b/Modules/_testcapi/hash.c
@@ -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},
 };
 
diff --git a/Objects/classobject.c b/Objects/classobject.c
index d7e520f556d9a0..9cbb9442c6059c 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -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;
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index df546a090c28e4..3423f152ce862d 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -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)
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 599fb05cb5874f..d6773a264101dc 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -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)
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 06c2fc8e6ca072..82822784aaf407 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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 */
diff --git a/PC/winreg.c b/PC/winreg.c
index 77b80217ac0ab1..8096d17e43b7bc 100644
--- a/PC/winreg.c
+++ b/PC/winreg.c
@@ -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);
 }
 
 
diff --git a/Python/pyhash.c b/Python/pyhash.c
index 141407c265677a..d508d78092a9e7 100644
--- a/Python/pyhash.c
+++ b/Python/pyhash.c
@@ -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)
 {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to