From ddfbee1cb692a537c1473a0717b20890ce9ee211 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 23 May 2024 09:22:34 -0400 Subject: [PATCH] Add `InterpreterRequest::Any` instead of using `VersionRequest::Any` (#3789) A follow-up to #3266 addressing some awkwardness where there was no "empty" or default interpreter request kind. --- crates/uv-interpreter/src/discovery.rs | 57 +++++++++++++++++------- crates/uv-interpreter/src/environment.rs | 6 +-- crates/uv-interpreter/src/lib.rs | 8 ++-- crates/uv/src/commands/pip/compile.rs | 2 +- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/crates/uv-interpreter/src/discovery.rs b/crates/uv-interpreter/src/discovery.rs index 3562be28a..814705721 100644 --- a/crates/uv-interpreter/src/discovery.rs +++ b/crates/uv-interpreter/src/discovery.rs @@ -26,8 +26,11 @@ use std::{path::Path, path::PathBuf, str::FromStr}; /// A request to find a Python interpreter. /// /// See [`InterpreterRequest::from_str`]. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub enum InterpreterRequest { + /// Use any discovered Python interpreter + #[default] + Any, /// A Python version without an implementation name e.g. `3.10` Version(VersionRequest), /// A path to a directory containing a Python installation, e.g. `.venv` @@ -60,7 +63,7 @@ pub enum SourceSelector { #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum VersionRequest { #[default] - Default, + Any, Major(u8), MajorMinor(u8, u8), MajorMinorPatch(u8, u8, u8), @@ -297,7 +300,7 @@ fn python_executables_from_search_path<'a>( env::var_os("UV_TEST_PYTHON_PATH").unwrap_or(env::var_os("PATH").unwrap_or_default()); let possible_names: Vec<_> = version - .unwrap_or(&VersionRequest::Default) + .unwrap_or(&VersionRequest::Any) .possible_names(implementation) .collect(); @@ -565,6 +568,27 @@ pub fn find_interpreter( interpreter, } } + InterpreterRequest::Any => { + let Some((source, interpreter)) = + python_interpreters(None, None, system, sources, cache) + .find(|result| { + match result { + // Return the first critical error or interpreter + Err(err) => should_stop_discovery(err), + Ok(_) => true, + } + }) + .transpose()? + else { + return Ok(InterpreterResult::Err( + InterpreterNotFound::NoPythonInstallation(sources.clone(), None), + )); + }; + DiscoveredInterpreter { + source, + interpreter, + } + } InterpreterRequest::Version(version) => { let Some((source, interpreter)) = python_interpreters(Some(version), None, system, sources, cache) @@ -577,7 +601,7 @@ pub fn find_interpreter( }) .transpose()? else { - let err = if matches!(version, VersionRequest::Default) { + let err = if matches!(version, VersionRequest::Any) { InterpreterNotFound::NoPythonInstallation(sources.clone(), Some(*version)) } else { InterpreterNotFound::NoMatchingVersion(sources.clone(), *version) @@ -600,7 +624,7 @@ pub fn find_interpreter( /// /// See [`find_interpreter`] for more details on interpreter discovery. pub fn find_default_interpreter(cache: &Cache) -> Result { - let request = InterpreterRequest::Version(VersionRequest::Default); + let request = InterpreterRequest::default(); let sources = SourceSelector::System; let result = find_interpreter(&request, SystemPython::Required, &sources, cache)?; @@ -665,7 +689,7 @@ pub fn find_best_interpreter( // If a Python version was requested but cannot be fulfilled, just take any version debug!("Looking for Python interpreter with any version"); - let request = InterpreterRequest::Version(VersionRequest::Default); + let request = InterpreterRequest::Any; Ok(find_interpreter( // TODO(zanieb): Add a dedicated `Default` variant to `InterpreterRequest` &request, system, &sources, cache, @@ -917,7 +941,7 @@ impl VersionRequest { }; match self { - Self::Default => [Some(python3), Some(python), None, None], + Self::Any => [Some(python3), Some(python), None, None], Self::Major(major) => [ Some(Cow::Owned(format!("python{major}{extension}"))), Some(python), @@ -960,7 +984,7 @@ impl VersionRequest { }; match self { - Self::Default => [Some(python3), Some(python), None, None], + Self::Any => [Some(python3), Some(python), None, None], Self::Major(major) => [ Some(Cow::Owned(format!("{name}{major}{extension}"))), Some(python), @@ -990,7 +1014,7 @@ impl VersionRequest { /// Check if a interpreter matches the requested Python version. fn matches_interpreter(self, interpreter: &Interpreter) -> bool { match self { - Self::Default => true, + Self::Any => true, Self::Major(major) => interpreter.python_major() == major, Self::MajorMinor(major, minor) => { (interpreter.python_major(), interpreter.python_minor()) == (major, minor) @@ -1007,7 +1031,7 @@ impl VersionRequest { fn matches_version(self, version: &PythonVersion) -> bool { match self { - Self::Default => true, + Self::Any => true, Self::Major(major) => version.major() == major, Self::MajorMinor(major, minor) => (version.major(), version.minor()) == (major, minor), Self::MajorMinorPatch(major, minor, patch) => { @@ -1018,7 +1042,7 @@ impl VersionRequest { fn matches_major_minor(self, major: u8, minor: u8) -> bool { match self { - Self::Default => true, + Self::Any => true, Self::Major(self_major) => self_major == major, Self::MajorMinor(self_major, self_minor) => (self_major, self_minor) == (major, minor), Self::MajorMinorPatch(self_major, self_minor, _) => { @@ -1030,7 +1054,7 @@ impl VersionRequest { /// Return true if a patch version is present in the request. fn has_patch(self) -> bool { match self { - Self::Default => false, + Self::Any => false, Self::Major(..) => false, Self::MajorMinor(..) => false, Self::MajorMinorPatch(..) => true, @@ -1041,7 +1065,7 @@ impl VersionRequest { #[must_use] fn without_patch(self) -> Self { match self { - Self::Default => Self::Default, + Self::Any => Self::Any, Self::Major(major) => Self::Major(major), Self::MajorMinor(major, minor) => Self::MajorMinor(major, minor), Self::MajorMinorPatch(major, minor, _) => Self::MajorMinor(major, minor), @@ -1082,7 +1106,7 @@ impl From<&PythonVersion> for VersionRequest { impl fmt::Display for VersionRequest { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Self::Default => f.write_str("default"), + Self::Any => f.write_str("default"), Self::Major(major) => write!(f, "{major}"), Self::MajorMinor(major, minor) => write!(f, "{major}.{minor}"), Self::MajorMinorPatch(major, minor, patch) => { @@ -1161,6 +1185,7 @@ impl SystemPython { impl fmt::Display for InterpreterRequest { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { + Self::Any => write!(f, "any Python"), Self::Version(version) => write!(f, "Python @ {version}"), Self::Directory(path) => write!(f, "directory {}", path.user_display()), Self::File(path) => write!(f, "file {}", path.user_display()), @@ -1193,13 +1218,13 @@ impl fmt::Display for InterpreterSource { impl fmt::Display for InterpreterNotFound { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Self::NoPythonInstallation(sources, None | Some(VersionRequest::Default)) => { + Self::NoPythonInstallation(sources, None | Some(VersionRequest::Any)) => { write!(f, "No Python interpreters found in {sources}") } Self::NoPythonInstallation(sources, Some(version)) => { write!(f, "No Python {version} interpreters found in {sources}") } - Self::NoMatchingVersion(sources, VersionRequest::Default) => { + Self::NoMatchingVersion(sources, VersionRequest::Any) => { write!(f, "No Python interpreter found in {sources}") } Self::NoMatchingVersion(sources, version) => { diff --git a/crates/uv-interpreter/src/environment.rs b/crates/uv-interpreter/src/environment.rs index 95edfe4e5..51345d0eb 100644 --- a/crates/uv-interpreter/src/environment.rs +++ b/crates/uv-interpreter/src/environment.rs @@ -8,7 +8,7 @@ use same_file::is_same_file; use uv_cache::Cache; use uv_fs::{LockedFile, Simplified}; -use crate::discovery::{InterpreterRequest, SourceSelector, SystemPython, VersionRequest}; +use crate::discovery::{InterpreterRequest, SourceSelector, SystemPython}; use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration}; use crate::{ find_default_interpreter, find_interpreter, Error, Interpreter, InterpreterSource, Target, @@ -54,7 +54,7 @@ impl PythonEnvironment { /// Allows Conda environments (via `CONDA_PREFIX`) though they are not technically virtual environments. pub fn from_virtualenv(cache: &Cache) -> Result { let sources = SourceSelector::VirtualEnv; - let request = InterpreterRequest::Version(VersionRequest::Default); + let request = InterpreterRequest::Any; let found = find_interpreter(&request, SystemPython::Disallowed, &sources, cache)??; debug_assert!( @@ -74,7 +74,7 @@ impl PythonEnvironment { /// Create a [`PythonEnvironment`] for the parent interpreter i.e. the executable in `python -m uv ...` pub fn from_parent_interpreter(system: SystemPython, cache: &Cache) -> Result { let sources = SourceSelector::from_sources([InterpreterSource::ParentInterpreter]); - let request = InterpreterRequest::Version(VersionRequest::Default); + let request = InterpreterRequest::Any; let found = find_interpreter(&request, system, &sources, cache)??; Ok(Self(Arc::new(PythonEnvironmentShared { diff --git a/crates/uv-interpreter/src/lib.rs b/crates/uv-interpreter/src/lib.rs index 7325b4996..83d84f843 100644 --- a/crates/uv-interpreter/src/lib.rs +++ b/crates/uv-interpreter/src/lib.rs @@ -586,7 +586,7 @@ mod tests { ], || { let result = find_interpreter( - &InterpreterRequest::Version(VersionRequest::Default), + &InterpreterRequest::Any, SystemPython::Allowed, &SourceSelector::All, &cache, @@ -620,7 +620,7 @@ mod tests { ], || { let result = find_interpreter( - &InterpreterRequest::Version(VersionRequest::Default), + &InterpreterRequest::Any, SystemPython::Allowed, &SourceSelector::All, &cache, @@ -661,7 +661,7 @@ mod tests { ], || { let result = find_interpreter( - &InterpreterRequest::Version(VersionRequest::Default), + &InterpreterRequest::Any, SystemPython::Required, &SourceSelector::All, &cache, @@ -701,7 +701,7 @@ mod tests { ], || { let result = find_interpreter( - &InterpreterRequest::Version(VersionRequest::Default), + &InterpreterRequest::Any, SystemPython::Disallowed, &SourceSelector::All, &cache, diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 918e263dc..922b5e638 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -178,7 +178,7 @@ pub(crate) async fn pip_compile( // TODO(zanieb): We should consolidate `VersionRequest` and `PythonVersion` InterpreterRequest::Version(VersionRequest::from(version)) } else { - InterpreterRequest::Version(VersionRequest::Default) + InterpreterRequest::default() }; find_best_interpreter(&request, system, &cache)?? }