Skip to content
Open
Show file tree
Hide file tree
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
22 changes: 20 additions & 2 deletions Doc/c-api/extension-modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,29 @@ For example, a module called ``spam`` would be defined like this::
The export hook is typically the only non-\ ``static``
item defined in the module's C source.

.. _pymodexport-api-caveats:

The hook should be kept short -- ideally, one line as above.
If you do need to use Python C API in this function, it is recommended to call
``PyABIInfo_Check(&abi_info, "modulename")`` first to raise an exception,
If you need to use any Python C API, it is recommended to call
:c:func:`PyABIInfo_Check` first to raise an exception,
rather than crash, in common cases of ABI mismatch.
Also, note that in :term:`free-threaded <free threading>` builds the export

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a :term:`free-threaded build` term for this.

function may be called without the :term:`GIL` held even if the extension
specifies that the GIL is required.
Comment on lines +110 to +111

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify what this means? "Calling with the GIL held" sounds like the old terminology for calling without an attached thread state. If export functions just need synchronization, then let's just say that.

For example::

PyMODEXPORT_FUNC
PyModExport_modulename(void)
{
if (PyABIInfo_Check(&abi_info, "modulename") < 0) {
/* ABI mismatch. It's not safe to examine the raised exception. */
return NULL;
}

/* use Python API (as little as possible); don't rely on GIL */

return modulename_slots;
}

.. note::

Expand Down
5 changes: 5 additions & 0 deletions Doc/c-api/import.rst
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ Importing Modules

Initialization function for a module built into the interpreter.

Note that the inittab uses "``PyInit``"
:ref:`initialization functions <extension-pyinit>`;
there is currently no way to include "``PyModExport_``"
:ref:`export hooks <extension-export-hook>`.


.. c:function:: int PyImport_ExtendInittab(struct _inittab *newtab)

Expand Down
102 changes: 101 additions & 1 deletion Doc/howto/abi3t-migration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ versions you support.
This will ensure that nothing breaks as you are porting.


.. _abi3t-howto-modexport:

Module export hook
==================

Expand Down Expand Up @@ -290,6 +292,104 @@ and substitute your own values.
See the :c:type:`PySlot` and :c:ref:`export hook <extension-export-hook>`
documentation for details on this API.

As in the example, your ``PyModExport_`` function should *only* return a
pointer to static data.
If you cannot avoid additional code, refer to the
:ref:`caveats in PyModExport documentation <pymodexport-api-caveats>`.


Existing slots
--------------

If you have a ``Py_mod_slots`` slot, check the array it refers to.
It should be a :c:type:`PyModuleDef_Slot` array like the following:

.. code-block::
:class: bad

static PyObject *create_module(PyObject *spec, PyModuleDef *def) { ... }
static int my_first_module_exec(PyObject *module) { ... }
static int my_second_module_exec(PyObject *module) { ... }

static PyModuleDef_Slot my_slots[] = {
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_create, my_module_create},
{Py_mod_exec, my_first_module_exec},
{Py_mod_exec, my_second_module_exec},
{0, NULL}
};

``py_mod_create``
.................


If you have a :c:macro:`Py_mod_create` entry, make sure the function can be
called with ``NULL`` as its second argument (instead of the
:c:type:`PyModuleDef`, which you are removing).
Often, this argument isn't used at all; you can check by renaming it:

.. code-block::
:class: good

static PyObject *create_module(PyObject *spec, PyModuleDef *_unused) { ... }

If the argument is used, find a different way to pass in the data.
Commonly, the information is static and you can refer to it directly.
(If you're reusing a single function for several different modules, consider
defining several functions instead.)


Multiple ``py_mod_exec``
........................

If you have *more than one* :c:macro:`Py_mod_exec` entries, consolidate them:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe "entries" is grammatically incorrect here:

Suggested change
If you have *more than one* :c:macro:`Py_mod_exec` entries, consolidate them:
If you have *more than one* :c:macro:`Py_mod_exec` entry, consolidate them:

create a new function that calls the others, and replace existing slots
with it.

.. code-block::
:class: good

static int my_module_exec(PyObject *module) {
if (my_first_module_exec(module) < 0) return -1;
if (my_second_module_exec(module) < 0) return -1;
}

static PyModuleDef_Slot my_slots[] = {
...
/* (remove other Py_mod_exec slots) */
...
{Py_mod_exec, my_module_exec},
{0, NULL}
};

If the functions aren't used elsewhere, you can combine their bodies instead.


Merging slot arrays
...................

Optionally, when you break compatibility with Python 3.14, you may clean up
the code by moving slots into the :c:type:`PySlot` array, and converting the
definitions to :c:macro:`PySlot_DATA` and :c:macro:`PySlot_FUNC`:

.. code-block::
:class: good

static PySlot my_slot_array[] = {
...
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
PySlot_DATA(Py_mod_multiple_interpreters,
Py_MOD_PER_INTERPRETER_GIL_SUPPORTED)
PySlot_FUNC(Py_mod_create, my_module_create),
PySlot_FUNC(Py_mod_exec, my_module_exec),
PySlot_END
};

If you do this, delete the original :c:type:`PyModuleDef_Slot` array and
its ``Py_mod_slots`` entry.


Associated ``PyModuleDef``
--------------------------

Expand Down Expand Up @@ -483,7 +583,7 @@ For example, if a user makes a subclass like this:
class Sub(YourCustomClass):
__slots__ = ('a', 'b')

then ``Py_TYPE(obj)`` is ``YourCustomClass``, and the underlying memory may
then ``Py_TYPE(obj)`` is ``Sub``, and the underlying memory may
look like this:

.. code-block:: text
Expand Down
Loading