Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
215 commits
Select commit Hold shift + click to select a range
eb53c2d
Use PyModExport and PyABIInfo APIs in pymodule implementation
ngoldbaum Jan 26, 2026
e41b509
Add PyModExport function
ngoldbaum Jan 26, 2026
91becaf
DNM: temporarily disable append_to_inittab doctest
ngoldbaum Jan 26, 2026
1020688
fix issues seen on older pythons in CI
ngoldbaum Jan 26, 2026
3afa9ae
fix incorrect ModuleDef setup on 3.15
ngoldbaum Jan 26, 2026
f8d6cae
Expose both the PyInit and PyModExport initialization hooks
ngoldbaum Jan 27, 2026
a590874
fix clippy
ngoldbaum Jan 27, 2026
a96219f
add changelog entry
ngoldbaum Jan 27, 2026
e7ac9c0
try use only slots for both init hooks on 3.15
ngoldbaum Jan 29, 2026
d981be7
Always pass m_name and m_doc, following cpython-gh-144340
ngoldbaum Jan 30, 2026
55b6acd
WIP: opaque pyobject support (without Py_GIL_DISABLED)
ngoldbaum Feb 13, 2026
733aa82
delete debug prints
ngoldbaum Feb 13, 2026
c43061e
WIP: fix segfault
ngoldbaum Feb 13, 2026
3812a64
disable append_to_inittab tests
ngoldbaum Feb 13, 2026
25a65a6
fix clippy
ngoldbaum Feb 13, 2026
4a83024
fix ruff
ngoldbaum Feb 13, 2026
9d0e2ed
implement David's suggestion for pyobject_subclassable_native_type
ngoldbaum Feb 13, 2026
a78b5df
replace skipped test with real test
ngoldbaum Feb 13, 2026
42a73e1
fix check-feature-powerset
ngoldbaum Feb 13, 2026
060c3ca
fix clippy-all
ngoldbaum Feb 13, 2026
c1bd2c7
skip test that depend on struct layout on opaque pyobject builds
ngoldbaum Feb 13, 2026
ba8b09a
Expose PyModuleDef as an opaque pointer on opaque PyObject builds
ngoldbaum Feb 16, 2026
f15a7fc
add comments about location of opaque pointers in CPython headers
ngoldbaum Feb 16, 2026
3fa17d0
fix test_inherited_size
ngoldbaum Feb 16, 2026
1970421
Fix doctest on _Py_OPAQUE_PYOBJECT builds
ngoldbaum Feb 17, 2026
57c2045
Merge branch 'main' into opaque-pyobject
ngoldbaum Mar 3, 2026
f80849e
fix build error on non-opaque builds
ngoldbaum Mar 3, 2026
c0805a9
mark BaseWithoutData as subclassable
ngoldbaum Mar 3, 2026
719cef5
relax assert for Windows
ngoldbaum Mar 3, 2026
072ef0a
Make PyAny a PyVarObject only on the opaque PyObject build
ngoldbaum Mar 4, 2026
0b743b6
Merge branch 'main' into opaque-pyobject
ngoldbaum Mar 24, 2026
264307f
fix merge conflict resolution error
ngoldbaum Mar 24, 2026
ed48e75
fix buggy assertion
ngoldbaum Mar 24, 2026
b4138c4
Expose critical section in the limited API starting in Python 3.15
ngoldbaum Mar 24, 2026
f3ee6af
expose critical section API in limited API
ngoldbaum Mar 24, 2026
ba47f50
disable warning in pyo3-ffi build script on sufficiently new Pythons
ngoldbaum Mar 25, 2026
e52cb59
fixup features
ngoldbaum Mar 26, 2026
3cf60b1
Add missing error handling for `PyModule_FromSlotsAndSpec`
ngoldbaum Mar 26, 2026
607e4b0
Merge branch 'main' into expose-critical-section
ngoldbaum Mar 26, 2026
449eb8a
passes cargo tests with the abi3t feature enabled
ngoldbaum Mar 26, 2026
5371fd8
passes unit tests on GIL-enabled build with abi3t feature
ngoldbaum Mar 26, 2026
19d78d2
fix merge mistake
ngoldbaum Mar 26, 2026
14bf4d1
rustfmt
ngoldbaum Mar 27, 2026
91a0419
fix FIXME
ngoldbaum Mar 27, 2026
285e79c
Merge branch 'main' into opaque-pyobject
ngoldbaum Apr 3, 2026
9f23720
replace extern "C" with extern_libpython!
ngoldbaum Apr 3, 2026
d38c274
fix test
ngoldbaum Apr 3, 2026
e2d8f8c
Merge branch 'opaque-pyobject' into expose-critical-section
ngoldbaum Apr 6, 2026
15ff21c
fix merge error
ngoldbaum Apr 6, 2026
930e1a9
Allow abi3t builds without critical section bindings
ngoldbaum Apr 6, 2026
a609f8c
add FIXME
ngoldbaum Apr 6, 2026
dd5b3a9
remove default feature
ngoldbaum Apr 6, 2026
7419b86
fix syntax error in noxfile
ngoldbaum Apr 6, 2026
603aaf9
fix issues spotted by clippy
ngoldbaum Apr 6, 2026
c9b9157
bring over refactoring from PyO3 PR
ngoldbaum Apr 7, 2026
7560348
working!
ngoldbaum Apr 7, 2026
becc8af
Fix memory leak of iterator
ngoldbaum Apr 8, 2026
b734e77
fix size hints
ngoldbaum Apr 8, 2026
a6c8710
delete debug statement
ngoldbaum Apr 8, 2026
a827be9
Use Py_TARGET_ABI3T
ngoldbaum Apr 8, 2026
0ceda48
run formatter
ngoldbaum Apr 8, 2026
d1ef669
fix check-feature-powerset
ngoldbaum Apr 8, 2026
26c7dc0
increment SUPPORTED_VERSIONS_CPYTHON.max
ngoldbaum Apr 8, 2026
95ca7d2
fix conditional compilation for critical section API
ngoldbaum Apr 8, 2026
b596885
fix ruff
ngoldbaum Apr 8, 2026
22a5332
fix incorrect conditional compilation guargs
ngoldbaum Apr 8, 2026
ec1269a
fix noxfile and ban abi3t builds on 3.14 and older
ngoldbaum Apr 8, 2026
04eb8bb
ruff format
ngoldbaum Apr 8, 2026
101949c
attempt to fix semver-checks
ngoldbaum Apr 8, 2026
828afdd
fix test-version-limits
ngoldbaum Apr 8, 2026
cd78153
fix issues spotted by claude
ngoldbaum Apr 8, 2026
5e5671e
strip 't' for version parsing
ngoldbaum Apr 8, 2026
266527f
Adjust comments and error messages
ngoldbaum Apr 8, 2026
294a4f0
fix compiler warning
ngoldbaum Apr 8, 2026
b37445d
more noxfile fixes
ngoldbaum Apr 8, 2026
a2584f5
fix ruff
ngoldbaum Apr 8, 2026
1b5ec7a
use a 3.15 interpreter to run the feature-powerset tests
ngoldbaum Apr 8, 2026
553ef54
fix a few more issues caught by claude
ngoldbaum Apr 8, 2026
2f755ae
add comment
ngoldbaum Apr 8, 2026
f326c74
Merge branch 'main' into opaque-pyobject
ngoldbaum Apr 15, 2026
710e835
use correct windows library name for abi3t
ngoldbaum Apr 16, 2026
861edd8
try to fix linking
ngoldbaum Apr 16, 2026
c8e40b9
add CPythonABI enum for pyo3-build-config InterpreterConfig
ngoldbaum Mar 30, 2026
45d5cc1
add release note
ngoldbaum Mar 30, 2026
e3dc133
store ABI details in a new PythonAbi struct
ngoldbaum Apr 15, 2026
966a82f
fix nox config file
ngoldbaum Apr 15, 2026
9d9a0e2
fix ffi-check
ngoldbaum Apr 17, 2026
f52f1d8
Refactor to use the builder pattern for InterpreterConfig
ngoldbaum Apr 24, 2026
77039d2
revert noxfile change
ngoldbaum Apr 24, 2026
0765953
fix clippy-all
ngoldbaum Apr 24, 2026
cde688c
fix build-config test
ngoldbaum Apr 24, 2026
b11b262
Fix issues linking against onld stable ABI versions
ngoldbaum Apr 24, 2026
9cb420a
remove load-bearing debug impls
ngoldbaum Apr 28, 2026
7fe98ab
fix issue with InterpreterConfig::from_interpreter never returning an…
ngoldbaum Apr 28, 2026
41be837
fix clippy lint
ngoldbaum Apr 28, 2026
58162a4
fix PythonAbiBuilder::from_build_env
ngoldbaum Apr 28, 2026
48a7d20
Only target abi3 for non-free-threaded builds
ngoldbaum Apr 28, 2026
c899390
fix logic error in InterpreterConfig::from_reader
ngoldbaum Apr 28, 2026
e0c8826
Merge branch 'abi-tag-refactor' into opaque-pyobject
ngoldbaum Apr 30, 2026
942ecc9
smooth over merge
ngoldbaum Apr 30, 2026
90bef3c
tests pass
ngoldbaum Apr 30, 2026
ea2eab7
Add stub abi3t implementation
ngoldbaum Apr 30, 2026
8354ef7
Improve test coverage
ngoldbaum Apr 30, 2026
6854756
fix clippy
ngoldbaum Apr 30, 2026
3a21665
fix 3.14t abi3 builds being a no-op
ngoldbaum Apr 30, 2026
97087aa
fix ffi-check to account for private _tp_iteritem PyTypeObject slot
ngoldbaum May 2, 2026
80a591c
expose the critical section API
ngoldbaum May 3, 2026
be1b77f
Add initial support for Python 3.15.0b1
ngoldbaum May 3, 2026
090d11d
fix ffi docstring example
ngoldbaum May 3, 2026
81daf34
typo fix
ngoldbaum May 3, 2026
3ce9275
fix ffi-check macro
ngoldbaum May 4, 2026
4763179
fix ffi examples
ngoldbaum May 4, 2026
b6d6f99
make PySlot_FUNC a Rust macro
ngoldbaum May 4, 2026
4828759
fix clippy
ngoldbaum May 4, 2026
1e2a2c3
keep macros version-independent
ngoldbaum May 4, 2026
ecc365a
format extern_libpython blocks
ngoldbaum May 5, 2026
14c80db
use an explicit type in the PySlot_FUNC macro
ngoldbaum May 5, 2026
af92c51
fix issues seen in CI builds on older Pythons
ngoldbaum May 5, 2026
17583a8
fix one more spot in the ffi tests
ngoldbaum May 5, 2026
1caf685
fix type error in string-sum implementation
ngoldbaum May 5, 2026
85bc302
add changelog entries
ngoldbaum May 5, 2026
75b8962
remove unnecessary PartialEq implementation
ngoldbaum May 5, 2026
cbedefc
use std::slice::from_raw_parts instead of libc::memcmp
ngoldbaum May 5, 2026
ec46a20
re-add incorrectly deleted end slot
ngoldbaum May 5, 2026
63486a3
Use PartialEq on Python 3.14 and older
ngoldbaum May 5, 2026
f4e86f7
fix compiler error due to missing Debug impl
ngoldbaum May 5, 2026
ee68ff0
Finish updating slots_generated and adjust types for slot IDs
ngoldbaum May 5, 2026
816fd56
add missing cfg gate
ngoldbaum May 5, 2026
8e04584
remove unused import
ngoldbaum May 5, 2026
1e073e7
Merge branch 'main' into abi-tag-refactor
ngoldbaum May 6, 2026
8fd0acb
make InterpreterConfigBuilder.extra_build_script_lines a Vec
ngoldbaum May 6, 2026
7d5881c
Add an abi3 version sanitization function, adjust error handling
ngoldbaum May 6, 2026
5c73549
Adjust comment fragment, add TODO
ngoldbaum May 6, 2026
1e71ed4
apply focused comments from self-review
ngoldbaum May 6, 2026
b3ef667
simplify modeling of free-threaded builds and Py_GIL_DISABLED build flag
ngoldbaum May 6, 2026
df3bf04
fix clippy
ngoldbaum May 6, 2026
fbacc26
relax accepting Py_GIL_DISABLED in build_flags if no target ABI is set
ngoldbaum May 6, 2026
0efa792
more fixes and suggestions from claude
ngoldbaum May 6, 2026
766ecbf
fix fallback to version-specific builds for free-threaded abi3 builds
ngoldbaum May 6, 2026
deb83a7
More minor fixes
ngoldbaum May 6, 2026
9174ca7
expand test coverage
ngoldbaum May 6, 2026
01a6b0a
more fixes
ngoldbaum May 6, 2026
b8b5bff
update changelog entries
ngoldbaum May 6, 2026
285eb19
Add missing exports to public pyo3-build-config API
ngoldbaum May 6, 2026
95be8d0
fix linux test
ngoldbaum May 6, 2026
9a33592
minor touch-ups
ngoldbaum May 7, 2026
0c7b53a
minor touch-ups
ngoldbaum May 7, 2026
505d9e1
skip redundant test
ngoldbaum May 7, 2026
20c8f57
whitespace
ngoldbaum May 7, 2026
afd0eec
more tweaks
ngoldbaum May 7, 2026
b40805a
grammar in comment
ngoldbaum May 7, 2026
4dba451
Merge branch 'pep-820' into opaque-pyobject
ngoldbaum May 7, 2026
107ab27
fix cfg gating
ngoldbaum May 7, 2026
5334eaa
activate abi3t builds
ngoldbaum May 8, 2026
92ad56c
Merge branch 'abi-tag-refactor' into opaque-pyobject
ngoldbaum May 11, 2026
12fa458
fix PyMutex gating
ngoldbaum May 11, 2026
9337202
simplify windows stable ABI lib name text manipulation
ngoldbaum May 11, 2026
f76a5c8
Merge branch 'main' into opaque-pyobject
ngoldbaum May 11, 2026
ab1333b
Merge branch 'main' into opaque-pyobject
ngoldbaum May 11, 2026
52124f8
fix ruff
ngoldbaum May 11, 2026
d9133fe
fix cfg gating for critical section API
ngoldbaum May 11, 2026
87d6ae4
Merge branch 'main' into opaque-pyobject
ngoldbaum May 12, 2026
1aea9b0
adjust PyMutex cfg gating
ngoldbaum May 12, 2026
4bb2202
attempt to fix abi3 feature for free-threaded targets from a config file
ngoldbaum May 12, 2026
3e1ec34
introduce a builder for InterpreterConfig
ngoldbaum Apr 24, 2026
781648b
newsfragments
davidhewitt May 12, 2026
f091c9a
fixup ubuntu test
davidhewitt May 12, 2026
ef4fb92
fixup
davidhewitt May 12, 2026
7ee1ed0
fix missing `mut`
davidhewitt May 12, 2026
0709d59
Merge remote-tracking branch 'origin/main' into opaque-pyobject
davidhewitt May 12, 2026
f509e72
fix merge error
ngoldbaum May 12, 2026
f8aa9a5
fix incorrect merge conflict resolution
ngoldbaum May 12, 2026
037563a
refactor is_abi3(t) in get_abit3(t)_version.is_some()
ngoldbaum May 12, 2026
23bf337
apply suggestions from icxolu
ngoldbaum May 12, 2026
14c2d9f
Make PythonAbi fields private, replace with accessor methods
ngoldbaum May 12, 2026
9af08e1
fix test-version-limits
ngoldbaum May 12, 2026
13448e3
Apply suggestions from code review
davidhewitt May 12, 2026
7ba3daf
review feedback
davidhewitt May 12, 2026
a59f8dc
Merge branch 'interpreter-builder' into opaque-pyobject
ngoldbaum May 12, 2026
4bcd9fa
fix merge conflicts
ngoldbaum May 12, 2026
5a47645
Merge branch 'interpreter-builder' into opaque-pyobject
ngoldbaum May 12, 2026
9a32727
fix clippy
ngoldbaum May 12, 2026
c536ebe
re-enable functionality that depends on critical sections
ngoldbaum May 12, 2026
53014c0
revert more unnecessary changes
ngoldbaum May 12, 2026
208fe8f
reduce diff further
ngoldbaum May 13, 2026
3b32ea1
Merge branch 'main' into opaque-pyobject
ngoldbaum May 13, 2026
d9ade28
fix cfg gating for critical section guards
ngoldbaum May 13, 2026
dab0337
Merge branch 'main' into opaque-pyobject
ngoldbaum May 14, 2026
0e4d6d8
Merging with David's default_lib_name_for_config_file branch
ngoldbaum May 14, 2026
c85c89c
Merge branch 'main' into opaque-pyobject
ngoldbaum May 14, 2026
6c514fb
remove unnecessary get_abi3_version().is_some()
ngoldbaum May 14, 2026
5518b84
bring back Option
ngoldbaum May 14, 2026
0123231
fix comparison for ABI3T_FORWARD_COMPATIBILITY
ngoldbaum May 14, 2026
ed9807c
Apply minor suggestions from code review
ngoldbaum May 14, 2026
2b8d7a2
delete docs for non-pub field
ngoldbaum May 14, 2026
b772cf3
Don't define a Py_TARGET_ABI3T cfg check
ngoldbaum May 14, 2026
c345a0d
fix merge errors
ngoldbaum May 14, 2026
caf1af8
implement Icxolu's suggestion to get rid of is_abi3 and is_abi3t
ngoldbaum May 14, 2026
2531c96
fix clippy-all
ngoldbaum May 14, 2026
1908b69
delete unnecessary get_abit3_version() check
ngoldbaum May 14, 2026
d232ac7
Allow simultaneously enabling abi3 and abi3t features
ngoldbaum May 14, 2026
941aca4
add release notes
ngoldbaum May 14, 2026
4d38642
fix ruff
ngoldbaum May 14, 2026
dd200da
really fix ruff
ngoldbaum May 14, 2026
fd0f14f
don't run abi3-py38 tests on pypy
ngoldbaum May 15, 2026
13e04c7
add docs
ngoldbaum May 15, 2026
ebc99b9
disable windows gil-enabled abi3t builds to work around upstream bug
ngoldbaum May 15, 2026
6214c3d
fix guide formatting
ngoldbaum May 15, 2026
7725030
fix internal links in guide
ngoldbaum May 15, 2026
303877a
fix typo in noxfile
ngoldbaum May 15, 2026
cb5a464
Merge branch 'main' into opaque-pyobject
ngoldbaum May 16, 2026
607f8bc
fix release notes
ngoldbaum May 16, 2026
335fa8a
revert now-unnecessary CI configuration change
ngoldbaum May 16, 2026
be8b1a1
fix abi3t-py315 feature not re-exporting pyo3-ffi feature
ngoldbaum May 18, 2026
68ffca0
fix typos in comments
ngoldbaum May 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ jobs:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
python-version: "3.15-dev"
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
Expand Down
2 changes: 1 addition & 1 deletion Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ In the [`pyo3-ffi`] crate, there is lots of conditional compilation such as `#[c
`#[cfg(Py_3_8)]`, and `#[cfg(PyPy)]`.
`Py_LIMITED_API` corresponds to `#define Py_LIMITED_API` macro in Python/C API.
With `Py_LIMITED_API`, we can build a Python-version-agnostic binary called an
[abi3 wheel](https://pyo3.rs/latest/building-and-distribution.html#py_limited_apiabi3).
[abi3 wheel](https://pyo3.rs/latest/building-and-distribution.html#py_limited_apiabi3abi3t).
`Py_3_8` means that the API is available from Python >= 3.8.
There are also `Py_3_9`, `Py_3_10`, and so on.
`PyPy` means that the API definition is for PyPy.
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ abi3-py313 = ["abi3-py314", "pyo3-ffi/abi3-py313"]
abi3-py314 = ["abi3-py315", "pyo3-ffi/abi3-py314"]
abi3-py315 = ["abi3", "pyo3-ffi/abi3-py315"]

abi3t = ["pyo3-ffi/abi3t"]

# With abi3t, we can manually set the minimum Python version.
abi3t-py315 = ["abi3t", "pyo3-ffi/abi3t-py315"]

# deprecated: no longer needed, raw-dylib is used instead
generate-import-lib = ["pyo3-ffi/generate-import-lib"]

Expand Down
96 changes: 70 additions & 26 deletions guide/src/building-and-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,57 @@ An example output of doing this is shown below:

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

Caused by:
process didn't exit successfully: `/home/david/dev/pyo3/target/debug/build/pyo3-7a8cf4fe22e959b7/build-script-build` (exit status: 101)
process didn't exit successfully: `/Users/goldbaum/Documents/pyo3/target/debug/build/pyo3-ffi-120b26b17564bf98/build-script-build` (exit status: 101)
--- stdout
cargo:rustc-check-cfg=cfg(Py_LIMITED_API)
cargo:rustc-check-cfg=cfg(Py_GIL_DISABLED)
cargo:rustc-check-cfg=cfg(PyPy)
cargo:rustc-check-cfg=cfg(GraalPy)
cargo:rustc-check-cfg=cfg(RustPython)
cargo:rustc-check-cfg=cfg(py_sys_config, values("Py_DEBUG", "Py_REF_DEBUG", "Py_TRACE_REFS", "COUNT_ALLOCS"))
cargo:rustc-check-cfg=cfg(pyo3_disable_reference_pool)
cargo:rustc-check-cfg=cfg(pyo3_leak_on_drop_without_reference_pool)
cargo:rustc-check-cfg=cfg(Py_3_8)
cargo:rustc-check-cfg=cfg(Py_3_9)
cargo:rustc-check-cfg=cfg(Py_3_10)
cargo:rustc-check-cfg=cfg(Py_3_11)
cargo:rustc-check-cfg=cfg(Py_3_12)
cargo:rustc-check-cfg=cfg(Py_3_13)
cargo:rustc-check-cfg=cfg(Py_3_14)
cargo:rustc-check-cfg=cfg(Py_3_15)
cargo:rustc-check-cfg=cfg(Py_3_16)
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"))
cargo:rerun-if-env-changed=PYO3_CONFIG_FILE
cargo:rerun-if-env-changed=PYO3_CROSS
cargo:rerun-if-env-changed=PYO3_CROSS_LIB_DIR
cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_VERSION
cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_IMPLEMENTATION
cargo:rerun-if-env-changed=PYO3_NO_PYTHON
cargo:rerun-if-env-changed=PYO3_ENVIRONMENT_SIGNATURE
cargo:rerun-if-env-changed=PYO3_PYTHON
cargo:rerun-if-env-changed=VIRTUAL_ENV
cargo:rerun-if-env-changed=CONDA_PREFIX
cargo:rerun-if-env-changed=PATH
cargo:rerun-if-env-changed=PYO3_PRINT_CONFIG

-- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --
implementation=CPython
version=3.8
version=3.14
shared=true
abi3=false
lib_name=python3.8
lib_dir=/usr/lib
executable=/usr/bin/python
target_abi=CPython-free_threaded-3.14
lib_name=python3.14t
lib_dir=/Users/goldbaum/.pyenv/versions/3.14.4t/lib
executable=/Users/goldbaum/.pyenv/versions/3.14.4t/bin/python
pointer_width=64
build_flags=
build_flags=Py_GIL_DISABLED
python_framework_prefix=
suppress_build_script_link_lines=false

note: unset the PYO3_PRINT_CONFIG environment variable and retry to compile with the above config
```

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

- The `PYO3_BUILD_EXTENSION_MODULE` environment variable, which must be set when building Python extension modules. `maturin` and `setuptools-rust` set this automatically.
- 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.
- 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.
The free-threaded build of CPython cannot load `abi3` wheels but both builds can load `abi3t` wheels.

This section describes the packaging tools before describing how to build manually without them.
It then proceeds with an explanation of the `PYO3_BUILD_EXTENSION_MODULE` environment variable.
Expand Down Expand Up @@ -212,60 +242,74 @@ This should only be set when building a library for distribution.
>
> 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.

### `Py_LIMITED_API`/`abi3`
### `Py_LIMITED_API`/`abi3`/`abi3t`

By default, Python extension modules can only be used with the same Python version they were compiled against.
For example, an extension module built for Python 3.5 can't be imported in Python 3.8.
[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.
This is also known as `abi3`.
The ABI defined by PEP 384 is called `abi3`, since it's stable for all Python 3.X releases.
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.
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.
So, `abi3t` extensions built for the Python 3.15 limited API will be importable on Python 3.16, 3.17, and all future Python 3.X versions.

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.
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.
The downside of this is that PyO3 can't use optimizations which rely on being compiled against a known exact Python version.
It's up to you to decide whether this matters for your extension module.
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.
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.

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

1. Enable the `abi3` feature in `pyo3`.
1. Enable the `abi3` and/or `abi3t` feature in `pyo3`.
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):

```toml
[dependencies]
pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["abi3"] }
pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["abi3", "abi3t"] }
```

2. Ensure that the built shared objects are correctly marked as `abi3`.
Enabling both the `"abi3"` and `"abi3t"` features will produce an abi3 extension if the host interpreter is a GIL-enabled interpreter and an abi3t extension with a host free-threaded interpreter.
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.
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.

We suggest enabling both features and using multiple python interpreters to build several wheels.
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.
This will produce wheels targeting all versions of CPython that PyO3 supports.

2. Ensure that the built shared objects are correctly marked as `abi3` and `abi3t`.
This is accomplished by telling your build system that you're using the limited API.
[`maturin`] >= 0.9.0 and [`setuptools-rust`] >= 0.11.4 support `abi3` wheels.
[`maturin`] >= 0.9.0 and [`setuptools-rust`] >= 0.11.4 support `abi3` wheels and [`maturin`] >= 1.14 supports `abi3t` wheels.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping @messense, it probably makes sense to coordinate the release of PyO3 0.29 and a maturin release that supports abi3t.


See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://github.com/PyO3/setuptools-rust/pull/82) for more.
See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://github.com/PyO3/setuptools-rust/pull/82) [for](https://github.com/PyO3/maturin/pull/3113) more.

3. Ensure that the `.whl` is correctly marked as `abi3`.
3. Ensure that the `.whl` is correctly marked as `abi3` or `abi3t`.
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`.

#### Minimum Python version for `abi3`
#### Minimum Python version for `abi3` and `abi3t` builds

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.
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.
`maturin` and `setuptools-rust` will give the wheel a name like `my-extension-1.0-cp38-abi3-manylinux2020_x86_64.whl`.

Similarly, there is an `abi3t-py315` feature and future PyO3 versions will offer `abi3t-py316` and so on.

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.
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.

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

> [!NOTE]
> 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.
> 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.

#### Building `abi3` extension modules without a Python interpreter
#### Building stable ABI extension modules without a Python interpreter

As an advanced feature, you can build a PyO3 wheel without calling Python interpreter with the environment variable `PYO3_NO_PYTHON` set.
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.
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.

#### Missing features

Due to limitations in the Python API, there are a few `pyo3` features that do not work when compiling for `abi3`.
Due to limitations in the Python API, there are a few `pyo3` features that do not work when compiling for either `abi3` or `abi3t`.
These are:

- `#[pyo3(text_signature = "...")]` does not work on classes until Python 3.10 or greater.
Expand Down
22 changes: 18 additions & 4 deletions guide/src/building-and-distribution/multiple-python-versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn function_only_supported_on_python_3_8_and_up() {}
fn function_only_supported_before_python_3_8() {}

#[cfg(not(Py_LIMITED_API))]
fn function_incompatible_with_abi3_feature() {}
fn function_incompatible_with_stable_abi_feature() {}
```

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.
Expand Down Expand Up @@ -72,12 +72,19 @@ There are similar options `Py_3_9`, `Py_3_10`, `Py_3_11` and so on for each mino

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

```text
#[cfg(Py_GIL_DISABLED)]
```

This `#[cfg]` marks code that will only be present for free-threaded builds of CPython.
You might use this in situations where the GIL provides thread safety but the free-threaded build needs extra synchronization logic.

```text
#[cfg(not(Py_LIMITED_API))]
```

This `#[cfg]` marks code that is only available when building for the unlimited Python API (i.e. PyO3's `abi3` feature is not enabled).
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.
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).
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.

```text
#[cfg(any(Py_3_9, not(Py_LIMITED_API)))]
Expand All @@ -86,6 +93,13 @@ This might be useful if you want to ship your extension module as an `abi3` whee
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.
Patterns like this are commonly seen on Python APIs which were added to the limited Python API in a specific minor version.

```text
#[cfg(all(Py_GIL_DISABLED, Py_LIMITED_API))]
```

This marks code which is available only for the free-threaded `abi3t` stable ABI.
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.

```text
#[cfg(PyPy)]
```
Expand All @@ -94,7 +108,7 @@ This `#[cfg]` marks code which is running on PyPy.

## Checking the Python version at runtime

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.
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.

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