diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst index 8f233e16fb1..c3c6cf413dc 100644 --- a/Doc/c-api/weakref.rst +++ b/Doc/c-api/weakref.rst @@ -88,6 +88,15 @@ as much as it can. Use :c:func:`PyWeakref_GetRef` instead. +.. c:function:: int PyWeakref_IsDead(PyObject *ref) + + Test if the weak reference *ref* is dead. Returns 1 if the reference is + dead, 0 if it is alive, and -1 with an error set if *ref* is not a weak + reference object. + + .. versionadded:: 3.14 + + .. c:function:: void PyObject_ClearWeakRefs(PyObject *object) This function is called by the :c:member:`~PyTypeObject.tp_dealloc` handler diff --git a/Include/cpython/weakrefobject.h b/Include/cpython/weakrefobject.h index 9aa1a92c413..da8e77cddac 100644 --- a/Include/cpython/weakrefobject.h +++ b/Include/cpython/weakrefobject.h @@ -45,6 +45,9 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); #define _PyWeakref_CAST(op) \ (assert(PyWeakref_Check(op)), _Py_CAST(PyWeakReference*, (op))) +// Test if a weak reference is dead. +PyAPI_FUNC(int) PyWeakref_IsDead(PyObject *ref); + Py_DEPRECATED(3.13) static inline PyObject* PyWeakref_GET_OBJECT(PyObject *ref_obj) { PyWeakReference *ref = _PyWeakref_CAST(ref_obj); diff --git a/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst b/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst new file mode 100644 index 00000000000..2349eccac8f --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst @@ -0,0 +1,2 @@ +Add :c:func:`PyWeakref_IsDead` function, which tests if a weak reference is +dead. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 8d86b535eff..f737250ac29 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3144,6 +3144,7 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) PyObject *ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(weakref, &ref) == 1); assert(ref == obj); + assert(!PyWeakref_IsDead(weakref)); assert(Py_REFCNT(obj) == (refcnt + 1)); Py_DECREF(ref); @@ -3159,6 +3160,8 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(Py_REFCNT(obj) == 1); Py_DECREF(obj); + assert(PyWeakref_IsDead(weakref)); + // test PyWeakref_GET_OBJECT(), reference is dead assert(PyWeakref_GET_OBJECT(weakref) == Py_None); @@ -3181,6 +3184,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) PyErr_Clear(); assert(ref == NULL); + // test PyWeakRef_IsDead(), invalid type + assert(!PyErr_Occurred()); + assert(PyWeakref_IsDead(invalid_weakref) == -1); + assert(PyErr_ExceptionMatches(PyExc_TypeError)); + PyErr_Clear(); + // test PyWeakref_GetObject(), invalid type assert(PyWeakref_GetObject(invalid_weakref) == NULL); assert(PyErr_ExceptionMatches(PyExc_SystemError)); @@ -3193,6 +3202,11 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(ref == NULL); PyErr_Clear(); + // test PyWeakref_IsDead(NULL) + assert(PyWeakref_IsDead(NULL) == -1); + assert(PyErr_ExceptionMatches(PyExc_SystemError)); + PyErr_Clear(); + // test PyWeakref_GetObject(NULL) assert(PyWeakref_GetObject(NULL) == NULL); assert(PyErr_ExceptionMatches(PyExc_SystemError)); diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 9e3da1c3394..0ee64ed70a6 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -932,6 +932,19 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback) return (PyObject *)get_or_create_weakref(type, ob, callback); } +int +PyWeakref_IsDead(PyObject *ref) +{ + if (ref == NULL) { + PyErr_BadInternalCall(); + return -1; + } + if (!PyWeakref_Check(ref)) { + PyErr_Format(PyExc_TypeError, "expected a weakref, got %T", ref); + return -1; + } + return _PyWeakref_IS_DEAD(ref); +} int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)