]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-105927: Add PyWeakref_GetRef() function (#105932)
authorVictor Stinner <vstinner@python.org>
Wed, 21 Jun 2023 09:40:09 +0000 (11:40 +0200)
committerGitHub <noreply@github.com>
Wed, 21 Jun 2023 09:40:09 +0000 (11:40 +0200)
Add tests on PyWeakref_NewRef(), PyWeakref_GetObject(),
PyWeakref_GET_OBJECT() and PyWeakref_GetRef().

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

index f27ec4411b4a2610299dcf84e554d54f14a294ff..44f4dce9ea02383cccdc25617938a11f99ef48fe 100644 (file)
@@ -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::
 
index d6ab0b28e658747e0737f2aa8fdc5ab405f3c62a..ef9ac1617a284bea3091910b6e1978a4be16fa65 100644 (file)
@@ -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:
index a3fde01cf67f3c5af6fb5efe1fa2d5e8e6abfe85..7fb002cd80369b6db516c8452940aa21eb32dc97 100644 (file)
@@ -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,,
index bbe02a9e85b42c22464064e67e139e76cdbf6003..6cf2bd2426370c4f160581c0ccdff041c1335bf6 100644 (file)
@@ -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
 ----------------------
index 8e1fa1b9286a77c3ea5405a061e3997ae84361b3..2c69f9e4564ab3f83528a3f8eb3be8cfbc0b1571 100644 (file)
@@ -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
index c26dff5aaf370bcd7e948a460cd1e333325a65e2..038c978e7bbd02b7bfd08d3cefbe56990deccb45 100644 (file)
@@ -794,6 +794,7 @@ SYMBOL_NAMES = (
     "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 (file)
index 0000000..afa40c8
--- /dev/null
@@ -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.
index 7025ed4e66b69859409447a838cbc3062f9f3f89..bc7259f11816f3d48fffc8b053b9666afa462cca 100644 (file)
     added = '3.12'
 [function.PyImport_AddModuleRef]
     added = '3.13'
+[function.PyWeakref_GetRef]
+    added = '3.13'
index 9d4517c8f68b09bee6aee7a8f7908d89cd5f2e8e..8b63f9c90a385d8b202aa47b39dc82c902a3aa1a 100644 (file)
@@ -3372,6 +3372,84 @@ error:
 }
 
 
+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 */
 };
 
index 9995a7756570cace284d453675f55b8da566952c..49342d0658de6bb9f17118625066872a00e1782c 100644 (file)
@@ -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)
 {
index fea19b77185dd544cea9e74ef81dc9f21bea1ed2..65bdf326ffbc7f0627647baf0dfd8f8dfaff331f 100755 (executable)
@@ -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)