diff --git a/docs/how-to-guides/limited-api.rst b/docs/how-to-guides/limited-api.rst new file mode 100644 index 000000000..2037fd4b2 --- /dev/null +++ b/docs/how-to-guides/limited-api.rst @@ -0,0 +1,174 @@ +.. SPDX-FileCopyrightText: 2026 The meson-python developers +.. +.. SPDX-License-Identifier: MIT + +.. _howto-limited-api: + +*********************************** +Targeting the CPython Limited C API +*********************************** + +This page describes how to configure your package to build against the +CPython `limited API`_ and build `stable ABI`_ wheels. Limited API builds +target a minimum interpreter version and produce a single wheel that +can support newer interpreter versions. + +You can enable limited API builds by default or make them opt-in. +If your package does not yet use the limited API, we recommend making +limited API builds opt-in so you can transition to stable ABI wheels +gradually or distribute stable ABI wheels only on selected platforms. + +Compile your extension modules for the limited API by specifying the +``limited_api`` argument to the ``extension_module()`` function in the +Meson Python module. The version passed to ``limited_api`` selects +the limited API your extension uses. Meson converts this version to +the corresponding value of the ``Py_LIMITED_API`` preprocessor macro. + +The wheel's :ref:`Python compatibility tag ` +still corresponds to the interpreter used for the build, regardless of +the ``limited_api`` version you select. + +Enabling the +:option:`tool.meson-python.limited-api` setting declares that all the +extension modules in your package target the limited API and adjusts the +wheel filename ABI tag accordingly: + +.. code-block:: meson + + py = import('python').find_installation(pure: false) + + py.extension_module( + '_core', + '_core.c', + limited_api: '3.10', + install: true, + subdir: 'example', + ) + +.. code-block:: toml + + [tool.meson-python] + limited-api = true + + +For limited API builds, on platforms where Python extension modules +targeting the stable ABI use a dedicated filename suffix, ``meson-python`` +verifies that all the extension modules included in the wheel use the +stable ABI filename suffix and fails the build otherwise. + + +.. _opt-in-limited-api-example: + +Set up Limited API support as opt-in +------------------------------------ + +To make limited API builds opt-in, add +``python.allow_limited_api=false`` to the ``default_options`` in your +existing ``project()`` call in ``meson.build``: + +.. code-block:: meson + + project( + 'example', + 'c', + default_options: ['python.allow_limited_api=false'], + ) + +Keep ``limited-api = true`` in ``pyproject.toml`` and the ``limited_api`` +argument to ``extension_module()`` as shown above. When +``python.allow_limited_api`` is false, Meson ignores the ``limited_api`` +argument, compiles extension modules for the ABI of the interpreter +used for the build, and produces a wheel tagged as compatible with +the build interpreter. + +To build a stable ABI wheel, explicitly enable limited API builds: + +.. tab-set:: + + .. tab-item:: pypa/build + :sync: key_pypa_build + + .. code-block:: console + + $ python -m build --wheel -Csetup-args="-Dpython.allow_limited_api=true" . + + .. tab-item:: pip + :sync: key_pip + + .. code-block:: console + + $ python -m pip wheel -Csetup-args="-Dpython.allow_limited_api=true" . + + +.. _limited-api-wheel-tags: + +Limited API compatibility +------------------------- + +``meson-python`` conservatively chooses the wheel's Python compatibility +tag based only on the interpreter used for the build, regardless of the +target ``limited_api`` version. For example, a wheel built with CPython +3.12 for the 3.10 limited API is tagged ``cp312-abi3`` and can be +installed only on GIL-enabled CPython 3.12 and later. When building +stable ABI wheels for distribution, for example on PyPI, build with the +oldest CPython version you want the wheel to support, as `recommended by +CPython`_. If you are installing from source into a particular +environment, use that environment's interpreter; you do not need to +build with the oldest supported version. + +PyPy supports the limited API, but does not implement a stable ABI, thus +the :option:`tool.meson-python.limited-api` setting has no effect on the +wheel tags when building with PyPy. + + +The ``abi3t`` stable ABI +------------------------ + +CPython 3.15 introduces the ``abi3t`` stable ABI, see +the `abi3t migration guide`_. Extension modules built for ``abi3t`` +can be loaded by both the GIL-enabled and the free-threaded builds of +CPython 3.15 and later: a single wheel tagged ``abi3.abi3t`` supports +all CPython interpreters from version 3.15 on. + +``abi3t`` extension modules require limited API version 3.15 or later +and, currently, a free-threaded interpreter for the build: compiling +for ``abi3t`` with a GIL-enabled interpreter is not supported yet. +Nothing else is required: the CPython headers select ``abi3t`` when +``Py_LIMITED_API`` is defined while compiling for a free-threaded +interpreter. + +Meson refuses a ``limited_api`` version newer than the interpreter +used for the build. If you build ``abi3`` wheels for older +CPython versions and ``abi3.abi3t`` wheels for CPython 3.15 and later, +you can select the limited API version by querying the ``Py_GIL_DISABLED`` +sysconfig variable, which is 1 for free-threaded builds: + +.. code-block:: meson + + py = import('python').find_installation(pure: false) + + limited_api = '3.10' + if py.language_version().version_compare('>=3.15') + if py.get_variable('Py_GIL_DISABLED') == 1 + limited_api = '3.15' + endif + endif + + py.extension_module( + '_core', + '_core.c', + limited_api: limited_api, + install: true, + subdir: 'example', + ) + +Free-threaded CPython 3.13 and 3.14 do not support the limited API: +disable limited API builds when using these interpreters. +The :ref:`opt-in configuration above ` +disables them by default. + +.. _limited API: https://docs.python.org/3/c-api/stable.html#limited-c-api +.. _stable ABI: https://docs.python.org/3/c-api/stable.html#stable-application-binary-interface +.. _recommended by CPython: https://docs.python.org/3/c-api/stable.html#limited-api-caveats +.. _improve performance: https://docs.python.org/3/c-api/stable.html#limited-api-scope-and-performance +.. _abi3t migration guide: https://docs.python.org/3.15/howto/abi3t-migration.html diff --git a/docs/index.rst b/docs/index.rst index f56b7ebc0..2bbc407ee 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -83,6 +83,7 @@ the use of ``meson-python`` and Meson for Python packaging. how-to-guides/meson-args how-to-guides/debug-builds how-to-guides/shared-libraries + how-to-guides/limited-api reference/limitations projects-using-meson-python diff --git a/docs/reference/pyproject-settings.rst b/docs/reference/pyproject-settings.rst index 6a7177c10..516757423 100644 --- a/docs/reference/pyproject-settings.rst +++ b/docs/reference/pyproject-settings.rst @@ -33,11 +33,12 @@ use them and examples. modules can be compiled for the Python limited API specifying the ``limited_api`` argument to the |extension_module()|_ function in the Meson Python module. When this setting is set to true, the - value ``abi3`` is used for the Python wheel filename ABI tag. - - This setting is automatically reverted to false when the + Python wheel filename ABI tag is set to ``abi3``, or ``abi3.abi3t`` + when building with free-threaded CPython 3.15 or later. This + setting is automatically reverted to false when the ``-Dpython.allow_limited_api=false`` option is passed to ``meson - setup``. + setup``. See :ref:`howto-limited-api` for more information on limited + API builds. .. option:: tool.meson-python.meson @@ -91,7 +92,7 @@ use them and examples. glob pattern is useful exclusively to limit the effect of an exclude pattern that matches too many files. -.. _python limited api: https://docs.python.org/3/c-api/stable.html?highlight=limited%20api#stable-application-binary-interface +.. _python limited api: https://docs.python.org/3/c-api/stable.html#stable-application-binary-interface .. _extension_module(): `https://mesonbuild.com/Python-module.html#extension_module .. _meson introspection data: https://mesonbuild.com/IDE-integration.html#install-plan