summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/weakref.rst27
-rw-r--r--Doc/data/refcounts.dat4
-rw-r--r--Doc/data/stable_abi.dat1
-rw-r--r--Doc/whatsnew/3.13.rst4
-rw-r--r--Include/weakrefobject.h1
-rw-r--r--Lib/test/test_stable_abi_ctypes.py1
-rw-r--r--Misc/NEWS.d/next/C API/2023-06-20-08-59-05.gh-issue-105927.DfGeEA.rst3
-rw-r--r--Misc/stable_abi.toml2
-rw-r--r--Modules/_testcapimodule.c79
-rw-r--r--Objects/weakrefobject.c18
-rwxr-xr-xPC/python3dll.c1
11 files changed, 133 insertions, 8 deletions
diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst
index f27ec44..44f4dce 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 d6ab0b2..ef9ac16 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 a3fde01..7fb002c 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 bbe02a9..6cf2bd2 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 8e1fa1b..2c69f9e 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 c26dff5..038c978 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -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
index 0000000..afa40c8
--- /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 7025ed4..bc7259f 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 9d4517c..8b63f9c 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -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 */
};
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 9995a77..49342d0 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 fea19b7..65bdf32 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)