https://github.com/python/cpython/commit/7cfb6552ffd10068cf75abde2d653b36878cc8d5
commit: 7cfb6552ffd10068cf75abde2d653b36878cc8d5
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-09-23T11:17:39+02:00
summary:

gh-148233: Add Py_HashBuffer() to the limited C API (#157966)

files:
A Misc/NEWS.d/next/C_API/2026-09-22-23-20-53.gh-issue-148233.U_2x-F.rst
A Modules/_testlimitedcapi/hash.c
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.16.rst
M Include/cpython/pyhash.h
M Include/pyhash.h
M Lib/test/test_capi/test_hash.py
M Lib/test/test_stable_abi_ctypes.py
M Misc/stable_abi.toml
M Modules/Setup.stdlib.in
M Modules/_testcapi/hash.c
M Modules/_testlimitedcapi.c
M Modules/_testlimitedcapi/parts.h
M PC/python3dll.c
M PCbuild/_testlimitedcapi.vcxproj
M PCbuild/_testlimitedcapi.vcxproj.filters

diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 86080fac7163838..3fbff9a0d65362b 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -969,6 +969,7 @@ func,Py_GetPlatform,3.2,,
 func,Py_GetRecursionLimit,3.2,,
 func,Py_GetVersion,3.2,,
 data,Py_HasFileSystemDefaultEncoding,3.2,,
+func,Py_HashBuffer,3.16,,
 func,Py_IS_TYPE,3.15,,
 func,Py_IncRef,3.2,,
 func,Py_Initialize,3.2,,
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 7e02924babe9523..8362b1ef7e312b0 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -1084,7 +1084,8 @@ C API changes
 New features
 ------------
 
-* TODO
+* Add :c:func:`Py_HashBuffer` to the limited C API.
+  (Contributed by Victor Stinner in :gh:`148233`.)
 
 Porting to Python 3.16
 ----------------------
diff --git a/Include/cpython/pyhash.h b/Include/cpython/pyhash.h
index dac223368dbe5e4..39ccffa6f2b90f1 100644
--- a/Include/cpython/pyhash.h
+++ b/Include/cpython/pyhash.h
@@ -50,5 +50,3 @@ _Py_HashPointer(const void *ptr)
 }
 
 PyAPI_FUNC(Py_hash_t) PyObject_GenericHash(PyObject *);
-
-PyAPI_FUNC(Py_hash_t) Py_HashBuffer(const void *ptr, Py_ssize_t len);
diff --git a/Include/pyhash.h b/Include/pyhash.h
index 3e23e2758808d79..acadf28f63c5c36 100644
--- a/Include/pyhash.h
+++ b/Include/pyhash.h
@@ -47,6 +47,10 @@ extern "C" {
 #  endif /* uint64_t && uint32_t && aligned */
 #endif /* Py_HASH_ALGORITHM */
 
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 16)
+PyAPI_FUNC(Py_hash_t) Py_HashBuffer(const void *ptr, Py_ssize_t len);
+#endif
+
 #ifndef Py_LIMITED_API
 #  define Py_CPYTHON_HASH_H
 #  include "cpython/pyhash.h"
diff --git a/Lib/test/test_capi/test_hash.py b/Lib/test/test_capi/test_hash.py
index f553ffb0d90f33b..d03c3e88087df57 100644
--- a/Lib/test/test_capi/test_hash.py
+++ b/Lib/test/test_capi/test_hash.py
@@ -1,7 +1,9 @@
 import sys
 import unittest
 from test.support import import_helper
+
 _testcapi = import_helper.import_module('_testcapi')
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
 
 
 SIZEOF_VOID_P = _testcapi.SIZEOF_VOID_P
@@ -79,7 +81,8 @@ def python_hash_pointer(x):
         self.assertEqual(hash_pointer(VOID_P_MAX), -2)
 
     def test_hash_buffer(self):
-        hash_buffer = _testcapi.hash_buffer
+        # Test Py_HashBuffer()
+        hash_buffer = _testlimitedcapi.hash_buffer
 
         def check(data):
             self.assertEqual(hash_buffer(data), hash(data))
diff --git a/Lib/test/test_stable_abi_ctypes.py 
b/Lib/test/test_stable_abi_ctypes.py
index 09ee2d53f98f585..aed7451e89b1563 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -929,6 +929,7 @@ def test_windows_feature_macros(self):
     "Py_GetRecursionLimit",
     "Py_GetVersion",
     "Py_HasFileSystemDefaultEncoding",
+    "Py_HashBuffer",
     "Py_IS_TYPE",
     "Py_IncRef",
     "Py_Initialize",
diff --git 
a/Misc/NEWS.d/next/C_API/2026-09-22-23-20-53.gh-issue-148233.U_2x-F.rst 
b/Misc/NEWS.d/next/C_API/2026-09-22-23-20-53.gh-issue-148233.U_2x-F.rst
new file mode 100644
index 000000000000000..069d547a3022b38
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2026-09-22-23-20-53.gh-issue-148233.U_2x-F.rst
@@ -0,0 +1 @@
+Add :c:func:`Py_HashBuffer` to the limited C API. Patch by Victor Stinner.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index d59a7c788fa9e02..36d4f094f9e85ac 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2773,6 +2773,8 @@
     added = '3.15'
 [const.Py_tp_module]
     added = '3.15'
+[function.Py_HashBuffer]
+    added = '3.16'
 
 # PEP 757 import/export API.
 
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index d10ed146db92b6b..696fec1290e5f3e 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -174,7 +174,7 @@
 @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
 @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c 
_testinternalcapi/test_lock.c _testinternalcapi/pytime.c 
_testinternalcapi/set.c _testinternalcapi/test_critical_sections.c 
_testinternalcapi/complex.c _testinternalcapi/interpreter.c 
_testinternalcapi/tokenizer.c _testinternalcapi/tuple.c 
_testinternalcapi/typecache.c
 @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c 
_testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c 
_testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c 
_testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c 
_testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c 
_testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c 
_testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c 
_testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c 
_testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c 
_testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c 
_testcapi/type.c _testcapi/function.c _testcapi/module.c _testcapi/weakref.c 
_testcapi/marshal.c
-@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.c _testlimitedcapi/capsule.c _testlimitedcapi/codec.c 
_testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c 
_testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c 
_testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c 
_testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c 
_testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c 
_testlimitedcapi/tuple.c _testlimitedcapi/unicode.c 
_testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c 
_testlimitedcapi/file.c _testlimitedcapi/weakref.c _testlimitedcapi/run.c 
_testlimitedcapi/type.c
+@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.c _testlimitedcapi/capsule.c _testlimitedcapi/codec.c 
_testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c 
_testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c 
_testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c 
_testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c 
_testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c 
_testlimitedcapi/tuple.c _testlimitedcapi/unicode.c 
_testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c 
_testlimitedcapi/file.c _testlimitedcapi/weakref.c _testlimitedcapi/run.c 
_testlimitedcapi/type.c _testlimitedcapi/hash.c
 @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
 @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
 
diff --git a/Modules/_testcapi/hash.c b/Modules/_testcapi/hash.c
index 1525344a93fbcf0..87d535c585cc27a 100644
--- a/Modules/_testcapi/hash.c
+++ b/Modules/_testcapi/hash.c
@@ -66,20 +66,6 @@ hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg)
 }
 
 
-static PyObject *
-hash_buffer(PyObject *Py_UNUSED(module), PyObject *args)
-{
-    char *ptr;
-    Py_ssize_t len;
-    if (!PyArg_ParseTuple(args, "y#", &ptr, &len)) {
-        return NULL;
-    }
-
-    Py_hash_t hash = Py_HashBuffer(ptr, len);
-    return long_from_hash(hash);
-}
-
-
 static PyObject *
 object_generichash(PyObject *Py_UNUSED(module), PyObject *arg)
 {
@@ -92,7 +78,6 @@ object_generichash(PyObject *Py_UNUSED(module), PyObject *arg)
 static PyMethodDef test_methods[] = {
     {"hash_getfuncdef", hash_getfuncdef, METH_NOARGS},
     {"hash_pointer", hash_pointer, METH_O},
-    {"hash_buffer", hash_buffer, METH_VARARGS},
     {"object_generichash", object_generichash, METH_O},
     {NULL},
 };
diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c
index 1f7392dd430d4cb..b30e32c56c57041 100644
--- a/Modules/_testlimitedcapi.c
+++ b/Modules/_testlimitedcapi.c
@@ -91,6 +91,9 @@ module_exec(PyObject *mod)
     if (_PyTestLimitedCAPI_Init_Type(mod) < 0) {
         return -1;
     }
+    if (_PyTestLimitedCAPI_Init_Hash(mod) < 0) {
+        return -1;
+    }
     return 0;
 }
 
diff --git a/Modules/_testlimitedcapi/hash.c b/Modules/_testlimitedcapi/hash.c
new file mode 100644
index 000000000000000..a17359d5aa45fe5
--- /dev/null
+++ b/Modules/_testlimitedcapi/hash.c
@@ -0,0 +1,40 @@
+// Need limited C API version 3.16 for Py_HashBuffer()
+#define Py_LIMITED_API 0x03100000
+
+#include "parts.h"
+#include "util.h"
+
+
+static PyObject*
+long_from_hash(Py_hash_t hash)
+{
+    Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash));
+    return PyLong_FromLongLong(hash);
+}
+
+
+// Test Py_HashBuffer()
+static PyObject*
+hash_buffer(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    char *ptr;
+    Py_ssize_t len;
+    if (!PyArg_ParseTuple(args, "y#", &ptr, &len)) {
+        return NULL;
+    }
+
+    Py_hash_t hash = Py_HashBuffer(ptr, len);
+    return long_from_hash(hash);
+}
+
+
+static PyMethodDef test_methods[] = {
+    {"hash_buffer", hash_buffer, METH_VARARGS},
+    {NULL},
+};
+
+int
+_PyTestLimitedCAPI_Init_Hash(PyObject *m)
+{
+    return PyModule_AddFunctions(m, test_methods);
+}
diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h
index e34f7dd68095723..e8a2d82fb94aea4 100644
--- a/Modules/_testlimitedcapi/parts.h
+++ b/Modules/_testlimitedcapi/parts.h
@@ -51,5 +51,6 @@ int _PyTestLimitedCAPI_Init_File(PyObject *module);
 int _PyTestLimitedCAPI_Init_Weakref(PyObject *module);
 int _PyTestLimitedCAPI_Init_Run(PyObject *module);
 int _PyTestLimitedCAPI_Init_Type(PyObject *module);
+int _PyTestLimitedCAPI_Init_Hash(PyObject *module);
 
 #endif // Py_TESTLIMITEDCAPI_PARTS_H
diff --git a/PC/python3dll.c b/PC/python3dll.c
index e0be9d65a93cda6..d1b7bafe6abb3cb 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -67,6 +67,7 @@ EXPORT_FUNC(Py_GetProgramName)
 EXPORT_FUNC(Py_GetPythonHome)
 EXPORT_FUNC(Py_GetRecursionLimit)
 EXPORT_FUNC(Py_GetVersion)
+EXPORT_FUNC(Py_HashBuffer)
 EXPORT_FUNC(Py_IncRef)
 EXPORT_FUNC(Py_Initialize)
 EXPORT_FUNC(Py_InitializeEx)
diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj
index c2bdea923f0f6d9..4218e0ed3945d27 100644
--- a/PCbuild/_testlimitedcapi.vcxproj
+++ b/PCbuild/_testlimitedcapi.vcxproj
@@ -121,6 +121,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\weakref.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\run.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\type.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\hash.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc" />
diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters 
b/PCbuild/_testlimitedcapi.vcxproj.filters
index 4c999949474b976..ddef60e599d4853 100644
--- a/PCbuild/_testlimitedcapi.vcxproj.filters
+++ b/PCbuild/_testlimitedcapi.vcxproj.filters
@@ -37,6 +37,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\weakref.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\run.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\type.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\hash.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi.c" />
   </ItemGroup>
   <ItemGroup>

_______________________________________________
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