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 @@
+
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 @@
Source Files
+
+ Source Files
+
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 @@
+
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 @@
+