From 3774b2f3e03d3a72e08e74fa677aa914bfc13ab6 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 18 Jul 2024 20:28:08 -0400 Subject: [PATCH] Make Python install robust to individual failures --- crates/uv/src/commands/python/install.rs | 44 ++++++++++++++++------ crates/uv/src/commands/python/uninstall.rs | 7 ++-- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index 18136863407f..6af8cf282e58 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -158,24 +158,44 @@ pub(crate) async fn install( .collect::>() .await; + let mut failed = false; for (version, result) in results { - let path = match result? { - // We should only encounter already-available during concurrent installs - DownloadResult::AlreadyAvailable(path) => path, - DownloadResult::Fetched(path) => { + match result { + Ok(download) => { + let path = match download { + // We should only encounter already-available during concurrent installs + DownloadResult::AlreadyAvailable(path) => path, + DownloadResult::Fetched(path) => { + writeln!( + printer.stderr(), + "Installed {} to: {}", + format!("Python {version}").cyan(), + path.user_display().cyan() + )?; + path + } + }; + + // Ensure the installations have externally managed markers + let installed = ManagedPythonInstallation::new(path.clone())?; + installed.ensure_externally_managed()?; + } + Err(err) => { + failed = true; writeln!( printer.stderr(), - "Installed {} to: {}", - format!("Python {version}").cyan(), - path.user_display().cyan() + "Failed to install {}: {err}", + version.green() )?; - path } - }; + } + } - // Ensure the installations have externally managed markers - let installed = ManagedPythonInstallation::new(path.clone())?; - installed.ensure_externally_managed()?; + if failed { + if downloads.len() > 1 { + writeln!(printer.stderr(), "Failed to install some Python versions")?; + } + return Ok(ExitStatus::Failure); } let s = if downloads.len() == 1 { "" } else { "s" }; diff --git a/crates/uv/src/commands/python/uninstall.rs b/crates/uv/src/commands/python/uninstall.rs index 1353bab1cb53..5ab7c9dc6be3 100644 --- a/crates/uv/src/commands/python/uninstall.rs +++ b/crates/uv/src/commands/python/uninstall.rs @@ -103,16 +103,17 @@ pub(crate) async fn uninstall( return Ok(ExitStatus::Failure); } - let tasks = futures::stream::iter(matching_installations.iter()) + let results = futures::stream::iter(matching_installations.iter()) .map(|installation| async { ( installation.key(), fs_err::tokio::remove_dir_all(installation.path()).await, ) }) - .buffered(4); + .buffered(4) + .collect::>() + .await; - let results = tasks.collect::>().await; let mut failed = false; for (key, result) in results.iter().sorted_by_key(|(key, _)| key) { if let Err(err) = result {