Skip to content

Commit

Permalink
Make Python install robust to individual failures (#5199)
Browse files Browse the repository at this point in the history
## Summary

We have roughly this code for uninstalls, but for installs, we eject as
soon as we hit a failure, leaving some things in a partial state.
  • Loading branch information
charliermarsh authored Jul 19, 2024
1 parent 24a0268 commit 5d6f793
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 15 deletions.
44 changes: 32 additions & 12 deletions crates/uv/src/commands/python/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,24 +158,44 @@ pub(crate) async fn install(
.collect::<Vec<_>>()
.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" };
Expand Down
7 changes: 4 additions & 3 deletions crates/uv/src/commands/python/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>()
.await;

let results = tasks.collect::<Vec<_>>().await;
let mut failed = false;
for (key, result) in results.iter().sorted_by_key(|(key, _)| key) {
if let Err(err) = result {
Expand Down

0 comments on commit 5d6f793

Please sign in to comment.