bpo-42327: C API: Add PyModule_Add() function (GH-23443)

It is a fixed implementation of PyModule_AddObject() which consistently
steals reference both on success and on failure.
This commit is contained in:
Serhiy Storchaka 2023-07-18 09:42:05 +03:00 committed by GitHub
parent 745492355b
commit 83ac128490
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 61 additions and 56 deletions

View file

@ -486,12 +486,29 @@ state:
.. versionadded:: 3.10 .. versionadded:: 3.10
.. c:function:: int PyModule_Add(PyObject *module, const char *name, PyObject *value)
Similar to :c:func:`PyModule_AddObjectRef`, but "steals" a reference
to *value*.
It can be called with a result of function that returns a new reference
without bothering to check its result or even saving it to a variable.
Example usage::
if (PyModule_Add(module, "spam", PyBytes_FromString(value)) < 0) {
goto error;
}
.. versionadded:: 3.13
.. c:function:: int PyModule_AddObject(PyObject *module, const char *name, PyObject *value) .. c:function:: int PyModule_AddObject(PyObject *module, const char *name, PyObject *value)
Similar to :c:func:`PyModule_AddObjectRef`, but steals a reference to Similar to :c:func:`PyModule_AddObjectRef`, but steals a reference to
*value* on success (if it returns ``0``). *value* on success (if it returns ``0``).
The new :c:func:`PyModule_AddObjectRef` function is recommended, since it is The new :c:func:`PyModule_Add` or :c:func:`PyModule_AddObjectRef`
functions are recommended, since it is
easy to introduce reference leaks by misusing the easy to introduce reference leaks by misusing the
:c:func:`PyModule_AddObject` function. :c:func:`PyModule_AddObject` function.
@ -501,44 +518,24 @@ state:
only decrements the reference count of *value* **on success**. only decrements the reference count of *value* **on success**.
This means that its return value must be checked, and calling code must This means that its return value must be checked, and calling code must
:c:func:`Py_DECREF` *value* manually on error. :c:func:`Py_XDECREF` *value* manually on error.
Example usage:: Example usage::
static int PyObject *obj = PyBytes_FromString(value);
add_spam(PyObject *module, int value) if (PyModule_AddObject(module, "spam", obj) < 0) {
{ // If 'obj' is not NULL and PyModule_AddObject() failed,
PyObject *obj = PyLong_FromLong(value); // 'obj' strong reference must be deleted with Py_XDECREF().
if (obj == NULL) { // If 'obj' is NULL, Py_XDECREF() does nothing.
return -1; Py_XDECREF(obj);
} goto error;
if (PyModule_AddObject(module, "spam", obj) < 0) { }
Py_DECREF(obj); // PyModule_AddObject() stole a reference to obj:
return -1; // Py_XDECREF(obj) is not needed here.
}
// 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 .. deprecated:: 3.13
``NULL``::
static int :c:func:`PyModule_AddObject` is :term:`soft deprecated`.
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) .. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value)

View file

@ -399,6 +399,7 @@ type,PyModuleDef,3.2,,full-abi
type,PyModuleDef_Base,3.2,,full-abi type,PyModuleDef_Base,3.2,,full-abi
function,PyModuleDef_Init,3.5,, function,PyModuleDef_Init,3.5,,
var,PyModuleDef_Type,3.5,, var,PyModuleDef_Type,3.5,,
function,PyModule_Add,3.13,,
function,PyModule_AddFunctions,3.7,, function,PyModule_AddFunctions,3.7,,
function,PyModule_AddIntConstant,3.2,, function,PyModule_AddIntConstant,3.2,,
function,PyModule_AddObject,3.2,, function,PyModule_AddObject,3.2,,

View file

@ -774,6 +774,11 @@ New Features
If the assertion fails, make sure that the size is set before. If the assertion fails, make sure that the size is set before.
(Contributed by Victor Stinner in :gh:`106168`.) (Contributed by Victor Stinner in :gh:`106168`.)
* Add :c:func:`PyModule_Add` function: similar to
:c:func:`PyModule_AddObjectRef` and :c:func:`PyModule_AddObject` but
always steals a reference to the value.
(Contributed by Serhiy Storchaka in :gh:`86493`.)
Porting to Python 3.13 Porting to Python 3.13
---------------------- ----------------------

View file

@ -23,12 +23,18 @@ PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list); PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list);
// Add an attribute with name 'name' and value 'obj' to the module 'mod. // Add an attribute with name 'name' and value 'obj' to the module 'mod.
// On success, return 0 on success. // On success, return 0.
// On error, raise an exception and return -1. // On error, raise an exception and return -1.
PyAPI_FUNC(int) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value); PyAPI_FUNC(int) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value);
// Similar to PyModule_AddObjectRef() but steal a reference to 'obj' #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
// (Py_DECREF(obj)) on success (if it returns 0). // Similar to PyModule_AddObjectRef() but steal a reference to 'value'.
PyAPI_FUNC(int) PyModule_Add(PyObject *mod, const char *name, PyObject *value);
#endif /* Py_LIMITED_API */
// Similar to PyModule_AddObjectRef() and PyModule_Add() but steal
// a reference to 'value' on success and only on success.
// Errorprone. Should not be used in new code.
PyAPI_FUNC(int) PyModule_AddObject(PyObject *mod, const char *, PyObject *value); PyAPI_FUNC(int) PyModule_AddObject(PyObject *mod, const char *, PyObject *value);
PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long); PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long);

View file

@ -425,6 +425,7 @@ SYMBOL_NAMES = (
"PyMethodDescr_Type", "PyMethodDescr_Type",
"PyModuleDef_Init", "PyModuleDef_Init",
"PyModuleDef_Type", "PyModuleDef_Type",
"PyModule_Add",
"PyModule_AddFunctions", "PyModule_AddFunctions",
"PyModule_AddIntConstant", "PyModule_AddIntConstant",
"PyModule_AddObject", "PyModule_AddObject",

View file

@ -0,0 +1 @@
Add :func:`PyModule_Add` function: similar to :c:func:`PyModule_AddObjectRef` and :c:func:`PyModule_AddObject`, but always steals a reference to the value.

View file

@ -2444,3 +2444,5 @@
added = '3.13' added = '3.13'
[function.PyMapping_GetOptionalItemString] [function.PyMapping_GetOptionalItemString]
added = '3.13' added = '3.13'
[function.PyModule_Add]
added = '3.13'

1
PC/python3dll.c generated
View file

@ -374,6 +374,7 @@ EXPORT_FUNC(PyMemoryView_FromBuffer)
EXPORT_FUNC(PyMemoryView_FromMemory) EXPORT_FUNC(PyMemoryView_FromMemory)
EXPORT_FUNC(PyMemoryView_FromObject) EXPORT_FUNC(PyMemoryView_FromObject)
EXPORT_FUNC(PyMemoryView_GetContiguous) EXPORT_FUNC(PyMemoryView_GetContiguous)
EXPORT_FUNC(PyModule_Add)
EXPORT_FUNC(PyModule_AddFunctions) EXPORT_FUNC(PyModule_AddFunctions)
EXPORT_FUNC(PyModule_AddIntConstant) EXPORT_FUNC(PyModule_AddIntConstant)
EXPORT_FUNC(PyModule_AddObject) EXPORT_FUNC(PyModule_AddObject)

View file

@ -606,13 +606,16 @@ PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
PyModule_GetName(mod)); PyModule_GetName(mod));
return -1; return -1;
} }
return PyDict_SetItemString(dict, name, value);
if (PyDict_SetItemString(dict, name, value)) {
return -1;
}
return 0;
} }
int
PyModule_Add(PyObject *mod, const char *name, PyObject *value)
{
int res = PyModule_AddObjectRef(mod, name, value);
Py_XDECREF(value);
return res;
}
int int
PyModule_AddObject(PyObject *mod, const char *name, PyObject *value) PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
@ -627,25 +630,13 @@ PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
int int
PyModule_AddIntConstant(PyObject *m, const char *name, long value) PyModule_AddIntConstant(PyObject *m, const char *name, long value)
{ {
PyObject *obj = PyLong_FromLong(value); return PyModule_Add(m, name, PyLong_FromLong(value));
if (!obj) {
return -1;
}
int res = PyModule_AddObjectRef(m, name, obj);
Py_DECREF(obj);
return res;
} }
int int
PyModule_AddStringConstant(PyObject *m, const char *name, const char *value) PyModule_AddStringConstant(PyObject *m, const char *name, const char *value)
{ {
PyObject *obj = PyUnicode_FromString(value); return PyModule_Add(m, name, PyUnicode_FromString(value));
if (!obj) {
return -1;
}
int res = PyModule_AddObjectRef(m, name, obj);
Py_DECREF(obj);
return res;
} }
int int