From 6126e76159956ab4e1828fdf9cde08205ffe51ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 12:40:00 +0200 Subject: [PATCH 01/13] remove `PyWeakref_GetObject` and `PyWeakref_GET_OBJECT` --- Doc/c-api/weakref.rst | 24 ----------------- Doc/data/refcounts.dat | 6 ----- Doc/data/stable_abi.dat | 1 - .../c-api-pending-removal-in-3.15.rst | 4 --- Doc/howto/free-threading-extensions.rst | 4 --- Doc/whatsnew/3.11.rst | 2 +- Doc/whatsnew/3.13.rst | 6 ++--- Doc/whatsnew/3.15.rst | 2 ++ Include/cpython/weakrefobject.h | 17 ------------ Include/weakrefobject.h | 1 - Lib/test/test_stable_abi_ctypes.py | 1 - Misc/NEWS.d/3.11.0a2.rst | 2 +- Misc/NEWS.d/3.13.0a1.rst | 6 ++--- ...-05-08-12-40-59.gh-issue-133644.FNexLJ.rst | 3 +++ Misc/stable_abi.toml | 2 -- Modules/_testcapimodule.c | 27 ------------------- Objects/weakrefobject.c | 15 ----------- PC/python3dll.c | 1 - 18 files changed, 13 insertions(+), 111 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-05-08-12-40-59.gh-issue-133644.FNexLJ.rst diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst index c3c6cf413dcef5..14ec9d951c4a5f 100644 --- a/Doc/c-api/weakref.rst +++ b/Doc/c-api/weakref.rst @@ -64,30 +64,6 @@ as much as it can. .. versionadded:: 3.13 -.. c:function:: PyObject* PyWeakref_GetObject(PyObject *ref) - - 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:: - - This function returns a :term:`borrowed reference` to the referenced object. - This means that you should always call :c:func:`Py_INCREF` on the object - except when it cannot be destroyed before the last usage of the borrowed - reference. - - .. deprecated-removed:: 3.13 3.15 - Use :c:func:`PyWeakref_GetRef` instead. - - -.. c:function:: PyObject* PyWeakref_GET_OBJECT(PyObject *ref) - - Similar to :c:func:`PyWeakref_GetObject`, but does no error checking. - - .. deprecated-removed:: 3.13 3.15 - 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 diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index ca99b9e6d37141..d82db3c09d1e60 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -2922,12 +2922,6 @@ PyWeakref_CheckProxy:PyObject*:ob:0: PyWeakref_CheckRef:int::: PyWeakref_CheckRef:PyObject*:ob:0: -PyWeakref_GET_OBJECT:PyObject*::0: -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: diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 3d68487d07baf2..572a6b60e1daad 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -829,7 +829,6 @@ member,PyVarObject.ob_size,3.2,, func,PyVectorcall_Call,3.12,, func,PyVectorcall_NARGS,3.12,, type,PyWeakReference,3.2,,opaque -func,PyWeakref_GetObject,3.2,, func,PyWeakref_GetRef,3.13,, func,PyWeakref_NewProxy,3.2,, func,PyWeakref_NewRef,3.2,, diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index a5cc8f1d5b3475..0a853832c4db05 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -4,10 +4,6 @@ Pending removal in Python 3.15 * The bundled copy of ``libmpdecimal``. * The :c:func:`PyImport_ImportModuleNoBlock`: Use :c:func:`PyImport_ImportModule` instead. -* :c:func:`PyWeakref_GetObject` and :c:func:`PyWeakref_GET_OBJECT`: - Use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project - `__ can be used to get - :c:func:`PyWeakref_GetRef` on Python 3.12 and older. * :c:type:`Py_UNICODE` type and the :c:macro:`!Py_UNICODE_WIDE` macro: Use :c:type:`wchar_t` instead. * :c:func:`!PyUnicode_AsDecodedObject`: diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst index 3f6ee517050bd8..87db6bcadcb438 100644 --- a/Doc/howto/free-threading-extensions.rst +++ b/Doc/howto/free-threading-extensions.rst @@ -163,10 +163,6 @@ that return :term:`strong references `. +-----------------------------------+-----------------------------------+ | :c:func:`PyDict_Next` | none (see :ref:`PyDict_Next`) | +-----------------------------------+-----------------------------------+ -| :c:func:`PyWeakref_GetObject` | :c:func:`PyWeakref_GetRef` | -+-----------------------------------+-----------------------------------+ -| :c:func:`PyWeakref_GET_OBJECT` | :c:func:`PyWeakref_GetRef` | -+-----------------------------------+-----------------------------------+ | :c:func:`PyImport_AddModule` | :c:func:`PyImport_AddModuleRef` | +-----------------------------------+-----------------------------------+ | :c:func:`PyCell_GET` | :c:func:`PyCell_Get` | diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 2dd205dd2b8831..a095d887352127 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2673,7 +2673,7 @@ Removed (Contributed by Victor Stinner in :issue:`45474`.) -* Exclude :c:func:`PyWeakref_GET_OBJECT` from the limited C API. It never +* Exclude :c:func:`!PyWeakref_GET_OBJECT` from the limited C API. It never worked since the :c:type:`!PyWeakReference` structure is opaque in the limited C API. (Contributed by Victor Stinner in :issue:`35134`.) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index e20e49325c01d5..8ca1ae4fd64f33 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -2231,7 +2231,7 @@ New Features (Contributed by Serhiy Storchaka in :gh:`110289`.) * Add the :c:func:`PyWeakref_GetRef` function - as an alternative to :c:func:`PyWeakref_GetObject` + as an alternative to :c:func:`!PyWeakref_GetObject` that returns a :term:`strong reference` or ``NULL`` if the referent is no longer live. (Contributed by Victor Stinner in :gh:`105927`.) @@ -2516,8 +2516,8 @@ Deprecated C APIs are just aliases to :c:type:`!wchar_t`. (Contributed by Victor Stinner in :gh:`105156`.) -* Deprecate the :c:func:`PyWeakref_GetObject` and - :c:func:`PyWeakref_GET_OBJECT` functions, +* Deprecate the :c:func:`!PyWeakref_GetObject` and + :c:func:`!PyWeakref_GET_OBJECT` functions, which return a :term:`borrowed reference`. Replace them with the new :c:func:`PyWeakref_GetRef` function, which returns a :term:`strong reference`. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 5e9922069aa42c..09109d271792c4 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -155,3 +155,5 @@ Deprecated C APIs Removed C APIs -------------- +* :c:func:`!PyWeakref_GetObject` and :c:macro:`!PyWeakref_GET_OBJECT`: + use :c:func:`PyWeakref_GetRef` instead. diff --git a/Include/cpython/weakrefobject.h b/Include/cpython/weakrefobject.h index da8e77cddaca63..e0711407cee470 100644 --- a/Include/cpython/weakrefobject.h +++ b/Include/cpython/weakrefobject.h @@ -47,20 +47,3 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); // 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); - PyObject *obj = ref->wr_object; - // Explanation for the Py_REFCNT() check: when a weakref's target is part - // of a long chain of deallocations which triggers the trashcan mechanism, - // clearing the weakrefs can be delayed long after the target's refcount - // has dropped to zero. In the meantime, code accessing the weakref will - // be able to "see" the target object even though it is supposed to be - // unreachable. See issue gh-60806. - if (Py_REFCNT(obj) > 0) { - return obj; - } - return Py_None; -} -#define PyWeakref_GET_OBJECT(ref) PyWeakref_GET_OBJECT(_PyObject_CAST(ref)) diff --git a/Include/weakrefobject.h b/Include/weakrefobject.h index a6e71eb178b124..17fac62961c0fb 100644 --- a/Include/weakrefobject.h +++ b/Include/weakrefobject.h @@ -27,7 +27,6 @@ PyAPI_FUNC(PyObject *) PyWeakref_NewRef(PyObject *ob, PyObject *callback); PyAPI_FUNC(PyObject *) PyWeakref_NewProxy(PyObject *ob, PyObject *callback); -Py_DEPRECATED(3.13) PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *ref); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030D0000 PyAPI_FUNC(int) PyWeakref_GetRef(PyObject *ref, PyObject **pobj); diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 1e6f69d49e9335..9b4493bc05493a 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -849,7 +849,6 @@ def test_windows_feature_macros(self): "PyUnicode_WriteChar", "PyVectorcall_Call", "PyVectorcall_NARGS", - "PyWeakref_GetObject", "PyWeakref_GetRef", "PyWeakref_NewProxy", "PyWeakref_NewRef", diff --git a/Misc/NEWS.d/3.11.0a2.rst b/Misc/NEWS.d/3.11.0a2.rst index 48cf2c1e428d87..12e03b46db0b3f 100644 --- a/Misc/NEWS.d/3.11.0a2.rst +++ b/Misc/NEWS.d/3.11.0a2.rst @@ -1188,7 +1188,7 @@ context objects can now be disabled. .. nonce: Z0Zk_m .. section: C API -Exclude :c:func:`PyWeakref_GET_OBJECT` from the limited C API. It never +Exclude :c:func:`!PyWeakref_GET_OBJECT` from the limited C API. It never worked since the :c:type:`!PyWeakReference` structure is opaque in the limited C API. diff --git a/Misc/NEWS.d/3.13.0a1.rst b/Misc/NEWS.d/3.13.0a1.rst index 91e9fee7e37437..fc5c7d89da8ca5 100644 --- a/Misc/NEWS.d/3.13.0a1.rst +++ b/Misc/NEWS.d/3.13.0a1.rst @@ -6458,8 +6458,8 @@ Victor Stinner. .. nonce: GRxZtI .. section: C API -Deprecate the :c:func:`PyWeakref_GetObject` and -:c:func:`PyWeakref_GET_OBJECT` functions: use the new +Deprecate the :c:func:`!PyWeakref_GetObject` and +:c:func:`!PyWeakref_GET_OBJECT` functions: use the new :c:func:`PyWeakref_GetRef` function instead. Patch by Victor Stinner. .. @@ -6470,7 +6470,7 @@ Deprecate the :c:func:`PyWeakref_GetObject` and .. section: C API Add :c:func:`PyWeakref_GetRef` function: similar to -:c:func:`PyWeakref_GetObject` but returns a :term:`strong reference`, or +: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/NEWS.d/next/C_API/2025-05-08-12-40-59.gh-issue-133644.FNexLJ.rst b/Misc/NEWS.d/next/C_API/2025-05-08-12-40-59.gh-issue-133644.FNexLJ.rst new file mode 100644 index 00000000000000..71f1eaa5290d46 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-05-08-12-40-59.gh-issue-133644.FNexLJ.rst @@ -0,0 +1,3 @@ +Remove deprecated function :c:func:`!PyWeakref_GetObject` and macro +:c:macro:`!PyWeakref_GET_OBJECT`. Use :c:func:`PyWeakref_GetRef` instead. +Patch by Bénédikt Tran. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index d3e1f0db057023..1c01000568755e 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1592,8 +1592,6 @@ added = '3.2' [data.PyUnicode_Type] added = '3.2' -[function.PyWeakref_GetObject] - added = '3.2' [function.PyWeakref_NewProxy] added = '3.2' [function.PyWeakref_NewRef] diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3aa6e4c9e43a26..3823a3479376a2 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2207,10 +2207,6 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { - // Ignore PyWeakref_GetObject() deprecation, we test it on purpose - _Py_COMP_DIAG_PUSH - _Py_COMP_DIAG_IGNORE_DEPR_DECLS - // 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, @@ -2246,23 +2242,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(Py_REFCNT(obj) == (refcnt + 1)); Py_DECREF(ref); - // test PyWeakref_GetObject(), reference is alive - ref = PyWeakref_GetObject(weakref); // borrowed ref - assert(ref == obj); - - // test PyWeakref_GET_OBJECT(), reference is alive - ref = PyWeakref_GET_OBJECT(weakref); // borrowed ref - assert(ref == obj); - // delete the referenced object: clear the weakref 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); - // test PyWeakref_GetRef(), reference is dead ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(weakref, &ref) == 0); @@ -2288,11 +2273,6 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(PyErr_ExceptionMatches(PyExc_TypeError)); PyErr_Clear(); - // test PyWeakref_GetObject(), invalid type - assert(PyWeakref_GetObject(invalid_weakref) == NULL); - assert(PyErr_ExceptionMatches(PyExc_SystemError)); - PyErr_Clear(); - // test PyWeakref_GetRef(NULL) ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(NULL, &ref) == -1); @@ -2305,16 +2285,9 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(PyErr_ExceptionMatches(PyExc_SystemError)); PyErr_Clear(); - // test PyWeakref_GetObject(NULL) - assert(PyWeakref_GetObject(NULL) == NULL); - assert(PyErr_ExceptionMatches(PyExc_SystemError)); - PyErr_Clear(); - Py_DECREF(weakref); Py_RETURN_NONE; - - _Py_COMP_DIAG_POP } struct simpletracer_data { diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index bd4c4ac9b3475a..aa409f19da1eac 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -964,21 +964,6 @@ PyWeakref_GetRef(PyObject *ref, PyObject **pobj) } -PyObject * -PyWeakref_GetObject(PyObject *ref) -{ - if (ref == NULL || !PyWeakref_Check(ref)) { - PyErr_BadInternalCall(); - return NULL; - } - PyObject *obj = _PyWeakref_GET_REF(ref); - if (obj == NULL) { - return Py_None; - } - Py_DECREF(obj); - return obj; // borrowed reference -} - /* Note that there's an inlined copy-paste of handle_callback() in gcmodule.c's * handle_weakrefs(). */ diff --git a/PC/python3dll.c b/PC/python3dll.c index f0c578e11c643b..cfb317646221ac 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -786,7 +786,6 @@ EXPORT_FUNC(PyUnicodeTranslateError_SetReason) 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) From ab08a8e22f19f97d7928989269b0eb7452a3e168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 13:43:03 +0200 Subject: [PATCH 02/13] restore file --- Doc/deprecations/c-api-pending-removal-in-3.15.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index 0a853832c4db05..6e58f269baeec4 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -4,6 +4,10 @@ Pending removal in Python 3.15 * The bundled copy of ``libmpdecimal``. * The :c:func:`PyImport_ImportModuleNoBlock`: Use :c:func:`PyImport_ImportModule` instead. +* :c:func:`!PyWeakref_GetObject` and :c:func:`!PyWeakref_GET_OBJECT`: + Use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project + `__ can be used to get + :c:func:`PyWeakref_GetRef` on Python 3.12 and older. * :c:type:`Py_UNICODE` type and the :c:macro:`!Py_UNICODE_WIDE` macro: Use :c:type:`wchar_t` instead. * :c:func:`!PyUnicode_AsDecodedObject`: From 266c6f97d578de37eeba816a24ad687a477cdc89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:11:51 +0200 Subject: [PATCH 03/13] restore stable ABI symbols --- Doc/data/stable_abi.dat | 1 + Include/cpython/weakrefobject.h | 18 +++++++++++++++++ Lib/test/test_stable_abi_ctypes.py | 1 + Misc/stable_abi.toml | 2 ++ Modules/_testcapimodule.c | 31 ++++++++++++++++++++++++++++++ Objects/weakrefobject.c | 16 +++++++++++++++ PC/python3dll.c | 1 + 7 files changed, 70 insertions(+) diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 572a6b60e1daad..3d68487d07baf2 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -829,6 +829,7 @@ member,PyVarObject.ob_size,3.2,, func,PyVectorcall_Call,3.12,, func,PyVectorcall_NARGS,3.12,, type,PyWeakReference,3.2,,opaque +func,PyWeakref_GetObject,3.2,, func,PyWeakref_GetRef,3.13,, func,PyWeakref_NewProxy,3.2,, func,PyWeakref_NewRef,3.2,, diff --git a/Include/cpython/weakrefobject.h b/Include/cpython/weakrefobject.h index e0711407cee470..b848a81521c4b9 100644 --- a/Include/cpython/weakrefobject.h +++ b/Include/cpython/weakrefobject.h @@ -47,3 +47,21 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); // Test if a weak reference is dead. PyAPI_FUNC(int) PyWeakref_IsDead(PyObject *ref); + +/* removed in 3.15, kept for stable ABI compatibility */ +Py_DEPRECATED(3.13) static inline PyObject* PyWeakref_GET_OBJECT(PyObject *ref_obj) +{ + PyWeakReference *ref = _PyWeakref_CAST(ref_obj); + PyObject *obj = ref->wr_object; + // Explanation for the Py_REFCNT() check: when a weakref's target is part + // of a long chain of deallocations which triggers the trashcan mechanism, + // clearing the weakrefs can be delayed long after the target's refcount + // has dropped to zero. In the meantime, code accessing the weakref will + // be able to "see" the target object even though it is supposed to be + // unreachable. See issue gh-60806. + if (Py_REFCNT(obj) > 0) { + return obj; + } + return Py_None; +} +#define PyWeakref_GET_OBJECT(ref) PyWeakref_GET_OBJECT(_PyObject_CAST(ref)) diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 9b4493bc05493a..1e6f69d49e9335 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -849,6 +849,7 @@ def test_windows_feature_macros(self): "PyUnicode_WriteChar", "PyVectorcall_Call", "PyVectorcall_NARGS", + "PyWeakref_GetObject", "PyWeakref_GetRef", "PyWeakref_NewProxy", "PyWeakref_NewRef", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 1c01000568755e..d3e1f0db057023 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1592,6 +1592,8 @@ added = '3.2' [data.PyUnicode_Type] added = '3.2' +[function.PyWeakref_GetObject] + added = '3.2' [function.PyWeakref_NewProxy] added = '3.2' [function.PyWeakref_NewRef] diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3823a3479376a2..95ba2e8238eabe 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2207,6 +2207,8 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { + extern PyObject *PyWeakref_GetObject(PyObject *); + // 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, @@ -2242,12 +2244,29 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(Py_REFCNT(obj) == (refcnt + 1)); Py_DECREF(ref); + // test PyWeakref_GetObject(), reference is alive + ref = PyWeakref_GetObject(weakref); // borrowed ref + assert(ref == obj); + + // test PyWeakref_GET_OBJECT(), reference is alive + _Py_COMP_DIAG_PUSH + _Py_COMP_DIAG_IGNORE_DEPR_DECLS + ref = PyWeakref_GET_OBJECT(weakref); // borrowed ref + _Py_COMP_DIAG_POP + assert(ref == obj); + // delete the referenced object: clear the weakref assert(Py_REFCNT(obj) == 1); Py_DECREF(obj); assert(PyWeakref_IsDead(weakref)); + // test PyWeakref_GET_OBJECT(), reference is dead + _Py_COMP_DIAG_PUSH + _Py_COMP_DIAG_IGNORE_DEPR_DECLS + assert(PyWeakref_GET_OBJECT(weakref) == Py_None); + _Py_COMP_DIAG_POP + // test PyWeakref_GetRef(), reference is dead ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(weakref, &ref) == 0); @@ -2273,6 +2292,11 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(PyErr_ExceptionMatches(PyExc_TypeError)); PyErr_Clear(); + // test PyWeakref_GetObject(), invalid type + assert(PyWeakref_GetObject(invalid_weakref) == NULL); + assert(PyErr_ExceptionMatches(PyExc_SystemError)); + PyErr_Clear(); + // test PyWeakref_GetRef(NULL) ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(NULL, &ref) == -1); @@ -2285,9 +2309,16 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(PyErr_ExceptionMatches(PyExc_SystemError)); PyErr_Clear(); + // test PyWeakref_GetObject(NULL) + assert(PyWeakref_GetObject(NULL) == NULL); + assert(PyErr_ExceptionMatches(PyExc_SystemError)); + PyErr_Clear(); + Py_DECREF(weakref); Py_RETURN_NONE; + + _Py_COMP_DIAG_POP } struct simpletracer_data { diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index aa409f19da1eac..61fa3ddad0bfd8 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -964,6 +964,22 @@ PyWeakref_GetRef(PyObject *ref, PyObject **pobj) } +/* removed in 3.15, but kept for stable ABI compatibility */ +PyAPI_FUNC(PyObject *) +PyWeakref_GetObject(PyObject *ref) +{ + if (ref == NULL || !PyWeakref_Check(ref)) { + PyErr_BadInternalCall(); + return NULL; + } + PyObject *obj = _PyWeakref_GET_REF(ref); + if (obj == NULL) { + return Py_None; + } + Py_DECREF(obj); + return obj; // borrowed reference +} + /* Note that there's an inlined copy-paste of handle_callback() in gcmodule.c's * handle_weakrefs(). */ diff --git a/PC/python3dll.c b/PC/python3dll.c index cfb317646221ac..f0c578e11c643b 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -786,6 +786,7 @@ EXPORT_FUNC(PyUnicodeTranslateError_SetReason) 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) From 36de136ed76ab6dfc2d0c6a65f8f235649acc155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:33:54 +0200 Subject: [PATCH 04/13] update stable abi files --- Doc/data/stable_abi.dat | 1 - Misc/stable_abi.toml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 3d68487d07baf2..572a6b60e1daad 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -829,7 +829,6 @@ member,PyVarObject.ob_size,3.2,, func,PyVectorcall_Call,3.12,, func,PyVectorcall_NARGS,3.12,, type,PyWeakReference,3.2,,opaque -func,PyWeakref_GetObject,3.2,, func,PyWeakref_GetRef,3.13,, func,PyWeakref_NewProxy,3.2,, func,PyWeakref_NewRef,3.2,, diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index d3e1f0db057023..74d0d4e0ee740c 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1594,6 +1594,7 @@ added = '3.2' [function.PyWeakref_GetObject] added = '3.2' + abi_only = true [function.PyWeakref_NewProxy] added = '3.2' [function.PyWeakref_NewRef] From 121c51fa89c4bcdb4057c8feeaad2a170336c489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:36:24 +0200 Subject: [PATCH 05/13] update ABI imports --- Modules/_testcapimodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 95ba2e8238eabe..aa12d11f9ef3cb 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2207,7 +2207,8 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { - extern PyObject *PyWeakref_GetObject(PyObject *); + // Get the function from the stable ABI. + PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *); // Create a new heap type, create an instance of this type, and delete the // type. This object supports weak references. From b711d04aa521dbc28573be2ba5e0c47c40ce4b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:38:18 +0200 Subject: [PATCH 06/13] update ABI imports --- Modules/_testcapimodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index aa12d11f9ef3cb..b60a24102df1ac 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2207,7 +2207,7 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { - // Get the function from the stable ABI. + // Get the function (removed in 3.15) from the stable ABI. PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *); // Create a new heap type, create an instance of this type, and delete the From 9a4b77e4793e1f307c70e11538c17bcc8ed6b57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:42:10 +0200 Subject: [PATCH 07/13] entirely remove `PyWeakRef_GET_OBJECT` --- Include/cpython/weakrefobject.h | 18 ------------------ Modules/_testcapimodule.c | 13 ------------- 2 files changed, 31 deletions(-) diff --git a/Include/cpython/weakrefobject.h b/Include/cpython/weakrefobject.h index b848a81521c4b9..e0711407cee470 100644 --- a/Include/cpython/weakrefobject.h +++ b/Include/cpython/weakrefobject.h @@ -47,21 +47,3 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); // Test if a weak reference is dead. PyAPI_FUNC(int) PyWeakref_IsDead(PyObject *ref); - -/* removed in 3.15, kept for stable ABI compatibility */ -Py_DEPRECATED(3.13) static inline PyObject* PyWeakref_GET_OBJECT(PyObject *ref_obj) -{ - PyWeakReference *ref = _PyWeakref_CAST(ref_obj); - PyObject *obj = ref->wr_object; - // Explanation for the Py_REFCNT() check: when a weakref's target is part - // of a long chain of deallocations which triggers the trashcan mechanism, - // clearing the weakrefs can be delayed long after the target's refcount - // has dropped to zero. In the meantime, code accessing the weakref will - // be able to "see" the target object even though it is supposed to be - // unreachable. See issue gh-60806. - if (Py_REFCNT(obj) > 0) { - return obj; - } - return Py_None; -} -#define PyWeakref_GET_OBJECT(ref) PyWeakref_GET_OBJECT(_PyObject_CAST(ref)) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b60a24102df1ac..f83ed293eed921 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2249,25 +2249,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) ref = PyWeakref_GetObject(weakref); // borrowed ref assert(ref == obj); - // test PyWeakref_GET_OBJECT(), reference is alive - _Py_COMP_DIAG_PUSH - _Py_COMP_DIAG_IGNORE_DEPR_DECLS - ref = PyWeakref_GET_OBJECT(weakref); // borrowed ref - _Py_COMP_DIAG_POP - assert(ref == obj); - // delete the referenced object: clear the weakref assert(Py_REFCNT(obj) == 1); Py_DECREF(obj); assert(PyWeakref_IsDead(weakref)); - // test PyWeakref_GET_OBJECT(), reference is dead - _Py_COMP_DIAG_PUSH - _Py_COMP_DIAG_IGNORE_DEPR_DECLS - assert(PyWeakref_GET_OBJECT(weakref) == Py_None); - _Py_COMP_DIAG_POP - // test PyWeakref_GetRef(), reference is dead ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(weakref, &ref) == 0); From b819a753983a02b76bda236117d759dbb2d468c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:44:00 +0200 Subject: [PATCH 08/13] restore table --- Doc/howto/free-threading-extensions.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst index 87db6bcadcb438..3f6ee517050bd8 100644 --- a/Doc/howto/free-threading-extensions.rst +++ b/Doc/howto/free-threading-extensions.rst @@ -163,6 +163,10 @@ that return :term:`strong references `. +-----------------------------------+-----------------------------------+ | :c:func:`PyDict_Next` | none (see :ref:`PyDict_Next`) | +-----------------------------------+-----------------------------------+ +| :c:func:`PyWeakref_GetObject` | :c:func:`PyWeakref_GetRef` | ++-----------------------------------+-----------------------------------+ +| :c:func:`PyWeakref_GET_OBJECT` | :c:func:`PyWeakref_GetRef` | ++-----------------------------------+-----------------------------------+ | :c:func:`PyImport_AddModule` | :c:func:`PyImport_AddModuleRef` | +-----------------------------------+-----------------------------------+ | :c:func:`PyCell_GET` | :c:func:`PyCell_Get` | From 03ca635e2f17a17ecbf9dbf9072465f42e2c84c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:58:27 +0200 Subject: [PATCH 09/13] fix docs --- Doc/howto/free-threading-extensions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst index 3f6ee517050bd8..3e68a2ff35348f 100644 --- a/Doc/howto/free-threading-extensions.rst +++ b/Doc/howto/free-threading-extensions.rst @@ -163,9 +163,9 @@ that return :term:`strong references `. +-----------------------------------+-----------------------------------+ | :c:func:`PyDict_Next` | none (see :ref:`PyDict_Next`) | +-----------------------------------+-----------------------------------+ -| :c:func:`PyWeakref_GetObject` | :c:func:`PyWeakref_GetRef` | +| :c:func:`!PyWeakref_GetObject` | :c:func:`PyWeakref_GetRef` | +-----------------------------------+-----------------------------------+ -| :c:func:`PyWeakref_GET_OBJECT` | :c:func:`PyWeakref_GetRef` | +| :c:func:`!PyWeakref_GET_OBJECT` | :c:func:`PyWeakref_GetRef` | +-----------------------------------+-----------------------------------+ | :c:func:`PyImport_AddModule` | :c:func:`PyImport_AddModuleRef` | +-----------------------------------+-----------------------------------+ From 55987c32a85011e002b1a2a72db0e879f11e5998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:59:20 +0200 Subject: [PATCH 10/13] update porting --- Doc/whatsnew/3.15.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 09109d271792c4..04a3ff8c7a2a99 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -156,4 +156,6 @@ Removed C APIs -------------- * :c:func:`!PyWeakref_GetObject` and :c:macro:`!PyWeakref_GET_OBJECT`: - use :c:func:`PyWeakref_GetRef` instead. + use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project + `__ can be used to get + :c:func:`PyWeakref_GetRef` on Python 3.12 and older. From 94237effd96d2bb85be6d87e2b43cddcae97e6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 14:59:37 +0200 Subject: [PATCH 11/13] update porting --- Doc/whatsnew/3.15.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 04a3ff8c7a2a99..25c575c715955d 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -158,4 +158,4 @@ Removed C APIs * :c:func:`!PyWeakref_GetObject` and :c:macro:`!PyWeakref_GET_OBJECT`: use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project `__ can be used to get - :c:func:`PyWeakref_GetRef` on Python 3.12 and older. + :c:func:`!PyWeakref_GetRef` on Python 3.12 and older. From 5bd4ba31295b004330dfedd53c335dde074b1910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 8 May 2025 15:34:27 +0200 Subject: [PATCH 12/13] remove orphan `_Py_COMP_DIAG_POP` --- Modules/_testcapimodule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index f83ed293eed921..c113e01234848e 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2305,8 +2305,6 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_DECREF(weakref); Py_RETURN_NONE; - - _Py_COMP_DIAG_POP } struct simpletracer_data { From 0884e0778f8c42f447fa251481639ef5ef665910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 9 May 2025 18:07:13 +0200 Subject: [PATCH 13/13] update docs --- Doc/whatsnew/3.15.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 940872580de7b5..b520242a43b6ce 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -186,9 +186,8 @@ Removed C APIs of :c:func:`PyImport_ImportModule`. * :c:func:`!PyWeakref_GetObject` and :c:macro:`!PyWeakref_GET_OBJECT`: - use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project - `__ can be used to get - :c:func:`!PyWeakref_GetRef` on Python 3.12 and older. + use :c:func:`PyWeakref_GetRef` instead. The |pythoncapi_compat_project| + can be used to get :c:func:`!PyWeakref_GetRef` on Python 3.12 and older. The following functions are removed in favor of :c:func:`PyConfig_Get`. The |pythoncapi_compat_project| can be used to get :c:func:`!PyConfig_Get`