Skip to content
65 changes: 31 additions & 34 deletions Doc/c-api/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -479,12 +479,35 @@ state:
.. versionadded:: 3.10


.. c:function:: int PyModule_Add(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 ``-1`` if *value* is ``NULL``. It must be called with an exception
raised in this case.

This function "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.10


.. 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_Add`, but only steals a reference to
*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` function is recommended, since it is
easy to introduce reference leaks by misusing the
:c:func:`PyModule_AddObject` function.

Expand All @@ -494,41 +517,15 @@ 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.
:c:func:`Py_XDECREF` *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;
}
PyObject *obj = PyBytes_FromString(value);
if (PyModule_AddObject(module, "spam", obj) < 0) {
Py_XDECREF(obj);
goto error;
}

Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in
this case, since *obj* can be ``NULL``.
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,10 @@ New Features
success.
(Contributed by Victor Stinner in :issue:`1635741`.)

* Added :c:func:`PyModule_Add` function: similar to
:c:func:`PyModule_AddObject` but always steals a reference to the value.
(Contributed by Serhiy Storchaka in :issue:`42327`.)

* Added :c:func:`Py_NewRef` and :c:func:`Py_XNewRef` functions to increment the
reference count of an object and return the object.
(Contributed by Victor Stinner in :issue:`42262`.)
Expand Down
11 changes: 9 additions & 2 deletions Include/modsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,20 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords(
void _PyArg_Fini(void);
#endif /* Py_LIMITED_API */

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
// Add an attribute with name 'name' and value 'value' to the module 'mod'.
// Steal a reference to 'value'.
// On success, return 0.
// On error, raise an exception and return -1.
PyAPI_FUNC(int) PyModule_Add(PyObject *mod, const char *name, PyObject *value);
#endif /* Py_LIMITED_API */

// 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).
// Similar to PyModule_Add() but steal a reference to 'value' only on success.
PyAPI_FUNC(int) PyModule_AddObject(PyObject *mod, const char *, PyObject *value);

PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added :func:`PyModule_Add`.
26 changes: 14 additions & 12 deletions Modules/_csv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1642,36 +1642,38 @@ PyInit__csv(void)
/* Add version to the module. */
if (PyModule_AddStringConstant(module, "__version__",
MODULE_VERSION) == -1)
return NULL;
goto error;

/* Set the field limit */
get_csv_state(module)->field_limit = 128 * 1024;
/* Do I still need to add this var to the Module Dict? */

/* Add _dialects dictionary */
get_csv_state(module)->dialects = PyDict_New();
if (get_csv_state(module)->dialects == NULL)
return NULL;
Py_INCREF(get_csv_state(module)->dialects);
if (PyModule_AddObject(module, "_dialects", get_csv_state(module)->dialects))
return NULL;
Py_XINCREF(get_csv_state(module)->dialects);
if (PyModule_Add(module, "_dialects", get_csv_state(module)->dialects))
goto error;

/* Add quote styles into dictionary */
for (style = quote_styles; style->name; style++) {
if (PyModule_AddIntConstant(module, style->name,
style->style) == -1)
return NULL;
goto error;
}

if (PyModule_AddType(module, &Dialect_Type)) {
return NULL;
goto error;
}

/* Add the CSV exception object to the module. */
get_csv_state(module)->error_obj = PyErr_NewException("_csv.Error", NULL, NULL);
if (get_csv_state(module)->error_obj == NULL)
return NULL;
Py_INCREF(get_csv_state(module)->error_obj);
PyModule_AddObject(module, "Error", get_csv_state(module)->error_obj);
Py_XINCREF(get_csv_state(module)->error_obj);
if (PyModule_Add(module, "Error", get_csv_state(module)->error_obj) < 0) {
goto error;
}
return module;

error:
Py_DECREF(module);
return NULL;
}
5 changes: 2 additions & 3 deletions Modules/_curses_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -668,9 +668,8 @@ _curses_panel_exec(PyObject *mod)
state->PyCursesError = PyErr_NewException(
"_curses_panel.error", NULL, NULL);

Py_INCREF(state->PyCursesError);
if (PyModule_AddObject(mod, "error", state->PyCursesError) < 0) {
Py_DECREF(state->PyCursesError);
Py_XINCREF(state->PyCursesError);
if (PyModule_Add(mod, "error", state->PyCursesError) < 0) {
return -1;
}

Expand Down
36 changes: 19 additions & 17 deletions Modules/_elementtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -4410,42 +4410,40 @@ PyInit__elementtree(void)
st = get_elementtree_state(m);

if (!(temp = PyImport_ImportModule("copy")))
return NULL;
goto error;
st->deepcopy_obj = PyObject_GetAttrString(temp, "deepcopy");
Py_XDECREF(temp);

if (st->deepcopy_obj == NULL) {
return NULL;
goto error;
}

assert(!PyErr_Occurred());
if (!(st->elementpath_obj = PyImport_ImportModule("xml.etree.ElementPath")))
return NULL;
goto error;

/* link against pyexpat */
expat_capi = PyCapsule_Import(PyExpat_CAPSULE_NAME, 0);
if (expat_capi) {
/* check that it's usable */
if (strcmp(expat_capi->magic, PyExpat_CAPI_MAGIC) != 0 ||
if (!expat_capi) {
goto error;
}
/* check that it's usable */
if (strcmp(expat_capi->magic, PyExpat_CAPI_MAGIC) != 0 ||
(size_t)expat_capi->size < sizeof(struct PyExpat_CAPI) ||
expat_capi->MAJOR_VERSION != XML_MAJOR_VERSION ||
expat_capi->MINOR_VERSION != XML_MINOR_VERSION ||
expat_capi->MICRO_VERSION != XML_MICRO_VERSION) {
PyErr_SetString(PyExc_ImportError,
"pyexpat version is incompatible");
return NULL;
}
} else {
return NULL;
PyErr_SetString(PyExc_ImportError,
"pyexpat version is incompatible");
goto error;
}

st->parseerror_obj = PyErr_NewException(
"xml.etree.ElementTree.ParseError", PyExc_SyntaxError, NULL
);
Py_INCREF(st->parseerror_obj);
if (PyModule_AddObject(m, "ParseError", st->parseerror_obj) < 0) {
Py_DECREF(st->parseerror_obj);
return NULL;
Py_XINCREF(st->parseerror_obj);
if (PyModule_Add(m, "ParseError", st->parseerror_obj) < 0) {
goto error;
}

PyTypeObject *types[] = {
Expand All @@ -4456,9 +4454,13 @@ PyInit__elementtree(void)

for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) {
if (PyModule_AddType(m, types[i]) < 0) {
return NULL;
goto error;
}
}

return m;

error:
Py_DECREF(m);
return NULL;
}
12 changes: 5 additions & 7 deletions Modules/_io/_iomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -668,17 +668,15 @@ PyInit__io(void)
state->unsupported_operation = PyObject_CallFunction(
(PyObject *)&PyType_Type, "s(OO){}",
"UnsupportedOperation", PyExc_OSError, PyExc_ValueError);
if (state->unsupported_operation == NULL)
goto fail;
Py_INCREF(state->unsupported_operation);
if (PyModule_AddObject(m, "UnsupportedOperation",
state->unsupported_operation) < 0)
Py_XINCREF(state->unsupported_operation);
if (PyModule_Add(m, "UnsupportedOperation",
state->unsupported_operation) < 0)
goto fail;

/* BlockingIOError, for compatibility */
Py_INCREF(PyExc_BlockingIOError);
if (PyModule_AddObject(m, "BlockingIOError",
(PyObject *) PyExc_BlockingIOError) < 0)
if (PyModule_Add(m, "BlockingIOError",
(PyObject *) PyExc_BlockingIOError) < 0)
goto fail;

/* Concrete base types of the IO ABCs.
Expand Down
39 changes: 19 additions & 20 deletions Modules/_pickle.c
Original file line number Diff line number Diff line change
Expand Up @@ -7973,42 +7973,41 @@ PyInit__pickle(void)

/* Add types */
if (PyModule_AddType(m, &Pickler_Type) < 0) {
return NULL;
goto error;
}
if (PyModule_AddType(m, &Unpickler_Type) < 0) {
return NULL;
goto error;
}
if (PyModule_AddType(m, &PyPickleBuffer_Type) < 0) {
return NULL;
goto error;
}

st = _Pickle_GetState(m);

/* Initialize the exceptions. */
st->PickleError = PyErr_NewException("_pickle.PickleError", NULL, NULL);
if (st->PickleError == NULL)
return NULL;
Py_XINCREF(st->PickleError);
if (PyModule_Add(m, "PickleError", st->PickleError) < 0)
goto error;

st->PicklingError = \
PyErr_NewException("_pickle.PicklingError", st->PickleError, NULL);
if (st->PicklingError == NULL)
return NULL;
Py_XINCREF(st->PicklingError);
if (PyModule_Add(m, "PicklingError", st->PicklingError) < 0)
goto error;

st->UnpicklingError = \
PyErr_NewException("_pickle.UnpicklingError", st->PickleError, NULL);
if (st->UnpicklingError == NULL)
return NULL;

Py_INCREF(st->PickleError);
if (PyModule_AddObject(m, "PickleError", st->PickleError) < 0)
return NULL;
Py_INCREF(st->PicklingError);
if (PyModule_AddObject(m, "PicklingError", st->PicklingError) < 0)
return NULL;
Py_INCREF(st->UnpicklingError);
if (PyModule_AddObject(m, "UnpicklingError", st->UnpicklingError) < 0)
return NULL;
Py_XINCREF(st->UnpicklingError);
if (PyModule_Add(m, "UnpicklingError", st->UnpicklingError) < 0)
goto error;

if (_Pickle_InitState(st) < 0)
return NULL;
goto error;

return m;

error:
Py_DECREF(m);
return NULL;
}
Loading