From 8e203ebb5730b0f7cb424575aa5f767dd88f8f36 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 31 Mar 2026 18:10:55 +0200 Subject: [PATCH 1/6] 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/6] 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/6] 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/6] 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/6] 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/6] 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.