From 61c03b8954c9ea773da4bb52c9c74febcf4a6f52 Mon Sep 17 00:00:00 2001 From: j178 <10510431+j178@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:19:07 +0800 Subject: [PATCH 1/4] Use reinstall report formatting for `uv python install --reinstall` --- crates/uv/src/commands/python/install.rs | 35 +++++++++++++++------- crates/uv/src/commands/python/mod.rs | 2 ++ crates/uv/src/commands/python/uninstall.rs | 4 +-- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index 601694359783..91ec9011ed5c 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -64,7 +64,7 @@ pub(crate) async fn install( .inspect(|installation| debug!("Found existing installation {}", installation.key())) .collect(); let mut unfilled_requests = Vec::new(); - let mut uninstalled = Vec::new(); + let mut uninstalled = BTreeSet::new(); for (request, download_request) in requests.iter().zip(download_requests) { if matches!(requests.as_slice(), [PythonRequest::Default]) { writeln!(printer.stderr(), "Searching for Python installations")?; @@ -91,7 +91,7 @@ pub(crate) async fn install( } if reinstall { fs::remove_dir_all(installation.path())?; - uninstalled.push(installation.key().clone()); + uninstalled.insert(installation.key()); unfilled_requests.push(download_request); } } else { @@ -151,7 +151,7 @@ pub(crate) async fn install( }); } - let mut installed = vec![]; + let mut installed = BTreeSet::new(); let mut errors = vec![]; while let Some((key, result)) = tasks.next().await { match result { @@ -162,7 +162,7 @@ pub(crate) async fn install( DownloadResult::Fetched(path) => path, }; - installed.push(key.clone()); + installed.insert(key); // Ensure the installations have externally managed markers let managed = ManagedPythonInstallation::new(path.clone())?; @@ -176,7 +176,8 @@ pub(crate) async fn install( } if !installed.is_empty() { - if let [installed] = installed.as_slice() { + if installed.len() == 1 { + let installed = installed.iter().next().unwrap(); // Ex) "Installed Python 3.9.7 in 1.68s" writeln!( printer.stderr(), @@ -190,29 +191,38 @@ pub(crate) async fn install( )?; } else { // Ex) "Installed 2 versions in 1.68s" - let s = if installed.len() == 1 { "" } else { "s" }; writeln!( printer.stderr(), "{}", format!( "Installed {} {}", - format!("{} version{s}", installed.len()).bold(), + format!("{} versions", installed.len()).bold(), format!("in {}", elapsed(start.elapsed())).dimmed() ) .dimmed() )?; } + let reinstalled = uninstalled + .intersection(&installed) + .copied() + .collect::>(); + let uninstalled = uninstalled.difference(&reinstalled).copied(); + let installed = installed.difference(&reinstalled).copied(); + for event in uninstalled - .into_iter() .map(|key| ChangeEvent { - key, + key: key.clone(), kind: ChangeEventKind::Removed, }) - .chain(installed.into_iter().map(|key| ChangeEvent { - key, + .chain(installed.map(|key| ChangeEvent { + key: key.clone(), kind: ChangeEventKind::Added, })) + .chain(reinstalled.iter().map(|&key| ChangeEvent { + key: key.clone(), + kind: ChangeEventKind::Reinstalled, + })) .sorted_unstable_by(|a, b| a.key.cmp(&b.key).then_with(|| a.kind.cmp(&b.kind))) { match event.kind { @@ -222,6 +232,9 @@ pub(crate) async fn install( ChangeEventKind::Removed => { writeln!(printer.stderr(), " {} {}", "-".red(), event.key.bold())?; } + ChangeEventKind::Reinstalled => { + writeln!(printer.stderr(), " {} {}", "~".yellow(), event.key.bold(),)?; + } } } } diff --git a/crates/uv/src/commands/python/mod.rs b/crates/uv/src/commands/python/mod.rs index 80a39fae14e1..afc700d2335b 100644 --- a/crates/uv/src/commands/python/mod.rs +++ b/crates/uv/src/commands/python/mod.rs @@ -11,6 +11,8 @@ pub(super) enum ChangeEventKind { Removed, /// The Python version was installed. Added, + /// The Python version was reinstalled. + Reinstalled, } #[derive(Debug)] diff --git a/crates/uv/src/commands/python/uninstall.rs b/crates/uv/src/commands/python/uninstall.rs index 531cda41586e..cd83cecb4e42 100644 --- a/crates/uv/src/commands/python/uninstall.rs +++ b/crates/uv/src/commands/python/uninstall.rs @@ -179,12 +179,10 @@ async fn do_uninstall( .sorted_unstable_by(|a, b| a.key.cmp(&b.key).then_with(|| a.kind.cmp(&b.kind))) { match event.kind { - ChangeEventKind::Added => { - writeln!(printer.stderr(), " {} {}", "+".green(), event.key.bold())?; - } ChangeEventKind::Removed => { writeln!(printer.stderr(), " {} {}", "-".red(), event.key.bold())?; } + _ => unreachable!(), } } } From 79a54a0a2699d4331860b6aa974b1db5dca4fd80 Mon Sep 17 00:00:00 2001 From: j178 <10510431+j178@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:53:54 +0800 Subject: [PATCH 2/4] Fix PythonInstallationKey Ord impl --- crates/uv-python/src/discovery.rs | 2 +- crates/uv-python/src/installation.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index 549eb3808f86..13fd159bf678 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -133,7 +133,7 @@ pub enum EnvironmentPreference { Any, } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PythonVariant { #[default] Default, diff --git a/crates/uv-python/src/installation.rs b/crates/uv-python/src/installation.rs index b0a326bad96c..a464659e371b 100644 --- a/crates/uv-python/src/installation.rs +++ b/crates/uv-python/src/installation.rs @@ -405,5 +405,6 @@ impl Ord for PythonInstallationKey { .then_with(|| self.os.to_string().cmp(&other.os.to_string())) .then_with(|| self.arch.to_string().cmp(&other.arch.to_string())) .then_with(|| self.libc.to_string().cmp(&other.libc.to_string())) + .then_with(|| self.variant.cmp(&other.variant)) } } From f2649d6d56dfac4572ef53493ac4317e96ab8757 Mon Sep 17 00:00:00 2001 From: j178 <10510431+j178@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:02:32 +0800 Subject: [PATCH 3/4] Use HashSet --- crates/uv/src/commands/python/install.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index 91ec9011ed5c..da5d6a04be3e 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -4,7 +4,7 @@ use futures::stream::FuturesUnordered; use futures::StreamExt; use itertools::Itertools; use owo_colors::OwoColorize; -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashSet}; use std::fmt::Write; use std::path::Path; use tracing::debug; @@ -64,7 +64,7 @@ pub(crate) async fn install( .inspect(|installation| debug!("Found existing installation {}", installation.key())) .collect(); let mut unfilled_requests = Vec::new(); - let mut uninstalled = BTreeSet::new(); + let mut uninstalled = HashSet::new(); for (request, download_request) in requests.iter().zip(download_requests) { if matches!(requests.as_slice(), [PythonRequest::Default]) { writeln!(printer.stderr(), "Searching for Python installations")?; @@ -151,7 +151,7 @@ pub(crate) async fn install( }); } - let mut installed = BTreeSet::new(); + let mut installed = HashSet::new(); let mut errors = vec![]; while let Some((key, result)) = tasks.next().await { match result { @@ -206,7 +206,7 @@ pub(crate) async fn install( let reinstalled = uninstalled .intersection(&installed) .copied() - .collect::>(); + .collect::>(); let uninstalled = uninstalled.difference(&reinstalled).copied(); let installed = installed.difference(&reinstalled).copied(); From 571a386935fe2ebad39517ede062b8c0b7520bab Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 23 Oct 2024 14:39:55 -0400 Subject: [PATCH 4/4] Use FxHash --- crates/uv/src/commands/python/install.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index da5d6a04be3e..78aca888006a 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -4,7 +4,8 @@ use futures::stream::FuturesUnordered; use futures::StreamExt; use itertools::Itertools; use owo_colors::OwoColorize; -use std::collections::{BTreeSet, HashSet}; +use rustc_hash::FxHashSet; +use std::collections::BTreeSet; use std::fmt::Write; use std::path::Path; use tracing::debug; @@ -64,7 +65,7 @@ pub(crate) async fn install( .inspect(|installation| debug!("Found existing installation {}", installation.key())) .collect(); let mut unfilled_requests = Vec::new(); - let mut uninstalled = HashSet::new(); + let mut uninstalled = FxHashSet::default(); for (request, download_request) in requests.iter().zip(download_requests) { if matches!(requests.as_slice(), [PythonRequest::Default]) { writeln!(printer.stderr(), "Searching for Python installations")?; @@ -151,7 +152,7 @@ pub(crate) async fn install( }); } - let mut installed = HashSet::new(); + let mut installed = FxHashSet::default(); let mut errors = vec![]; while let Some((key, result)) = tasks.next().await { match result { @@ -206,7 +207,7 @@ pub(crate) async fn install( let reinstalled = uninstalled .intersection(&installed) .copied() - .collect::>(); + .collect::>(); let uninstalled = uninstalled.difference(&reinstalled).copied(); let installed = installed.difference(&reinstalled).copied();