From d7df7455d1d2d7cb2f482963c7635f1f642df8d3 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 17 Jun 2026 18:12:39 +0200 Subject: [PATCH 1/3] Update abi3t migration guide's slot-related advice This integrates content from PEP 793's Porting Guide section. --- Doc/howto/abi3t-migration.rst | 94 +++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/Doc/howto/abi3t-migration.rst b/Doc/howto/abi3t-migration.rst index ed7a324c4af6f0a..3a1b0209aba375b 100644 --- a/Doc/howto/abi3t-migration.rst +++ b/Doc/howto/abi3t-migration.rst @@ -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,98 @@ and substitute your own values. See the :c:type:`PySlot` and :c:ref:`export hook ` documentation for details on this API. +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: +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`` -------------------------- From cb7593c113987c31d2db588a8d98efad399fdf11 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 18 Jun 2026 16:15:17 +0200 Subject: [PATCH 2/3] Correct class name in PyTYPE discussion --- Doc/howto/abi3t-migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/abi3t-migration.rst b/Doc/howto/abi3t-migration.rst index 3a1b0209aba375b..7306144467323a4 100644 --- a/Doc/howto/abi3t-migration.rst +++ b/Doc/howto/abi3t-migration.rst @@ -577,7 +577,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 From 686adffbff54fdf5d995d6b804a50a13f0e58211 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 18 Jun 2026 16:46:23 +0200 Subject: [PATCH 3/3] Inittab note & caveats for code in the export function --- Doc/c-api/extension-modules.rst | 22 ++++++++++++++++++++-- Doc/c-api/import.rst | 5 +++++ Doc/howto/abi3t-migration.rst | 6 ++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/extension-modules.rst b/Doc/c-api/extension-modules.rst index 34ee86c7876ae74..8bc8764d31b1bb8 100644 --- a/Doc/c-api/extension-modules.rst +++ b/Doc/c-api/extension-modules.rst @@ -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 ` builds the export +function may be called without the :term:`GIL` held even if the extension +specifies that the GIL is required. +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:: diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index ec9462931d56c2c..b48cf951137e511 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -304,6 +304,11 @@ Importing Modules Initialization function for a module built into the interpreter. + Note that the inittab uses "``PyInit``" + :ref:`initialization functions `; + there is currently no way to include "``PyModExport_``" + :ref:`export hooks `. + .. c:function:: int PyImport_ExtendInittab(struct _inittab *newtab) diff --git a/Doc/howto/abi3t-migration.rst b/Doc/howto/abi3t-migration.rst index 7306144467323a4..de6419c400fa0b3 100644 --- a/Doc/howto/abi3t-migration.rst +++ b/Doc/howto/abi3t-migration.rst @@ -292,6 +292,12 @@ and substitute your own values. See the :c:type:`PySlot` and :c:ref:`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 `. + + Existing slots --------------