Fix issues around crates enabling abi3 and abi3t features#3226
Fix issues around crates enabling abi3 and abi3t features#3226ngoldbaum wants to merge 4 commits into
Conversation
|
PyPy tests are failing. |
|
Looks like pypy is fixed in the latest commit. |
There was a problem hiding this comment.
Pull request overview
This PR fixes stable-ABI (abi3/abi3t) selection bugs when crates enable both feature families, by deferring selection until after interpreter resolution and ensuring maturin emits at most one stable-ABI wheel per build (with version-specific fallback wheels for non-matching interpreters). It also adds new unit/integration tests and a fixture crate to cover mixed abi3/abi3t configurations, and updates the user guide to reflect the actual behavior.
Changes:
- Move stable-ABI selection to after interpreter resolution and introduce per-interpreter stable-ABI compatibility (
stable_abi_for_interpreter) to drive wheel/tag decisions. - Update wheel tag generation and PyO3 config-file generation to support “one stable ABI + fallbacks” behavior cleanly.
- Add new fixture crate plus unit/integration tests for mixed abi3/abi3t builds; update documentation for abi3t behavior and limitations.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/run/integration.rs | Adds integration test entry points for combined abi3/abi3t wheel selection. |
| tests/common/other.rs | Implements the new integration test helper that builds the fixture and asserts produced wheel tags. |
| test-crates/pyo3-abi3-and-abi3t/src/lib.rs | New PyO3 fixture module used to exercise mixed abi3/abi3t feature sets. |
| test-crates/pyo3-abi3-and-abi3t/README.md | Documents the purpose of the new fixture crate. |
| test-crates/pyo3-abi3-and-abi3t/pyproject.toml | Adds Python packaging metadata for the fixture crate. |
| test-crates/pyo3-abi3-and-abi3t/Cargo.toml | Defines mixed abi3/abi3t feature families used by the tests. |
| test-crates/pyo3-abi3-and-abi3t/Cargo.lock | Locks dependencies for the new fixture crate. |
| src/python_interpreter/resolver.rs | Adjusts interpreter resolution behavior for stable ABI detection (abi3-specific fixed version handling). |
| src/python_interpreter/mod.rs | Adds explicit abi3/abi3t capability checks and routes has_stable_api by stable-ABI kind. |
| src/python_interpreter/config.rs | Refactors PyO3 config generation to take Option<StableAbi> and correctly encode target ABI/version. |
| src/compile.rs | Updates PyO3 env/config-file logic to support “forced target_abi config” for stable-ABI builds where appropriate. |
| src/build_orchestrator.rs | Updates tag computation and stable-ABI wheel building to emit one stable-ABI wheel plus version-specific fallbacks. |
| src/build_options.rs | Adds unit tests covering mixed abi3/abi3t bridge detection and post-resolution stable-ABI selection. |
| src/build_context/builder.rs | Ensures stable-ABI upgrade/selection always happens after interpreter resolution (and respects CLI feature overrides). |
| src/bridge/mod.rs | Introduces ABI3T_MINIMUM_PYTHON_MINOR and adds stable_abi_for_interpreter helper for per-interpreter stable-ABI decisions. |
| src/bridge/detection.rs | Updates stable-ABI detection/selection to consider resolved interpreters and pick a single stable-ABI family. |
| guide/src/bindings.md | Updates documentation to describe the “one stable ABI per build” behavior and fallback wheels. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| maturin build --interpreter python3.10 | ||
| maturin build --interpreter python3.15t |
There was a problem hiding this comment.
I had problems with this --interpreter value/format. The example in question simply does not work for Windows builds. I found it worked better (cross-platform -- windows-latest, ubuntu-latest, macos-15-intel, macos-latest) when I used that same version syntax passed to the actions/setup-python action:
strategy:
matrix:
# note, '3.11' is min available build on `windows-11-arm` runner
include:
- python-version: '3.10'
pyo3-feature: 'pyo3/abi3-py310'
- python-version: '3.15t'
pyo3-feature: 'pyo3/abi3t-py315'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
# 3.15 is not stable yet, so ...
allow-prereleases: ${{ startsWith(matrix.python-version, '3.15') }}
- uses: pyo3/maturin-action@v1
with:
args: >-
--release
--out dist
--interpreter ${{ matrix.python-version }}
--features ${{ matrix.pyo3-feature }}FYI, passing the ${{ steps.python-setup.outputs.python-path }} is a bad idea for Linux builds (if pyo3/maturin-action manylinux input is not 'off').
There was a problem hiding this comment.
Did you have those problems using the version of Maturin from this PR or the released version?
There was a problem hiding this comment.
I checked the CI logs...
Installing 'maturin' from tag 'v1.14.0'
There was a problem hiding this comment.
So then I think this PR will fix things for you, there is Windows CI running on this PR and the invocation in the docs is what the integration test I added here does.
There was a problem hiding this comment.
But the windows CI logs show:
python_version=$(echo 3.15t-dev | sed -e s/-dev//)
echo "PYTHON_VERSION=$python_version" >> "${GITHUB_ENV}"where that output is
python_version=3.15t
echo PYTHON_VERSION=3.15tand then it is passed to maturin build:
cargo run build -i $PYTHON_VERSION -m test-crates/pyo3-pure/Cargo.tomlThis coincides with my observation and seems contrary to the example in question.
There was a problem hiding this comment.
Ah, I see what you're getting at now. Yes, the CI over here doesn't set allow-prereleases but I don't think that matters for the question you have? The maturin integration tests don't know about setup-python or its naming conventions.
So the place to handle the problem you're having is probably over in maturin-action. Maybe in the PR I have open over there?
Also with this PR merged you won't need to explicitly pass in abi3 and abi3t features at build time anymore. You can just enable both features, like in the test crates in this PR.
I'm going to bed now so I won't deal with subsequent replies here until tomorrow.
There was a problem hiding this comment.
Ah, I didn't think there was any pre-processing in pyo3/maturin-action's args input. I'll look at that PR too.
Also with this PR merged you won't need to explicitly pass in abi3 and abi3t features at build time anymore. You can just enable both features, like in the test crates in this PR.
The way this patch reads, I think I still need to specify each feature separately if I want a abi3 wheel and a abi3t wheel. My understanding is that it prevents building abi3t wheels for any version of python prior to 3.15t.
Anyway, I bid you well-deserved sweet dreams! Thanks again.
There was a problem hiding this comment.
The way this patch reads, I think I still need to specify each feature separately if I want a abi3 wheel and a abi3t wheel. My understanding is that it prevents building abi3t wheels for any version of python prior to 3.15t.
One last comment:
PyO3 is perfectly happy to let you enable both features.
If you enable, say abi3-py310 and abi3t-py315, the wheel you get out of maturin depends on the Python version used to build the wheel:
- 3.14 or older Gil-enabled build -> abi3 wheel targeting the Python 3.10 limited API
- 3.14t -> cp314t wheel
- 3.15 or newer, both builds -> abi3.abi3t wheel targeting the 3.15 limited API
The idea is you get a working build no matter what, with abi3t preferred and falling back to abi3 on the GIL-enabled build where abi3t can't work and a version-specific wheel on 3.14t whhere neither stable ABI is supported.
Please try it yourself by checking out this PR locally, building and installing it in your Python environment, and trying it with a test crate you come up with or one of the test crates I added in this PR.
This fixes issues reported by @2bndy5 over at PyO3/maturin-action#368 (comment).
Unfortunately, I completely skipped adding tests here for crates that mix abi3 and abi3t features and introduced some pretty serious bugs.
This PR makes it structurally impossible to generate abi3.abi3t wheels targeting ABIs before Python 3.15. Stable ABI selection now happens after interpreter resolution and chooses at most one stable ABI family per build: abi3t only when a compatible CPython 3.15+ interpreter is present, otherwise abi3 when available. Interpreters that do not support the selected stable ABI family fall back to version-specific wheels, e.g. CPython 3.14t builds cp314-cp314t instead of cp314-abi3.abi3t.
Updates the docs I added for abi3t support to describe the actual behavior of Maturin. I considered making it possible to build two stable ABI wheels in the same call using a GIL-enabled interpreter but decided that required too many internal code changes to change the
stable_abimember of the PyO3 bridge struct from an Option wrapping a single value into a vec! that might contain multiple values.The bulk of the PR is new tests, including new unit tests and integration tests. The current python versions used in CI capture interesting behaviors in this test: Python 3.9 should produce a py38-abi3 wheel, Python 3.15 and 3.15t should produce an abi3t wheel.