Skip to content

gh-134160: Block multiple module initialization #134773

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 4 commits into from
May 28, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions Doc/extending/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -204,17 +204,32 @@ value must be in a particular range or must satisfy other conditions,
:c:data:`PyExc_ValueError` is appropriate.

You can also define a new exception that is unique to your module.
For this, you can declare a static global object variable at the beginning
of the file::
The simplest way to do this is to declare a static global object variable at
the beginning of the file::

static PyObject *SpamError;
static PyObject *SpamError = NULL;

and initialize it with an exception object in the module's
and initialize it by calling :c:func:`PyErr_NewException` in the module's
:c:data:`Py_mod_exec` function (:c:func:`!spam_module_exec`)::

SpamError = PyErr_NewException("spam.error", NULL, NULL);

Since :c:data:`!SpamError` is a global variable, it will be overwitten every time
the module is reinitialized, when the :c:data:`Py_mod_exec` function is called.

For now, let's avoid the issue: we will block repeated initialization by raising an
:py:exc:`ImportError`::

static PyObject *SpamError = NULL;

static int
spam_module_exec(PyObject *m)
{
if (SpamError != NULL) {
PyErr_SetString(PyExc_ImportError,
"cannot initialize spam module more than once");
return -1;
}
SpamError = PyErr_NewException("spam.error", NULL, NULL);
if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0) {
return -1;
Expand Down Expand Up @@ -253,6 +268,11 @@ needed to ensure that it will not be discarded, causing :c:data:`!SpamError` to
become a dangling pointer. Should it become a dangling pointer, C code which
raises the exception could cause a core dump or other unintended side effects.

For now, the :c:func:`Py_DECREF` call to remove this reference is missing.
Even when the Python interpreter shuts down, the global :c:data:`!SpamError`
variable will not be garbage-collected. It will "leak".
We did, however, ensure that this will happen at most once per process.

We discuss the use of :c:macro:`PyMODINIT_FUNC` as a function return type later in this
sample.

Expand Down
Loading