From be42b1fcadbc20174f20454baebd68d4028d1648 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 1 Dec 2020 12:21:33 +0100 Subject: [PATCH 1/3] bpo-42522: Add _Py_Borrow() and _Py_XBorrow() functions Added _Py_Borrow() and _Py_XBorrow() functions to borrow a reference to an object: decrement the object reference count and return the object. --- Doc/c-api/refcounting.rst | 33 +++++++++++++++++ Doc/whatsnew/3.10.rst | 5 +++ Include/object.h | 15 ++++++++ .../2020-12-01-12-30-18.bpo-42522.0cnTLW.rst | 3 ++ Modules/_testcapimodule.c | 37 +++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2020-12-01-12-30-18.bpo-42522.0cnTLW.rst diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 391907c8c2976a..13247906fd1b9d 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -97,6 +97,39 @@ objects. :c:func:`Py_DECREF`, and the same warning applies. +.. c:function:: PyObject* _Py_Borrow(PyObject *o) + + :term:`Borrow a reference ` to an object: decrement the + object reference count and return the object. + + The object must not be ``NULL``; if you aren't sure that it isn't + ``NULL``, use :c:func:`_Py_XBorrow`. + + This function can be used as an expression to update an old C extension to + newer C API functions which return a :term:`strong reference`. For example, + replace ``frame->f_code`` with ``_Py_Borrow(PyFrame_GetCode(frame))``. + + If the object can be destroyed before the last usage of the borrowed + reference, the :term:`borrowed reference` must be converted in-place to a + :term:`strong reference` by calling :c:func:`Py_INCREF`, or the + :c:func:`Py_NewRef` function can be used to create a new :term:`strong + reference`. + + In other terms, the :c:func:`_Py_Borrow` function should be avoided whenever + possible. + + .. versionadded:: 3.10 + + +.. c:function:: PyObject* _Py_XBorrow(PyObject *o) + + Similar to :c:func:`_Py_Borrow`, but the object *o* can be NULL. + + If the object *o* is ``NULL``, the function just returns ``NULL``. + + .. versionadded:: 3.10 + + .. c:function:: void Py_CLEAR(PyObject *o) Decrement the reference count for object *o*. The object may be ``NULL``, in diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index a8f1080a504c70..daebbbcbd9581d 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -552,6 +552,11 @@ New Features * The :c:func:`PyType_GetSlot` function can accept static types. (Contributed by Hai Shi and Petr Viktorin in :issue:`41073`.) +* Added :c:func:`_Py_Borrow` and :c:func:`_Py_XBorrow` private functions to + :ref:`borrow a reference ` to an object: decrement the + object reference count and return the object. + (Contributed by Victor Stinner in :issue:`42522`.) + Porting to Python 3.10 ---------------------- diff --git a/Include/object.h b/Include/object.h index f68423a09c4e4b..87d40b699fcb77 100644 --- a/Include/object.h +++ b/Include/object.h @@ -551,6 +551,21 @@ static inline PyObject* _Py_XNewRef(PyObject *obj) #define Py_NewRef(obj) _Py_NewRef(obj) #define Py_XNewRef(obj) _Py_XNewRef(obj) +// Borrow a reference to an object: +// decrement the object reference count and return the object. +static inline PyObject* _Py_Borrow(PyObject *obj) +{ + Py_DECREF(obj); + return obj; +} + +// Similar to _Py_Borrow(), but the object can be NULL. +static inline PyObject* _Py_XBorrow(PyObject *obj) +{ + Py_XDECREF(obj); + return obj; +} + /* _Py_NoneStruct is an object of undefined type which can be used in contexts diff --git a/Misc/NEWS.d/next/C API/2020-12-01-12-30-18.bpo-42522.0cnTLW.rst b/Misc/NEWS.d/next/C API/2020-12-01-12-30-18.bpo-42522.0cnTLW.rst new file mode 100644 index 00000000000000..081d5e0333fde6 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-12-01-12-30-18.bpo-42522.0cnTLW.rst @@ -0,0 +1,3 @@ +Added :c:func:`_Py_Borrow` and :c:func:`_Py_XBorrow` functions to :ref:`borrow +a reference ` to an object: decrement the object reference +count and return the object. Patch by Victor Stinner. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index d2104423c5890c..c6d0287b0d343f 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5636,6 +5636,42 @@ test_set_type_size(PyObject* self, PyObject* ignored) } +static PyObject* +test_refcount(PyObject* self, PyObject* ignored) +{ + // Test Py_NewRef(), Py_XNewRef(), _Py_Borrow() and _Py_XBorrow() + // functions. + PyObject *obj = PyList_New(0); + if (obj == NULL) { + return NULL; + } + assert(Py_REFCNT(obj) == 1); + + PyObject *ref = Py_NewRef(obj); + assert(ref == obj); + assert(Py_REFCNT(obj) == 2); + Py_DECREF(ref); + + PyObject *xref = Py_XNewRef(obj); + assert(xref == obj); + assert(Py_REFCNT(obj) == 2); + Py_DECREF(xref); + + Py_INCREF(obj); + PyObject *borrowed = _Py_Borrow(obj); + assert(borrowed == obj); + assert(Py_REFCNT(obj) == 1); + + Py_INCREF(obj); + PyObject *xborrowed = _Py_XBorrow(obj); + assert(xborrowed == obj); + assert(Py_REFCNT(obj) == 1); + + Py_DECREF(obj); + Py_RETURN_NONE; +} + + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, @@ -5908,6 +5944,7 @@ static PyMethodDef TestMethods[] = { {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, {"without_gc", without_gc, METH_O}, {"test_set_type_size", test_set_type_size, METH_NOARGS}, + {"test_refcount", test_refcount, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; From eefe4849586a630f84807b3f146644fb3068a276 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 1 Dec 2020 16:14:00 +0100 Subject: [PATCH 2/3] Rephase the documentation --- Doc/c-api/refcounting.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 13247906fd1b9d..f453e764fd1ada 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -105,17 +105,14 @@ objects. The object must not be ``NULL``; if you aren't sure that it isn't ``NULL``, use :c:func:`_Py_XBorrow`. - This function can be used as an expression to update an old C extension to + This function is intended to be used when updating an old C extension to newer C API functions which return a :term:`strong reference`. For example, replace ``frame->f_code`` with ``_Py_Borrow(PyFrame_GetCode(frame))``. If the object can be destroyed before the last usage of the borrowed - reference, the :term:`borrowed reference` must be converted in-place to a - :term:`strong reference` by calling :c:func:`Py_INCREF`, or the - :c:func:`Py_NewRef` function can be used to create a new :term:`strong - reference`. + reference, the :c:func:`_Py_Borrow` function should be avoided. - In other terms, the :c:func:`_Py_Borrow` function should be avoided whenever + More generally, the :c:func:`_Py_Borrow` function should be avoided whenever possible. .. versionadded:: 3.10 From 6e90d1bb3257339610e12380bf6e00817b3e1c2d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 1 Dec 2020 18:44:42 +0100 Subject: [PATCH 3/3] Add tests on NULL --- Modules/_testcapimodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c6d0287b0d343f..b5172a83797660 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5657,6 +5657,8 @@ test_refcount(PyObject* self, PyObject* ignored) assert(Py_REFCNT(obj) == 2); Py_DECREF(xref); + assert(Py_XNewRef(NULL) == NULL); + Py_INCREF(obj); PyObject *borrowed = _Py_Borrow(obj); assert(borrowed == obj); @@ -5667,6 +5669,8 @@ test_refcount(PyObject* self, PyObject* ignored) assert(xborrowed == obj); assert(Py_REFCNT(obj) == 1); + assert(_Py_XBorrow(NULL) == NULL); + Py_DECREF(obj); Py_RETURN_NONE; }