[Python-checkins] gh-105927: Add PyWeakref_GetRef() function (#105932)

vstinner webhook-mailer at python.org
Wed Jun 21 05:40:13 EDT 2023


https://github.com/python/cpython/commit/9c44656febdcf72583e192ea4530fcfb0936c309
commit: 9c44656febdcf72583e192ea4530fcfb0936c309
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2023-06-21T11:40:09+02:00
summary:

gh-105927: Add PyWeakref_GetRef() function (#105932)

Add tests on PyWeakref_NewRef(), PyWeakref_GetObject(),
PyWeakref_GET_OBJECT() and PyWeakref_GetRef().

files:
A Misc/NEWS.d/next/C API/2023-06-20-08-59-05.gh-issue-105927.DfGeEA.rst
M Doc/c-api/weakref.rst
M Doc/data/refcounts.dat
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.13.rst
M Include/weakrefobject.h
M Lib/test/test_stable_abi_ctypes.py
M Misc/stable_abi.toml
M Modules/_testcapimodule.c
M Objects/weakrefobject.c
M PC/python3dll.c

diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst
index f27ec4411b4a..44f4dce9ea02 100644
--- a/Doc/c-api/weakref.rst
+++ b/Doc/c-api/weakref.rst
@@ -11,20 +11,20 @@ simple reference object, and the second acts as a proxy for the original object
 as much as it can.
 
 
-.. c:function:: int PyWeakref_Check(ob)
+.. c:function:: int PyWeakref_Check(PyObject *ob)
 
-   Return true if *ob* is either a reference or proxy object.  This function
+   Return non-zero if *ob* is either a reference or proxy object.  This function
    always succeeds.
 
 
-.. c:function:: int PyWeakref_CheckRef(ob)
+.. c:function:: int PyWeakref_CheckRef(PyObject *ob)
 
-   Return true if *ob* is a reference object.  This function always succeeds.
+   Return non-zero if *ob* is a reference object.  This function always succeeds.
 
 
-.. c:function:: int PyWeakref_CheckProxy(ob)
+.. c:function:: int PyWeakref_CheckProxy(PyObject *ob)
 
-   Return true if *ob* is a proxy object.  This function always succeeds.
+   Return non-zero if *ob* is a proxy object.  This function always succeeds.
 
 
 .. c:function:: PyObject* PyWeakref_NewRef(PyObject *ob, PyObject *callback)
@@ -51,10 +51,21 @@ as much as it can.
    ``None``, or ``NULL``, this will return ``NULL`` and raise :exc:`TypeError`.
 
 
+.. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
+
+   Get a :term:`strong reference` to the referenced object from a weak
+   reference, *ref*, into *\*pobj*.
+   Return 0 on success. Raise an exception and return -1 on error.
+
+   If the referent is no longer live, set *\*pobj* to ``NULL`` and return 0.
+
+   .. versionadded:: 3.13
+
+
 .. c:function:: PyObject* PyWeakref_GetObject(PyObject *ref)
 
-   Return the referenced object from a weak reference, *ref*.  If the referent is
-   no longer live, returns :const:`Py_None`.
+   Return a :term:`borrowed reference` to the referenced object from a weak
+   reference, *ref*.  If the referent is no longer live, returns ``Py_None``.
 
    .. note::
 
diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat
index d6ab0b28e658..ef9ac1617a28 100644
--- a/Doc/data/refcounts.dat
+++ b/Doc/data/refcounts.dat
@@ -2813,6 +2813,10 @@ PyWeakref_GET_OBJECT:PyObject*:ref:0:
 PyWeakref_GetObject:PyObject*::0:
 PyWeakref_GetObject:PyObject*:ref:0:
 
+PyWeakref_GetRef:int:::
+PyWeakref_GetRef:PyObject*:ref:0:
+PyWeakref_GetRef:PyObject**:pobj:+1:
+
 PyWeakref_NewProxy:PyObject*::+1:
 PyWeakref_NewProxy:PyObject*:ob:0:
 PyWeakref_NewProxy:PyObject*:callback:0:
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index a3fde01cf67f..7fb002cd8036 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -781,6 +781,7 @@ function,PyVectorcall_Call,3.12,,
 function,PyVectorcall_NARGS,3.12,,
 type,PyWeakReference,3.2,,opaque
 function,PyWeakref_GetObject,3.2,,
+function,PyWeakref_GetRef,3.13,,
 function,PyWeakref_NewProxy,3.2,,
 function,PyWeakref_NewRef,3.2,,
 var,PyWrapperDescr_Type,3.2,,
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index bbe02a9e85b4..6cf2bd242637 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -431,6 +431,10 @@ New Features
   of a :term:`borrowed reference`.
   (Contributed by Victor Stinner in :gh:`105922`.)
 
+* Add :c:func:`PyWeakref_GetRef` function: similar to
+  :c:func:`PyWeakref_GetObject` but returns a :term:`strong reference`, or
+  ``NULL`` if the referent is no longer live.
+  (Contributed by Victor Stinner in :gh:`105927`.)
 
 Porting to Python 3.13
 ----------------------
diff --git a/Include/weakrefobject.h b/Include/weakrefobject.h
index 8e1fa1b9286a..2c69f9e4564a 100644
--- a/Include/weakrefobject.h
+++ b/Include/weakrefobject.h
@@ -28,6 +28,7 @@ PyAPI_FUNC(PyObject *) PyWeakref_NewRef(PyObject *ob,
 PyAPI_FUNC(PyObject *) PyWeakref_NewProxy(PyObject *ob,
                                           PyObject *callback);
 PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *ref);
+PyAPI_FUNC(int) PyWeakref_GetRef(PyObject *ref, PyObject **pobj);
 
 
 #ifndef Py_LIMITED_API
diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py
index c26dff5aaf37..038c978e7bbd 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -794,6 +794,7 @@ def test_windows_feature_macros(self):
     "PyVectorcall_Call",
     "PyVectorcall_NARGS",
     "PyWeakref_GetObject",
+    "PyWeakref_GetRef",
     "PyWeakref_NewProxy",
     "PyWeakref_NewRef",
     "PyWrapperDescr_Type",
diff --git a/Misc/NEWS.d/next/C API/2023-06-20-08-59-05.gh-issue-105927.DfGeEA.rst b/Misc/NEWS.d/next/C API/2023-06-20-08-59-05.gh-issue-105927.DfGeEA.rst
new file mode 100644
index 000000000000..afa40c8ef5d6
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-06-20-08-59-05.gh-issue-105927.DfGeEA.rst	
@@ -0,0 +1,3 @@
+Add :c:func:`PyWeakref_GetRef` function: similar to
+:c:func:`PyWeakref_GetObject` but returns a :term:`strong reference`, or
+``NULL`` if the referent is no longer live. Patch by Victor Stinner.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index 7025ed4e66b6..bc7259f11816 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2430,3 +2430,5 @@
     added = '3.12'
 [function.PyImport_AddModuleRef]
     added = '3.13'
+[function.PyWeakref_GetRef]
+    added = '3.13'
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 9d4517c8f68b..8b63f9c90a38 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -3372,6 +3372,84 @@ check_pyimport_addmodule(PyObject *self, PyObject *args)
 }
 
 
+static PyObject *
+test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
+{
+    // Create a new heap type, create an instance of this type, and delete the
+    // type. This object supports weak references.
+    PyObject *new_type = PyObject_CallFunction((PyObject*)&PyType_Type,
+                                               "s(){}", "TypeName");
+    if (new_type == NULL) {
+        return NULL;
+    }
+    PyObject *obj = PyObject_CallNoArgs(new_type);
+    Py_DECREF(new_type);
+    if (obj == NULL) {
+        return NULL;
+    }
+    Py_ssize_t refcnt = Py_REFCNT(obj);
+
+    // test PyWeakref_NewRef(), reference is alive
+    PyObject *weakref = PyWeakref_NewRef(obj, NULL);
+    if (weakref == NULL) {
+        Py_DECREF(obj);
+        return NULL;
+    }
+    assert(PyWeakref_Check(weakref));
+    assert(PyWeakref_CheckRefExact(weakref));
+    assert(PyWeakref_CheckRefExact(weakref));
+    assert(Py_REFCNT(obj) == refcnt);
+
+    // test PyWeakref_GetRef(), reference is alive
+    PyObject *ref1;
+    assert(PyWeakref_GetRef(weakref, &ref1) == 0);
+    assert(ref1 == obj);
+    assert(Py_REFCNT(obj) == (refcnt + 1));
+    Py_DECREF(ref1);
+
+    // test PyWeakref_GetObject(), reference is alive
+    PyObject *ref2 = PyWeakref_GetObject(weakref);
+    assert(ref2 == obj);
+
+    // test PyWeakref_GET_OBJECT(), reference is alive
+    PyObject *ref3 = PyWeakref_GET_OBJECT(weakref);
+    assert(ref3 == obj);
+
+    // delete the referenced object
+    assert(Py_REFCNT(obj) == 1);
+    Py_DECREF(obj);
+
+    // test PyWeakref_GET_OBJECT(), reference is dead
+    assert(PyWeakref_GET_OBJECT(weakref) == Py_None);
+
+    // test PyWeakref_GetRef(), reference is dead
+    PyObject *ref4 = Py_True;  // marker to check that value was set
+    assert(PyWeakref_GetRef(weakref, &ref4) == 0);
+    assert(ref4 == NULL);
+
+    // None is not a weak reference object
+    PyObject *invalid_weakref = Py_None;
+    assert(!PyWeakref_Check(invalid_weakref));
+    assert(!PyWeakref_CheckRefExact(invalid_weakref));
+    assert(!PyWeakref_CheckRefExact(invalid_weakref));
+
+    // test PyWeakref_GetRef(), invalid type
+    assert(!PyErr_Occurred());
+    PyObject *ref5 = Py_True;  // marker to check that value was set
+    assert(PyWeakref_GetRef(invalid_weakref, &ref5) == -1);
+    assert(PyErr_ExceptionMatches(PyExc_TypeError));
+    PyErr_Clear();
+    assert(ref5 == NULL);
+
+    // test PyWeakref_GetObject(), invalid type
+    assert(PyWeakref_GetObject(invalid_weakref) == NULL);
+    assert(PyErr_ExceptionMatches(PyExc_SystemError));
+    PyErr_Clear();
+
+    Py_RETURN_NONE;
+}
+
+
 static PyMethodDef TestMethods[] = {
     {"set_errno",               set_errno,                       METH_VARARGS},
     {"test_config",             test_config,                     METH_NOARGS},
@@ -3516,6 +3594,7 @@ static PyMethodDef TestMethods[] = {
     {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
     {"test_atexit", test_atexit, METH_NOARGS},
     {"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS},
+    {"test_weakref_capi", test_weakref_capi, METH_NOARGS},
     {NULL, NULL} /* sentinel */
 };
 
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 9995a7756570..49342d0658de 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -894,6 +894,24 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
 }
 
 
+int
+PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
+{
+    if (ref == NULL) {
+        *pobj = NULL;
+        PyErr_BadInternalCall();
+        return -1;
+    }
+    if (!PyWeakref_Check(ref)) {
+        *pobj = NULL;
+        PyErr_SetString(PyExc_TypeError, "expected a weakref");
+        return -1;
+    }
+    *pobj = _PyWeakref_GET_REF(ref);
+    return 0;
+}
+
+
 PyObject *
 PyWeakref_GetObject(PyObject *ref)
 {
diff --git a/PC/python3dll.c b/PC/python3dll.c
index fea19b77185d..65bdf326ffbc 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -735,6 +735,7 @@ EXPORT_FUNC(PyUnicodeTranslateError_SetStart)
 EXPORT_FUNC(PyVectorcall_Call)
 EXPORT_FUNC(PyVectorcall_NARGS)
 EXPORT_FUNC(PyWeakref_GetObject)
+EXPORT_FUNC(PyWeakref_GetRef)
 EXPORT_FUNC(PyWeakref_NewProxy)
 EXPORT_FUNC(PyWeakref_NewRef)
 EXPORT_FUNC(PyWrapper_New)



More information about the Python-checkins mailing list