https://github.com/python/cpython/commit/52ffdeda1dfa2807e2af24ed73b32ba5f7d2b004
commit: 52ffdeda1dfa2807e2af24ed73b32ba5f7d2b004
branch: 3.13
author: Serhiy Storchaka <[email protected]>
committer: Yhg1s <[email protected]>
date: 2026-06-09T22:24:39+02:00
summary:

[3.13] gh-151130: Add more tests for PyWeakref_* C API (GH-151131) (GH-151141) 
(#151148)

(cherry picked from commit cb96d5ea4a63faa0db47213f386012214447be8a)


(cherry picked from commit c3cd75afdf86f6a811663c71da22cc24c784a6f4)

files:
A Lib/test/test_capi/test_weakref.py
A Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst
A Modules/_testcapi/weakref.c
A Modules/_testlimitedcapi/weakref.c
M Modules/Setup.stdlib.in
M Modules/_testcapi/parts.h
M Modules/_testcapimodule.c
M Modules/_testlimitedcapi.c
M Modules/_testlimitedcapi/parts.h
M PCbuild/_testcapi.vcxproj
M PCbuild/_testcapi.vcxproj.filters
M PCbuild/_testlimitedcapi.vcxproj
M PCbuild/_testlimitedcapi.vcxproj.filters

diff --git a/Lib/test/test_capi/test_weakref.py 
b/Lib/test/test_capi/test_weakref.py
new file mode 100644
index 000000000000000..1d5b5d3625baf0e
--- /dev/null
+++ b/Lib/test/test_capi/test_weakref.py
@@ -0,0 +1,121 @@
+import weakref
+import unittest
+from test.support import import_helper
+
+_testcapi = import_helper.import_module('_testcapi')
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
+NULL = None
+
+class Object:
+    pass
+
+class Ref(weakref.ReferenceType):
+    pass
+
+
+class CAPIWeakrefTest(unittest.TestCase):
+    def test_pyweakref_check(self):
+        # Test PyWeakref_Check()
+        check = _testlimitedcapi.pyweakref_check
+        obj = Object()
+        self.assertEqual(check(obj), 0)
+        self.assertEqual(check(weakref.ref(obj)), 1)
+        self.assertEqual(check(Ref(obj)), 1)
+        self.assertEqual(check(weakref.proxy(obj)), 1)
+
+        # CRASHES check(NULL)
+
+    def test_pyweakref_checkref(self):
+        # Test PyWeakref_CheckRef()
+        checkref = _testlimitedcapi.pyweakref_checkref
+        obj = Object()
+        self.assertEqual(checkref(obj), 0)
+        self.assertEqual(checkref(weakref.ref(obj)), 1)
+        self.assertEqual(checkref(Ref(obj)), 1)
+        self.assertEqual(checkref(weakref.proxy(obj)), 0)
+
+        # CRASHES checkref(NULL)
+
+    def test_pyweakref_checkrefexact(self):
+        # Test PyWeakref_CheckRefExact()
+        checkrefexact = _testlimitedcapi.pyweakref_checkrefexact
+        obj = Object()
+        self.assertEqual(checkrefexact(obj), 0)
+        self.assertEqual(checkrefexact(weakref.ref(obj)), 1)
+        self.assertEqual(checkrefexact(Ref(obj)), 0)
+        self.assertEqual(checkrefexact(weakref.proxy(obj)), 0)
+
+        # CRASHES checkrefexact(NULL)
+
+    def test_pyweakref_checkproxy(self):
+        # Test PyWeakref_CheckProxy()
+        checkproxy = _testlimitedcapi.pyweakref_checkproxy
+        obj = Object()
+        self.assertEqual(checkproxy(obj), 0)
+        self.assertEqual(checkproxy(weakref.ref(obj)), 0)
+        self.assertEqual(checkproxy(Ref(obj)), 0)
+        self.assertEqual(checkproxy(weakref.proxy(obj)), 1)
+
+        # CRASHES checkproxy(NULL)
+
+    def test_pyweakref_getref(self):
+        # Test PyWeakref_GetRef()
+        getref = _testcapi.pyweakref_getref
+        obj = Object()
+        wr = weakref.ref(obj)
+        wp = weakref.proxy(obj)
+        self.assertEqual(getref(wr), (1, obj))
+        self.assertEqual(getref(wp), (1, obj))
+        del obj
+        self.assertEqual(getref(wr), 0)
+        self.assertEqual(getref(wp), 0)
+
+        self.assertRaises(TypeError, getref, 42)
+        self.assertRaises(SystemError, getref, NULL)
+
+    def test_pyweakref_newref(self):
+        # Test PyWeakref_NewRef()
+        newref = _testlimitedcapi.pyweakref_newref
+        obj = Object()
+        wr = newref(obj)
+        self.assertIs(type(wr), weakref.ReferenceType)
+        # PyWeakref_NewRef() handles None callback as NULL callback
+        wr = newref(obj, None)
+        self.assertIs(type(wr), weakref.ReferenceType)
+        log = []
+        wr = newref(obj, log.append)
+        self.assertIs(type(wr), weakref.ReferenceType)
+        self.assertEqual(log, [])
+        del obj
+        self.assertEqual(log, [wr])
+
+        self.assertRaises(TypeError, newref, [])
+        # CRASHES newref(NULL)
+
+    def test_pyweakref_newproxy(self):
+        # Test PyWeakref_NewProxy()
+        newproxy = _testlimitedcapi.pyweakref_newproxy
+        obj = Object()
+        wp = newproxy(obj)
+        self.assertIs(type(wp), weakref.ProxyType)
+        # PyWeakref_NewProxy() handles None callback as NULL callback
+        wp = newproxy(obj, None)
+        self.assertIs(type(wp), weakref.ProxyType)
+        log = []
+        wp = newproxy(obj, log.append)
+        self.assertIs(type(wp), weakref.ProxyType)
+        self.assertEqual(log, [])
+        del obj
+        self.assertEqual(log, [wp])
+
+        def func():
+            pass
+        wp = newproxy(func)
+        self.assertIs(type(wp), weakref.CallableProxyType)
+
+        self.assertRaises(TypeError, newproxy, [])
+        # CRASHES newproxy(NULL)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git 
a/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst 
b/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst
new file mode 100644
index 000000000000000..0333e66446ce161
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst
@@ -0,0 +1 @@
+Add more tests for ``PyWeakref_*`` C API.
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index 57b90101bbe4a6c..971549950a64eb5 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -163,8 +163,8 @@
 @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c 
_xxtestfuzz/fuzzer.c
 @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
-@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/monitoring.c
-@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.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/sys.c 
_testlimitedcapi/tuple.c _testlimitedcapi/unicode.c 
_testlimitedcapi/vectorcall_limited.c _testlimitedcapi/file.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/monitoring.c 
_testcapi/weakref.c
+@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c 
_testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c 
_testlimitedcapi/bytes.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/sys.c 
_testlimitedcapi/tuple.c _testlimitedcapi/unicode.c 
_testlimitedcapi/vectorcall_limited.c _testlimitedcapi/file.c 
_testlimitedcapi/weakref.c
 @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
 @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
 
diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h
index 41d190961c69ee4..e348587b9208f5e 100644
--- a/Modules/_testcapi/parts.h
+++ b/Modules/_testcapi/parts.h
@@ -60,5 +60,6 @@ int _PyTestCapi_Init_Hash(PyObject *module);
 int _PyTestCapi_Init_Time(PyObject *module);
 int _PyTestCapi_Init_Monitoring(PyObject *module);
 int _PyTestCapi_Init_Object(PyObject *module);
+int _PyTestCapi_Init_Weakref(PyObject *mod);
 
 #endif // Py_TESTCAPI_PARTS_H
diff --git a/Modules/_testcapi/weakref.c b/Modules/_testcapi/weakref.c
new file mode 100644
index 000000000000000..71378956491afde
--- /dev/null
+++ b/Modules/_testcapi/weakref.c
@@ -0,0 +1,34 @@
+#include "parts.h"
+#include "util.h"
+
+
+static PyObject *
+pyweakref_getref(PyObject *module, PyObject *ref)
+{
+    NULLABLE(ref);
+    PyObject *obj = UNINITIALIZED_PTR;
+    int rc = PyWeakref_GetRef(ref, &obj);
+    if (rc == -1 && PyErr_Occurred()) {
+        assert(obj == NULL);
+        return NULL;
+    }
+    if (obj == NULL) {
+        return Py_BuildValue("i", rc);
+    }
+    else {
+        assert(obj != UNINITIALIZED_PTR);
+        return Py_BuildValue("iN", rc, obj);
+    }
+}
+
+
+static PyMethodDef test_methods[] = {
+    {"pyweakref_getref", pyweakref_getref, METH_O},
+    {NULL},
+};
+
+int
+_PyTestCapi_Init_Weakref(PyObject *m)
+{
+    return PyModule_AddFunctions(m, test_methods);
+}
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 51baf1e8ef667d0..c18b9713b0c3fe8 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -4329,6 +4329,9 @@ PyInit__testcapi(void)
     if (_PyTestCapi_Init_Object(m) < 0) {
         return NULL;
     }
+    if (_PyTestCapi_Init_Weakref(m) < 0) {
+        return NULL;
+    }
 
     PyState_AddModule(m, &_testcapimodule);
     return m;
diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c
index b183df7751d8dba..880d8836d1a7fd7 100644
--- a/Modules/_testlimitedcapi.c
+++ b/Modules/_testlimitedcapi.c
@@ -86,5 +86,8 @@ PyInit__testlimitedcapi(void)
     if (_PyTestLimitedCAPI_Init_File(mod) < 0) {
         return NULL;
     }
+    if (_PyTestLimitedCAPI_Init_Weakref(mod) < 0) {
+        return NULL;
+    }
     return mod;
 }
diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h
index 11b2e5c6b833bb3..4058df7fdef4336 100644
--- a/Modules/_testlimitedcapi/parts.h
+++ b/Modules/_testlimitedcapi/parts.h
@@ -41,5 +41,6 @@ int _PyTestLimitedCAPI_Init_Tuple(PyObject *module);
 int _PyTestLimitedCAPI_Init_Unicode(PyObject *module);
 int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module);
 int _PyTestLimitedCAPI_Init_File(PyObject *module);
+int _PyTestLimitedCAPI_Init_Weakref(PyObject *module);
 
 #endif // Py_TESTLIMITEDCAPI_PARTS_H
diff --git a/Modules/_testlimitedcapi/weakref.c 
b/Modules/_testlimitedcapi/weakref.c
new file mode 100644
index 000000000000000..e7f9d54d1a0d59e
--- /dev/null
+++ b/Modules/_testlimitedcapi/weakref.c
@@ -0,0 +1,78 @@
+#include "pyconfig.h"   // Py_GIL_DISABLED
+#ifndef Py_GIL_DISABLED
+   // Need limited C API 3.5 for PyModule_AddFunctions()
+#  define Py_LIMITED_API 0x03050000
+#endif
+
+#include "parts.h"
+#include "util.h"
+
+
+static PyObject *
+pyweakref_check(PyObject *module, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyWeakref_Check(obj));
+}
+
+static PyObject *
+pyweakref_checkref(PyObject *module, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyWeakref_CheckRef(obj));
+}
+
+static PyObject *
+pyweakref_checkrefexact(PyObject *module, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyWeakref_CheckRefExact(obj));
+}
+
+static PyObject *
+pyweakref_checkproxy(PyObject *module, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyWeakref_CheckProxy(obj));
+}
+
+static PyObject *
+pyweakref_newref(PyObject *module, PyObject *args)
+{
+    PyObject *obj;
+    PyObject *callback = NULL;
+    if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) {
+        return NULL;
+    }
+    NULLABLE(obj);
+    return PyWeakref_NewRef(obj, callback);
+}
+
+static PyObject *
+pyweakref_newproxy(PyObject *module, PyObject *args)
+{
+    PyObject *obj;
+    PyObject *callback = NULL;
+    if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) {
+        return NULL;
+    }
+    NULLABLE(obj);
+    return PyWeakref_NewProxy(obj, callback);
+}
+
+
+static PyMethodDef test_methods[] = {
+    {"pyweakref_check", pyweakref_check, METH_O},
+    {"pyweakref_checkref", pyweakref_checkref, METH_O},
+    {"pyweakref_checkrefexact", pyweakref_checkrefexact, METH_O},
+    {"pyweakref_checkproxy", pyweakref_checkproxy, METH_O},
+    {"pyweakref_newref", pyweakref_newref, METH_VARARGS},
+    {"pyweakref_newproxy", pyweakref_newproxy, METH_VARARGS},
+    {NULL},
+};
+
+int
+_PyTestLimitedCAPI_Init_Weakref(PyObject *m)
+{
+    return PyModule_AddFunctions(m, test_methods);
+}
diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj
index 44dbf2348137e17..02d755f8d5ce5b0 100644
--- a/PCbuild/_testcapi.vcxproj
+++ b/PCbuild/_testcapi.vcxproj
@@ -126,6 +126,7 @@
     <ClCompile Include="..\Modules\_testcapi\gc.c" />
     <ClCompile Include="..\Modules\_testcapi\run.c" />
     <ClCompile Include="..\Modules\_testcapi\monitoring.c" />
+    <ClCompile Include="..\Modules\_testcapi\weakref.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc" />
diff --git a/PCbuild/_testcapi.vcxproj.filters 
b/PCbuild/_testcapi.vcxproj.filters
index cae44bc955f7f12..63025cecd6d6edb 100644
--- a/PCbuild/_testcapi.vcxproj.filters
+++ b/PCbuild/_testcapi.vcxproj.filters
@@ -111,6 +111,9 @@
     <ClCompile Include="..\Modules\_testcapi\monitoring.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\Modules\_testcapi\weakref.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc">
diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj
index a5e0be93ab93902..e020c05fb8df9e8 100644
--- a/PCbuild/_testlimitedcapi.vcxproj
+++ b/PCbuild/_testlimitedcapi.vcxproj
@@ -113,6 +113,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\weakref.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc" />
diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters 
b/PCbuild/_testlimitedcapi.vcxproj.filters
index 4b3521afc06158c..73979d971eec7ec 100644
--- a/PCbuild/_testlimitedcapi.vcxproj.filters
+++ b/PCbuild/_testlimitedcapi.vcxproj.filters
@@ -29,6 +29,7 @@
     <ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
     <ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
+    <ClCompile Include="..\Modules\_testlimitedcapi\weakref.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