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::
This will ensure that nothing breaks as you are porting.
+.. _abi3t-howto-modexport:
+
Module export hook
==================
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``
--------------------------
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