From 5e25583c42b01fe8b1fae3b8ef05057cfdb4090c Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 19 Mar 2026 08:21:23 -0500 Subject: [PATCH] Construct `PythonInstallation` early instead of passing around source / interpreter separately (#18564) --- crates/uv-python/src/discovery.rs | 107 +++++++++++++++------------ crates/uv-python/src/installation.rs | 9 --- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index 01ee8ebd9..13840df66 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -738,7 +738,7 @@ fn find_all_minor( /// the interpreter. The caller is responsible for ensuring it is applied otherwise. /// /// See [`python_executables`] for more information on discovery. -fn python_interpreters<'a>( +fn python_installations<'a>( version: &'a VersionRequest, implementation: Option<&'a ImplementationName>, platform: PlatformRequest, @@ -746,8 +746,8 @@ fn python_interpreters<'a>( preference: PythonPreference, cache: &'a Cache, preview: Preview, -) -> impl Iterator> + 'a { - let interpreters = python_interpreters_from_executables( +) -> impl Iterator> + 'a { + let installations = python_installations_from_executables( // Perform filtering on the discovered executables based on their source. This avoids // unnecessary interpreter queries, which are generally expensive. We'll filter again // with `interpreter_satisfies_environment_preference` after querying. @@ -764,52 +764,59 @@ fn python_interpreters<'a>( }), cache, ) - .filter_ok(move |(source, interpreter)| { - interpreter_satisfies_environment_preference(*source, interpreter, environments) + .filter_ok(move |installation| { + interpreter_satisfies_environment_preference( + installation.source, + &installation.interpreter, + environments, + ) }) - .filter_ok(move |(source, interpreter)| { - let request = version.clone().into_request_for_source(*source); - if request.matches_interpreter(interpreter) { + .filter_ok(move |installation| { + let request = version.clone().into_request_for_source(installation.source); + if request.matches_interpreter(&installation.interpreter) { true } else { debug!( - "Skipping interpreter at `{}` from {source}: does not satisfy request `{request}`", - interpreter.sys_executable().user_display() + "Skipping interpreter at `{}` from {}: does not satisfy request `{request}`", + installation.interpreter.sys_executable().user_display(), + installation.source, ); false } }) - .filter_ok(move |(source, interpreter)| { - satisfies_python_preference(*source, interpreter, preference) + .filter_ok(move |installation| { + satisfies_python_preference(installation.source, &installation.interpreter, preference) }); if std::env::var(uv_static::EnvVars::UV_INTERNAL__TEST_PYTHON_MANAGED).is_ok() { - Either::Left(interpreters.map_ok(|(source, interpreter)| { + Either::Left(installations.map_ok(|mut installation| { // In test mode, change the source to `Managed` if a version was marked as such via // `TestContext::with_versions_as_managed`. - if interpreter.is_managed() { - (PythonSource::Managed, interpreter) - } else { - (source, interpreter) + if installation.interpreter.is_managed() { + installation.source = PythonSource::Managed; } + installation })) } else { - Either::Right(interpreters) + Either::Right(installations) } } -/// Lazily convert Python executables into interpreters. -fn python_interpreters_from_executables<'a>( +/// Lazily convert Python executables into installations. +fn python_installations_from_executables<'a>( executables: impl Iterator> + 'a, cache: &'a Cache, -) -> impl Iterator> + 'a { +) -> impl Iterator> + 'a { executables.map(|result| match result { Ok((source, path)) => Interpreter::query(&path, cache) - .map(|interpreter| (source, interpreter)) - .inspect(|(source, interpreter)| { + .map(|interpreter| PythonInstallation { + source, + interpreter, + }) + .inspect(|installation| { debug!( "Found `{}` at `{}` ({source})", - interpreter.key(), + installation.key(), path.display() ); }) @@ -1097,12 +1104,12 @@ fn python_installation_from_directory( python_installation_from_executable(&executable, cache) } -/// Lazily iterate over all Python interpreters on the path with the given executable name. -fn python_interpreters_with_executable_name<'a>( +/// Lazily iterate over all Python installations on the path with the given executable name. +fn python_installations_with_executable_name<'a>( name: &'a str, cache: &'a Cache, -) -> impl Iterator> + 'a { - python_interpreters_from_executables( +) -> impl Iterator> + 'a { + python_installations_from_executables( which_all(name) .into_iter() .flat_map(|inner| inner.map(|path| Ok((PythonSource::SearchPath, path)))), @@ -1181,15 +1188,15 @@ pub fn find_python_installations<'a>( if preference.allows(PythonSource::SearchPath) { debug!("Searching for Python interpreter with {request}"); Box::new( - python_interpreters_with_executable_name(name, cache) - .filter_ok(move |(source, interpreter)| { + python_installations_with_executable_name(name, cache) + .filter_ok(move |installation| { interpreter_satisfies_environment_preference( - *source, - interpreter, + installation.source, + &installation.interpreter, environments, ) }) - .map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple))), + .map_ok(Ok), ) } else { Box::new(iter::once(Err(Error::SourceNotAllowed( @@ -1201,7 +1208,7 @@ pub fn find_python_installations<'a>( } PythonRequest::Any => Box::new({ debug!("Searching for any Python interpreter in {sources}"); - python_interpreters( + python_installations( &VersionRequest::Any, None, PlatformRequest::default(), @@ -1210,11 +1217,11 @@ pub fn find_python_installations<'a>( cache, preview, ) - .map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple))) + .map_ok(Ok) }), PythonRequest::Default => Box::new({ debug!("Searching for default Python interpreter in {sources}"); - python_interpreters( + python_installations( &VersionRequest::Default, None, PlatformRequest::default(), @@ -1223,7 +1230,7 @@ pub fn find_python_installations<'a>( cache, preview, ) - .map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple))) + .map_ok(Ok) }), PythonRequest::Version(version) => { if let Err(err) = version.check_supported() { @@ -1231,7 +1238,7 @@ pub fn find_python_installations<'a>( } Box::new({ debug!("Searching for {request} in {sources}"); - python_interpreters( + python_installations( version, None, PlatformRequest::default(), @@ -1240,12 +1247,12 @@ pub fn find_python_installations<'a>( cache, preview, ) - .map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple))) + .map_ok(Ok) }) } PythonRequest::Implementation(implementation) => Box::new({ debug!("Searching for a {request} interpreter in {sources}"); - python_interpreters( + python_installations( &VersionRequest::Default, Some(implementation), PlatformRequest::default(), @@ -1254,8 +1261,8 @@ pub fn find_python_installations<'a>( cache, preview, ) - .filter_ok(|(_source, interpreter)| implementation.matches_interpreter(interpreter)) - .map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple))) + .filter_ok(|installation| implementation.matches_interpreter(&installation.interpreter)) + .map_ok(Ok) }), PythonRequest::ImplementationVersion(implementation, version) => { if let Err(err) = version.check_supported() { @@ -1263,7 +1270,7 @@ pub fn find_python_installations<'a>( } Box::new({ debug!("Searching for {request} in {sources}"); - python_interpreters( + python_installations( version, Some(implementation), PlatformRequest::default(), @@ -1272,8 +1279,10 @@ pub fn find_python_installations<'a>( cache, preview, ) - .filter_ok(|(_source, interpreter)| implementation.matches_interpreter(interpreter)) - .map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple))) + .filter_ok(|installation| { + implementation.matches_interpreter(&installation.interpreter) + }) + .map_ok(Ok) }) } PythonRequest::Key(request) => { @@ -1285,7 +1294,7 @@ pub fn find_python_installations<'a>( Box::new({ debug!("Searching for {request} in {sources}"); - python_interpreters( + python_installations( request.version().unwrap_or(&VersionRequest::Default), request.implementation(), request.platform(), @@ -1294,10 +1303,10 @@ pub fn find_python_installations<'a>( cache, preview, ) - .filter_ok(move |(_source, interpreter)| { - request.satisfied_by_interpreter(interpreter) + .filter_ok(move |installation| { + request.satisfied_by_interpreter(&installation.interpreter) }) - .map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple))) + .map_ok(Ok) }) } } diff --git a/crates/uv-python/src/installation.rs b/crates/uv-python/src/installation.rs index adbcbd326..04c8c3651 100644 --- a/crates/uv-python/src/installation.rs +++ b/crates/uv-python/src/installation.rs @@ -38,15 +38,6 @@ pub struct PythonInstallation { } impl PythonInstallation { - /// Create a new [`PythonInstallation`] from a source, interpreter tuple. - pub(crate) fn from_tuple(tuple: (PythonSource, Interpreter)) -> Self { - let (source, interpreter) = tuple; - Self { - source, - interpreter, - } - } - /// Find an installed [`PythonInstallation`]. /// /// This is the standard interface for discovering a Python installation for creating