Skip to content

Commit a7fcba4

Browse files
ngoldbaumdavidhewittTptPerson-93
authored
Opaque PyObject ABI support (#5807)
* Use PyModExport and PyABIInfo APIs in pymodule implementation * Add PyModExport function * DNM: temporarily disable append_to_inittab doctest * fix issues seen on older pythons in CI * fix incorrect ModuleDef setup on 3.15 * Expose both the PyInit and PyModExport initialization hooks * fix clippy * add changelog entry * try use only slots for both init hooks on 3.15 * Always pass m_name and m_doc, following cpython-gh-144340 * WIP: opaque pyobject support (without Py_GIL_DISABLED) * delete debug prints * WIP: fix segfault * disable append_to_inittab tests * fix clippy * fix ruff * implement David's suggestion for pyobject_subclassable_native_type * replace skipped test with real test * fix check-feature-powerset * fix clippy-all * skip test that depend on struct layout on opaque pyobject builds * Expose PyModuleDef as an opaque pointer on opaque PyObject builds * add comments about location of opaque pointers in CPython headers * fix test_inherited_size * Fix doctest on _Py_OPAQUE_PYOBJECT builds * fix build error on non-opaque builds * mark BaseWithoutData as subclassable * relax assert for Windows * Make PyAny a PyVarObject only on the opaque PyObject build * fix merge conflict resolution error * fix buggy assertion * Expose critical section in the limited API starting in Python 3.15 * expose critical section API in limited API * disable warning in pyo3-ffi build script on sufficiently new Pythons * fixup features * Add missing error handling for `PyModule_FromSlotsAndSpec` * passes cargo tests with the abi3t feature enabled * passes unit tests on GIL-enabled build with abi3t feature * fix merge mistake * rustfmt * fix FIXME * replace extern "C" with extern_libpython! * fix test * fix merge error * Allow abi3t builds without critical section bindings * add FIXME * remove default feature * fix syntax error in noxfile * fix issues spotted by clippy * bring over refactoring from PyO3 PR * working! * Fix memory leak of iterator * fix size hints * delete debug statement * Use Py_TARGET_ABI3T * run formatter * fix check-feature-powerset * increment SUPPORTED_VERSIONS_CPYTHON.max * fix conditional compilation for critical section API * fix ruff * fix incorrect conditional compilation guargs * fix noxfile and ban abi3t builds on 3.14 and older * ruff format * attempt to fix semver-checks * fix test-version-limits * fix issues spotted by claude * strip 't' for version parsing * Adjust comments and error messages * fix compiler warning * more noxfile fixes * fix ruff * use a 3.15 interpreter to run the feature-powerset tests * fix a few more issues caught by claude * add comment * use correct windows library name for abi3t * try to fix linking * add CPythonABI enum for pyo3-build-config InterpreterConfig * add release note * store ABI details in a new PythonAbi struct * fix nox config file * fix ffi-check * Refactor to use the builder pattern for InterpreterConfig * revert noxfile change * fix clippy-all * fix build-config test * Fix issues linking against onld stable ABI versions * remove load-bearing debug impls * fix issue with InterpreterConfig::from_interpreter never returning an abi3 config * fix clippy lint * fix PythonAbiBuilder::from_build_env * Only target abi3 for non-free-threaded builds * fix logic error in InterpreterConfig::from_reader * smooth over merge * tests pass * Add stub abi3t implementation * Improve test coverage * fix clippy * fix 3.14t abi3 builds being a no-op * fix ffi-check to account for private _tp_iteritem PyTypeObject slot * expose the critical section API * Add initial support for Python 3.15.0b1 * fix ffi docstring example * typo fix * fix ffi-check macro * fix ffi examples * make PySlot_FUNC a Rust macro * fix clippy * keep macros version-independent * format extern_libpython blocks * use an explicit type in the PySlot_FUNC macro * fix issues seen in CI builds on older Pythons * fix one more spot in the ffi tests * fix type error in string-sum implementation * add changelog entries * remove unnecessary PartialEq implementation * use std::slice::from_raw_parts instead of libc::memcmp * re-add incorrectly deleted end slot * Use PartialEq on Python 3.14 and older * fix compiler error due to missing Debug impl * Finish updating slots_generated and adjust types for slot IDs * add missing cfg gate * remove unused import * make InterpreterConfigBuilder.extra_build_script_lines a Vec * Add an abi3 version sanitization function, adjust error handling * Adjust comment fragment, add TODO * apply focused comments from self-review * simplify modeling of free-threaded builds and Py_GIL_DISABLED build flag * fix clippy * relax accepting Py_GIL_DISABLED in build_flags if no target ABI is set * more fixes and suggestions from claude * fix fallback to version-specific builds for free-threaded abi3 builds * More minor fixes * expand test coverage * more fixes * update changelog entries * Add missing exports to public pyo3-build-config API * fix linux test * minor touch-ups * minor touch-ups * skip redundant test * whitespace * more tweaks * grammar in comment * fix cfg gating * activate abi3t builds * fix PyMutex gating * simplify windows stable ABI lib name text manipulation * fix ruff * fix cfg gating for critical section API * adjust PyMutex cfg gating * attempt to fix abi3 feature for free-threaded targets from a config file * introduce a builder for InterpreterConfig Co-authored-by: David Hewitt <mail@davidhewitt.dev> * newsfragments * fixup ubuntu test * fixup * fix missing `mut` * fix merge error * fix incorrect merge conflict resolution * refactor is_abi3(t) in get_abit3(t)_version.is_some() * apply suggestions from icxolu * Make PythonAbi fields private, replace with accessor methods * fix test-version-limits * Apply suggestions from code review Co-authored-by: Thomas Tanon <thomas@pellissier-tanon.fr> * review feedback * fix merge conflicts * fix clippy * re-enable functionality that depends on critical sections * revert more unnecessary changes * reduce diff further * fix cfg gating for critical section guards * Merging with David's default_lib_name_for_config_file branch * remove unnecessary get_abi3_version().is_some() * bring back Option * fix comparison for ABI3T_FORWARD_COMPATIBILITY * Apply minor suggestions from code review * delete docs for non-pub field * Don't define a Py_TARGET_ABI3T cfg check * fix merge errors * implement Icxolu's suggestion to get rid of is_abi3 and is_abi3t * fix clippy-all * delete unnecessary get_abit3_version() check * Allow simultaneously enabling abi3 and abi3t features * add release notes * fix ruff * really fix ruff * don't run abi3-py38 tests on pypy * add docs * disable windows gil-enabled abi3t builds to work around upstream bug * fix guide formatting * fix internal links in guide * fix typo in noxfile * fix release notes * revert now-unnecessary CI configuration change * fix abi3t-py315 feature not re-exporting pyo3-ffi feature * fix typos in comments * make get_host_interpreter accept explicit abi3_version and abi3t_version arguments * never use an abi3 version on the free-threaded build * make build-config tests pass on 3.14t * version-gate build-config test * fix logic error in get_host_interpreter * skip build-config tests for stable ABI lowering on 3.14t, which doesn't support any stable ABI * further gate test * attempt to fix cross-compilation issue in maturin * only use abi3 version for non-free-threaded cross-compile * fix incorrect version gate * further gate test onolder python versions * expand test-version-limits tests to cover pyo3-ffi build script logic * Apply David's review comments * expand cross-compilation tests * fix ruff * don't lower to abi3 on pypy or graalpy * fix cross-compiling from PyPy * skip stable abi lowering test on graalpy and pypy * fix logic error * simplify skipping logic * Fix Py_UNICODE_TODECIMAL definition for PyPy * rename USE_ABI3T_FORWARD_COMPATIBILITY to USE_STABLE_ABI_FORWARD_COMPATIBILITY * format * Always target abi3 on rustpython * fix clippy * Apply suggestions from code review Co-authored-by: David Hewitt <mail@davidhewitt.dev> * internal: use `hashbrown` internally if it's enabled (#6055) * use `hashbrown` internally if it's enabled * empty commit to trigger CI * empty commit to trigger CI * ci: install debug interpreter with uv (#5447) * ci: install debug interpreter with uv * try use rpath to link to exact interpreter * don't emit rpath on wasm targets * fixup clippy * update now ui-test has landed * fix clippy * Update pyo3-build-config/src/lib.rs Co-authored-by: Thomas Tanon <thomas@pellissier-tanon.fr> * newsfragment --------- Co-authored-by: Thomas Tanon <thomas@pellissier-tanon.fr> * fix reference count leak in `PyFrame::new` (#6094) * regenerate PYO3_PRINT_CONFIG example output * delete documentation line that links to PRs * fix build error from github suggestion --------- Co-authored-by: David Hewitt <mail@davidhewitt.dev> Co-authored-by: Thomas Tanon <thomas@pellissier-tanon.fr> Co-authored-by: person93 <person93.person93@gmail.com>
1 parent 3d48a54 commit a7fcba4

32 files changed

Lines changed: 1808 additions & 610 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ jobs:
641641
- uses: actions/checkout@v6.0.2
642642
- uses: actions/setup-python@v6
643643
with:
644-
python-version: "3.14"
644+
python-version: "3.15-dev"
645645
- uses: Swatinem/rust-cache@v2
646646
with:
647647
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}

Architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ In the [`pyo3-ffi`] crate, there is lots of conditional compilation such as `#[c
4646
`#[cfg(Py_3_8)]`, and `#[cfg(PyPy)]`.
4747
`Py_LIMITED_API` corresponds to `#define Py_LIMITED_API` macro in Python/C API.
4848
With `Py_LIMITED_API`, we can build a Python-version-agnostic binary called an
49-
[abi3 wheel](https://pyo3.rs/latest/building-and-distribution.html#py_limited_apiabi3).
49+
[abi3 wheel](https://pyo3.rs/latest/building-and-distribution.html#py_limited_apiabi3abi3t).
5050
`Py_3_8` means that the API is available from Python >= 3.8.
5151
There are also `Py_3_9`, `Py_3_10`, and so on.
5252
`PyPy` means that the API definition is for PyPy.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ abi3-py313 = ["abi3-py314", "pyo3-ffi/abi3-py313"]
130130
abi3-py314 = ["abi3-py315", "pyo3-ffi/abi3-py314"]
131131
abi3-py315 = ["abi3", "pyo3-ffi/abi3-py315"]
132132

133+
abi3t = ["pyo3-ffi/abi3t"]
134+
135+
# With abi3t, we can manually set the minimum Python version.
136+
abi3t-py315 = ["abi3t", "pyo3-ffi/abi3t-py315"]
137+
133138
# deprecated: no longer needed, raw-dylib is used instead
134139
generate-import-lib = ["pyo3-ffi/generate-import-lib"]
135140

guide/src/building-and-distribution.md

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,55 @@ An example output of doing this is shown below:
2828

2929
```console
3030
$ PYO3_PRINT_CONFIG=1 cargo build
31-
Compiling pyo3 v0.14.1 (/home/david/dev/pyo3)
32-
error: failed to run custom build command for `pyo3 v0.14.1 (/home/david/dev/pyo3)`
31+
Compiling pyo3-ffi v0.28.3 (/Users/goldbaum/Documents/pyo3/pyo3-ffi)
32+
error: failed to run custom build command for `pyo3-ffi v0.28.3 (/Users/goldbaum/Documents/pyo3/pyo3-ffi)`
3333

3434
Caused by:
35-
process didn't exit successfully: `/home/david/dev/pyo3/target/debug/build/pyo3-7a8cf4fe22e959b7/build-script-build` (exit status: 101)
35+
process didn't exit successfully: `/Users/goldbaum/Documents/pyo3/target/debug/build/pyo3-ffi-71f0882ba738a1f0/build-script-build` (exit status: 101)
3636
--- stdout
37+
cargo:rustc-check-cfg=cfg(Py_LIMITED_API)
38+
cargo:rustc-check-cfg=cfg(Py_GIL_DISABLED)
39+
cargo:rustc-check-cfg=cfg(PyPy)
40+
cargo:rustc-check-cfg=cfg(GraalPy)
41+
cargo:rustc-check-cfg=cfg(RustPython)
42+
cargo:rustc-check-cfg=cfg(py_sys_config, values("Py_DEBUG", "Py_REF_DEBUG", "Py_TRACE_REFS", "COUNT_ALLOCS"))
43+
cargo:rustc-check-cfg=cfg(Py_3_8)
44+
cargo:rustc-check-cfg=cfg(Py_3_9)
45+
cargo:rustc-check-cfg=cfg(Py_3_10)
46+
cargo:rustc-check-cfg=cfg(Py_3_11)
47+
cargo:rustc-check-cfg=cfg(Py_3_12)
48+
cargo:rustc-check-cfg=cfg(Py_3_13)
49+
cargo:rustc-check-cfg=cfg(Py_3_14)
50+
cargo:rustc-check-cfg=cfg(Py_3_15)
51+
cargo:rustc-check-cfg=cfg(Py_3_16)
52+
cargo:rustc-check-cfg=cfg(pyo3_dll, values("python3", "python3_d", "python3t", "python3t_d", "python38", "python38_d", "python39", "python39_d", "python310", "python310_d", "python311", "python311_d", "python312", "python312_d", "python313", "python313_d", "python313t", "python313t_d", "python314", "python314_d", "python314t", "python314t_d", "python315", "python315_d", "python315t", "python315t_d", "python316", "python316_d", "python316t", "python316t_d", "libpypy3.11-c"))
53+
cargo:rerun-if-env-changed=PYO3_CONFIG_FILE
3754
cargo:rerun-if-env-changed=PYO3_CROSS
3855
cargo:rerun-if-env-changed=PYO3_CROSS_LIB_DIR
3956
cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_VERSION
57+
cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_IMPLEMENTATION
58+
cargo:rerun-if-env-changed=PYO3_NO_PYTHON
59+
cargo:rerun-if-env-changed=PYO3_ENVIRONMENT_SIGNATURE
60+
cargo:rerun-if-env-changed=PYO3_PYTHON
61+
cargo:rerun-if-env-changed=VIRTUAL_ENV
62+
cargo:rerun-if-env-changed=CONDA_PREFIX
63+
cargo:rerun-if-env-changed=PATH
4064
cargo:rerun-if-env-changed=PYO3_PRINT_CONFIG
4165

4266
-- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --
4367
implementation=CPython
44-
version=3.8
68+
version=3.14
4569
shared=true
46-
abi3=false
47-
lib_name=python3.8
48-
lib_dir=/usr/lib
49-
executable=/usr/bin/python
70+
target_abi=CPython-free_threaded-3.14
71+
lib_name=python3.14t
72+
lib_dir=/Users/goldbaum/.pyenv/versions/3.14.5t/lib
73+
executable=/Users/goldbaum/.pyenv/versions/3.14.5t/bin/python
5074
pointer_width=64
51-
build_flags=
75+
build_flags=Py_GIL_DISABLED
76+
python_framework_prefix=
5277
suppress_build_script_link_lines=false
78+
79+
note: unset the PYO3_PRINT_CONFIG environment variable and retry to compile with the above config
5380
```
5481

5582
The `PYO3_ENVIRONMENT_SIGNATURE` environment variable can be used to trigger rebuilds when its value changes, it has no other effect.
@@ -74,7 +101,8 @@ The PyO3 ecosystem has two packaging tools, [`maturin`] and [`setuptools-rust`],
74101
PyO3 has some functionality for configuring projects when building Python extension modules:
75102

76103
- The `PYO3_BUILD_EXTENSION_MODULE` environment variable, which must be set when building Python extension modules. `maturin` and `setuptools-rust` set this automatically.
77-
- The `abi3` Cargo feature and its version-specific `abi3-pyXY` companions, which are used to opt-in to the limited Python API in order to support multiple Python versions in a single wheel.
104+
- The `abi3` and `abi3t` Cargo feature and their version-specific `abi3-pyXY` and `abi3t-pyXY` companions, which are used to opt-in to the limited Python API and a flavor of stable ABI in order to support multiple Python versions in a single wheel.
105+
The free-threaded build of CPython cannot load `abi3` wheels but both builds can load `abi3t` wheels.
78106

79107
This section describes the packaging tools before describing how to build manually without them.
80108
It then proceeds with an explanation of the `PYO3_BUILD_EXTENSION_MODULE` environment variable.
@@ -212,60 +240,72 @@ This should only be set when building a library for distribution.
212240
>
213241
> Projects are encouraged to migrate off the feature, as it caused [major development pain](faq.md#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror) due to the lack of linking.
214242
215-
### `Py_LIMITED_API`/`abi3`
243+
### `Py_LIMITED_API`/`abi3`/`abi3t`
216244

217245
By default, Python extension modules can only be used with the same Python version they were compiled against.
218246
For example, an extension module built for Python 3.5 can't be imported in Python 3.8.
219247
[PEP 384](https://www.python.org/dev/peps/pep-0384/) introduced the idea of the limited Python API, which would have a stable ABI enabling extension modules built with it to be used against multiple Python versions.
220-
This is also known as `abi3`.
248+
The ABI defined by PEP 384 is called `abi3`, since it's stable for all Python 3.X releases.
249+
In 2026, the steering council approved [PEP 803](https://www.python.org/dev/peps/pep-0803/)) which defines a new stable ABI, `abi3t`, that can be used on the free-threaded build of CPython.
250+
Extensions built using the `abit3` ABI are importable on both the GIL-enabled and free-threaded builds of any CPython version newer than the target version.
251+
So, `abi3t` extensions built for the Python 3.15 limited API will be importable on Python 3.15, 3.16, 3.17, and all future Python 3.X versions.
221252

222-
The advantage of building extension modules using the limited Python API is that package vendors only need to build and distribute a single copy (for each OS / architecture), and users can install it on all Python versions from the [minimum version](#minimum-python-version-for-abi3) and up.
253+
The advantage of building extension modules using the limited Python API is that package vendors only need to build and distribute a single copy (for each OS / architecture), and users can install it on all Python versions from the [minimum version](#minimum-python-version-for-abi3-and-abi3t-builds) and up.
223254
The downside of this is that PyO3 can't use optimizations which rely on being compiled against a known exact Python version.
224255
It's up to you to decide whether this matters for your extension module.
225-
It's also possible to design your extension module such that you can distribute `abi3` wheels but allow users compiling from source to benefit from additional optimizations - see the [support for multiple python versions](./building-and-distribution/multiple-python-versions.md) section of this guide, in particular the `#[cfg(Py_LIMITED_API)]` flag.
256+
It's also possible to design your extension module such that you can distribute `abi3` or `abi3t` wheels but allow users compiling from source to benefit from additional optimizations - see the [support for multiple python versions](./building-and-distribution/multiple-python-versions.md) section of this guide, in particular the `#[cfg(Py_LIMITED_API)]` flag.
226257

227-
There are three steps involved in making use of `abi3` when building Python packages as wheels:
258+
There are three steps involved in targeting `abi3` or `abi3t` when building Python packages as wheels:
228259

229-
1. Enable the `abi3` feature in `pyo3`.
260+
1. Enable the `abi3` and/or `abi3t` feature in `pyo3`.
230261
This ensures `pyo3` only calls Python C-API functions which are part of the stable API, and on Windows also ensures that the project links against the correct shared object (no special behavior is required on other platforms):
231262

232263
```toml
233264
[dependencies]
234-
pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["abi3"] }
265+
pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["abi3", "abi3t"] }
235266
```
236267

237-
2. Ensure that the built shared objects are correctly marked as `abi3`.
238-
This is accomplished by telling your build system that you're using the limited API.
239-
[`maturin`] >= 0.9.0 and [`setuptools-rust`] >= 0.11.4 support `abi3` wheels.
268+
Enabling both the `"abi3"` and `"abi3t"` features will produce an `abi3` extension if targeting Python 3.14 or older and an `abi3t` extension if targeting 3.15 or newer.
269+
If you always want to produce `abi3t` wheels, you can enable just the `"abi3t"` feature, which will produce extensions targeting `abi3t` if the host interpreter is Python 3.15 or newer, but will produce version-specific extensions otherwise.
270+
If you only enable the `"abi3"` feature, you will produce `abi3` extensions if the host interpreter is a GIL-enabled build of CPython and version-specific extensions otherwise.
240271

241-
See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://github.com/PyO3/setuptools-rust/pull/82) for more.
272+
We suggest enabling both features and using multiple python interpreters to build several wheels.
273+
You can build an `abi3` wheel for your minimum supported Python version, a `cp314t` version-specific wheel using a free-threaded Python 3.14 interpreter, and an `abi3t` wheel using a Python 3.15 interpreter.
274+
This will produce wheels targeting all versions of CPython that PyO3 supports.
242275

243-
3. Ensure that the `.whl` is correctly marked as `abi3`.
276+
2. Ensure that the built shared objects are correctly marked as `abi3` and `abi3t`.
277+
This is accomplished by telling your build system that you're using the limited API.
278+
[`maturin`] >= 0.9.0 and [`setuptools-rust`] >= 0.11.4 support `abi3` wheels and [`maturin`] >= 1.14 supports `abi3t` wheels.
279+
280+
3. Ensure that the `.whl` is correctly marked as `abi3` or `abi3t`.
244281
For projects using `setuptools`, this is accomplished by passing `--py-limited-api=cp3x` (where `x` is the minimum Python version supported by the wheel, e.g. `--py-limited-api=cp35` for Python 3.5) to `setup.py bdist_wheel`.
245282

246-
#### Minimum Python version for `abi3`
283+
#### Minimum Python version for `abi3` and `abi3t` builds
247284

248285
Because a single `abi3` wheel can be used with many different Python versions, PyO3 has feature flags `abi3-py38`, `abi3-py39`, `abi3-py310` etc. to set the minimum required Python version for your `abi3` wheel.
249286
For example, if you set the `abi3-py38` feature, your wheel can be used on all Python 3 versions from Python 3.8 and up.
250287
`maturin` and `setuptools-rust` will give the wheel a name like `my-extension-1.0-cp38-abi3-manylinux2020_x86_64.whl`.
251288

289+
Similarly, there is an `abi3t-py315` feature and future PyO3 versions will offer `abi3t-py316` and so on.
290+
252291
As your extension module may be run with multiple different Python versions you may occasionally find you need to check the Python version at runtime to customize behavior.
253292
See [the relevant section of this guide](./building-and-distribution/multiple-python-versions.md#checking-the-python-version-at-runtime) on supporting multiple Python versions at runtime.
254293

255294
PyO3 is only able to link your extension module to abi3 version up to and including your host Python version.
256295
E.g., if you set `abi3-py39` and try to compile the crate with a host of Python 3.8, the build will fail.
257296

258297
> [!NOTE]
259-
> If you set more that one of these `abi3` version feature flags the lowest version always wins. For example, with both `abi3-py38` and `abi3-py39` set, PyO3 would build a wheel which supports Python 3.8 and up.
298+
> If you set more that one of these `abi3` version feature flags the lowest version always wins.
299+
> For example, with both `abi3-py38` and `abi3-py39` set, PyO3 would build a wheel which supports Python 3.8 and up.
260300
261-
#### Building `abi3` extension modules without a Python interpreter
301+
#### Building stable ABI extension modules without a Python interpreter
262302

263303
As an advanced feature, you can build a PyO3 wheel without calling Python interpreter with the environment variable `PYO3_NO_PYTHON` set.
264-
Also, if the build host Python interpreter is not found or is too old or otherwise unusable, PyO3 will still attempt to compile `abi3` extension modules after displaying a warning message.
304+
Also, if the build host Python interpreter is not found or is too old or otherwise unusable, PyO3 will still attempt to compile stable ABI extension modules after displaying a warning message.
265305

266306
#### Missing features
267307

268-
Due to limitations in the Python API, there are a few `pyo3` features that do not work when compiling for `abi3`.
308+
Due to limitations in the Python API, there are a few `pyo3` features that do not work when compiling for the stable ABI.
269309
These are:
270310

271311
- `#[pyo3(text_signature = "...")]` does not work on classes until Python 3.10 or greater.

guide/src/building-and-distribution/multiple-python-versions.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn function_only_supported_on_python_3_8_and_up() {}
2525
fn function_only_supported_before_python_3_8() {}
2626
2727
#[cfg(not(Py_LIMITED_API))]
28-
fn function_incompatible_with_abi3_feature() {}
28+
fn function_incompatible_with_stable_abi_feature() {}
2929
```
3030

3131
The following sections first show how to add these `#[cfg]` flags to your build process, and then cover some common patterns flags in a little more detail.
@@ -72,12 +72,19 @@ There are similar options `Py_3_9`, `Py_3_10`, `Py_3_11` and so on for each mino
7272

7373
This `#[cfg]` marks code that will only be present on Python versions before (but not including) Python 3.8.
7474

75+
```text
76+
#[cfg(Py_GIL_DISABLED)]
77+
```
78+
79+
This `#[cfg]` marks code that will only be present for free-threaded builds of CPython.
80+
You might use this in situations where the GIL provides thread safety but the free-threaded build needs extra synchronization logic.
81+
7582
```text
7683
#[cfg(not(Py_LIMITED_API))]
7784
```
7885

79-
This `#[cfg]` marks code that is only available when building for the unlimited Python API (i.e. PyO3's `abi3` feature is not enabled).
80-
This might be useful if you want to ship your extension module as an `abi3` wheel and also allow users to compile it from source to make use of optimizations only possible with the unlimited API.
86+
This `#[cfg]` marks code that is only available when building for the unlimited Python API (i.e. PyO3's `abi3` or `abi3t` features are not enabled).
87+
This might be useful if you want to ship your extension module as an `abi3` or `abi3t` wheel and also allow users to compile it from source to make use of optimizations only possible with the unlimited API.
8188

8289
```text
8390
#[cfg(any(Py_3_9, not(Py_LIMITED_API)))]
@@ -86,6 +93,13 @@ This might be useful if you want to ship your extension module as an `abi3` whee
8693
This `#[cfg]` marks code which is available when running Python 3.9 or newer, or when using the unlimited API with an older Python version.
8794
Patterns like this are commonly seen on Python APIs which were added to the limited Python API in a specific minor version.
8895

96+
```text
97+
#[cfg(all(Py_GIL_DISABLED, Py_LIMITED_API))]
98+
```
99+
100+
This marks code which is available only for the free-threaded `abi3t` stable ABI.
101+
You might use this if you need some sort of custom locking implementation in code that makes strong assumptions about the GIL providing thread safety.
102+
89103
```text
90104
#[cfg(PyPy)]
91105
```
@@ -94,7 +108,7 @@ This `#[cfg]` marks code which is running on PyPy.
94108

95109
## Checking the Python version at runtime
96110

97-
When building with PyO3's `abi3` feature, your extension module will be compiled against a specific [minimum version](../building-and-distribution.md#minimum-python-version-for-abi3) of Python, but may be running on newer Python versions.
111+
When building with PyO3's `abi3` or `abi3t` features, your extension module will be compiled against a specific [minimum version](../building-and-distribution.md#minimum-python-version-for-abi3-and-abi3t-builds) of Python, but may be running on newer Python versions.
98112

99113
For example with PyO3's `abi3-py38` feature, your extension module will be compiled as if it were for Python 3.8.
100114
If you were using `pyo3-build-config`, `#[cfg(Py_3_8)]` would be present.

0 commit comments

Comments
 (0)