gh-140550: Docs additions & fixups for PEP 793 (GH-151661)

Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
This commit is contained in:
Petr Viktorin
2026-06-24 13:15:25 +02:00
committed by GitHub
parent 21c4b7359d
commit 763cc2209d
3 changed files with 134 additions and 5 deletions
+28 -4
View File
@@ -100,11 +100,35 @@ 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.
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,
rather than crash, in common cases of ABI mismatch.
.. _pymodexport-api-caveats:
The hook should be kept short.
If it does more than ``return`` a static array, several caveats apply:
- 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.
- Code in the export hook must never rely on the :term:`GIL`:
:term:`free-threaded builds <free-threaded build>` of Python can only check
the :c:macro:`Py_mod_gil` slot (or the lack of it) after the hook returns,
- Similarly, the hook may be called in any subinterpreter, since the
:c:macro:`Py_mod_multiple_interpreters` slot (or lack of it)
is only checked after the hook returns.
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::
+5
View File
@@ -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)
+101 -1
View File
@@ -210,6 +210,8 @@ versions you support.
This will ensure that nothing breaks as you are porting.
.. _abi3t-howto-modexport:
Module export hook
==================
@@ -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` 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``
--------------------------
@@ -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