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.
This commit is contained in:
Zanie Blue 2024-05-23 09:22:34 -04:00 committed by GitHub
parent 91ed1c6c7c
commit ddfbee1cb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 24 deletions

View file

@ -26,8 +26,11 @@ use std::{path::Path, path::PathBuf, str::FromStr};
/// A request to find a Python interpreter. /// A request to find a Python interpreter.
/// ///
/// See [`InterpreterRequest::from_str`]. /// See [`InterpreterRequest::from_str`].
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum InterpreterRequest { pub enum InterpreterRequest {
/// Use any discovered Python interpreter
#[default]
Any,
/// A Python version without an implementation name e.g. `3.10` /// A Python version without an implementation name e.g. `3.10`
Version(VersionRequest), Version(VersionRequest),
/// A path to a directory containing a Python installation, e.g. `.venv` /// 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)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum VersionRequest { pub enum VersionRequest {
#[default] #[default]
Default, Any,
Major(u8), Major(u8),
MajorMinor(u8, u8), MajorMinor(u8, u8),
MajorMinorPatch(u8, 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()); env::var_os("UV_TEST_PYTHON_PATH").unwrap_or(env::var_os("PATH").unwrap_or_default());
let possible_names: Vec<_> = version let possible_names: Vec<_> = version
.unwrap_or(&VersionRequest::Default) .unwrap_or(&VersionRequest::Any)
.possible_names(implementation) .possible_names(implementation)
.collect(); .collect();
@ -565,6 +568,27 @@ pub fn find_interpreter(
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) => { InterpreterRequest::Version(version) => {
let Some((source, interpreter)) = let Some((source, interpreter)) =
python_interpreters(Some(version), None, system, sources, cache) python_interpreters(Some(version), None, system, sources, cache)
@ -577,7 +601,7 @@ pub fn find_interpreter(
}) })
.transpose()? .transpose()?
else { else {
let err = if matches!(version, VersionRequest::Default) { let err = if matches!(version, VersionRequest::Any) {
InterpreterNotFound::NoPythonInstallation(sources.clone(), Some(*version)) InterpreterNotFound::NoPythonInstallation(sources.clone(), Some(*version))
} else { } else {
InterpreterNotFound::NoMatchingVersion(sources.clone(), *version) InterpreterNotFound::NoMatchingVersion(sources.clone(), *version)
@ -600,7 +624,7 @@ pub fn find_interpreter(
/// ///
/// See [`find_interpreter`] for more details on interpreter discovery. /// See [`find_interpreter`] for more details on interpreter discovery.
pub fn find_default_interpreter(cache: &Cache) -> Result<InterpreterResult, Error> { pub fn find_default_interpreter(cache: &Cache) -> Result<InterpreterResult, Error> {
let request = InterpreterRequest::Version(VersionRequest::Default); let request = InterpreterRequest::default();
let sources = SourceSelector::System; let sources = SourceSelector::System;
let result = find_interpreter(&request, SystemPython::Required, &sources, cache)?; 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 // If a Python version was requested but cannot be fulfilled, just take any version
debug!("Looking for Python interpreter with any version"); debug!("Looking for Python interpreter with any version");
let request = InterpreterRequest::Version(VersionRequest::Default); let request = InterpreterRequest::Any;
Ok(find_interpreter( Ok(find_interpreter(
// TODO(zanieb): Add a dedicated `Default` variant to `InterpreterRequest` // TODO(zanieb): Add a dedicated `Default` variant to `InterpreterRequest`
&request, system, &sources, cache, &request, system, &sources, cache,
@ -917,7 +941,7 @@ impl VersionRequest {
}; };
match self { match self {
Self::Default => [Some(python3), Some(python), None, None], Self::Any => [Some(python3), Some(python), None, None],
Self::Major(major) => [ Self::Major(major) => [
Some(Cow::Owned(format!("python{major}{extension}"))), Some(Cow::Owned(format!("python{major}{extension}"))),
Some(python), Some(python),
@ -960,7 +984,7 @@ impl VersionRequest {
}; };
match self { match self {
Self::Default => [Some(python3), Some(python), None, None], Self::Any => [Some(python3), Some(python), None, None],
Self::Major(major) => [ Self::Major(major) => [
Some(Cow::Owned(format!("{name}{major}{extension}"))), Some(Cow::Owned(format!("{name}{major}{extension}"))),
Some(python), Some(python),
@ -990,7 +1014,7 @@ impl VersionRequest {
/// Check if a interpreter matches the requested Python version. /// Check if a interpreter matches the requested Python version.
fn matches_interpreter(self, interpreter: &Interpreter) -> bool { fn matches_interpreter(self, interpreter: &Interpreter) -> bool {
match self { match self {
Self::Default => true, Self::Any => true,
Self::Major(major) => interpreter.python_major() == major, Self::Major(major) => interpreter.python_major() == major,
Self::MajorMinor(major, minor) => { Self::MajorMinor(major, minor) => {
(interpreter.python_major(), interpreter.python_minor()) == (major, minor) (interpreter.python_major(), interpreter.python_minor()) == (major, minor)
@ -1007,7 +1031,7 @@ impl VersionRequest {
fn matches_version(self, version: &PythonVersion) -> bool { fn matches_version(self, version: &PythonVersion) -> bool {
match self { match self {
Self::Default => true, Self::Any => true,
Self::Major(major) => version.major() == major, Self::Major(major) => version.major() == major,
Self::MajorMinor(major, minor) => (version.major(), version.minor()) == (major, minor), Self::MajorMinor(major, minor) => (version.major(), version.minor()) == (major, minor),
Self::MajorMinorPatch(major, minor, patch) => { Self::MajorMinorPatch(major, minor, patch) => {
@ -1018,7 +1042,7 @@ impl VersionRequest {
fn matches_major_minor(self, major: u8, minor: u8) -> bool { fn matches_major_minor(self, major: u8, minor: u8) -> bool {
match self { match self {
Self::Default => true, Self::Any => true,
Self::Major(self_major) => self_major == major, Self::Major(self_major) => self_major == major,
Self::MajorMinor(self_major, self_minor) => (self_major, self_minor) == (major, minor), Self::MajorMinor(self_major, self_minor) => (self_major, self_minor) == (major, minor),
Self::MajorMinorPatch(self_major, self_minor, _) => { Self::MajorMinorPatch(self_major, self_minor, _) => {
@ -1030,7 +1054,7 @@ impl VersionRequest {
/// Return true if a patch version is present in the request. /// Return true if a patch version is present in the request.
fn has_patch(self) -> bool { fn has_patch(self) -> bool {
match self { match self {
Self::Default => false, Self::Any => false,
Self::Major(..) => false, Self::Major(..) => false,
Self::MajorMinor(..) => false, Self::MajorMinor(..) => false,
Self::MajorMinorPatch(..) => true, Self::MajorMinorPatch(..) => true,
@ -1041,7 +1065,7 @@ impl VersionRequest {
#[must_use] #[must_use]
fn without_patch(self) -> Self { fn without_patch(self) -> Self {
match self { match self {
Self::Default => Self::Default, Self::Any => Self::Any,
Self::Major(major) => Self::Major(major), Self::Major(major) => Self::Major(major),
Self::MajorMinor(major, minor) => Self::MajorMinor(major, minor), Self::MajorMinor(major, minor) => Self::MajorMinor(major, minor),
Self::MajorMinorPatch(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 { impl fmt::Display for VersionRequest {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Default => f.write_str("default"), Self::Any => f.write_str("default"),
Self::Major(major) => write!(f, "{major}"), Self::Major(major) => write!(f, "{major}"),
Self::MajorMinor(major, minor) => write!(f, "{major}.{minor}"), Self::MajorMinor(major, minor) => write!(f, "{major}.{minor}"),
Self::MajorMinorPatch(major, minor, patch) => { Self::MajorMinorPatch(major, minor, patch) => {
@ -1161,6 +1185,7 @@ impl SystemPython {
impl fmt::Display for InterpreterRequest { impl fmt::Display for InterpreterRequest {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Any => write!(f, "any Python"),
Self::Version(version) => write!(f, "Python @ {version}"), Self::Version(version) => write!(f, "Python @ {version}"),
Self::Directory(path) => write!(f, "directory {}", path.user_display()), Self::Directory(path) => write!(f, "directory {}", path.user_display()),
Self::File(path) => write!(f, "file {}", 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 { impl fmt::Display for InterpreterNotFound {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
Self::NoPythonInstallation(sources, None | Some(VersionRequest::Default)) => { Self::NoPythonInstallation(sources, None | Some(VersionRequest::Any)) => {
write!(f, "No Python interpreters found in {sources}") write!(f, "No Python interpreters found in {sources}")
} }
Self::NoPythonInstallation(sources, Some(version)) => { Self::NoPythonInstallation(sources, Some(version)) => {
write!(f, "No Python {version} interpreters found in {sources}") 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}") write!(f, "No Python interpreter found in {sources}")
} }
Self::NoMatchingVersion(sources, version) => { Self::NoMatchingVersion(sources, version) => {

View file

@ -8,7 +8,7 @@ use same_file::is_same_file;
use uv_cache::Cache; use uv_cache::Cache;
use uv_fs::{LockedFile, Simplified}; 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::virtualenv::{virtualenv_python_executable, PyVenvConfiguration};
use crate::{ use crate::{
find_default_interpreter, find_interpreter, Error, Interpreter, InterpreterSource, Target, 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. /// Allows Conda environments (via `CONDA_PREFIX`) though they are not technically virtual environments.
pub fn from_virtualenv(cache: &Cache) -> Result<Self, Error> { pub fn from_virtualenv(cache: &Cache) -> Result<Self, Error> {
let sources = SourceSelector::VirtualEnv; let sources = SourceSelector::VirtualEnv;
let request = InterpreterRequest::Version(VersionRequest::Default); let request = InterpreterRequest::Any;
let found = find_interpreter(&request, SystemPython::Disallowed, &sources, cache)??; let found = find_interpreter(&request, SystemPython::Disallowed, &sources, cache)??;
debug_assert!( debug_assert!(
@ -74,7 +74,7 @@ impl PythonEnvironment {
/// Create a [`PythonEnvironment`] for the parent interpreter i.e. the executable in `python -m uv ...` /// 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<Self, Error> { pub fn from_parent_interpreter(system: SystemPython, cache: &Cache) -> Result<Self, Error> {
let sources = SourceSelector::from_sources([InterpreterSource::ParentInterpreter]); 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)??; let found = find_interpreter(&request, system, &sources, cache)??;
Ok(Self(Arc::new(PythonEnvironmentShared { Ok(Self(Arc::new(PythonEnvironmentShared {

View file

@ -586,7 +586,7 @@ mod tests {
], ],
|| { || {
let result = find_interpreter( let result = find_interpreter(
&InterpreterRequest::Version(VersionRequest::Default), &InterpreterRequest::Any,
SystemPython::Allowed, SystemPython::Allowed,
&SourceSelector::All, &SourceSelector::All,
&cache, &cache,
@ -620,7 +620,7 @@ mod tests {
], ],
|| { || {
let result = find_interpreter( let result = find_interpreter(
&InterpreterRequest::Version(VersionRequest::Default), &InterpreterRequest::Any,
SystemPython::Allowed, SystemPython::Allowed,
&SourceSelector::All, &SourceSelector::All,
&cache, &cache,
@ -661,7 +661,7 @@ mod tests {
], ],
|| { || {
let result = find_interpreter( let result = find_interpreter(
&InterpreterRequest::Version(VersionRequest::Default), &InterpreterRequest::Any,
SystemPython::Required, SystemPython::Required,
&SourceSelector::All, &SourceSelector::All,
&cache, &cache,
@ -701,7 +701,7 @@ mod tests {
], ],
|| { || {
let result = find_interpreter( let result = find_interpreter(
&InterpreterRequest::Version(VersionRequest::Default), &InterpreterRequest::Any,
SystemPython::Disallowed, SystemPython::Disallowed,
&SourceSelector::All, &SourceSelector::All,
&cache, &cache,

View file

@ -178,7 +178,7 @@ pub(crate) async fn pip_compile(
// TODO(zanieb): We should consolidate `VersionRequest` and `PythonVersion` // TODO(zanieb): We should consolidate `VersionRequest` and `PythonVersion`
InterpreterRequest::Version(VersionRequest::from(version)) InterpreterRequest::Version(VersionRequest::from(version))
} else { } else {
InterpreterRequest::Version(VersionRequest::Default) InterpreterRequest::default()
}; };
find_best_interpreter(&request, system, &cache)?? find_best_interpreter(&request, system, &cache)??
} }