diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index e287abcb4f05..9f6bb2d0ab8e 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -1490,45 +1490,74 @@ impl fmt::Display for PythonSource { } } -impl fmt::Display for PythonPreference { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let s = match self { - Self::OnlyManaged => "managed installations", +impl PythonPreference { + /// Return the sources that are considered when searching for a Python interpreter. + fn sources(self) -> &'static [&'static str] { + match self { + Self::OnlyManaged => &["managed installations"], Self::Managed | Self::Installed | Self::System => { if cfg!(windows) { - "managed installations, system path, or `py` launcher" + &["managed installations", "system path", "`py` launcher"] } else { - "managed installations or system path" + &["managed installations", "system path"] } } Self::OnlySystem => { if cfg!(windows) { - "system path or `py` launcher" + &["system path", "`py` launcher"] } else { - "system path" + &["system path"] } } - }; - f.write_str(s) + } + } + + /// Join a series of sources with commas and an "or" before the last source. + fn join_sources(sources: &[&str]) -> String { + match sources.len() { + 1 => sources[0].to_string(), + 2 => format!("{} or {}", sources[0], sources[1]), + _ => { + let last = sources.last().unwrap(); + format!( + "{}, or {}", + sources.iter().take(sources.len() - 1).join(", "), + last + ) + } + } + } +} + +impl fmt::Display for PythonPreference { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", Self::join_sources(self.sources())) } } impl fmt::Display for PythonNotFound { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let sources = match self.environment_preference { - EnvironmentPreference::Any => { - format!("virtual environments or {}", self.python_preference) - } - EnvironmentPreference::ExplicitSystem => { - if self.request.is_explicit_system() { - "virtual or system environment".to_string() - } else { - "virtual environment".to_string() + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let sources = { + let items: Vec<&str> = match self.environment_preference { + EnvironmentPreference::Any => ["virtual environments"] + .into_iter() + .chain(self.python_preference.sources().iter().copied()) + .collect(), + EnvironmentPreference::ExplicitSystem => { + if self.request.is_explicit_system() { + vec!["virtual", "system environment"] + } else { + vec!["virtual environment"] + } } - } - EnvironmentPreference::OnlySystem => self.python_preference.to_string(), - EnvironmentPreference::OnlyVirtual => "virtual environments".to_string(), + EnvironmentPreference::OnlySystem => { + self.python_preference.sources().to_vec() + } + EnvironmentPreference::OnlyVirtual => vec!["virtual environments"], + }; + PythonPreference::join_sources(&items) }; + match self.request { PythonRequest::Any => { write!(f, "No interpreter found in {sources}")