Skip to content

add PythonABI and new builder structs for pyo3-build-config InterpreterConfig#5924

Closed
ngoldbaum wants to merge 41 commits into
PyO3:mainfrom
ngoldbaum:abi-tag-refactor
Closed

add PythonABI and new builder structs for pyo3-build-config InterpreterConfig#5924
ngoldbaum wants to merge 41 commits into
PyO3:mainfrom
ngoldbaum:abi-tag-refactor

Conversation

@ngoldbaum
Copy link
Copy Markdown
Contributor

@ngoldbaum ngoldbaum commented Mar 30, 2026

Towards #5786.

Refactor pyo3_build_config::impl_::InterpreterConfig to use an enum to represent the kinds of stable ABI instead of boolean abi3 flag. Also replace names that contain "abi3" with "stable_abi".

This is extracted from a branch that enables abi3t builds and Python 3.15 stable ABI support, where I add a third enum variant to represent abi3t. My goal here is to make upstreaming that change simpler. PEP 803 was accepted over the weekend so a new ABI is definitely happening.

I also personally find the enum clearer to understand and easier to read code that uses it instead of the boolean flag.

@ngoldbaum ngoldbaum force-pushed the abi-tag-refactor branch 2 times, most recently from 2e92e57 to 4f2177f Compare March 30, 2026 18:37
messense added a commit to PyO3/maturin that referenced this pull request Mar 31, 2026
#3110)

Towards #3064.

This is purely refactoring, there should be no functional changes as a
result of this.

Currently the build metadata special-cases ABI3 builds or more generally
assumes stable ABI builds and ABI3 builds are the same thing. With PEP
803 and the new abi3t ABI in Python 3.15, that is no longer the case.

This replaces the old `ABI3Version` enum with a new struct combining two
enums:

```rust
/// struct describing ABI layout to use for build
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StableAbi {
    /// The "kind" of stable ABI. Either abi3 or abi3t currently.
    pub kind: StableAbiKind,
    /// The minimum Python version to build for.
    pub version: StableAbiVersion,
}

/// Python version to use as the abi3/abi3t target.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StableAbiVersion {
    /// Stable ABI wheels will have a minimum Python version matching the
    /// version of the current Python interpreter
    CurrentPython,
    /// Stable ABI wheels will have a fixed user-specified minimum Python
    /// version
    Version(u8, u8),
}

/// The "kind" of stable ABI. Either abi3 or abi3t currently.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StableAbiKind {
    /// The original stable ABI, supporting Python 3.2 and up
    Abi3,
}
```

`StableAbiVersion` is just the old `Abi3Version` enum renamed since the
concept of a minimum supported version is shared by abi3t.

I have [a
branch](main...ngoldbaum:maturin:abi3t)
that adds an `Abi3t` variant for `StableAbiKind`. My goal with this PR
is to make reviewing the subsequent PR adding abi3t support easier.

Also see PyO3/pyo3#5924 where I made a similar
change in PyO3. Here in Maturin I needed different types but in
principle I could make the two implementations use shared code. I'm not
sure if that's actually useful for anything in practice.

---------

Co-authored-by: messense <messense@icloud.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Thanks, some hazy thoughts here, not sure if I'm being helpful throwing these out there.

Comment thread pyo3-build-config/src/impl_.rs
Comment thread pyo3-build-config/src/impl_.rs Outdated
@ngoldbaum ngoldbaum changed the title add CPythonABI enum for pyo3-build-config InterpreterConfig add PythonABI struct and use it for pyo3-build-config InterpreterConfig Apr 15, 2026
@ngoldbaum ngoldbaum force-pushed the abi-tag-refactor branch 2 times, most recently from 51ff27b to dc5c1d1 Compare April 17, 2026 21:18
Comment thread pyo3-build-config/src/impl_.rs
Copy link
Copy Markdown
Member

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

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

I haven't worked too much inside the build code, so I'm not sure that I can help much here, but I gave this a brief read and left some comments.

I do like the builder pattern. I think it's nice to have a different type between the abi configuring phase and the usage phase (even tho it does not protect us from forgetting to configure something)

Comment thread pyo3-build-config/src/impl_.rs Outdated
Comment thread pyo3-build-config/src/impl_.rs Outdated
Comment thread pyo3-build-config/src/impl_.rs Outdated
Copy link
Copy Markdown
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

I have taken a look through some parts, I think I have a couple of pieces that I'd like clarifying in this PR:

  • I can't decide if we want to separate "host version" from target abi version. That seemed like it might help in #5960, but maybe it adds complexity for no practical benefit.
  • I think it'd be helpful to introduce the possibility to configure for abi3t in this PR through a config file, even if the full end-to-end build with abi3t is not done here (we could maybe just halt the build if abi3t is selected for now). I think that'd make it easier to see the full end state possible states we're heading towards, plus help understand where "stable abi" vs "abi3 and abi3t" are the right names.

Comment thread pyo3-build-config/src/impl_.rs
Comment thread pyo3-build-config/src/impl_.rs
Comment thread pyo3-build-config/src/impl_.rs
@ngoldbaum
Copy link
Copy Markdown
Contributor Author

ngoldbaum commented Apr 24, 2026

I just pushed a big commit that restores the version and implementation fields on InterpreterConfig, deprecates the abi3 field, and adds a new target_abi field which is a PythonAbi struct.

I also added a new InterpreterConfigBuilder struct to try to contain some of the boilerplate.

It ends up being an even bigger refactor! But I think we're getting close now.

I also didn't end up adding abi3t yet because this got kinda big. I'm going to try rebasing #5807 on top of this next week and then I can add a stub abi3t implementation here.

In the meantime, I'd very much appreciate feedback on the new approach.

Comment thread newsfragments/5924.changed.md
@ngoldbaum ngoldbaum changed the title add PythonABI struct and use it for pyo3-build-config InterpreterConfig add PythonABI and new builder structs for pyo3-build-config InterpreterConfig Apr 24, 2026
@ngoldbaum
Copy link
Copy Markdown
Contributor Author

@davidhewitt @Icxolu this is ready for another pass.

Comment thread pyo3-build-config/src/impl_.rs Outdated
Comment thread pyo3-build-config/src/impl_.rs
Comment thread pyo3-build-config/src/impl_.rs Outdated
Comment thread pyo3-build-config/src/impl_.rs
Comment thread pyo3-build-config/src/impl_.rs Outdated
ngoldbaum

This comment was marked as resolved.

@ngoldbaum
Copy link
Copy Markdown
Contributor Author

@davidhewitt @Icxolu I think this is ready for another pass whenever you have some capacity.

I'm also happy to try to minimize the diff more. Most of it is in the new IntepreterConfigBuilder struct, which I prefer over the raw struct construction which requires explicitly setting a bunch of fields. There's definitely some boilerplate, but IMO it's preferable from the perspective of any future contributor who needs to write new tests or update this code.

Copy link
Copy Markdown
Member

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

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

I think this is moving in the right direction. I really like that we now clearly use target_abi as the source of truth for the emitted abi related cfgs. I left some thoughts below, nothing mandatory, just some ideas that crossed my mind while reading through this.

(I've haven't really looked through the many test changes, I assume that it's basically mechanical changes to use the builders)

/// Gets the minimum supported Python version from PyO3 `abi3t-py*` features.
///
/// Must be called from a PyO3 crate build script.
pub fn get_abi3t_version() -> Option<PythonVersion> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if this should be combined with is_abi3t above. Basically is_abi3t should be equivalent to get_abi3t_version().is_some(), no? (also applies to is_abi3 and get_abi3_version)

Also should the MINIMUM_SUPPORTED_VERSION be different for abi3 and abi3t?

Comment on lines +516 to +519
apply_build_env_to_config(
InterpreterConfig::from_path(path)
.context("failed to parse contents of PYO3_CONFIG_FILE")?,
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if this should be a method on InterpreterConfig instead of a free function.

Comment on lines +384 to +387
let target_abi =
PythonAbiBuilder::from_sysconfig(implementation, version, abi3_version, gil_disabled)?
.finalize();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As this already specifies everything abi related directly, I think this should be directly on PythonAbi instead of the builder.

@@ -179,40 +231,50 @@ impl InterpreterConfig {
#[doc(hidden)]
pub fn build_script_outputs(&self) -> Vec<String> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice, I think this is very clear now!

/// Generates "default" interpreter configuration when compiling "abi3" extensions
/// without a working Python interpreter.
///
/// `version` specifies the minimum supported Stable ABI CPython version.
///
/// This should work for most CPython extension modules when compiling on
/// Windows, macOS and Linux.
///
/// Must be called from a PyO3 crate build script.
fn default_abi3_config(host: &Triple, version: PythonVersion) -> Result<InterpreterConfig> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't this be moved into the function above?

/// The Python implementation flavor.
///
/// Serialized to `implementation`.
pub implementation: PythonImplementation,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we maybe have accessor methods, to prevent modifying an already finalized abi?

@ngoldbaum
Copy link
Copy Markdown
Contributor Author

@davidhewitt and I had a 1-1 sync to discuss this PR and #5807. We decided to close this PR in favor of #5807 to avoid splitting work. @davidhewitt also offered to put in a PR that will upstream just the InterpreterConfig builder structs, along with deprecating constructing an InterpreterConfig directly.

@ngoldbaum ngoldbaum closed this May 11, 2026
@ngoldbaum
Copy link
Copy Markdown
Contributor Author

@Icxolu I'll be integrating your comments here into the implementation in #5807.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI-no-fail-fast If one job fails, allow the rest to keep testing free-threading refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants