From 3e1ec3469e18d35951114b7a5bee4f1c8a224848 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Fri, 24 Apr 2026 14:13:34 -0600 Subject: [PATCH 1/7] introduce a builder for InterpreterConfig Co-authored-by: David Hewitt --- build.rs | 2 +- pyo3-build-config/src/impl_.rs | 974 +++++++++++++++------------- pyo3-build-config/src/lib.rs | 83 +-- pyo3-ffi-check/definitions/build.rs | 2 +- pyo3-ffi-check/macro/src/lib.rs | 21 +- pyo3-ffi/build.rs | 43 +- 6 files changed, 586 insertions(+), 539 deletions(-) diff --git a/build.rs b/build.rs index 2f33c61dc35..7100e62e281 100644 --- a/build.rs +++ b/build.rs @@ -6,7 +6,7 @@ use pyo3_build_config::{ }; fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<()> { - if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() && !interpreter_config.shared { + if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() && !interpreter_config.shared() { bail!( "The `auto-initialize` feature is enabled, but your python installation only supports \ embedding the Python interpreter statically. If you are attempting to run tests, or a \ diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index ea6653d145c..b60d9ae1ba6 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -90,21 +90,37 @@ pub struct InterpreterConfig { /// The Python implementation flavor. /// /// Serialized to `implementation`. + #[deprecated( + since = "0.29.0", + note = "please use `.implementation()` getter or `InterpreterConfigBuilder` instead" + )] pub implementation: PythonImplementation, /// Python `X.Y` version. e.g. `3.9`. /// /// Serialized to `version`. + #[deprecated( + since = "0.29.0", + note = "please use `.version()` getter or `InterpreterConfigBuilder` instead" + )] pub version: PythonVersion, /// Whether link library is shared. /// /// Serialized to `shared`. + #[deprecated( + since = "0.29.0", + note = "please use `.shared()` getter or `InterpreterConfigBuilder` instead" + )] pub shared: bool, /// Whether linking against the stable/limited Python 3 API. /// /// Serialized to `abi3`. + #[deprecated( + since = "0.29.0", + note = "please use `.abi3()` getter or `InterpreterConfigBuilder` instead" + )] pub abi3: bool, /// The name of the link library defining Python. @@ -114,6 +130,10 @@ pub struct InterpreterConfig { /// prefix. /// /// Serialized to `lib_name`. + #[deprecated( + since = "0.29.0", + note = "please use `.lib_name()` getter or `InterpreterConfigBuilder` instead" + )] pub lib_name: Option, /// The directory containing the Python library to link against. @@ -122,6 +142,10 @@ pub struct InterpreterConfig { /// to add an additional library search path for the linker. /// /// Serialized to `lib_dir`. + #[deprecated( + since = "0.29.0", + note = "please use `.lib_dir()` getter or `InterpreterConfigBuilder` instead" + )] pub lib_dir: Option, /// Path of host `python` executable. @@ -131,16 +155,28 @@ pub struct InterpreterConfig { /// executable invoked. /// /// Serialized to `executable`. + #[deprecated( + since = "0.29.0", + note = "please use `.executable()` getter or `InterpreterConfigBuilder` instead" + )] pub executable: Option, /// Width in bits of pointers on the target machine. /// /// Serialized to `pointer_width`. + #[deprecated( + since = "0.29.0", + note = "please use `.pointer_width()` getter or `InterpreterConfigBuilder` instead" + )] pub pointer_width: Option, /// Additional relevant Python build flags / configuration settings. /// /// Serialized to `build_flags`. + #[deprecated( + since = "0.29.0", + note = "please use `.build_flags()` getter or `InterpreterConfigBuilder` instead" + )] pub build_flags: BuildFlags, /// Whether to suppress emitting of `cargo:rustc-link-*` lines from the build script. @@ -153,6 +189,10 @@ pub struct InterpreterConfig { /// /// If suppression is enabled, `extra_build_script_lines` should contain equivalent /// functionality or else a build failure is likely. + #[deprecated( + since = "0.29.0", + note = "please use `.suppress_build_script_link_lines()` getter or `InterpreterConfigBuilder` instead" + )] pub suppress_build_script_link_lines: bool, /// Additional lines to `println!()` from Cargo build scripts. @@ -165,12 +205,113 @@ pub struct InterpreterConfig { /// is build/configured. /// /// Serialized to multiple `extra_build_script_line` values. + #[deprecated( + since = "0.29.0", + note = "please use `.extra_build_script_lines()` getter or `InterpreterConfigBuilder` instead" + )] pub extra_build_script_lines: Vec, /// macOS Python3.framework requires special rpath handling + #[deprecated( + since = "0.29.0", + note = "please use `.python_framework_prefix()` getter or `InterpreterConfigBuilder` instead" + )] pub python_framework_prefix: Option, } +// Should no longer be deprecated once the internal fields are private +#[expect(deprecated, reason = "this impl block touches the internal fields")] impl InterpreterConfig { + /// The Python implementation flavor. + /// + /// Serialized to `implementation`. + pub fn implementation(&self) -> PythonImplementation { + self.implementation + } + + /// Python `X.Y` version. e.g. `3.9`. + /// + /// Serialized to `version`. + pub fn version(&self) -> PythonVersion { + self.version + } + + /// Whether link library is shared. + /// + /// Serialized to `shared`. + pub fn shared(&self) -> bool { + self.shared + } + + /// Whether linking against the stable/limited Python 3 API. + /// + /// Serialized to `abi3`. + pub fn abi3(&self) -> bool { + self.abi3 + } + + /// The name of the link library defining Python. + /// + /// This effectively controls the `cargo:rustc-link-lib=` value to + /// control how libpython is linked. Values should not contain the `lib` + /// prefix. + /// + /// Serialized to `lib_name`. + pub fn lib_name(&self) -> Option<&str> { + self.lib_name.as_deref() + } + + /// The directory containing the Python library to link against. + /// + /// The effectively controls the `cargo:rustc-link-search=native=` value + /// to add an additional library search path for the linker. + /// + /// Serialized to `lib_dir`. + pub fn lib_dir(&self) -> Option<&str> { + self.lib_dir.as_deref() + } + + /// Path of host `python` executable. + /// + /// This is a valid executable capable of running on the host/building machine. + /// For configurations derived by invoking a Python interpreter, it was the + /// executable invoked. + /// + /// Serialized to `executable`. + pub fn executable(&self) -> Option<&str> { + self.executable.as_deref() + } + + /// Width in bits of pointers on the target machine. + /// + /// Serialized to `pointer_width`. + pub fn pointer_width(&self) -> Option { + self.pointer_width + } + + /// Additional relevant Python build flags / configuration settings. + /// + /// Serialized to `build_flags`. + pub fn build_flags(&self) -> &BuildFlags { + &self.build_flags + } + + /// Whether to suppress emitting of `cargo:rustc-link-*` lines from the build script. + pub fn suppress_build_script_link_lines(&self) -> bool { + self.suppress_build_script_link_lines + } + + /// Additional lines to `println!()` from Cargo build scripts. + /// + /// Serialized to multiple `extra_build_script_line` values. + pub fn extra_build_script_lines(&self) -> &[String] { + &self.extra_build_script_lines + } + + /// macOS Python3.framework prefix used for special rpath handling. + pub fn python_framework_prefix(&self) -> Option<&str> { + self.python_framework_prefix.as_deref() + } + #[doc(hidden)] pub fn build_script_outputs(&self) -> Vec { // This should have been checked during pyo3-build-config build time. @@ -178,7 +319,7 @@ impl InterpreterConfig { let mut out = vec![]; - for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor { + for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version().minor { out.push(format!("cargo:rustc-cfg=Py_3_{i}")); } @@ -356,20 +497,17 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) .parse() .context("failed to parse calcsize_pointer")?; - Ok(InterpreterConfig { - version, - implementation, - shared, - abi3, - lib_name: Some(lib_name), - lib_dir, - executable: map.get("executable").cloned(), - pointer_width: Some(calcsize_pointer * 8), - build_flags: BuildFlags::from_interpreter(interpreter)?, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix, - }) + let builder = InterpreterConfigBuilder::new(implementation, version) + .abi3(abi3) + .shared(shared) + .lib_name(lib_name) + .lib_dir_opt(lib_dir) + .executable(map["executable"].clone()) + .pointer_width(calcsize_pointer * 8) + .build_flags(BuildFlags::from_interpreter(interpreter)?) + .python_framework_prefix_opt(python_framework_prefix); + + Ok(builder.finalize()) } /// Generate from parsed sysconfigdata file @@ -429,20 +567,16 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) .ok(); let build_flags = BuildFlags::from_sysconfigdata(sysconfigdata); - Ok(InterpreterConfig { - implementation, - version, - shared: shared || framework, - abi3, - lib_dir, - lib_name, - executable: None, - pointer_width, - build_flags, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix, - }) + let builder = InterpreterConfigBuilder::new(implementation, version) + .abi3(abi3) + .shared(shared || framework) + .pointer_width_opt(pointer_width) + .lib_name_opt(lib_name) + .lib_dir_opt(lib_dir) + .python_framework_prefix_opt(python_framework_prefix) + .build_flags(build_flags); + + Ok(builder.finalize()) } /// Import an externally-provided config file. @@ -559,20 +693,19 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) let abi3 = abi3.unwrap_or(false); let build_flags = build_flags.unwrap_or_default(); - Ok(InterpreterConfig { - implementation, - version, - shared: shared.unwrap_or(true), - abi3, - lib_name, - lib_dir, - executable, - pointer_width, - build_flags, - suppress_build_script_link_lines: suppress_build_script_link_lines.unwrap_or(false), - extra_build_script_lines, - python_framework_prefix, - }) + let builder = InterpreterConfigBuilder::new(implementation, version) + .abi3(abi3) + .shared(shared.unwrap_or(true)) + .lib_name_opt(lib_name) + .lib_dir_opt(lib_dir) + .executable_opt(executable) + .pointer_width_opt(pointer_width) + .build_flags(build_flags) + .suppress_build_script_link_lines(suppress_build_script_link_lines.unwrap_or(false)) + .extra_build_script_lines(extra_build_script_lines) + .python_framework_prefix_opt(python_framework_prefix); + + Ok(builder.finalize()) } /// Helper function to apply a default lib_name if none is set in `PYO3_CONFIG_FILE`. @@ -722,6 +855,157 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) } } +#[cfg_attr(test, derive(Debug))] +pub struct InterpreterConfigBuilder { + implementation: PythonImplementation, + version: PythonVersion, + shared: Option, + abi3: Option, + lib_name: Option, + lib_dir: Option, + executable: Option, + pointer_width: Option, + build_flags: Option, + suppress_build_script_link_lines: Option, + extra_build_script_lines: Vec, + python_framework_prefix: Option, +} + +impl InterpreterConfigBuilder { + pub fn new( + implementation: PythonImplementation, + version: PythonVersion, + ) -> InterpreterConfigBuilder { + InterpreterConfigBuilder { + implementation, + version, + shared: None, + abi3: None, + lib_name: None, + lib_dir: None, + executable: None, + pointer_width: None, + build_flags: None, + suppress_build_script_link_lines: None, + extra_build_script_lines: vec![], + python_framework_prefix: None, + } + } + + pub fn abi3(mut self, abi3: bool) -> InterpreterConfigBuilder { + self.abi3 = Some(abi3); + self + } + + pub fn lib_name(mut self, lib_name: String) -> InterpreterConfigBuilder { + self.lib_name = Some(lib_name); + self + } + + pub fn pointer_width(mut self, pointer_width: u32) -> InterpreterConfigBuilder { + self.pointer_width = Some(pointer_width); + self + } + + pub fn executable(mut self, executable: String) -> InterpreterConfigBuilder { + self.executable = Some(executable); + self + } + + pub fn suppress_build_script_link_lines( + mut self, + suppress_build_script_link_lines: bool, + ) -> InterpreterConfigBuilder { + self.suppress_build_script_link_lines = Some(suppress_build_script_link_lines); + self + } + + pub fn extra_build_script_lines( + mut self, + extra_build_script_lines: Vec, + ) -> InterpreterConfigBuilder { + self.extra_build_script_lines = extra_build_script_lines; + self + } + + pub fn lib_dir(mut self, lib_dir: String) -> InterpreterConfigBuilder { + self.lib_dir = Some(lib_dir); + self + } + + pub fn shared(mut self, shared: bool) -> InterpreterConfigBuilder { + self.shared = Some(shared); + self + } + + pub fn build_flags(mut self, build_flags: BuildFlags) -> InterpreterConfigBuilder { + self.build_flags = Some(build_flags); + self + } + + pub fn python_framework_prefix( + self, + python_framework_prefix: String, + ) -> InterpreterConfigBuilder { + InterpreterConfigBuilder { + python_framework_prefix: Some(python_framework_prefix), + ..self + } + } + + pub fn finalize(self) -> InterpreterConfig { + #[expect( + deprecated, + reason = "constructing an InterpreterConfig directly, need to write to fields" + )] + InterpreterConfig { + implementation: self.implementation, + version: self.version, + shared: self.shared.unwrap_or(true), + abi3: self.abi3.unwrap_or(false), + lib_name: self.lib_name, + lib_dir: self.lib_dir, + executable: self.executable, + pointer_width: self.pointer_width, + build_flags: self.build_flags.unwrap_or_default(), + suppress_build_script_link_lines: self + .suppress_build_script_link_lines + .unwrap_or(false), + extra_build_script_lines: self.extra_build_script_lines, + python_framework_prefix: self.python_framework_prefix, + } + } + + // private variants of some methods where it's convenient to potentially pass None + fn pointer_width_opt(mut self, pointer_width: Option) -> InterpreterConfigBuilder { + self.pointer_width = pointer_width; + self + } + + fn executable_opt(mut self, executable: Option) -> InterpreterConfigBuilder { + self.executable = executable; + self + } + + fn lib_name_opt(mut self, lib_name: Option) -> InterpreterConfigBuilder { + self.lib_name = lib_name; + self + } + + fn lib_dir_opt(mut self, lib_dir: Option) -> InterpreterConfigBuilder { + self.lib_dir = lib_dir; + self + } + + fn python_framework_prefix_opt( + mut self, + python_framework_prefix: Option, + ) -> InterpreterConfigBuilder { + self.python_framework_prefix = python_framework_prefix; + self + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct PythonVersion { pub major: u8, @@ -729,22 +1013,35 @@ pub struct PythonVersion { } impl PythonVersion { - pub const PY315: Self = PythonVersion { - major: 3, - minor: 15, - }; + #[deprecated( + since = "0.29.0", + note = "please construct `PythonVersion` directly rather than use these constants" + )] pub const PY313: Self = PythonVersion { major: 3, minor: 13, }; + #[deprecated( + since = "0.29.0", + note = "please construct `PythonVersion` directly rather than use these constants" + )] pub const PY312: Self = PythonVersion { major: 3, minor: 12, }; + #[cfg(test)] + const PY311: Self = PythonVersion { + major: 3, + minor: 11, + }; const PY310: Self = PythonVersion { major: 3, minor: 10, }; + #[cfg(test)] + const PY39: Self = PythonVersion { major: 3, minor: 9 }; + #[cfg(test)] + const PY38: Self = PythonVersion { major: 3, minor: 8 }; } impl Display for PythonVersion { @@ -1526,6 +1823,7 @@ fn cross_compile_from_sysconfigdata( if let Some(path) = find_sysconfigdata(cross_compile_config)? { let data = parse_sysconfigdata(path)?; let mut config = InterpreterConfig::from_sysconfigdata(&data)?; + #[expect(deprecated, reason = "modifying config inline")] if let Some(cross_lib_dir) = cross_compile_config.lib_dir_string() { config.lib_dir = Some(cross_lib_dir) } @@ -1569,20 +1867,13 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result Result= PythonVersion::PY313, "Cannot compile C extensions for the free-threaded build on Python versions earlier than 3.13, found {}.{}", version.major, version.minor); + #[expect(deprecated, reason = "using constant internally")] + { + ensure!(version >= PythonVersion::PY313, "Cannot compile C extensions for the free-threaded build on Python versions earlier than 3.13, found {}.{}", version.major, version.minor); + } if debug { Ok(format!("python{}{}t_d", version.major, version.minor)) } else { @@ -1742,7 +2028,10 @@ fn default_lib_name_unix( if cygwin && abi3 { Ok("python3".to_string()) } else if gil_disabled { - ensure!(version >= PythonVersion::PY313, "Cannot compile C extensions for the free-threaded build on Python versions earlier than 3.13, found {}.{}", version.major, version.minor); + #[expect(deprecated, reason = "using constant internally")] + { + ensure!(version >= PythonVersion::PY313, "Cannot compile C extensions for the free-threaded build on Python versions earlier than 3.13, found {}.{}", version.major, version.minor); + } Ok(format!("python{}.{}t", version.major, version.minor)) } else { Ok(format!("python{}.{}", version.major, version.minor)) @@ -1968,6 +2257,8 @@ fn unescape(escaped: &str) -> Vec { } #[cfg(test)] +// can remove this expect when fields are private +#[expect(deprecated, reason = "accessing config fields directly for testing")] mod tests { use target_lexicon::triple; @@ -1975,49 +2266,34 @@ mod tests { #[test] fn test_config_file_roundtrip() { - let config = InterpreterConfig { - abi3: true, - build_flags: BuildFlags::default(), - pointer_width: Some(32), - executable: Some("executable".into()), - implementation: PythonImplementation::CPython, - lib_name: Some("lib_name".into()), - lib_dir: Some("lib_dir".into()), - shared: true, - version: MINIMUM_SUPPORTED_VERSION, - suppress_build_script_link_lines: true, - extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()], - python_framework_prefix: None, - }; + let implementation = PythonImplementation::CPython; + let version = MINIMUM_SUPPORTED_VERSION; + let config = InterpreterConfigBuilder::new(implementation, version) + .abi3(true) + .pointer_width(32) + .executable("executable".into()) + .lib_dir("lib_name".into()) + .lib_name("lib_name".into()) + .extra_build_script_lines(vec!["cargo:test1".to_string(), "cargo:test2".to_string()]) + .finalize(); let mut buf: Vec = Vec::new(); config.to_writer(&mut buf).unwrap(); assert_eq!(config, InterpreterConfig::from_reader(&*buf).unwrap()); // And some different options, for variety - - let config = InterpreterConfig { - abi3: false, - build_flags: { - let mut flags = HashSet::new(); - flags.insert(BuildFlag::Py_DEBUG); - flags.insert(BuildFlag::Other(String::from("Py_SOME_FLAG"))); - BuildFlags(flags) - }, - pointer_width: None, - executable: None, - implementation: PythonImplementation::PyPy, - lib_dir: None, - lib_name: None, - shared: true, - version: PythonVersion { - major: 3, - minor: 10, - }, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, + let version = PythonVersion::PY310; + let implementation = PythonImplementation::PyPy; + let build_flags = { + let mut flags = HashSet::new(); + flags.insert(BuildFlag::Py_DEBUG); + flags.insert(BuildFlag::Other(String::from("Py_SOME_FLAG"))); + BuildFlags(flags) }; + let config = InterpreterConfigBuilder::new(implementation, version) + .build_flags(build_flags) + .finalize(); + let mut buf: Vec = Vec::new(); config.to_writer(&mut buf).unwrap(); @@ -2026,20 +2302,16 @@ mod tests { #[test] fn test_config_file_roundtrip_with_escaping() { - let config = InterpreterConfig { - abi3: true, - build_flags: BuildFlags::default(), - pointer_width: Some(32), - executable: Some("executable".into()), - implementation: PythonImplementation::CPython, - lib_name: Some("lib_name".into()), - lib_dir: Some("lib_dir\\n".into()), - shared: true, - version: MINIMUM_SUPPORTED_VERSION, - suppress_build_script_link_lines: true, - extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()], - python_framework_prefix: None, - }; + let implementation = PythonImplementation::CPython; + let version = MINIMUM_SUPPORTED_VERSION; + let config = InterpreterConfigBuilder::new(implementation, version) + .abi3(true) + .pointer_width(32) + .executable("executable".into()) + .lib_name("lib_name".into()) + .lib_dir("lib_dir\\n".into()) + .extra_build_script_lines(vec!["cargo:test1".to_string(), "cargo:test2".to_string()]) + .finalize(); let mut buf: Vec = Vec::new(); config.to_writer(&mut buf).unwrap(); @@ -2051,45 +2323,23 @@ mod tests { #[test] fn test_config_file_defaults() { // Only version is required + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; assert_eq!( InterpreterConfig::from_reader("version=3.8".as_bytes()).unwrap(), - InterpreterConfig { - version: PythonVersion { major: 3, minor: 8 }, - implementation: PythonImplementation::CPython, - shared: true, - abi3: false, - lib_name: None, - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } + InterpreterConfigBuilder::new(implementation, version,).finalize() ) } #[test] fn test_config_file_unknown_keys() { // ext_suffix is unknown to pyo3-build-config, but it shouldn't error + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; assert_eq!( InterpreterConfig::from_reader("version=3.8\next_suffix=.python38.so".as_bytes()) .unwrap(), - InterpreterConfig { - version: PythonVersion { major: 3, minor: 8 }, - implementation: PythonImplementation::CPython, - shared: true, - abi3: false, - lib_name: None, - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } + InterpreterConfigBuilder::new(implementation, version,).finalize() ) } @@ -2177,22 +2427,16 @@ mod tests { sysconfigdata.insert("LIBDIR", "/usr/lib"); sysconfigdata.insert("LDVERSION", "3.8"); sysconfigdata.insert("SIZEOF_VOID_P", "8"); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; assert_eq!( InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(), - InterpreterConfig { - abi3: false, - build_flags: BuildFlags::from_sysconfigdata(&sysconfigdata), - pointer_width: Some(64), - executable: None, - implementation: PythonImplementation::CPython, - lib_dir: Some("/usr/lib".into()), - lib_name: Some("python3.8".into()), - shared: true, - version: PythonVersion { major: 3, minor: 8 }, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } + InterpreterConfigBuilder::new(implementation, version,) + .build_flags(BuildFlags::from_sysconfigdata(&sysconfigdata)) + .lib_dir("/usr/lib".into()) + .lib_name("python3.8".into()) + .pointer_width(64) + .finalize() ); } @@ -2207,22 +2451,16 @@ mod tests { sysconfigdata.insert("LIBDIR", "/usr/lib"); sysconfigdata.insert("LDVERSION", "3.8"); sysconfigdata.insert("SIZEOF_VOID_P", "8"); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; assert_eq!( InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(), - InterpreterConfig { - abi3: false, - build_flags: BuildFlags::from_sysconfigdata(&sysconfigdata), - pointer_width: Some(64), - executable: None, - implementation: PythonImplementation::CPython, - lib_dir: Some("/usr/lib".into()), - lib_name: Some("python3.8".into()), - shared: true, - version: PythonVersion { major: 3, minor: 8 }, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } + InterpreterConfigBuilder::new(implementation, version,) + .build_flags(BuildFlags::from_sysconfigdata(&sysconfigdata)) + .lib_dir("/usr/lib".into()) + .lib_name("python3.8".into()) + .pointer_width(64) + .finalize() ); sysconfigdata = Sysconfigdata::new(); @@ -2234,22 +2472,17 @@ mod tests { sysconfigdata.insert("LIBDIR", "/usr/lib"); sysconfigdata.insert("LDVERSION", "3.8"); sysconfigdata.insert("SIZEOF_VOID_P", "8"); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; assert_eq!( InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(), - InterpreterConfig { - abi3: false, - build_flags: BuildFlags::from_sysconfigdata(&sysconfigdata), - pointer_width: Some(64), - executable: None, - implementation: PythonImplementation::CPython, - lib_dir: Some("/usr/lib".into()), - lib_name: Some("python3.8".into()), - shared: false, - version: PythonVersion { major: 3, minor: 8 }, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } + InterpreterConfigBuilder::new(implementation, version,) + .build_flags(BuildFlags::from_sysconfigdata(&sysconfigdata)) + .lib_dir("/usr/lib".into()) + .lib_name("python3.8".into()) + .pointer_width(64) + .shared(false) + .finalize() ); } @@ -2258,47 +2491,25 @@ mod tests { let host = triple!("x86_64-pc-windows-msvc"); let min_version = "3.8".parse().unwrap(); - assert_eq!( - default_abi3_config(&host, min_version).unwrap(), - InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 8 }, - shared: true, - abi3: true, - lib_name: Some("python3".into()), - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } - ); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; + let config = InterpreterConfigBuilder::new(implementation, version) + .abi3(true) + .lib_name("python3".into()) + .finalize(); + assert_eq!(default_abi3_config(&host, min_version).unwrap(), config); } #[test] fn unix_hardcoded_abi3_compile() { let host = triple!("x86_64-unknown-linux-gnu"); let min_version = "3.9".parse().unwrap(); - - assert_eq!( - default_abi3_config(&host, min_version).unwrap(), - InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 9 }, - shared: true, - abi3: true, - lib_name: None, - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } - ); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY39; + let config = InterpreterConfigBuilder::new(implementation, version) + .abi3(true) + .finalize(); + assert_eq!(default_abi3_config(&host, min_version).unwrap(), config); } #[test] @@ -2317,23 +2528,13 @@ mod tests { .unwrap() .unwrap(); - assert_eq!( - default_cross_compile(&cross_config).unwrap(), - InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 8 }, - shared: true, - abi3: false, - lib_name: Some("python38".into()), - lib_dir: Some("C:\\some\\path".into()), - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } - ); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; + let config = InterpreterConfigBuilder::new(implementation, version) + .lib_name("python38".into()) + .lib_dir("C:\\some\\path".into()) + .finalize(); + assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } #[test] @@ -2352,23 +2553,13 @@ mod tests { .unwrap() .unwrap(); - assert_eq!( - default_cross_compile(&cross_config).unwrap(), - InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 8 }, - shared: true, - abi3: false, - lib_name: Some("python38".into()), - lib_dir: Some("/usr/lib/mingw".into()), - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } - ); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; + let config = InterpreterConfigBuilder::new(implementation, version) + .lib_name("python38".into()) + .lib_dir("/usr/lib/mingw".into()) + .finalize(); + assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } #[test] @@ -2387,23 +2578,13 @@ mod tests { .unwrap() .unwrap(); - assert_eq!( - default_cross_compile(&cross_config).unwrap(), - InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 9 }, - shared: true, - abi3: false, - lib_name: Some("python3.9".into()), - lib_dir: Some("/usr/arm64/lib".into()), - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } - ); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY39; + let config = InterpreterConfigBuilder::new(implementation, version) + .lib_name("python3.9".into()) + .lib_dir("/usr/arm64/lib".into()) + .finalize(); + assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } #[test] @@ -2421,26 +2602,12 @@ mod tests { .unwrap() .unwrap(); - assert_eq!( - default_cross_compile(&cross_config).unwrap(), - InterpreterConfig { - implementation: PythonImplementation::PyPy, - version: PythonVersion { - major: 3, - minor: 11 - }, - shared: true, - abi3: false, - lib_name: Some("pypy3.11-c".into()), - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } - ); + let implementation = PythonImplementation::PyPy; + let version = PythonVersion::PY311; + let config = InterpreterConfigBuilder::new(implementation, version) + .lib_name("pypy3.11-c".into()) + .finalize(); + assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } #[test] @@ -2816,47 +2983,31 @@ mod tests { #[test] fn interpreter_version_reduced_to_abi3() { - let mut config = InterpreterConfig { - abi3: true, - build_flags: BuildFlags::default(), - pointer_width: None, - executable: None, - implementation: PythonImplementation::CPython, - lib_dir: None, - lib_name: None, - shared: true, + let builder = InterpreterConfigBuilder::new( + PythonImplementation::CPython, // Make this greater than the target abi3 version to reduce to below - version: PythonVersion { major: 3, minor: 9 }, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; + PythonVersion::PY39, + ) + .abi3(true); + + let mut config = builder.finalize(); config - .fixup_for_abi3_version(Some(PythonVersion { major: 3, minor: 8 })) + .fixup_for_abi3_version(Some(PythonVersion::PY38)) .unwrap(); - assert_eq!(config.version, PythonVersion { major: 3, minor: 8 }); + assert_eq!(config.version(), PythonVersion::PY38); } #[test] fn abi3_version_cannot_be_higher_than_interpreter() { - let mut config = InterpreterConfig { - abi3: true, - build_flags: BuildFlags::new(), - pointer_width: None, - executable: None, - implementation: PythonImplementation::CPython, - lib_dir: None, - lib_name: None, - shared: true, - version: PythonVersion { major: 3, minor: 8 }, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; + let builder = + InterpreterConfigBuilder::new(PythonImplementation::CPython, PythonVersion::PY38) + .abi3(true); + + let mut config = builder.finalize(); assert!(config - .fixup_for_abi3_version(Some(PythonVersion { major: 3, minor: 9 })) + .fixup_for_abi3_version(Some(PythonVersion::PY39)) .unwrap_err() .to_string() .contains( @@ -2901,33 +3052,22 @@ mod tests { _ => return, }; let sysconfigdata = super::parse_sysconfigdata(sysconfigdata_path).unwrap(); - let mut parsed_config = InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(); - - // Workaround case where empty `PYTHONFRAMEWORKPREFIX` is returned as empty string instead of None, - // which causes the assert_eq! below to fail. - // - // TODO: probably should deprecate using this variable at all, seemingly only used in `add_python_framework_link_args` - // which is probably a strictly worse version of `add_libpython_rpath_link_args`. - if parsed_config.python_framework_prefix.as_deref() == Some("") { - parsed_config.python_framework_prefix = None; - } + let parsed_config = InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(); + let implementation = PythonImplementation::CPython; assert_eq!( parsed_config, - InterpreterConfig { - abi3: false, - build_flags: BuildFlags(interpreter_config.build_flags.0.clone()), - pointer_width: Some(64), - executable: None, - implementation: PythonImplementation::CPython, - lib_dir: interpreter_config.lib_dir.to_owned(), - lib_name: interpreter_config.lib_name.to_owned(), - shared: true, - version: interpreter_config.version, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - } + InterpreterConfigBuilder::new( + implementation, + interpreter_config.version, + PythonAbiBuilder::new(implementation, interpreter_config.version).finalize() + ) + .build_flags(interpreter_config.build_flags.0.clone()) + .pointer_width(64) + .lib_dir(interpreter_config.lib_dir.to_owned()) + .lib_name(interpreter_config.lib_name.to_owned()) + .finalize() + .unwrap() ) } @@ -3046,23 +3186,9 @@ mod tests { #[test] fn test_build_script_outputs_base() { - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { - major: 3, - minor: 11, - }, - shared: true, - abi3: false, - lib_name: Some("python3".into()), - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY311; + let interpreter_config = InterpreterConfigBuilder::new(implementation, version).finalize(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3073,10 +3199,8 @@ mod tests { ] ); - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::PyPy, - ..interpreter_config - }; + let interpreter_config = + InterpreterConfigBuilder::new(PythonImplementation::PyPy, version).finalize(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3091,20 +3215,11 @@ mod tests { #[test] fn test_build_script_outputs_abi3() { - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 9 }, - shared: true, - abi3: true, - lib_name: Some("python3".into()), - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY39; + let interpreter_config = InterpreterConfigBuilder::new(implementation, version) + .abi3(true) + .finalize(); assert_eq!( interpreter_config.build_script_outputs(), @@ -3115,10 +3230,9 @@ mod tests { ] ); - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::PyPy, - ..interpreter_config - }; + let interpreter_config = InterpreterConfigBuilder::new(PythonImplementation::PyPy, version) + .abi3(true) + .finalize(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3134,24 +3248,11 @@ mod tests { fn test_build_script_outputs_gil_disabled() { let mut build_flags = BuildFlags::default(); build_flags.0.insert(BuildFlag::Py_GIL_DISABLED); - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { - major: 3, - minor: 13, - }, - shared: true, - abi3: false, - lib_name: Some("python3".into()), - lib_dir: None, - executable: None, - pointer_width: None, - build_flags, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; - + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY313; + let interpreter_config = InterpreterConfigBuilder::new(implementation, version) + .build_flags(build_flags) + .finalize(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3170,21 +3271,11 @@ mod tests { fn test_build_script_outputs_debug() { let mut build_flags = BuildFlags::default(); build_flags.0.insert(BuildFlag::Py_DEBUG); - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 8 }, - shared: true, - abi3: false, - lib_name: Some("python3".into()), - lib_dir: None, - executable: None, - pointer_width: None, - build_flags, - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; - + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY38; + let interpreter_config = InterpreterConfigBuilder::new(implementation, version) + .build_flags(build_flags) + .finalize(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3225,35 +3316,24 @@ mod tests { #[test] fn test_apply_default_lib_name_to_config_file() { - let mut config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { major: 3, minor: 9 }, - shared: true, - abi3: false, - lib_name: None, - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY39; + let mut config = InterpreterConfigBuilder::new(implementation, version).finalize(); let unix = Triple::from_str("x86_64-unknown-linux-gnu").unwrap(); let win_x64 = Triple::from_str("x86_64-pc-windows-msvc").unwrap(); let win_arm64 = Triple::from_str("aarch64-pc-windows-msvc").unwrap(); config.apply_default_lib_name_to_config_file(&unix); - assert_eq!(config.lib_name, Some("python3.9".into())); + assert_eq!(config.lib_name(), Some("python3.9".into())); config.lib_name = None; config.apply_default_lib_name_to_config_file(&win_x64); - assert_eq!(config.lib_name, Some("python39".into())); + assert_eq!(config.lib_name(), Some("python39".into())); config.lib_name = None; config.apply_default_lib_name_to_config_file(&win_arm64); - assert_eq!(config.lib_name, Some("python39".into())); + assert_eq!(config.lib_name(), Some("python39".into())); // PyPy config.implementation = PythonImplementation::PyPy; diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index fb56f848b94..0dd4a850a51 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -14,7 +14,8 @@ use std::{env, process::Command, str::FromStr, sync::LazyLock}; pub use impl_::{ cross_compiling_from_to, find_all_sysconfigdata, parse_sysconfigdata, BuildFlag, BuildFlags, - CrossCompileConfig, InterpreterConfig, PythonImplementation, PythonVersion, Triple, + CrossCompileConfig, InterpreterConfig, InterpreterConfigBuilder, PythonImplementation, + PythonVersion, Triple, }; use target_lexicon::OperatingSystem; @@ -107,7 +108,7 @@ fn _add_libpython_rpath_link_args( mut writer: impl std::io::Write, ) { if is_linking_libpython { - if let Some(lib_dir) = interpreter_config.lib_dir.as_ref() { + if let Some(lib_dir) = interpreter_config.lib_dir() { writeln!(writer, "cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}").unwrap(); } } @@ -138,7 +139,7 @@ fn _add_python_framework_link_args( mut writer: impl std::io::Write, ) { if matches!(triple.operating_system, OperatingSystem::Darwin(_)) && link_libpython { - if let Some(framework_prefix) = interpreter_config.python_framework_prefix.as_ref() { + if let Some(framework_prefix) = interpreter_config.python_framework_prefix() { writeln!(writer, "cargo:rustc-link-arg=-Wl,-rpath,{framework_prefix}").unwrap(); } } @@ -303,13 +304,13 @@ pub mod pyo3_build_script_impl { interpreter_config: &InterpreterConfig, supported_version: PythonVersion, ) -> Self { - let implementation = match interpreter_config.implementation { + let implementation = match interpreter_config.implementation() { PythonImplementation::CPython => "Python", PythonImplementation::PyPy => "PyPy", PythonImplementation::GraalPy => "GraalPy", PythonImplementation::RustPython => "RustPython", }; - let version = &interpreter_config.version; + let version = interpreter_config.version(); let message = format!( "the configured {implementation} version ({version}) is newer than PyO3's maximum supported version ({supported_version})\n\ = help: this package is being built with PyO3 version {current_version}\n\ @@ -346,6 +347,7 @@ fn rustc_minor_version() -> Option { } #[cfg(test)] +#[expect(deprecated, reason = "accessing config directly")] mod tests { use crate::impl_::escape; @@ -397,26 +399,14 @@ mod tests { #[test] fn python_framework_link_args() { let mut buf = Vec::new(); + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY313; + let interpreter_config = InterpreterConfigBuilder::new(implementation, version) + .python_framework_prefix( + "/Applications/Xcode.app/Contents/Developer/Library/Frameworks".into(), + ) + .finalize(); - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { - major: 3, - minor: 13, - }, - shared: true, - abi3: false, - lib_name: None, - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: Some( - "/Applications/Xcode.app/Contents/Developer/Library/Frameworks".to_string(), - ), - }; // Does nothing on non-mac _add_python_framework_link_args( &interpreter_config, @@ -440,29 +430,12 @@ mod tests { #[test] fn test_maximum_version_exceeded_formatting() { - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { - major: 3, - minor: 13, - }, - shared: true, - abi3: false, - lib_name: None, - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; + let implementation = PythonImplementation::CPython; + let version = PythonVersion::PY313; + let interpreter_config = InterpreterConfigBuilder::new(implementation, version).finalize(); let mut error = pyo3_build_script_impl::MaximumVersionExceeded::new( &interpreter_config, - PythonVersion { - major: 3, - minor: 12, - }, + PythonVersion::PY312, ); error.add_help("this is a help message"); let error = error.finish(); @@ -481,23 +454,9 @@ mod tests { // There should be no other tests or config in the environment assert!(InterpreterConfig::from_cargo_dep_env().is_none()); - let interpreter_config = InterpreterConfig { - implementation: PythonImplementation::CPython, - version: PythonVersion { - major: 3, - minor: 13, - }, - shared: true, - abi3: false, - lib_name: None, - lib_dir: None, - executable: None, - pointer_width: None, - build_flags: BuildFlags::default(), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - python_framework_prefix: None, - }; + let interpreter_config = + InterpreterConfigBuilder::new(PythonImplementation::CPython, PythonVersion::PY313) + .finalize(); let mut buf = Vec::new(); interpreter_config.to_writer(&mut buf).unwrap(); let config_string = escape(&buf); diff --git a/pyo3-ffi-check/definitions/build.rs b/pyo3-ffi-check/definitions/build.rs index 80e5a37fa56..992da8a360c 100644 --- a/pyo3-ffi-check/definitions/build.rs +++ b/pyo3-ffi-check/definitions/build.rs @@ -64,7 +64,7 @@ fn main() { .parse_callbacks(Box::new(ParseCallbacks)); if matches!( - config.implementation, + config.implementation(), pyo3_build_config::PythonImplementation::PyPy ) { builder = builder.parse_callbacks(Box::new(PyPyReplaceCallbacks)); diff --git a/pyo3-ffi-check/macro/src/lib.rs b/pyo3-ffi-check/macro/src/lib.rs index 7151e938c2a..1aa9b9f2f37 100644 --- a/pyo3-ffi-check/macro/src/lib.rs +++ b/pyo3-ffi-check/macro/src/lib.rs @@ -9,6 +9,16 @@ use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use pyo3_build_config::PythonVersion; use quote::quote; +const PY_3_15: PythonVersion = PythonVersion { + major: 3, + minor: 15, +}; + +const PY_3_12: PythonVersion = PythonVersion { + major: 3, + minor: 12, +}; + /// Macro which expands to multiple macro calls, one per pyo3-ffi struct. #[proc_macro] pub fn for_all_structs(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -34,8 +44,7 @@ pub fn for_all_structs(input: proc_macro::TokenStream) -> proc_macro::TokenStrea .strip_suffix(".html") .unwrap(); - if pyo3_build_config::get().version < PythonVersion::PY315 && struct_name == "PyBytesWriter" - { + if pyo3_build_config::get().version() < PY_3_15 && struct_name == "PyBytesWriter" { // PyBytesWriter was added in Python 3.15 continue; } @@ -158,8 +167,7 @@ pub fn for_all_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream if struct_name == "PyMemberDef" { // bindgen picked `type_` as the field name to avoid the `type` keyword, but PyO3 uses `type_code` all_fields.remove("type_"); - } else if struct_name == "PyObject" && pyo3_build_config::get().version >= PythonVersion::PY312 - { + } else if struct_name == "PyObject" && pyo3_build_config::get().version() >= PY_3_12 { // bindgen picked `__bindgen_anon_1` as the field name for the anonymous union containing ob_refcnt, // PyO3 uses ob_refcnt directly all_fields.remove("__bindgen_anon_1"); @@ -176,7 +184,7 @@ pub fn for_all_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream let field_ident = Ident::new(&field_name, Span::call_site()); - let bindgen_field_ident = if (pyo3_build_config::get().version >= PythonVersion::PY312) + let bindgen_field_ident = if (pyo3_build_config::get().version() >= PY_3_12) && struct_name == "PyObject" && field_name == "ob_refcnt" { @@ -551,7 +559,8 @@ pub fn for_all_functions(_input: proc_macro::TokenStream) -> proc_macro::TokenSt continue; } - if pyo3_build_config::get().implementation == pyo3_build_config::PythonImplementation::PyPy + if pyo3_build_config::get().implementation() + == pyo3_build_config::PythonImplementation::PyPy { // If the function doesn't exist in PyPy, for now we don't care: // - For PyO3 inline functions it's probably fine to include anyway diff --git a/pyo3-ffi/build.rs b/pyo3-ffi/build.rs index e59b7ba5eeb..d60dc34ff8b 100644 --- a/pyo3-ffi/build.rs +++ b/pyo3-ffi/build.rs @@ -45,27 +45,27 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { return Ok(()); } - match interpreter_config.implementation { + match interpreter_config.implementation() { PythonImplementation::CPython => { let versions = SUPPORTED_VERSIONS_CPYTHON; ensure!( - interpreter_config.version >= versions.min, + interpreter_config.version() >= versions.min, "the configured Python interpreter version ({}) is lower than PyO3's minimum supported version ({})", - interpreter_config.version, + interpreter_config.version(), versions.min, ); let v_plus_1 = PythonVersion { major: versions.max.major, minor: versions.max.minor + 1, }; - if interpreter_config.version == v_plus_1 { + if interpreter_config.version() == v_plus_1 { warn!( "Using experimental support for the Python {}.{} ABI. \ Build artifacts may not be compatible with the final release of CPython, \ so do not distribute them.", v_plus_1.major, v_plus_1.minor, ); - } else if interpreter_config.version > v_plus_1 { + } else if interpreter_config.version() > v_plus_1 { let mut error = MaximumVersionExceeded::new(interpreter_config, versions.max); if interpreter_config.is_free_threaded() { error.add_help( @@ -87,23 +87,23 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { minor: 14, }; ensure!( - interpreter_config.version >= min_free_threaded_version, + interpreter_config.version() >= min_free_threaded_version, "PyO3 does not support the free-threaded build of CPython versions below {}, the selected Python version is {}", min_free_threaded_version, - interpreter_config.version, + interpreter_config.version(), ); } } PythonImplementation::PyPy => { let versions = SUPPORTED_VERSIONS_PYPY; ensure!( - interpreter_config.version >= versions.min, + interpreter_config.version() >= versions.min, "the configured PyPy interpreter version ({}) is lower than PyO3's minimum supported version ({})", - interpreter_config.version, + interpreter_config.version(), versions.min, ); // PyO3 does not support abi3, so we cannot offer forward compatibility - if interpreter_config.version > versions.max { + if interpreter_config.version() > versions.max { let error = MaximumVersionExceeded::new(interpreter_config, versions.max); return Err(error.finish().into()); } @@ -111,13 +111,13 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { PythonImplementation::GraalPy => { let versions = SUPPORTED_VERSIONS_GRAALPY; ensure!( - interpreter_config.version >= versions.min, + interpreter_config.version() >= versions.min, "the configured GraalPy interpreter version ({}) is lower than PyO3's minimum supported version ({})", - interpreter_config.version, + interpreter_config.version(), versions.min, ); // GraalPy does not support abi3, so we cannot offer forward compatibility - if interpreter_config.version > versions.max { + if interpreter_config.version() > versions.max { let error = MaximumVersionExceeded::new(interpreter_config, versions.max); return Err(error.finish().into()); } @@ -125,8 +125,8 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { PythonImplementation::RustPython => {} } - if interpreter_config.abi3 { - match interpreter_config.implementation { + if interpreter_config.abi3() { + match interpreter_config.implementation() { PythonImplementation::CPython => { if interpreter_config.is_free_threaded() { warn!( @@ -149,7 +149,7 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { } fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> { - if let Some(pointer_width) = interpreter_config.pointer_width { + if let Some(pointer_width) = interpreter_config.pointer_width() { // Try to check whether the target architecture matches the python library let rust_target = match cargo_env_var("CARGO_CFG_TARGET_POINTER_WIDTH") .unwrap() @@ -175,8 +175,7 @@ fn emit_link_config(build_config: &BuildConfig) -> Result<()> { let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap(); let lib_name = interpreter_config - .lib_name - .as_ref() + .lib_name() .ok_or("attempted to link to Python shared library but config does not contain lib_name")?; if target_os == "windows" { @@ -191,14 +190,14 @@ fn emit_link_config(build_config: &BuildConfig) -> Result<()> { } else { println!( "cargo:rustc-link-lib={link_model}{lib_name}", - link_model = if interpreter_config.shared { + link_model = if interpreter_config.shared() { "" } else { "static=" }, ); - if let Some(lib_dir) = &interpreter_config.lib_dir { + if let Some(lib_dir) = interpreter_config.lib_dir() { println!("cargo:rustc-link-search=native={lib_dir}"); } else if matches!(build_config.source, BuildConfigSource::CrossCompile) { warn!( @@ -236,7 +235,7 @@ fn configure_pyo3() -> Result<()> { interpreter_config.to_cargo_dep_env()?; if is_linking_libpython_for_target(&target) - && !interpreter_config.suppress_build_script_link_lines + && !interpreter_config.suppress_build_script_link_lines() { emit_link_config(&build_config)?; } @@ -246,7 +245,7 @@ fn configure_pyo3() -> Result<()> { } // Extra lines come last, to support last write wins. - for line in &interpreter_config.extra_build_script_lines { + for line in interpreter_config.extra_build_script_lines() { println!("{line}"); } From 781648bebe34d3adfc5abc90d7f3471af39ebc2d Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 12 May 2026 08:33:23 +0100 Subject: [PATCH 2/7] newsfragments --- newsfragments/6034.added.md | 1 + newsfragments/6034.changed.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 newsfragments/6034.added.md create mode 100644 newsfragments/6034.changed.md diff --git a/newsfragments/6034.added.md b/newsfragments/6034.added.md new file mode 100644 index 00000000000..a64fa6cfa6e --- /dev/null +++ b/newsfragments/6034.added.md @@ -0,0 +1 @@ +Add `pyo3_build_config::InterpreterConfigBuilder`. diff --git a/newsfragments/6034.changed.md b/newsfragments/6034.changed.md new file mode 100644 index 00000000000..055a1f3bd75 --- /dev/null +++ b/newsfragments/6034.changed.md @@ -0,0 +1 @@ +Deprecate direct access to all `pyo3_build_config::InterpreterConfig` fields; getter methods have been added as replacements. From f091c9a1b7b778f62db6992223310dd7cff9e62f Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 12 May 2026 08:53:46 +0100 Subject: [PATCH 3/7] fixup ubuntu test --- pyo3-build-config/src/impl_.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index b60d9ae1ba6..50fe06d917e 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -3057,17 +3057,12 @@ mod tests { assert_eq!( parsed_config, - InterpreterConfigBuilder::new( - implementation, - interpreter_config.version, - PythonAbiBuilder::new(implementation, interpreter_config.version).finalize() - ) - .build_flags(interpreter_config.build_flags.0.clone()) - .pointer_width(64) - .lib_dir(interpreter_config.lib_dir.to_owned()) - .lib_name(interpreter_config.lib_name.to_owned()) - .finalize() - .unwrap() + InterpreterConfigBuilder::new(implementation, interpreter_config.version,) + .build_flags(interpreter_config.build_flags().clone()) + .pointer_width(64) + .lib_dir_opt(interpreter_config.lib_dir().map(str::to_owned)) + .lib_name_opt(interpreter_config.lib_name().map(str::to_owned)) + .finalize() ) } From ef4fb922202d55923ce81245e9ce0e04266dcf0f Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 12 May 2026 11:34:00 +0100 Subject: [PATCH 4/7] fixup --- pyo3-build-config/src/impl_.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 50fe06d917e..4c0ee838036 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -3053,16 +3053,27 @@ mod tests { }; let sysconfigdata = super::parse_sysconfigdata(sysconfigdata_path).unwrap(); let parsed_config = InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(); - let implementation = PythonImplementation::CPython; + + // Workaround case where empty `PYTHONFRAMEWORKPREFIX` is returned as empty string instead of None, + // which causes the assert_eq! below to fail. + // + // TODO: probably should deprecate using this variable at all, seemingly only used in `add_python_framework_link_args` + // which is probably a strictly worse version of `add_libpython_rpath_link_args`. + if parsed_config.python_framework_prefix.as_deref() == Some("") { + parsed_config.python_framework_prefix = None; + } assert_eq!( parsed_config, - InterpreterConfigBuilder::new(implementation, interpreter_config.version,) - .build_flags(interpreter_config.build_flags().clone()) - .pointer_width(64) - .lib_dir_opt(interpreter_config.lib_dir().map(str::to_owned)) - .lib_name_opt(interpreter_config.lib_name().map(str::to_owned)) - .finalize() + InterpreterConfigBuilder::new( + interpreter_config.implementation, + interpreter_config.version, + ) + .build_flags(interpreter_config.build_flags().clone()) + .pointer_width(64) + .lib_dir_opt(interpreter_config.lib_dir().map(str::to_owned)) + .lib_name_opt(interpreter_config.lib_name().map(str::to_owned)) + .finalize() ) } @@ -3320,15 +3331,15 @@ mod tests { let win_arm64 = Triple::from_str("aarch64-pc-windows-msvc").unwrap(); config.apply_default_lib_name_to_config_file(&unix); - assert_eq!(config.lib_name(), Some("python3.9".into())); + assert_eq!(config.lib_name, Some("python3.9".into())); config.lib_name = None; config.apply_default_lib_name_to_config_file(&win_x64); - assert_eq!(config.lib_name(), Some("python39".into())); + assert_eq!(config.lib_name, Some("python39".into())); config.lib_name = None; config.apply_default_lib_name_to_config_file(&win_arm64); - assert_eq!(config.lib_name(), Some("python39".into())); + assert_eq!(config.lib_name, Some("python39".into())); // PyPy config.implementation = PythonImplementation::PyPy; From 7ee1ed07aef82d3fdcf8205087f35b43c8cec5fc Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 12 May 2026 14:44:19 +0100 Subject: [PATCH 5/7] fix missing `mut` --- pyo3-build-config/src/impl_.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 4c0ee838036..ba10ee4a96e 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -3052,7 +3052,7 @@ mod tests { _ => return, }; let sysconfigdata = super::parse_sysconfigdata(sysconfigdata_path).unwrap(); - let parsed_config = InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(); + let mut parsed_config = InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(); // Workaround case where empty `PYTHONFRAMEWORKPREFIX` is returned as empty string instead of None, // which causes the assert_eq! below to fail. From 13448e3554f6138965a5db1faf0fb89289c4b650 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 12 May 2026 21:43:26 +0100 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Thomas Tanon --- pyo3-build-config/src/impl_.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index ba10ee4a96e..e525ea2910c 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -1868,7 +1868,6 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result Result Date: Tue, 12 May 2026 21:52:30 +0100 Subject: [PATCH 7/7] review feedback --- pyo3-build-config/src/impl_.rs | 246 ++++++++++++++++----------------- pyo3-build-config/src/lib.rs | 12 +- 2 files changed, 129 insertions(+), 129 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index e525ea2910c..e0f2fe0ebc1 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -501,13 +501,13 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) .abi3(abi3) .shared(shared) .lib_name(lib_name) - .lib_dir_opt(lib_dir) + .lib_dir(lib_dir) .executable(map["executable"].clone()) .pointer_width(calcsize_pointer * 8) .build_flags(BuildFlags::from_interpreter(interpreter)?) - .python_framework_prefix_opt(python_framework_prefix); + .python_framework_prefix(python_framework_prefix); - Ok(builder.finalize()) + builder.finalize() } /// Generate from parsed sysconfigdata file @@ -570,13 +570,13 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) let builder = InterpreterConfigBuilder::new(implementation, version) .abi3(abi3) .shared(shared || framework) - .pointer_width_opt(pointer_width) - .lib_name_opt(lib_name) - .lib_dir_opt(lib_dir) - .python_framework_prefix_opt(python_framework_prefix) + .pointer_width(pointer_width) + .lib_name(lib_name) + .lib_dir(lib_dir) + .python_framework_prefix(python_framework_prefix) .build_flags(build_flags); - Ok(builder.finalize()) + builder.finalize() } /// Import an externally-provided config file. @@ -696,16 +696,16 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) let builder = InterpreterConfigBuilder::new(implementation, version) .abi3(abi3) .shared(shared.unwrap_or(true)) - .lib_name_opt(lib_name) - .lib_dir_opt(lib_dir) - .executable_opt(executable) - .pointer_width_opt(pointer_width) + .lib_name(lib_name) + .lib_dir(lib_dir) + .executable(executable) + .pointer_width(pointer_width) .build_flags(build_flags) .suppress_build_script_link_lines(suppress_build_script_link_lines.unwrap_or(false)) .extra_build_script_lines(extra_build_script_lines) - .python_framework_prefix_opt(python_framework_prefix); + .python_framework_prefix(python_framework_prefix); - Ok(builder.finalize()) + builder.finalize() } /// Helper function to apply a default lib_name if none is set in `PYO3_CONFIG_FILE`. @@ -859,14 +859,14 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) pub struct InterpreterConfigBuilder { implementation: PythonImplementation, version: PythonVersion, - shared: Option, - abi3: Option, + shared: bool, + abi3: bool, lib_name: Option, lib_dir: Option, executable: Option, pointer_width: Option, - build_flags: Option, - suppress_build_script_link_lines: Option, + build_flags: BuildFlags, + suppress_build_script_link_lines: bool, extra_build_script_lines: Vec, python_framework_prefix: Option, } @@ -879,36 +879,39 @@ impl InterpreterConfigBuilder { InterpreterConfigBuilder { implementation, version, - shared: None, - abi3: None, + shared: true, + abi3: false, lib_name: None, lib_dir: None, executable: None, pointer_width: None, - build_flags: None, - suppress_build_script_link_lines: None, + build_flags: BuildFlags::default(), + suppress_build_script_link_lines: false, extra_build_script_lines: vec![], python_framework_prefix: None, } } pub fn abi3(mut self, abi3: bool) -> InterpreterConfigBuilder { - self.abi3 = Some(abi3); + self.abi3 = abi3; self } - pub fn lib_name(mut self, lib_name: String) -> InterpreterConfigBuilder { - self.lib_name = Some(lib_name); + pub fn lib_name(mut self, lib_name: impl Into>) -> InterpreterConfigBuilder { + self.lib_name = lib_name.into(); self } - pub fn pointer_width(mut self, pointer_width: u32) -> InterpreterConfigBuilder { - self.pointer_width = Some(pointer_width); + pub fn pointer_width( + mut self, + pointer_width: impl Into>, + ) -> InterpreterConfigBuilder { + self.pointer_width = pointer_width.into(); self } - pub fn executable(mut self, executable: String) -> InterpreterConfigBuilder { - self.executable = Some(executable); + pub fn executable(mut self, executable: impl Into>) -> InterpreterConfigBuilder { + self.executable = executable.into(); self } @@ -916,7 +919,7 @@ impl InterpreterConfigBuilder { mut self, suppress_build_script_link_lines: bool, ) -> InterpreterConfigBuilder { - self.suppress_build_script_link_lines = Some(suppress_build_script_link_lines); + self.suppress_build_script_link_lines = suppress_build_script_link_lines; self } @@ -928,81 +931,48 @@ impl InterpreterConfigBuilder { self } - pub fn lib_dir(mut self, lib_dir: String) -> InterpreterConfigBuilder { - self.lib_dir = Some(lib_dir); + pub fn lib_dir(mut self, lib_dir: impl Into>) -> InterpreterConfigBuilder { + self.lib_dir = lib_dir.into(); self } pub fn shared(mut self, shared: bool) -> InterpreterConfigBuilder { - self.shared = Some(shared); + self.shared = shared; self } pub fn build_flags(mut self, build_flags: BuildFlags) -> InterpreterConfigBuilder { - self.build_flags = Some(build_flags); + self.build_flags = build_flags; self } pub fn python_framework_prefix( - self, - python_framework_prefix: String, + mut self, + python_framework_prefix: impl Into>, ) -> InterpreterConfigBuilder { - InterpreterConfigBuilder { - python_framework_prefix: Some(python_framework_prefix), - ..self - } + self.python_framework_prefix = python_framework_prefix.into(); + self } - pub fn finalize(self) -> InterpreterConfig { + pub fn finalize(self) -> Result { #[expect( deprecated, reason = "constructing an InterpreterConfig directly, need to write to fields" )] - InterpreterConfig { + Ok(InterpreterConfig { implementation: self.implementation, version: self.version, - shared: self.shared.unwrap_or(true), - abi3: self.abi3.unwrap_or(false), + shared: self.shared, + abi3: self.abi3, lib_name: self.lib_name, lib_dir: self.lib_dir, executable: self.executable, pointer_width: self.pointer_width, - build_flags: self.build_flags.unwrap_or_default(), - suppress_build_script_link_lines: self - .suppress_build_script_link_lines - .unwrap_or(false), + build_flags: self.build_flags, + suppress_build_script_link_lines: self.suppress_build_script_link_lines, extra_build_script_lines: self.extra_build_script_lines, python_framework_prefix: self.python_framework_prefix, - } - } - - // private variants of some methods where it's convenient to potentially pass None - fn pointer_width_opt(mut self, pointer_width: Option) -> InterpreterConfigBuilder { - self.pointer_width = pointer_width; - self - } - - fn executable_opt(mut self, executable: Option) -> InterpreterConfigBuilder { - self.executable = executable; - self - } - - fn lib_name_opt(mut self, lib_name: Option) -> InterpreterConfigBuilder { - self.lib_name = lib_name; - self - } - - fn lib_dir_opt(mut self, lib_dir: Option) -> InterpreterConfigBuilder { - self.lib_dir = lib_dir; - self - } - - fn python_framework_prefix_opt( - mut self, - python_framework_prefix: Option, - ) -> InterpreterConfigBuilder { - self.python_framework_prefix = python_framework_prefix; - self + }) } } @@ -1870,9 +1840,9 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result Result = Vec::new(); config.to_writer(&mut buf).unwrap(); @@ -2290,7 +2261,8 @@ mod tests { }; let config = InterpreterConfigBuilder::new(implementation, version) .build_flags(build_flags) - .finalize(); + .finalize() + .unwrap(); let mut buf: Vec = Vec::new(); config.to_writer(&mut buf).unwrap(); @@ -2305,11 +2277,12 @@ mod tests { let config = InterpreterConfigBuilder::new(implementation, version) .abi3(true) .pointer_width(32) - .executable("executable".into()) - .lib_name("lib_name".into()) - .lib_dir("lib_dir\\n".into()) + .executable("executable".to_string()) + .lib_name("lib_name".to_string()) + .lib_dir("lib_dir\\n".to_string()) .extra_build_script_lines(vec!["cargo:test1".to_string(), "cargo:test2".to_string()]) - .finalize(); + .finalize() + .unwrap(); let mut buf: Vec = Vec::new(); config.to_writer(&mut buf).unwrap(); @@ -2325,7 +2298,9 @@ mod tests { let version = PythonVersion::PY38; assert_eq!( InterpreterConfig::from_reader("version=3.8".as_bytes()).unwrap(), - InterpreterConfigBuilder::new(implementation, version,).finalize() + InterpreterConfigBuilder::new(implementation, version,) + .finalize() + .unwrap() ) } @@ -2337,7 +2312,9 @@ mod tests { assert_eq!( InterpreterConfig::from_reader("version=3.8\next_suffix=.python38.so".as_bytes()) .unwrap(), - InterpreterConfigBuilder::new(implementation, version,).finalize() + InterpreterConfigBuilder::new(implementation, version,) + .finalize() + .unwrap() ) } @@ -2431,10 +2408,11 @@ mod tests { InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(), InterpreterConfigBuilder::new(implementation, version,) .build_flags(BuildFlags::from_sysconfigdata(&sysconfigdata)) - .lib_dir("/usr/lib".into()) - .lib_name("python3.8".into()) + .lib_dir("/usr/lib".to_string()) + .lib_name("python3.8".to_string()) .pointer_width(64) .finalize() + .unwrap() ); } @@ -2455,10 +2433,11 @@ mod tests { InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(), InterpreterConfigBuilder::new(implementation, version,) .build_flags(BuildFlags::from_sysconfigdata(&sysconfigdata)) - .lib_dir("/usr/lib".into()) - .lib_name("python3.8".into()) + .lib_dir("/usr/lib".to_string()) + .lib_name("python3.8".to_string()) .pointer_width(64) .finalize() + .unwrap() ); sysconfigdata = Sysconfigdata::new(); @@ -2476,11 +2455,12 @@ mod tests { InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(), InterpreterConfigBuilder::new(implementation, version,) .build_flags(BuildFlags::from_sysconfigdata(&sysconfigdata)) - .lib_dir("/usr/lib".into()) - .lib_name("python3.8".into()) + .lib_dir("/usr/lib".to_string()) + .lib_name("python3.8".to_string()) .pointer_width(64) .shared(false) .finalize() + .unwrap() ); } @@ -2493,8 +2473,9 @@ mod tests { let version = PythonVersion::PY38; let config = InterpreterConfigBuilder::new(implementation, version) .abi3(true) - .lib_name("python3".into()) - .finalize(); + .lib_name("python3".to_string()) + .finalize() + .unwrap(); assert_eq!(default_abi3_config(&host, min_version).unwrap(), config); } @@ -2506,7 +2487,8 @@ mod tests { let version = PythonVersion::PY39; let config = InterpreterConfigBuilder::new(implementation, version) .abi3(true) - .finalize(); + .finalize() + .unwrap(); assert_eq!(default_abi3_config(&host, min_version).unwrap(), config); } @@ -2529,9 +2511,10 @@ mod tests { let implementation = PythonImplementation::CPython; let version = PythonVersion::PY38; let config = InterpreterConfigBuilder::new(implementation, version) - .lib_name("python38".into()) - .lib_dir("C:\\some\\path".into()) - .finalize(); + .lib_name("python38".to_string()) + .lib_dir("C:\\some\\path".to_string()) + .finalize() + .unwrap(); assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } @@ -2554,9 +2537,10 @@ mod tests { let implementation = PythonImplementation::CPython; let version = PythonVersion::PY38; let config = InterpreterConfigBuilder::new(implementation, version) - .lib_name("python38".into()) - .lib_dir("/usr/lib/mingw".into()) - .finalize(); + .lib_name("python38".to_string()) + .lib_dir("/usr/lib/mingw".to_string()) + .finalize() + .unwrap(); assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } @@ -2579,9 +2563,10 @@ mod tests { let implementation = PythonImplementation::CPython; let version = PythonVersion::PY39; let config = InterpreterConfigBuilder::new(implementation, version) - .lib_name("python3.9".into()) - .lib_dir("/usr/arm64/lib".into()) - .finalize(); + .lib_name("python3.9".to_string()) + .lib_dir("/usr/arm64/lib".to_string()) + .finalize() + .unwrap(); assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } @@ -2603,8 +2588,9 @@ mod tests { let implementation = PythonImplementation::PyPy; let version = PythonVersion::PY311; let config = InterpreterConfigBuilder::new(implementation, version) - .lib_name("pypy3.11-c".into()) - .finalize(); + .lib_name("pypy3.11-c".to_string()) + .finalize() + .unwrap(); assert_eq!(default_cross_compile(&cross_config).unwrap(), config); } @@ -2988,7 +2974,7 @@ mod tests { ) .abi3(true); - let mut config = builder.finalize(); + let mut config = builder.finalize().unwrap(); config .fixup_for_abi3_version(Some(PythonVersion::PY38)) @@ -3002,7 +2988,7 @@ mod tests { InterpreterConfigBuilder::new(PythonImplementation::CPython, PythonVersion::PY38) .abi3(true); - let mut config = builder.finalize(); + let mut config = builder.finalize().unwrap(); assert!(config .fixup_for_abi3_version(Some(PythonVersion::PY39)) @@ -3069,9 +3055,10 @@ mod tests { ) .build_flags(interpreter_config.build_flags().clone()) .pointer_width(64) - .lib_dir_opt(interpreter_config.lib_dir().map(str::to_owned)) - .lib_name_opt(interpreter_config.lib_name().map(str::to_owned)) + .lib_dir(interpreter_config.lib_dir().map(str::to_owned)) + .lib_name(interpreter_config.lib_name().map(str::to_owned)) .finalize() + .unwrap() ) } @@ -3192,7 +3179,9 @@ mod tests { fn test_build_script_outputs_base() { let implementation = PythonImplementation::CPython; let version = PythonVersion::PY311; - let interpreter_config = InterpreterConfigBuilder::new(implementation, version).finalize(); + let interpreter_config = InterpreterConfigBuilder::new(implementation, version) + .finalize() + .unwrap(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3203,8 +3192,9 @@ mod tests { ] ); - let interpreter_config = - InterpreterConfigBuilder::new(PythonImplementation::PyPy, version).finalize(); + let interpreter_config = InterpreterConfigBuilder::new(PythonImplementation::PyPy, version) + .finalize() + .unwrap(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3223,7 +3213,8 @@ mod tests { let version = PythonVersion::PY39; let interpreter_config = InterpreterConfigBuilder::new(implementation, version) .abi3(true) - .finalize(); + .finalize() + .unwrap(); assert_eq!( interpreter_config.build_script_outputs(), @@ -3236,7 +3227,8 @@ mod tests { let interpreter_config = InterpreterConfigBuilder::new(PythonImplementation::PyPy, version) .abi3(true) - .finalize(); + .finalize() + .unwrap(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3256,7 +3248,8 @@ mod tests { let version = PythonVersion::PY313; let interpreter_config = InterpreterConfigBuilder::new(implementation, version) .build_flags(build_flags) - .finalize(); + .finalize() + .unwrap(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3279,7 +3272,8 @@ mod tests { let version = PythonVersion::PY38; let interpreter_config = InterpreterConfigBuilder::new(implementation, version) .build_flags(build_flags) - .finalize(); + .finalize() + .unwrap(); assert_eq!( interpreter_config.build_script_outputs(), [ @@ -3322,7 +3316,9 @@ mod tests { fn test_apply_default_lib_name_to_config_file() { let implementation = PythonImplementation::CPython; let version = PythonVersion::PY39; - let mut config = InterpreterConfigBuilder::new(implementation, version).finalize(); + let mut config = InterpreterConfigBuilder::new(implementation, version) + .finalize() + .unwrap(); let unix = Triple::from_str("x86_64-unknown-linux-gnu").unwrap(); let win_x64 = Triple::from_str("x86_64-pc-windows-msvc").unwrap(); diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 0dd4a850a51..febb9637cf2 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -403,9 +403,10 @@ mod tests { let version = PythonVersion::PY313; let interpreter_config = InterpreterConfigBuilder::new(implementation, version) .python_framework_prefix( - "/Applications/Xcode.app/Contents/Developer/Library/Frameworks".into(), + "/Applications/Xcode.app/Contents/Developer/Library/Frameworks".to_string(), ) - .finalize(); + .finalize() + .unwrap(); // Does nothing on non-mac _add_python_framework_link_args( @@ -432,7 +433,9 @@ mod tests { fn test_maximum_version_exceeded_formatting() { let implementation = PythonImplementation::CPython; let version = PythonVersion::PY313; - let interpreter_config = InterpreterConfigBuilder::new(implementation, version).finalize(); + let interpreter_config = InterpreterConfigBuilder::new(implementation, version) + .finalize() + .unwrap(); let mut error = pyo3_build_script_impl::MaximumVersionExceeded::new( &interpreter_config, PythonVersion::PY312, @@ -456,7 +459,8 @@ mod tests { let interpreter_config = InterpreterConfigBuilder::new(PythonImplementation::CPython, PythonVersion::PY313) - .finalize(); + .finalize() + .unwrap(); let mut buf = Vec::new(); interpreter_config.to_writer(&mut buf).unwrap(); let config_string = escape(&buf);