Skip to content

bpo-42327: Add PyModule_Add() (smaller). #23443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 18, 2023
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;
}
12 changes: 6 additions & 6 deletions Modules/_stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,16 +592,16 @@ stat_exec(PyObject *module)
ADD_INT_MACRO(module, FILE_ATTRIBUTE_TEMPORARY);
ADD_INT_MACRO(module, FILE_ATTRIBUTE_VIRTUAL);

if (PyModule_AddObject(module, "IO_REPARSE_TAG_SYMLINK",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_SYMLINK)) < 0) {
if (PyModule_Add(module, "IO_REPARSE_TAG_SYMLINK",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_SYMLINK)) < 0) {
return -1;
}
if (PyModule_AddObject(module, "IO_REPARSE_TAG_MOUNT_POINT",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_MOUNT_POINT)) < 0) {
if (PyModule_Add(module, "IO_REPARSE_TAG_MOUNT_POINT",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_MOUNT_POINT)) < 0) {
return -1;
}
if (PyModule_AddObject(module, "IO_REPARSE_TAG_APPEXECLINK",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_APPEXECLINK)) < 0) {
if (PyModule_Add(module, "IO_REPARSE_TAG_APPEXECLINK",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_APPEXECLINK)) < 0) {
return -1;
}
#endif
Expand Down
4 changes: 2 additions & 2 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ PyInit__testinternalcapi(void)
return NULL;
}

if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD",
PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
if (PyModule_Add(module, "SIZEOF_PYGC_HEAD",
PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
goto error;
}

Expand Down
Loading