From c81735ac91b070de5c37336bc8e8c10d5a1ce7ae Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 3 Nov 2020 11:35:41 +0100 Subject: [PATCH 1/2] bpo-1635741: Add PyModule_AddObjectRef() function Added PyModule_AddObjectRef() function: similar to PyModule_AddObjectRef() but don't steal a reference to the value on success. --- Doc/c-api/module.rst | 95 ++++++++++++++++--- Doc/whatsnew/3.10.rst | 5 + Include/modsupport.h | 10 +- ...2020-11-03-11-52-27.bpo-1635741.aDYJKB.rst | 3 + Python/modsupport.c | 70 ++++++++------ 5 files changed, 140 insertions(+), 43 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-11-03-11-52-27.bpo-1635741.aDYJKB.rst diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 6e9474bfa40eba..0e949673967c9c 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -437,26 +437,99 @@ a function called from a module execution slot (if using multi-phase initialization), can use the following functions to help initialize the module state: +.. c:function:: int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) + + Add an object to *module* as *name*. This is a convenience function which + can be used from the module's initialization function. + + On success, return ``0``. On error, raise an exception and return ``-1``. + + Return ``NULL`` if *value* is ``NULL``. It must be called with an exception + raised in this case. + + Example usage:: + + static int + add_spam(PyObject *module, int value) + { + PyObject *obj = PyLong_FromLong(value); + if (obj == NULL) { + return -1; + } + int res = PyModule_AddObjectRef(module, "spam", obj); + Py_DECREF(obj); + return res; + } + + The example can also be written without checking explicitly if *obj* is + ``NULL``:: + + static int + add_spam(PyObject *module, int value) + { + PyObject *obj = PyLong_FromLong(value); + int res = PyModule_AddObjectRef(module, "spam", obj); + Py_XDECREF(obj); + return res; + } + + Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in + this case, since *obj* can be ``NULL``. + + .. versionadded:: 3.10 + + .. c:function:: int PyModule_AddObject(PyObject *module, const char *name, PyObject *value) - Add an object to *module* as *name*. This is a convenience function which can - be used from the module's initialization function. This steals a reference to - *value* on success. Return ``-1`` on error, ``0`` on success. + Similar to :c:func:`PyModule_AddObjectRef`, but steals a reference to + *value* on success (if it returns ``0``). + + The new :c:func:`PyModule_AddObjectRef` function is recommended, since it is + easy to introduce reference leaks by misusing the + :c:func:`PyModule_AddObject` function. .. note:: - Unlike other functions that steal references, ``PyModule_AddObject()`` only - decrements the reference count of *value* **on success**. + Unlike other functions that steal references, ``PyModule_AddObject()`` + only decrements the reference count of *value* **on success**. This means that its return value must be checked, and calling code must :c:func:`Py_DECREF` *value* manually on error. Example usage:: - Py_INCREF(spam); - if (PyModule_AddObject(module, "spam", spam) < 0) { - Py_DECREF(module); - Py_DECREF(spam); - return NULL; - } + static int + add_spam(PyObject *module, int value) + { + PyObject *obj = PyLong_FromLong(value); + if (obj == NULL) { + return -1; + } + if (PyModule_AddObject(module, "spam", obj) < 0) { + Py_DECREF(obj); + return -1; + } + // PyModule_AddObject() stole a reference to obj: + // Py_DECREF(obj) is not needed here + return 0; + } + + The example can also be written without checking explicitly if *obj* is + ``NULL``:: + + static int + add_spam(PyObject *module, int value) + { + PyObject *obj = PyLong_FromLong(value); + if (PyModule_AddObject(module, "spam", obj) < 0) { + Py_XDECREF(obj); + return -1; + } + // PyModule_AddObject() stole a reference to obj: + // Py_DECREF(obj) is not needed here + return 0; + } + + Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in + this case, since *obj* can be ``NULL``. .. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 60dee0c6bd1651..618a007ee6cebe 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -366,6 +366,11 @@ New Features * Added :c:func:`PyUnicode_AsUTF8AndSize` to the limited C API. (Contributed by Alex Gaynor in :issue:`41784`.) +* Added :c:func:`PyModule_AddObjectRef` function: similar to + :c:func:`PyModule_AddObjectRef` but don't steal a reference to the value on + success. + (Contributed by Victor Stinner in :issue:`1635741`.) + Porting to Python 3.10 ---------------------- diff --git a/Include/modsupport.h b/Include/modsupport.h index 4c4aab65bac103..f009d586bf6202 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -136,7 +136,15 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords( void _PyArg_Fini(void); #endif /* Py_LIMITED_API */ -PyAPI_FUNC(int) PyModule_AddObject(PyObject *, const char *, PyObject *); +// Add an attribute with name 'name' and value 'obj' to the module 'mod. +// On success, return 0 on success. +// On error, raise an exception and return -1. +PyAPI_FUNC(int) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value); + +// Similar to PyModule_AddObjectRef() but steal a reference to 'obj' +// (Py_DECREF(obj)) on success (if it returns 0). +PyAPI_FUNC(int) PyModule_AddObject(PyObject *mod, const char *, PyObject *value); + PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long); PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 diff --git a/Misc/NEWS.d/next/C API/2020-11-03-11-52-27.bpo-1635741.aDYJKB.rst b/Misc/NEWS.d/next/C API/2020-11-03-11-52-27.bpo-1635741.aDYJKB.rst new file mode 100644 index 00000000000000..2ab1afb922fa8b --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-11-03-11-52-27.bpo-1635741.aDYJKB.rst @@ -0,0 +1,3 @@ +Added :c:func:`PyModule_AddObjectRef` function: similar to +:c:func:`PyModule_AddObjectRef` but don't steal a reference to the value on +success. Patch by Victor Stinner. diff --git a/Python/modsupport.c b/Python/modsupport.c index 2dabcf383409e9..8655daa1fc5e0e 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -634,56 +634,70 @@ va_build_stack(PyObject **small_stack, Py_ssize_t small_stack_len, int -PyModule_AddObject(PyObject *m, const char *name, PyObject *o) +PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value) { - PyObject *dict; - if (!PyModule_Check(m)) { + if (!PyModule_Check(mod)) { PyErr_SetString(PyExc_TypeError, - "PyModule_AddObject() needs module as first arg"); + "PyModule_AddObjectRef() first argument " + "must be a module"); return -1; } - if (!o) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_TypeError, - "PyModule_AddObject() needs non-NULL value"); + if (!value) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_SystemError, + "PyModule_AddObjectRef() must be called " + "with an exception raised if value is NULL"); + } return -1; } - dict = PyModule_GetDict(m); + PyObject *dict = PyModule_GetDict(mod); if (dict == NULL) { /* Internal error -- modules must have a dict! */ PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", - PyModule_GetName(m)); + PyModule_GetName(mod)); return -1; } - if (PyDict_SetItemString(dict, name, o)) + + if (PyDict_SetItemString(dict, name, value)) { return -1; - Py_DECREF(o); + } return 0; } + +int +PyModule_AddObject(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + if (res == 0) { + Py_DECREF(value); + } + return res; +} + int PyModule_AddIntConstant(PyObject *m, const char *name, long value) { - PyObject *o = PyLong_FromLong(value); - if (!o) + PyObject *obj = PyLong_FromLong(value); + if (!obj) { return -1; - if (PyModule_AddObject(m, name, o) == 0) - return 0; - Py_DECREF(o); - return -1; + } + int res = PyModule_AddObjectRef(m, name, obj); + Py_DECREF(obj); + return res; } int PyModule_AddStringConstant(PyObject *m, const char *name, const char *value) { - PyObject *o = PyUnicode_FromString(value); - if (!o) + PyObject *obj = PyUnicode_FromString(value); + if (!obj) { return -1; - if (PyModule_AddObject(m, name, o) == 0) - return 0; - Py_DECREF(o); - return -1; + } + int res = PyModule_AddObjectRef(m, name, obj); + Py_DECREF(obj); + return res; } int @@ -696,11 +710,5 @@ PyModule_AddType(PyObject *module, PyTypeObject *type) const char *name = _PyType_Name(type); assert(name != NULL); - Py_INCREF(type); - if (PyModule_AddObject(module, name, (PyObject *)type) < 0) { - Py_DECREF(type); - return -1; - } - - return 0; + return PyModule_AddObjectRef(module, name, (PyObject *)type); } From 756ceafba4acdf3c7c409e1d23b6c61d4927f7b6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Nov 2020 11:11:43 +0100 Subject: [PATCH 2/2] Fix Sphinx formatting --- Doc/c-api/module.rst | 73 +++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 0e949673967c9c..41a705d9e99156 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -264,7 +264,7 @@ of the following two module creation functions: instead; only use this if you are sure you need it. Before it is returned from in the initialization function, the resulting module -object is typically populated using functions like :c:func:`PyModule_AddObject`. +object is typically populated using functions like :c:func:`PyModule_AddObjectRef`. .. _multi-phase-initialization: @@ -473,8 +473,8 @@ state: return res; } - Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in - this case, since *obj* can be ``NULL``. + Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in + this case, since *obj* can be ``NULL``. .. versionadded:: 3.10 @@ -494,42 +494,45 @@ state: only decrements the reference count of *value* **on success**. This means that its return value must be checked, and calling code must - :c:func:`Py_DECREF` *value* manually on error. Example usage:: - - static int - add_spam(PyObject *module, int value) - { - PyObject *obj = PyLong_FromLong(value); - if (obj == NULL) { - return -1; - } - if (PyModule_AddObject(module, "spam", obj) < 0) { - Py_DECREF(obj); - return -1; - } - // PyModule_AddObject() stole a reference to obj: - // Py_DECREF(obj) is not needed here - return 0; + :c:func:`Py_DECREF` *value* manually on error. + + Example usage:: + + static int + add_spam(PyObject *module, int value) + { + PyObject *obj = PyLong_FromLong(value); + if (obj == NULL) { + return -1; + } + if (PyModule_AddObject(module, "spam", obj) < 0) { + Py_DECREF(obj); + return -1; } + // PyModule_AddObject() stole a reference to obj: + // Py_DECREF(obj) is not needed here + return 0; + } - The example can also be written without checking explicitly if *obj* is - ``NULL``:: - - static int - add_spam(PyObject *module, int value) - { - PyObject *obj = PyLong_FromLong(value); - if (PyModule_AddObject(module, "spam", obj) < 0) { - Py_XDECREF(obj); - return -1; - } - // PyModule_AddObject() stole a reference to obj: - // Py_DECREF(obj) is not needed here - return 0; + The example can also be written without checking explicitly if *obj* is + ``NULL``:: + + static int + add_spam(PyObject *module, int value) + { + PyObject *obj = PyLong_FromLong(value); + if (PyModule_AddObject(module, "spam", obj) < 0) { + Py_XDECREF(obj); + return -1; } + // PyModule_AddObject() stole a reference to obj: + // Py_DECREF(obj) is not needed here + return 0; + } + + Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in + this case, since *obj* can be ``NULL``. - Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in - this case, since *obj* can be ``NULL``. .. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy