bpo-1635741: Add PyModule_AddObjectRef() function (GH-23122)

Added PyModule_AddObjectRef() function: similar to
PyModule_AddObjectRef() but don't steal a reference to the value on
success.
This commit is contained in:
Victor Stinner 2020-11-04 13:59:15 +01:00 committed by GitHub
parent 3529718925
commit 8021875bbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 145 additions and 45 deletions

View File

@ -264,7 +264,7 @@ of the following two module creation functions:
instead; only use this if you are sure you need it. instead; only use this if you are sure you need it.
Before it is returned from in the initialization function, the resulting module 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: .. _multi-phase-initialization:
@ -437,26 +437,102 @@ a function called from a module execution slot (if using multi-phase
initialization), can use the following functions to help initialize the module initialization), can use the following functions to help initialize the module
state: 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) .. 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 Similar to :c:func:`PyModule_AddObjectRef`, but steals a reference to
be used from the module's initialization function. This steals a reference to *value* on success (if it returns ``0``).
*value* on success. Return ``-1`` on error, ``0`` on success.
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:: .. note::
Unlike other functions that steal references, ``PyModule_AddObject()`` only Unlike other functions that steal references, ``PyModule_AddObject()``
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. Example usage:: :c:func:`Py_DECREF` *value* manually on error.
Py_INCREF(spam); Example usage::
if (PyModule_AddObject(module, "spam", spam) < 0) {
Py_DECREF(module); static int
Py_DECREF(spam); add_spam(PyObject *module, int value)
return NULL; {
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) .. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value)

View File

@ -374,6 +374,11 @@ New Features
* Added :c:func:`PyUnicode_AsUTF8AndSize` to the limited C API. * Added :c:func:`PyUnicode_AsUTF8AndSize` to the limited C API.
(Contributed by Alex Gaynor in :issue:`41784`.) (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 Porting to Python 3.10
---------------------- ----------------------

View File

@ -136,7 +136,15 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords(
void _PyArg_Fini(void); void _PyArg_Fini(void);
#endif /* Py_LIMITED_API */ #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_AddIntConstant(PyObject *, const char *, long);
PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char *); PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char *);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000

View File

@ -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.

View File

@ -634,56 +634,70 @@ va_build_stack(PyObject **small_stack, Py_ssize_t small_stack_len,
int int
PyModule_AddObject(PyObject *m, const char *name, PyObject *o) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
{ {
PyObject *dict; if (!PyModule_Check(mod)) {
if (!PyModule_Check(m)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"PyModule_AddObject() needs module as first arg"); "PyModule_AddObjectRef() first argument "
"must be a module");
return -1; return -1;
} }
if (!o) { if (!value) {
if (!PyErr_Occurred()) if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_SystemError,
"PyModule_AddObject() needs non-NULL value"); "PyModule_AddObjectRef() must be called "
"with an exception raised if value is NULL");
}
return -1; return -1;
} }
dict = PyModule_GetDict(m); PyObject *dict = PyModule_GetDict(mod);
if (dict == NULL) { if (dict == NULL) {
/* Internal error -- modules must have a dict! */ /* Internal error -- modules must have a dict! */
PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
PyModule_GetName(m)); PyModule_GetName(mod));
return -1; return -1;
} }
if (PyDict_SetItemString(dict, name, o))
if (PyDict_SetItemString(dict, name, value)) {
return -1; return -1;
Py_DECREF(o); }
return 0; 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 int
PyModule_AddIntConstant(PyObject *m, const char *name, long value) PyModule_AddIntConstant(PyObject *m, const char *name, long value)
{ {
PyObject *o = PyLong_FromLong(value); PyObject *obj = PyLong_FromLong(value);
if (!o) if (!obj) {
return -1;
if (PyModule_AddObject(m, name, o) == 0)
return 0;
Py_DECREF(o);
return -1; 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 *o = PyUnicode_FromString(value); PyObject *obj = PyUnicode_FromString(value);
if (!o) if (!obj) {
return -1;
if (PyModule_AddObject(m, name, o) == 0)
return 0;
Py_DECREF(o);
return -1; return -1;
}
int res = PyModule_AddObjectRef(m, name, obj);
Py_DECREF(obj);
return res;
} }
int int
@ -696,11 +710,5 @@ PyModule_AddType(PyObject *module, PyTypeObject *type)
const char *name = _PyType_Name(type); const char *name = _PyType_Name(type);
assert(name != NULL); assert(name != NULL);
Py_INCREF(type); return PyModule_AddObjectRef(module, name, (PyObject *)type);
if (PyModule_AddObject(module, name, (PyObject *)type) < 0) {
Py_DECREF(type);
return -1;
}
return 0;
} }