diff --git a/src/build_options.rs b/src/build_options.rs index a7de6c5ed..e3c63183c 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -1301,7 +1301,12 @@ fn find_single_python_interpreter( target: &Target, bridge_name: &str, ) -> Result { - let err_message = "Failed to find a python interpreter"; + let interpreter_str = interpreter + .iter() + .map(|interpreter| format!("`{}`", interpreter.display())) + .collect::>() + .join(", "); + let err_message = format!("Failed to find a python interpreter from {interpreter_str}"); let executable = if interpreter.is_empty() { target.get_python() @@ -1315,7 +1320,7 @@ fn find_single_python_interpreter( }; let interpreter = PythonInterpreter::check_executable(executable, target, bridge) - .context(format_err!(err_message))? + .context(format_err!(err_message.clone()))? .ok_or_else(|| format_err!(err_message))?; Ok(interpreter) } @@ -1690,12 +1695,12 @@ impl CargoOptions { #[cfg(test)] mod tests { + use super::*; use cargo_metadata::MetadataCommand; + use insta::assert_snapshot; use pretty_assertions::assert_eq; use std::path::Path; - use super::*; - #[test] fn test_find_bridge_pyo3() { let pyo3_mixed = MetadataCommand::new() @@ -1861,4 +1866,15 @@ mod tests { assert_eq!(extract_cargo_metadata_args(&args).unwrap(), expected); } + + #[test] + fn test_find_single_python_interpreter_not_found() { + let target = Target::from_resolved_target_triple("x86_64-unknown-linux-gnu").unwrap(); + let bridge = BridgeModel::Cffi; + let interpreter = vec![PathBuf::from("nonexistent-python-xyz")]; + + let result = find_single_python_interpreter(&bridge, &interpreter, &target, "cffi"); + let err_msg = result.unwrap_err().to_string(); + assert_snapshot!(err_msg, @"Failed to find a python interpreter from `nonexistent-python-xyz`"); + } } diff --git a/src/python_interpreter/mod.rs b/src/python_interpreter/mod.rs index 5321cd025..780182617 100644 --- a/src/python_interpreter/mod.rs +++ b/src/python_interpreter/mod.rs @@ -783,6 +783,7 @@ impl PythonInterpreter { bridge: &BridgeModel, ) -> Result> { let mut available_versions = Vec::new(); + let mut missing = Vec::new(); for executable in executables { if let Some(version) = PythonInterpreter::check_executable(executable, target, bridge) .context(format!( @@ -791,13 +792,22 @@ impl PythonInterpreter { ))? { available_versions.push(version); } else { - bail!( - "Python interpreter `{}` doesn't exist", - executable.display() - ); + missing.push(executable); } } + if !missing.is_empty() { + let missing_str = missing + .iter() + .map(|p| format!("`{}`", p.display())) + .collect::>() + .join(", "); + bail!( + "The following Python interpreters could not be found: {}", + missing_str + ); + } + Ok(available_versions) } @@ -959,10 +969,10 @@ fn calculate_abi_tag(ext_suffix: &str) -> Option { #[cfg(test)] mod tests { + use super::*; use crate::bridge::{PyO3, PyO3Crate}; use expect_test::expect; - - use super::*; + use insta::assert_snapshot; #[test] fn test_find_interpreter_by_target() { @@ -1263,4 +1273,29 @@ mod tests { } ); } + + #[test] + fn test_check_executables_single_missing() { + let target = Target::from_resolved_target_triple("x86_64-unknown-linux-gnu").unwrap(); + let bridge = BridgeModel::Bin(None); + let executables = vec![PathBuf::from("nonexistent-python-1")]; + + let result = PythonInterpreter::check_executables(&executables, &target, &bridge); + let err_msg = result.unwrap_err().to_string(); + assert_snapshot!(err_msg, @"The following Python interpreters could not be found: `nonexistent-python-1`"); + } + + #[test] + fn test_check_executables_multiple_missing() { + let target = Target::from_resolved_target_triple("x86_64-unknown-linux-gnu").unwrap(); + let bridge = BridgeModel::Bin(None); + let executables = vec![ + PathBuf::from("nonexistent-python-1"), + PathBuf::from("nonexistent-python-2"), + ]; + + let result = PythonInterpreter::check_executables(&executables, &target, &bridge); + let err_msg = result.unwrap_err().to_string(); + assert_snapshot!(err_msg, @"The following Python interpreters could not be found: `nonexistent-python-1`, `nonexistent-python-2`"); + } }