diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 391907c8c2976a..f453e764fd1ada 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -97,6 +97,36 @@ 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 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 :c:func:`_Py_Borrow` function should be avoided. + + More generally, 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..b5172a83797660 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5636,6 +5636,46 @@ 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); + + assert(Py_XNewRef(NULL) == NULL); + + 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); + + assert(_Py_XBorrow(NULL) == NULL); + + Py_DECREF(obj); + Py_RETURN_NONE; +} + + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", raise_memoryerror, METH_NOARGS}, @@ -5908,6 +5948,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 */ };