From 8e203ebb5730b0f7cb424575aa5f767dd88f8f36 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 31 Mar 2026 18:10:55 +0200 Subject: [PATCH 1/8] abi3t docs --- Doc/c-api/stable.rst | 229 ++++++++++++++++---------- Doc/tools/extensions/c_annotations.py | 26 +++ Doc/whatsnew/3.15.rst | 26 +++ 3 files changed, 197 insertions(+), 84 deletions(-) diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index f8b41f6d87f975..1ded2fc4bbb963 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -51,123 +51,182 @@ It is generally intended for specialized, low-level tools like debuggers. Projects that use this API are expected to follow CPython development and spend extra effort adjusting to changes. +.. _stable-abi: .. _stable-application-binary-interface: -Stable Application Binary Interface -=================================== +Stable Application Binary Interfaces +==================================== -For simplicity, this document talks about *extensions*, but the Limited API -and Stable ABI work the same way for all uses of the API – for example, -embedding Python. +Python's :dfn:`Stable ABI` allows extensions to be compatible with multiple +versions of Python, without recompilation. -.. _limited-c-api: +.. note:: -Limited C API -------------- + For simplicity, this document talks about *extensions*, but the Stable ABI + works the same way for all uses of the API – for example, embedding Python. -Python 3.2 introduced the *Limited API*, a subset of Python's C API. -Extensions that only use the Limited API can be -compiled once and be loaded on multiple versions of Python. -Contents of the Limited API are :ref:`listed below `. +A Stable ABI is *versioned* using the first two numbers of the Python version. +For example, Stable ABI 3.14 corresponds to Python 3.14. +An extension compiled for Stable ABI 3.x is ABI-compatible with Python 3.x +and above. -.. c:macro:: Py_LIMITED_API +There are two Stable ABIs: - Define this macro before including ``Python.h`` to opt in to only use - the Limited API, and to select the Limited API version. +- ``abi3``, introduced in Pyton 3.2, is compatible with + non-:term:`free threaded ` builds of CPython. - Define ``Py_LIMITED_API`` to the value of :c:macro:`PY_VERSION_HEX` - corresponding to the lowest Python version your extension supports. - The extension will be ABI-compatible with all Python 3 releases - from the specified one onward, and can use Limited API introduced up to that - version. +- ``abi3t``, introduced in Pyton 3.15, is compatible with + :term:`free threaded ` builds of CPython. + It has stricter API limitations than ``abi3``. - Rather than using the ``PY_VERSION_HEX`` macro directly, hardcode a minimum - minor version (e.g. ``0x030A0000`` for Python 3.10) for stability when - compiling with future Python versions. + .. versionadded:: next - You can also define ``Py_LIMITED_API`` to ``3``. This works the same as - ``0x03020000`` (Python 3.2, the version that introduced Limited API). + ``abi3t`` was added in :pep:`803` -.. c:macro:: Py_TARGET_ABI3T +It is possible for an extension to be compiled for *both* ``abi3`` and +``abi3t`` at the same time. +Currently, this has no downsides compared to compiling for ``abi3t`` only. - Define this macro before including ``Python.h`` to opt in to only use - the Limited API for :term:`free-threaded builds `, - and to select the Limited API version. +The Stable ABIs come with several caveats: - .. seealso:: :pep:`803` +- Extensions that target a stable ABI must only use a limited subset of + the C API. This subset is known as the :dfn:`Limited API`; its contents + are :ref:`listed below `. - .. versionadded:: next +- Compiling for a Stable ABI will disable some optimizations. + In particular, common functions cannot be inlined to take advantage of the + internal implementation details. +- Stable ABI prevents *ABI* issues, like linker errors due to missing + symbols or data corruption due to changes in structure layouts or function + signatures. + However, other changes in Python can change the *behavior* of extensions. + See Python's Backwards Compatibility Policy (:pep:`387`) for details. -.. _stable-abi: +On Windows, extensions that use a Stable ABI should be linked against +``python3.dll`` rather than a version-specific library such as +``python39.dll``. +This library only exposes the relevant symbols. + +On some platforms, Python will look for and load shared library files named +with the ``abi3`` or ``abi3t`` tag (e.g. ``mymodule.abi3.so``). +:term:`Free threaded ` interpreters only recognize the +``abi3t`` tag, while non-free threaded ones will prefer ``abi3`` but fall back +to ``abi3t``. +Thus, extensions compatible with both flavors should use the ``abi3t`` tag. + +Python does not check if such extensions conform to a Stable ABI. +Extension authors are encouraged to check using the :c:macro:`Py_mod_abi` +slot or the :c:func:`PyABIInfo_Check` function, but, the user +(or their packaging tool) is ultimately responsible for ensuring that, +for example, extensions built for Stable ABI 3.10 are not installed for lower +versions of Python. + +All functions in the Stable ABI are present as functions in Python's shared +library, not solely as macros. +They are usable from languages that don't use the C preprocessor, +such as Python via :py:mod:`ctypes`. -Stable ABI ----------- -To enable this, Python provides a *Stable ABI*: a set of symbols that will -remain ABI-compatible across Python 3.x versions. +Compiling for Stable ABI +------------------------ .. note:: - The Stable ABI prevents ABI issues, like linker errors due to missing - symbols or data corruption due to changes in structure layouts or function - signatures. - However, other changes in Python can change the *behavior* of extensions. - See Python's Backwards Compatibility Policy (:pep:`387`) for details. + When using a build tool (for example, ``setuptools``), the tool is + generally responsible for setting macros and synchronizing them with + extension filenames and other metadata. + Prefer using the tool's options over defining the macros manually. -The Stable ABI contains symbols exposed in the :ref:`Limited API -`, but also other ones – for example, functions necessary to -support older versions of the Limited API. + The rest of this section is relevant for tool authors, and for people who + compile extensions manually. -On Windows, extensions that use the Stable ABI should be linked against -``python3.dll`` rather than a version-specific library such as -``python39.dll``. + .. seealso:: `list of recommended tools`_ in the Python Packaging User Guide -On some platforms, Python will look for and load shared library files named -with the ``abi3`` tag (e.g. ``mymodule.abi3.so``). -It does not check if such extensions conform to a Stable ABI. -The user (or their packaging tools) need to ensure that, for example, -extensions built with the 3.10+ Limited API are not installed for lower -versions of Python. + .. _list of recommended tools: https://packaging.python.org/en/latest/guides/tool-recommendations/#build-backends-for-extension-modules -All functions in the Stable ABI are present as functions in Python's shared -library, not solely as macros. This makes them usable from languages that don't -use the C preprocessor. +To compile for a Stable ABI, define one or both of the following macros +before including ``Python.h`` to the lowest Python version your extension +should support, in :c:macro:`Py_PACK_VERSION` format. +Typically, you should choose a specific value rather than the version of +the Python headers you are compiling against. +Since the :c:macro:`Py_PACK_VERSION` is not available before including +``Python.h``, you will need to use the number directly. +For reference, the values for a few Python versions are: -Limited API Scope and Performance ---------------------------------- +.. version-hex-cheatsheet:: -The goal for the Limited API is to allow everything that is possible with the +When the macro(s) are defined, ``Python.h`` will only expose API that is +compatible with the given Stable ABI -- that is, the +:ref:`Limited API ` plus some definitions that need to be +visible to the compiler but should not be used directly. + +.. c:macro:: Py_LIMITED_API + + Target ``abi3``, that is, + non-:term:`free threaded ` builds of CPython. + +.. c:macro:: Py_TARGET_ABI3T + + Target ``abi3t``, that is, + :term:`free threaded ` builds of CPython. + + .. versionadded:: next + +Despite the different naming, the macros are similar; +the name :c:macro:`!Py_LIMITED_API` is kept for backwards compatibility. + +.. admonition:: Historical note + + You can also define ``Py_LIMITED_API`` as ``3``. This works the same as + ``0x03020000`` (Python 3.2, the version that introduced Stable ABI). + +When both are defined, ``Python.h`` may, or may not, redefine +:c:macro:`!Py_LIMITED_API` to match :c:macro:`!Py_TARGET_ABI3T`. + +On a a :term:`free-threaded build` -- that is, when +:c:macro:`Py_GIL_DISABLED` is defined -- :c:macro:`!Py_TARGET_ABI3T` +defaults to the value of :c:macro:`!Py_TARGET_ABI3T`. +This means that there are two ways to build for both ``abi3`` and ``abi3t``: + +- define both :c:macro:`!Py_LIMITED_API` and :c:macro:`!Py_TARGET_ABI3T`, or +- define only :c:macro:`!Py_LIMITED_API` and build for free-threaded Python. + + +Stable ABI Scope and Performance +-------------------------------- + +The goal for Stable ABI is to allow everything that is possible with the full C API, but possibly with a performance penalty. +Generally, compatibility with Stable ABI will require some changes to an +extension's source code. -For example, while :c:func:`PyList_GetItem` is available, its “unsafe” macro +For example, while :c:func:`PyList_GetItem` is available, its "unsafe" macro variant :c:func:`PyList_GET_ITEM` is not. The macro can be faster because it can rely on version-specific implementation details of the list object. -Without ``Py_LIMITED_API`` defined, some C API functions are inlined or -replaced by macros. -Defining ``Py_LIMITED_API`` disables this inlining, allowing stability as +For another example, when *not* compiling for Stable ABI, some C API +functions are inlined or replaced by macros. +Compiling for Stable ABI disables this inlining, allowing stability as Python's data structures are improved, but possibly reducing performance. -By leaving out the ``Py_LIMITED_API`` definition, it is possible to compile -a Limited API extension with a version-specific ABI. This can improve -performance for that Python version, but will limit compatibility. -Compiling with ``Py_LIMITED_API`` will then yield an extension that can be -distributed where a version-specific one is not available – for example, -for prereleases of an upcoming Python version. +By leaving out the :c:macro:`!Py_LIMITED_API`` or :c:macro:`!Py_TARGET_ABI3T` +definition, it is possible to compile Stable-ABI-compatible source +for a version-specific ABI, possibly improving performance for a specific +Python version. Limited API Caveats ------------------- -Note that compiling with ``Py_LIMITED_API`` is *not* a complete guarantee that -code conforms to the :ref:`Limited API ` or the :ref:`Stable ABI -`. ``Py_LIMITED_API`` only covers definitions, but an API also -includes other issues, such as expected semantics. +Note that compiling with :c:macro:`Py_LIMITED_API` or :c:macro:`Py_TARGET_ABI3T` +is *not* a complete guarantee that code will be compatible with the +expected Python versions. +The macros only cover definitions, not other issues such as expected semantics. -One issue that ``Py_LIMITED_API`` does not guard against is calling a function +One issue that the macros do not guard against is calling a function with arguments that are invalid in a lower Python version. For example, consider a function that starts accepting ``NULL`` for an argument. In Python 3.9, ``NULL`` now selects a default behavior, but in @@ -175,20 +234,21 @@ Python 3.8, the argument will be used directly, causing a ``NULL`` dereference and crash. A similar argument works for fields of structs. Another issue is that some struct fields are currently not hidden when -``Py_LIMITED_API`` is defined, even though they're part of the Limited API. +the macros are defined, even though they're part of the Limited API. For these reasons, we recommend testing an extension with *all* minor Python -versions it supports, and preferably to build with the *lowest* such version. +versions it supports. We also recommend reviewing documentation of all used API to check if it is explicitly part of the Limited API. Even with ``Py_LIMITED_API`` defined, a few private declarations are exposed for technical reasons (or even unintentionally, as bugs). -Also note that the Limited API is not necessarily stable: compiling with -``Py_LIMITED_API`` with Python 3.8 means that the extension will -run with Python 3.12, but it will not necessarily *compile* with Python 3.12. -In particular, parts of the Limited API may be deprecated and removed, +Also note that while compiling with ``Py_LIMITED_API`` 3.8 means that the +extension will *load* on Python 3.12, and *compile* with Python 3.12, +the same source will not necessarily compile with ``Py_LIMITED_API`` +set to 3.12. +In general: parts of the Limited API may be deprecated and removed, provided that the Stable ABI stays stable. @@ -199,12 +259,12 @@ Platform Considerations ABI stability depends not only on Python, but also on the compiler used, lower-level libraries and compiler options. For the purposes of -the :ref:`Stable ABI `, these details define a “platform”. They +the :ref:`Stable ABIs `, these details define a “platform”. They usually depend on the OS type and processor architecture It is the responsibility of each particular distributor of Python to ensure that all Python versions on a particular platform are built -in a way that does not break the Stable ABI. +in a way that does not break the Stable ABIs, or the version-specific ABIs. This is the case with Windows and macOS releases from ``python.org`` and many third-party distributors. @@ -365,12 +425,13 @@ The full API is described below for advanced use cases. .. versionadded:: 3.15 +.. _limited-c-api: .. _limited-api-list: Contents of Limited API ======================= - -Currently, the :ref:`Limited API ` includes the following items: +This is the definitive list of :ref:`Limited API ` for +Python |version|: .. limited-api-list:: diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index 724dea625c4e21..380b56372d09f6 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -378,6 +378,31 @@ def run(self) -> list[nodes.Node]: return [node] +class VersionHexCheatsheet(SphinxDirective): + """Show results of Py_PACK_VERSION(3, x) for a few relevant Python versions + + This is useful for defining version before Python.h is included. + It should auto-update to the version being documented, hence the extension. + """ + has_content = False + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + + def run(self) -> list[nodes.Node]: + content = [ + ".. code-block:: c", + "", + ] + current_minor = int(self.config.version.removeprefix('3.')) + for minor in range(current_minor - 5, current_minor + 1): + value = (3 << 24) | (minor << 16) + content.append(f' {value:#x} /* Py_PACK_VERSION(3.{minor}) */') + node = nodes.paragraph() + self.state.nested_parse(StringList(content), 0, node) + return [node] + + class CorrespondingTypeSlot(SphinxDirective): """Type slot annotations @@ -443,6 +468,7 @@ def setup(app: Sphinx) -> ExtensionMetadata: app.add_config_value("stable_abi_file", "", "env", types={str}) app.add_config_value("threadsafety_file", "", "env", types={str}) app.add_directive("limited-api-list", LimitedAPIList) + app.add_directive("version-hex-cheatsheet", VersionHexCheatsheet) app.add_directive("corresponding-type-slot", CorrespondingTypeSlot) app.connect("builder-inited", init_annotations) app.connect("doctree-read", add_annotations) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 6f5d84a3b8ca80..24f0a960368247 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -82,6 +82,7 @@ Summary -- Release highlights ` * :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object ` +* :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds ` * :ref:`The JIT compiler has been significantly upgraded ` * :ref:`Improved error messages ` * :ref:`The official Windows 64-bit binaries now use the tail-calling interpreter @@ -381,6 +382,31 @@ agen() for x in a)``. (Contributed by Adam Hartz in :gh:`143055`.) +.. _whatsnew315-abi3t: + +:pep:`903`: ``abi3t`` -- Stable ABI for Free-Threaded Builds +------------------------------------------------------------ + +C extensions that target the :ref:`Stable ABI ` +can now be compiled to be compatible with +both :term:`free-threaded build ` builds of CPython +and "traditional" builds with the :term:`GIL` enabled. +This mode usually requires some non-trivial changes to the source code; +specifically: + +- Switching to API introduced in :pep:`697` (Python 3.12), such as + negative :c:member:`~PyType_Spec.basicsize` and + :c:func:`PyObject_GetTypeData`, rather than making :c:type:`PyObject` + part of the instance struct; and +- Switching from a ``PyInit_`` function to a new export hook, + :c:func:`PyModExport_* `, introduced for this + purpose in :pep:`903`. + +Note that Stable ABI does not offer all functionality CPython has to offer. +Extensions that cannot switch to ``abi3t`` should continue to build for +the existing Stable ABI (``abi3``) and the version-specific ABI for +free-threading (``cp315t``) separately. + .. _whatsnew315-improved-error-messages: From c1d7fef8678b745a36a1a1348053c302d61f94cc Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 2 Apr 2026 15:02:24 +0200 Subject: [PATCH 2/8] Manifest, annotations --- Doc/c-api/module.rst | 10 ++++++++++ Doc/c-api/structures.rst | 20 ++++++++++++++++++++ Doc/data/stable_abi.dat | 4 ++-- Doc/tools/extensions/c_annotations.py | 15 +++++---------- Misc/stable_abi.toml | 19 ++++++++++++++----- 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 8b967c285ac865..cefabf18e5f2b0 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -685,6 +685,11 @@ remove it. Usually, there is only one variable of this type for each extension module defined this way. + In the :ref:`Stable ABI ` for Free-Threaded Builds (``abi3t``), + this struct is opaque, and unusable in practice. + The struct, including all members, is part of Stable ABI for + non-free-threaded builds (``abi3``). + .. c:member:: PyModuleDef_Base m_base Always initialize this member to :c:macro:`PyModuleDef_HEAD_INIT`: @@ -695,6 +700,11 @@ remove it. The type of :c:member:`!PyModuleDef.m_base`. + In the :ref:`Stable ABI ` for Free-Threaded Builds + (``abi3t``), this struct is opaque, and unusable in practice. + The struct is part of Stable ABI for + non-free-threaded builds (``abi3``). + .. c:macro:: PyModuleDef_HEAD_INIT The required initial value for :c:member:`!PyModuleDef.m_base`. diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index c0d2663adefc6b..aeca412610317f 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -33,6 +33,13 @@ under :ref:`reference counting `. The members must not be accessed directly; instead use macros such as :c:macro:`Py_REFCNT` and :c:macro:`Py_TYPE`. + In the :ref:`Stable ABI ` for Free-Threaded Builds (``abi3t``), + this struct is opaque; its size and layout may change between + Python versions. + In Stable ABI for non-free-threaded builds (``abi3``), the + :c:member:`!ob_refcnt` and :c:member:`!ob_type` fields are available, + but using them directly is discouraged. + .. c:member:: Py_ssize_t ob_refcnt The object's reference count, as returned by :c:macro:`Py_REFCNT`. @@ -72,6 +79,19 @@ under :ref:`reference counting `. instead use macros such as :c:macro:`Py_SIZE`, :c:macro:`Py_REFCNT` and :c:macro:`Py_TYPE`. + In the :ref:`Stable ABI ` for Free-Threaded Builds (``abi3t``), + this struct is opaque; its size and layout may change between + Python versions. + In Stable ABI for non-free-threaded builds (``abi3``), the + :c:member:`!ob_base` and :c:member:`!ob_size` fields are available, + but using them directly is discouraged. + + .. c:member:: PyObject ob_base + + Common object header. + Typically, this field is not accessed directly; instead + :c:type:`!PyVarObject` can be cast to :c:type:`PyObject`. + .. c:member:: Py_ssize_t ob_size A size field, whose contents should be considered an object's internal diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 510e683c87e8b9..a14b7e15688f69 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -470,8 +470,8 @@ func,PyMemoryView_GetContiguous,3.2,, data,PyMemoryView_Type,3.2,, type,PyMethodDef,3.2,,full-abi data,PyMethodDescr_Type,3.2,, -type,PyModuleDef,3.2,,full-abi -type,PyModuleDef_Base,3.2,,full-abi +type,PyModuleDef,3.2,,abi3t-opaque +type,PyModuleDef_Base,3.2,,abi3t-opaque func,PyModuleDef_Init,3.5,, type,PyModuleDef_Slot,3.5,,full-abi data,PyModuleDef_Type,3.5,, diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index 380b56372d09f6..a692d6568604fb 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -249,18 +249,17 @@ def _stable_abi_annotation( reftype="ref", refexplicit="False", ) - struct_abi_kind = record.struct_abi_kind - if struct_abi_kind in {"opaque", "members"}: - ref_node += nodes.Text(sphinx_gettext("Limited API")) - else: - ref_node += nodes.Text(sphinx_gettext("Stable ABI")) + ref_node += nodes.Text(sphinx_gettext("Stable ABI")) emph_node += ref_node + struct_abi_kind = record.struct_abi_kind if struct_abi_kind == "opaque": emph_node += nodes.Text(" " + sphinx_gettext("(as an opaque struct)")) elif struct_abi_kind == "full-abi": emph_node += nodes.Text( " " + sphinx_gettext("(including all members)") ) + elif struct_abi_kind in {"members", "abi3t-opaque"}: + emph_node += nodes.Text(" " + sphinx_gettext("(see below)")) if record.ifdef_note: emph_node += nodes.Text(f" {record.ifdef_note}") if stable_added == "3.2": @@ -271,11 +270,7 @@ def _stable_abi_annotation( " " + sphinx_gettext("since version %s") % stable_added ) emph_node += nodes.Text(".") - if struct_abi_kind == "members": - msg = " " + sphinx_gettext( - "(Only some members are part of the stable ABI.)" - ) - emph_node += nodes.Text(msg) + return emph_node diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 63fd83868b644f..200b390bdbd217 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1,4 +1,4 @@ -# This file lists the contents of the Limited API and Stable ABI. +# This file lists the contents of Limited API and Stable ABI. # Please append new items at the end. # The syntax of this file is not fixed. @@ -46,15 +46,24 @@ # - 'opaque': No members are part of the ABI, nor is the size. The Limited # API only handles these via pointers. The C definition should be # incomplete (opaque). -# - 'members': Only specific members are part of the stable ABI. -# The struct's size may change, so it can't be used in arrays. +# - 'abi3t-opaque': 'full-abi' in abi3; 'opaque' in abi3t. +# For docs, the generated annotation refers to details that need to +# be added to the ReST file manually. +# - 'members': +# - 'opaque' in abi3t. +# - In abi3, only specific members are part of the stable ABI. +# The struct's size may change, so it can't be used in arrays. # Do not add new structs of this kind without an extremely good reason. +# For docs, the generated annotation refers to details that need to +# be added to the ReST file manually. # - members: For `struct` with struct_abi_kind = 'members', a list of the # exposed members. # - doc: for `feature_macro`, the blurb added in documentation # - windows: for `feature_macro`, this macro is defined on Windows. # (This info is used to generate the DLL manifest and needs to be available # on all platforms.) +# - abi3t_opaque: In abi3t, this struct is opaque (as if `struct_abi_kind` +# was 'opaque' and `members` was missing). # Removing items from this file is generally not allowed, and additions should # be considered with that in mind. See the devguide for exact rules: @@ -107,10 +116,10 @@ struct_abi_kind = 'full-abi' [struct.PyModuleDef_Base] added = '3.2' - struct_abi_kind = 'full-abi' + struct_abi_kind = 'abi3t-opaque' [struct.PyModuleDef] added = '3.2' - struct_abi_kind = 'full-abi' + struct_abi_kind = 'abi3t-opaque' [struct.PyStructSequence_Field] added = '3.2' struct_abi_kind = 'full-abi' From f83f6b4458d8a90631a5aea36953c47b2c9a17fd Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 2 Apr 2026 15:13:46 +0200 Subject: [PATCH 3/8] Fixup --- Doc/c-api/stable.rst | 140 ++++++++++++----------- Doc/tools/extensions/c_annotations.py | 3 +- Doc/whatsnew/3.15.rst | 21 +++- Tools/check-c-api-docs/ignored_c_api.txt | 2 - 4 files changed, 90 insertions(+), 76 deletions(-) diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 1ded2fc4bbb963..d99cc901e12d87 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -62,18 +62,13 @@ versions of Python, without recompilation. .. note:: - For simplicity, this document talks about *extensions*, but the Stable ABI + For simplicity, this document talks about *extensions*, but Stable ABI works the same way for all uses of the API – for example, embedding Python. -A Stable ABI is *versioned* using the first two numbers of the Python version. -For example, Stable ABI 3.14 corresponds to Python 3.14. -An extension compiled for Stable ABI 3.x is ABI-compatible with Python 3.x -and above. - There are two Stable ABIs: - ``abi3``, introduced in Pyton 3.2, is compatible with - non-:term:`free threaded ` builds of CPython. + **non**-:term:`free threaded ` builds of CPython. - ``abi3t``, introduced in Pyton 3.15, is compatible with :term:`free threaded ` builds of CPython. @@ -84,24 +79,18 @@ There are two Stable ABIs: ``abi3t`` was added in :pep:`803` It is possible for an extension to be compiled for *both* ``abi3`` and -``abi3t`` at the same time. +``abi3t`` at the same time; the result will be compatible with +both free-threaded and non-free-threaded builds of Python. Currently, this has no downsides compared to compiling for ``abi3t`` only. -The Stable ABIs come with several caveats: - -- Extensions that target a stable ABI must only use a limited subset of - the C API. This subset is known as the :dfn:`Limited API`; its contents - are :ref:`listed below `. - -- Compiling for a Stable ABI will disable some optimizations. - In particular, common functions cannot be inlined to take advantage of the - internal implementation details. +Each Stable ABI is versioned using the first two numbers of the Python version. +For example, Stable ABI 3.14 corresponds to Python 3.14. +An extension compiled for Stable ABI 3.x is ABI-compatible with Python 3.x +and above. -- Stable ABI prevents *ABI* issues, like linker errors due to missing - symbols or data corruption due to changes in structure layouts or function - signatures. - However, other changes in Python can change the *behavior* of extensions. - See Python's Backwards Compatibility Policy (:pep:`387`) for details. +Extensions that target a stable ABI must only use a limited subset of +the C API. This subset is known as the :dfn:`Limited API`; its contents +are :ref:`listed below `. On Windows, extensions that use a Stable ABI should be linked against ``python3.dll`` rather than a version-specific library such as @@ -109,25 +98,28 @@ On Windows, extensions that use a Stable ABI should be linked against This library only exposes the relevant symbols. On some platforms, Python will look for and load shared library files named -with the ``abi3`` or ``abi3t`` tag (e.g. ``mymodule.abi3.so``). +with the ``abi3`` or ``abi3t`` tag (for example, ``mymodule.abi3.so``). :term:`Free threaded ` interpreters only recognize the ``abi3t`` tag, while non-free threaded ones will prefer ``abi3`` but fall back to ``abi3t``. -Thus, extensions compatible with both flavors should use the ``abi3t`` tag. +Thus, extensions compatible with both ABIs should use the ``abi3t`` tag. -Python does not check if such extensions conform to a Stable ABI. -Extension authors are encouraged to check using the :c:macro:`Py_mod_abi` -slot or the :c:func:`PyABIInfo_Check` function, but, the user +Python does not necessarily check that extensions it loads +have compatible ABI. +Extension authors are encouraged to add a check using the :c:macro:`Py_mod_abi` +slot or the :c:func:`PyABIInfo_Check` function, but the user (or their packaging tool) is ultimately responsible for ensuring that, for example, extensions built for Stable ABI 3.10 are not installed for lower versions of Python. -All functions in the Stable ABI are present as functions in Python's shared +All functions in Stable ABI are present as functions in Python's shared library, not solely as macros. -They are usable from languages that don't use the C preprocessor, -such as Python via :py:mod:`ctypes`. +This makes them usable are usable from languages that don't use the C +preprocessor, including Python's :py:mod:`ctypes`. +.. _abi3-compiling: + Compiling for Stable ABI ------------------------ @@ -138,22 +130,23 @@ Compiling for Stable ABI extension filenames and other metadata. Prefer using the tool's options over defining the macros manually. - The rest of this section is relevant for tool authors, and for people who - compile extensions manually. + The rest of this section is mainly relevant for tool authors, and for + people who compile extensions manually. .. seealso:: `list of recommended tools`_ in the Python Packaging User Guide .. _list of recommended tools: https://packaging.python.org/en/latest/guides/tool-recommendations/#build-backends-for-extension-modules To compile for a Stable ABI, define one or both of the following macros -before including ``Python.h`` to the lowest Python version your extension -should support, in :c:macro:`Py_PACK_VERSION` format. +to the lowest Python version your extension should support, in +:c:macro:`Py_PACK_VERSION` format. Typically, you should choose a specific value rather than the version of the Python headers you are compiling against. -Since the :c:macro:`Py_PACK_VERSION` is not available before including -``Python.h``, you will need to use the number directly. -For reference, the values for a few Python versions are: +The macro(s) must be defined before including ``Python.h``. +Since :c:macro:`Py_PACK_VERSION` is not available at this point, you +will need to use the numeric value directly. +For reference, the values for a few recent Python versions are: .. version-hex-cheatsheet:: @@ -166,16 +159,18 @@ visible to the compiler but should not be used directly. Target ``abi3``, that is, non-:term:`free threaded ` builds of CPython. + See :ref:`above ` for common information. .. c:macro:: Py_TARGET_ABI3T Target ``abi3t``, that is, :term:`free threaded ` builds of CPython. + See :ref:`above ` for common information. .. versionadded:: next -Despite the different naming, the macros are similar; -the name :c:macro:`!Py_LIMITED_API` is kept for backwards compatibility. +Both macros specify a target ABI; the different naming style is due to +backwards compatibility. .. admonition:: Historical note @@ -212,30 +207,33 @@ functions are inlined or replaced by macros. Compiling for Stable ABI disables this inlining, allowing stability as Python's data structures are improved, but possibly reducing performance. -By leaving out the :c:macro:`!Py_LIMITED_API`` or :c:macro:`!Py_TARGET_ABI3T` +By leaving out the :c:macro:`!Py_LIMITED_API` or :c:macro:`!Py_TARGET_ABI3T` definition, it is possible to compile Stable-ABI-compatible source -for a version-specific ABI, possibly improving performance for a specific -Python version. - - -Limited API Caveats -------------------- - -Note that compiling with :c:macro:`Py_LIMITED_API` or :c:macro:`Py_TARGET_ABI3T` -is *not* a complete guarantee that code will be compatible with the -expected Python versions. -The macros only cover definitions, not other issues such as expected semantics. - -One issue that the macros do not guard against is calling a function -with arguments that are invalid in a lower Python version. +for a version-specific ABI. +A potentially faster version-specific extension can then be distributed +alongside a version compiled for Stable ABI -- a slower but more compatible +fallback. + + +Stable ABI Caveats +------------------ + +Note that compiling for Stable ABI is *not* a complete guarantee that code will +be compatible with the expected Python versions. +Stable ABI prevents *ABI* issues, like linker errors due to missing +symbols or data corruption due to changes in structure layouts or function +signatures. +However, other changes in Python can change the *behavior* of extensions. +See Python's Backwards Compatibility Policy (:pep:`387`) for details. + +One issue that the :c:macro:`Py_TARGET_ABI3T` and :c:macro:`Py_LIMITED_API` +macros do not guard against is calling a function with arguments that are +invalid in a lower Python version. For example, consider a function that starts accepting ``NULL`` for an argument. In Python 3.9, ``NULL`` now selects a default behavior, but in Python 3.8, the argument will be used directly, causing a ``NULL`` dereference and crash. A similar argument works for fields of structs. -Another issue is that some struct fields are currently not hidden when -the macros are defined, even though they're part of the Limited API. - For these reasons, we recommend testing an extension with *all* minor Python versions it supports. @@ -245,11 +243,11 @@ defined, a few private declarations are exposed for technical reasons (or even unintentionally, as bugs). Also note that while compiling with ``Py_LIMITED_API`` 3.8 means that the -extension will *load* on Python 3.12, and *compile* with Python 3.12, +extension should *load* on Python 3.12, and *compile* with Python 3.12, the same source will not necessarily compile with ``Py_LIMITED_API`` set to 3.12. -In general: parts of the Limited API may be deprecated and removed, -provided that the Stable ABI stays stable. +In general, parts of the Limited API may be deprecated and removed, +provided that Stable ABI stays stable. .. _stable-abi-platform: @@ -372,7 +370,7 @@ The full API is described below for advanced use cases. .. c:macro:: PyABIInfo_STABLE - Specifies that the stable ABI is used. + Specifies that Stable ABI is used. .. c:macro:: PyABIInfo_INTERNAL @@ -383,15 +381,22 @@ The full API is described below for advanced use cases. .. c:macro:: PyABIInfo_FREETHREADED - Specifies ABI compatible with free-threading builds of CPython. + Specifies ABI compatible with :term:`free-threaded builds + ` of CPython. (That is, ones compiled with :option:`--disable-gil`; with ``t`` in :py:data:`sys.abiflags`) .. c:macro:: PyABIInfo_GIL - Specifies ABI compatible with non-free-threading builds of CPython + Specifies ABI compatible with non-free-threaded builds of CPython (ones compiled *without* :option:`--disable-gil`). + .. c:macro:: PyABIInfo_FREETHREADING_AGNOSTIC + + Specifies ABI compatible with both free-threaded and + non-free-threaded builds of CPython, that is, both + ``abi3`` and ``abi3t``. + .. c:member:: uint32_t build_version The version of the Python headers used to build the code, in the format @@ -405,10 +410,11 @@ The full API is described below for advanced use cases. The ABI version. - For the Stable ABI, this field should be the value of - :c:macro:`Py_LIMITED_API` - (except if :c:macro:`Py_LIMITED_API` is ``3``; use - :c:expr:`Py_PACK_VERSION(3, 2)` in that case). + For Stable ABI, this field should be the value of + :c:macro:`Py_LIMITED_API` or :c:macro:`Py_TARGET_ABI3T`. + If both are defined, use the smaller value. + (If :c:macro:`Py_LIMITED_API` is ``3``; use + :c:expr:`Py_PACK_VERSION(3, 2)` instead of ``3``.) Otherwise, it should be set to :c:macro:`PY_VERSION_HEX`. diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index a692d6568604fb..a930cbfbf8acc9 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -377,7 +377,8 @@ class VersionHexCheatsheet(SphinxDirective): """Show results of Py_PACK_VERSION(3, x) for a few relevant Python versions This is useful for defining version before Python.h is included. - It should auto-update to the version being documented, hence the extension. + It should auto-update with the version being documented, so it must be an + extension. """ has_content = False required_arguments = 0 diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 24f0a960368247..4fc73a65adcafe 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -387,15 +387,15 @@ agen() for x in a)``. :pep:`903`: ``abi3t`` -- Stable ABI for Free-Threaded Builds ------------------------------------------------------------ -C extensions that target the :ref:`Stable ABI ` -can now be compiled to be compatible with -both :term:`free-threaded build ` builds of CPython -and "traditional" builds with the :term:`GIL` enabled. -This mode usually requires some non-trivial changes to the source code; +C extensions that target the :ref:`Stable ABI ` can now be +compiled for the new *Stable ABI for Free-Threaded Builds* (also known +as ``abi3t``), which makes them compatible with +:term:`free-threaded build ` builds of CPython. +This usually requires some non-trivial changes to the source code; specifically: - Switching to API introduced in :pep:`697` (Python 3.12), such as - negative :c:member:`~PyType_Spec.basicsize` and + negative :c:member:`~PyType_Spec.basicsize` :c:func:`PyObject_GetTypeData`, rather than making :c:type:`PyObject` part of the instance struct; and - Switching from a ``PyInit_`` function to a new export hook, @@ -407,6 +407,15 @@ Extensions that cannot switch to ``abi3t`` should continue to build for the existing Stable ABI (``abi3``) and the version-specific ABI for free-threading (``cp315t``) separately. +Stable ABI for Free-Threaded Builds should typically +be selected in a build tool (such as Setuptools, ``meson-python``, Cython, +Scikit-build-core, Maturin, and similar). +At the time of writing this entry, these tools did **not** support ``abi3t``. +If this is the case for "your" tool, compile for ``cp315t`` separately. +If not using a build tool -- or when writing such a tool -- you can select +``abi3t`` by setting the macro :c:macro:`!Py_TARGET_ABI3T` as discussed +in :ref:`abi3-compiling`. + .. _whatsnew315-improved-error-messages: diff --git a/Tools/check-c-api-docs/ignored_c_api.txt b/Tools/check-c-api-docs/ignored_c_api.txt index f3a3612b84947a..dfec0524cfe016 100644 --- a/Tools/check-c-api-docs/ignored_c_api.txt +++ b/Tools/check-c-api-docs/ignored_c_api.txt @@ -18,8 +18,6 @@ Py_HasFileSystemDefaultEncoding Py_UTF8Mode # pyhash.h Py_HASH_EXTERNAL -# modsupport.h -PyABIInfo_FREETHREADING_AGNOSTIC # object.h Py_INVALID_SIZE # pyexpat.h From b3e31c665f5ef4a8c04317d7c679192153877e76 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 2 Apr 2026 17:01:57 +0200 Subject: [PATCH 4/8] I'm not good with numbers --- Doc/whatsnew/3.15.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 4fc73a65adcafe..df6930412d7146 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -384,7 +384,7 @@ agen() for x in a)``. .. _whatsnew315-abi3t: -:pep:`903`: ``abi3t`` -- Stable ABI for Free-Threaded Builds +:pep:`803`: ``abi3t`` -- Stable ABI for Free-Threaded Builds ------------------------------------------------------------ C extensions that target the :ref:`Stable ABI ` can now be @@ -400,7 +400,7 @@ specifically: part of the instance struct; and - Switching from a ``PyInit_`` function to a new export hook, :c:func:`PyModExport_* `, introduced for this - purpose in :pep:`903`. + purpose in :pep:`793`. Note that Stable ABI does not offer all functionality CPython has to offer. Extensions that cannot switch to ``abi3t`` should continue to build for From b3799dea2782109e1ccb9773612eea731230a3f5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 3 Apr 2026 11:03:16 +0200 Subject: [PATCH 5/8] Address review --- Doc/c-api/module.rst | 2 +- Doc/c-api/stable.rst | 17 ++++++++++------- Doc/whatsnew/3.15.rst | 11 ++++++----- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index cefabf18e5f2b0..46af79e9b8f947 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -685,7 +685,7 @@ remove it. Usually, there is only one variable of this type for each extension module defined this way. - In the :ref:`Stable ABI ` for Free-Threaded Builds (``abi3t``), + In the :ref:`Stable ABI ` for free-threaded builds (``abi3t``), this struct is opaque, and unusable in practice. The struct, including all members, is part of Stable ABI for non-free-threaded builds (``abi3``). diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index d99cc901e12d87..372c92be22ada2 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -68,10 +68,10 @@ versions of Python, without recompilation. There are two Stable ABIs: - ``abi3``, introduced in Pyton 3.2, is compatible with - **non**-:term:`free threaded ` builds of CPython. + **non**-:term:`free-threaded ` builds of CPython. - ``abi3t``, introduced in Pyton 3.15, is compatible with - :term:`free threaded ` builds of CPython. + :term:`free-threaded ` builds of CPython. It has stricter API limitations than ``abi3``. .. versionadded:: next @@ -99,8 +99,8 @@ This library only exposes the relevant symbols. On some platforms, Python will look for and load shared library files named with the ``abi3`` or ``abi3t`` tag (for example, ``mymodule.abi3.so``). -:term:`Free threaded ` interpreters only recognize the -``abi3t`` tag, while non-free threaded ones will prefer ``abi3`` but fall back +:term:`Free-threaded ` interpreters only recognize the +``abi3t`` tag, while non-free-threaded ones will prefer ``abi3`` but fall back to ``abi3t``. Thus, extensions compatible with both ABIs should use the ``abi3t`` tag. @@ -158,13 +158,13 @@ visible to the compiler but should not be used directly. .. c:macro:: Py_LIMITED_API Target ``abi3``, that is, - non-:term:`free threaded ` builds of CPython. + non-:term:`free-threaded ` builds of CPython. See :ref:`above ` for common information. .. c:macro:: Py_TARGET_ABI3T Target ``abi3t``, that is, - :term:`free threaded ` builds of CPython. + :term:`free-threaded ` builds of CPython. See :ref:`above ` for common information. .. versionadded:: next @@ -189,6 +189,8 @@ This means that there are two ways to build for both ``abi3`` and ``abi3t``: - define only :c:macro:`!Py_LIMITED_API` and build for free-threaded Python. +.. _limited-api-scope-and-performance: + Stable ABI Scope and Performance -------------------------------- @@ -215,6 +217,8 @@ alongside a version compiled for Stable ABI -- a slower but more compatible fallback. +.. _limited-api-caveats: + Stable ABI Caveats ------------------ @@ -224,7 +228,6 @@ Stable ABI prevents *ABI* issues, like linker errors due to missing symbols or data corruption due to changes in structure layouts or function signatures. However, other changes in Python can change the *behavior* of extensions. -See Python's Backwards Compatibility Policy (:pep:`387`) for details. One issue that the :c:macro:`Py_TARGET_ABI3T` and :c:macro:`Py_LIMITED_API` macros do not guard against is calling a function with arguments that are diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 0a6447812be472..a786b7d39d3f94 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -390,28 +390,29 @@ agen() for x in a)``. C extensions that target the :ref:`Stable ABI ` can now be compiled for the new *Stable ABI for Free-Threaded Builds* (also known as ``abi3t``), which makes them compatible with -:term:`free-threaded build ` builds of CPython. +:term:`free-threaded ` builds of CPython. This usually requires some non-trivial changes to the source code; specifically: - Switching to API introduced in :pep:`697` (Python 3.12), such as - negative :c:member:`~PyType_Spec.basicsize` + negative :c:member:`~PyType_Spec.basicsize` and :c:func:`PyObject_GetTypeData`, rather than making :c:type:`PyObject` part of the instance struct; and - Switching from a ``PyInit_`` function to a new export hook, :c:func:`PyModExport_* `, introduced for this purpose in :pep:`793`. -Note that Stable ABI does not offer all functionality CPython has to offer. +Note that Stable ABI does not offer all the functionality that CPython +has to offer. Extensions that cannot switch to ``abi3t`` should continue to build for the existing Stable ABI (``abi3``) and the version-specific ABI for free-threading (``cp315t``) separately. Stable ABI for Free-Threaded Builds should typically -be selected in a build tool (such as Setuptools, ``meson-python``, Cython, +be selected in a build tool (such as Setuptools, meson-python, Cython, Scikit-build-core, Maturin, and similar). At the time of writing this entry, these tools did **not** support ``abi3t``. -If this is the case for "your" tool, compile for ``cp315t`` separately. +If this is the case for your tool, compile for ``cp315t`` separately. If not using a build tool -- or when writing such a tool -- you can select ``abi3t`` by setting the macro :c:macro:`!Py_TARGET_ABI3T` as discussed in :ref:`abi3-compiling`. From 3bc47ee84f64810f96c79255dc4c306bc4f6a817 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 3 Apr 2026 11:06:35 +0200 Subject: [PATCH 6/8] Add a blurb for the PEP --- .../next/C_API/2026-04-03-11-06-20.gh-issue-146636.zR6Jsn.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/C_API/2026-04-03-11-06-20.gh-issue-146636.zR6Jsn.rst diff --git a/Misc/NEWS.d/next/C_API/2026-04-03-11-06-20.gh-issue-146636.zR6Jsn.rst b/Misc/NEWS.d/next/C_API/2026-04-03-11-06-20.gh-issue-146636.zR6Jsn.rst new file mode 100644 index 00000000000000..7f84a6f954dc76 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-04-03-11-06-20.gh-issue-146636.zR6Jsn.rst @@ -0,0 +1 @@ +Implement :pep:`803` -- ``abi3t``: Stable ABI for Free-Threaded Builds. From 30d97b1c359771d58e729924f8748979fc7ae694 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 6 Apr 2026 12:06:20 +0200 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/c-api/stable.rst | 14 +++++++------- Doc/whatsnew/3.15.rst | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 372c92be22ada2..6f69c62920331b 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -67,10 +67,10 @@ versions of Python, without recompilation. There are two Stable ABIs: -- ``abi3``, introduced in Pyton 3.2, is compatible with +- ``abi3``, introduced in Python 3.2, is compatible with **non**-:term:`free-threaded ` builds of CPython. -- ``abi3t``, introduced in Pyton 3.15, is compatible with +- ``abi3t``, introduced in Python 3.15, is compatible with :term:`free-threaded ` builds of CPython. It has stricter API limitations than ``abi3``. @@ -125,7 +125,7 @@ Compiling for Stable ABI .. note:: - When using a build tool (for example, ``setuptools``), the tool is + When using a build tool (for example, Setuptools), the tool is generally responsible for setting macros and synchronizing them with extension filenames and other metadata. Prefer using the tool's options over defining the macros manually. @@ -143,7 +143,7 @@ to the lowest Python version your extension should support, in Typically, you should choose a specific value rather than the version of the Python headers you are compiling against. -The macro(s) must be defined before including ``Python.h``. +The macros must be defined before including ``Python.h``. Since :c:macro:`Py_PACK_VERSION` is not available at this point, you will need to use the numeric value directly. For reference, the values for a few recent Python versions are: @@ -158,13 +158,13 @@ visible to the compiler but should not be used directly. .. c:macro:: Py_LIMITED_API Target ``abi3``, that is, - non-:term:`free-threaded ` builds of CPython. + non-:term:`free-threaded builds ` of CPython. See :ref:`above ` for common information. .. c:macro:: Py_TARGET_ABI3T Target ``abi3t``, that is, - :term:`free-threaded ` builds of CPython. + :term:`free-threaded builds ` of CPython. See :ref:`above ` for common information. .. versionadded:: next @@ -180,7 +180,7 @@ backwards compatibility. When both are defined, ``Python.h`` may, or may not, redefine :c:macro:`!Py_LIMITED_API` to match :c:macro:`!Py_TARGET_ABI3T`. -On a a :term:`free-threaded build` -- that is, when +On a :term:`free-threaded build` -- that is, when :c:macro:`Py_GIL_DISABLED` is defined -- :c:macro:`!Py_TARGET_ABI3T` defaults to the value of :c:macro:`!Py_TARGET_ABI3T`. This means that there are two ways to build for both ``abi3`` and ``abi3t``: diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index a786b7d39d3f94..98cfdd6b866d4e 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -390,7 +390,7 @@ agen() for x in a)``. C extensions that target the :ref:`Stable ABI ` can now be compiled for the new *Stable ABI for Free-Threaded Builds* (also known as ``abi3t``), which makes them compatible with -:term:`free-threaded ` builds of CPython. +:term:`free-threaded builds ` of CPython. This usually requires some non-trivial changes to the source code; specifically: @@ -410,8 +410,8 @@ free-threading (``cp315t``) separately. Stable ABI for Free-Threaded Builds should typically be selected in a build tool (such as Setuptools, meson-python, Cython, -Scikit-build-core, Maturin, and similar). -At the time of writing this entry, these tools did **not** support ``abi3t``. +scikit-build-core, Maturin). +At the time of writing, these tools did **not** support ``abi3t``. If this is the case for your tool, compile for ``cp315t`` separately. If not using a build tool -- or when writing such a tool -- you can select ``abi3t`` by setting the macro :c:macro:`!Py_TARGET_ABI3T` as discussed From e7e102a99c65ef1dc1d2921afd2ab7c928c8a458 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 6 Apr 2026 12:20:44 +0200 Subject: [PATCH 8/8] Respond to reviews --- Doc/c-api/module.rst | 15 ++++++++------- Doc/c-api/stable.rst | 18 ++++++++++++------ Doc/tools/extensions/c_annotations.py | 1 + Doc/whatsnew/3.15.rst | 4 ++-- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 46af79e9b8f947..a66a1bfd7f8489 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -685,10 +685,11 @@ remove it. Usually, there is only one variable of this type for each extension module defined this way. - In the :ref:`Stable ABI ` for free-threaded builds (``abi3t``), - this struct is opaque, and unusable in practice. - The struct, including all members, is part of Stable ABI for - non-free-threaded builds (``abi3``). + The struct, including all members, is part of the + :ref:`Stable ABI ` for non-free-threaded builds (``abi3``). + In the Stable ABI for free-threaded builds (``abi3t``), + this struct is opaque, and unusable in practice; see :ref:`pymoduledef_slot` + for a replacement. .. c:member:: PyModuleDef_Base m_base @@ -700,10 +701,10 @@ remove it. The type of :c:member:`!PyModuleDef.m_base`. - In the :ref:`Stable ABI ` for Free-Threaded Builds - (``abi3t``), this struct is opaque, and unusable in practice. - The struct is part of Stable ABI for + The struct is part of the :ref:`Stable ABI ` for non-free-threaded builds (``abi3``). + In the Stable ABI for Free-Threaded Builds + (``abi3t``), this struct is opaque, and unusable in practice. .. c:macro:: PyModuleDef_HEAD_INIT diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 6f69c62920331b..ead9421b8d6f7e 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -125,10 +125,11 @@ Compiling for Stable ABI .. note:: - When using a build tool (for example, Setuptools), the tool is - generally responsible for setting macros and synchronizing them with - extension filenames and other metadata. - Prefer using the tool's options over defining the macros manually. + Build tools (such as, for example, meson-python, scikit-build-core, + or Setuptools) often have a mechanism for setting macros and synchronizing + them with extension filenames and other metadata. + Prefer using such a mechanism, if it exists, over defining the + macros manually. The rest of this section is mainly relevant for tool authors, and for people who compile extensions manually. @@ -150,10 +151,12 @@ For reference, the values for a few recent Python versions are: .. version-hex-cheatsheet:: -When the macro(s) are defined, ``Python.h`` will only expose API that is +When one of the macros is defined, ``Python.h`` will only expose API that is compatible with the given Stable ABI -- that is, the :ref:`Limited API ` plus some definitions that need to be visible to the compiler but should not be used directly. +When both are defined, ``Python.h`` will only expose API compatible with +both Stable ABIs. .. c:macro:: Py_LIMITED_API @@ -186,7 +189,10 @@ defaults to the value of :c:macro:`!Py_TARGET_ABI3T`. This means that there are two ways to build for both ``abi3`` and ``abi3t``: - define both :c:macro:`!Py_LIMITED_API` and :c:macro:`!Py_TARGET_ABI3T`, or -- define only :c:macro:`!Py_LIMITED_API` and build for free-threaded Python. +- define only :c:macro:`!Py_LIMITED_API` and: + + - on Windows, define :c:macro:`!Py_GIL_DISABLED`; + - on other systems, use the headers of free-threaded build of Python. .. _limited-api-scope-and-performance: diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index a930cbfbf8acc9..1409c77aed9c6b 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -380,6 +380,7 @@ class VersionHexCheatsheet(SphinxDirective): It should auto-update with the version being documented, so it must be an extension. """ + has_content = False required_arguments = 0 optional_arguments = 0 diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 98cfdd6b866d4e..3e70a22037fd77 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -409,8 +409,8 @@ the existing Stable ABI (``abi3``) and the version-specific ABI for free-threading (``cp315t``) separately. Stable ABI for Free-Threaded Builds should typically -be selected in a build tool (such as Setuptools, meson-python, Cython, -scikit-build-core, Maturin). +be selected in a build tool (such as, for example, Setuptools, meson-python, +scikit-build-core, or Maturin). At the time of writing, these tools did **not** support ``abi3t``. If this is the case for your tool, compile for ``cp315t`` separately. If not using a build tool -- or when writing such a tool -- you can select