Skip to content

Commit 9540d6a

Browse files
j178charliermarsh
andauthored
Use reinstall report formatting for uv python install --reinstall (#8487)
## Summary Resolves #8456 ## Test Plan ```console $ cargo run -- python install 3.13 $ cargo run -- python install --reinstall 3.13 Searching for Python versions matching: Python 3.13 Found existing installation for Python 3.13: cpython-3.13.0-macos-aarch64-none Installed Python 3.13.0 in 7.39s ~ cpython-3.13.0-macos-aarch64-none ``` --------- Co-authored-by: Charlie Marsh <[email protected]>
1 parent e9c08b1 commit 9540d6a

File tree

5 files changed

+30
-15
lines changed

5 files changed

+30
-15
lines changed

crates/uv-python/src/discovery.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ pub enum EnvironmentPreference {
133133
Any,
134134
}
135135

136-
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
136+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
137137
pub enum PythonVariant {
138138
#[default]
139139
Default,

crates/uv-python/src/installation.rs

+1
Original file line numberDiff line numberDiff line change
@@ -405,5 +405,6 @@ impl Ord for PythonInstallationKey {
405405
.then_with(|| self.os.to_string().cmp(&other.os.to_string()))
406406
.then_with(|| self.arch.to_string().cmp(&other.arch.to_string()))
407407
.then_with(|| self.libc.to_string().cmp(&other.libc.to_string()))
408+
.then_with(|| self.variant.cmp(&other.variant))
408409
}
409410
}

crates/uv/src/commands/python/install.rs

+25-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use futures::stream::FuturesUnordered;
33
use futures::StreamExt;
44
use itertools::Itertools;
55
use owo_colors::OwoColorize;
6+
use rustc_hash::FxHashSet;
67
use std::collections::BTreeSet;
78
use std::fmt::Write;
89
use std::path::Path;
@@ -63,7 +64,7 @@ pub(crate) async fn install(
6364
.inspect(|installation| debug!("Found existing installation {}", installation.key()))
6465
.collect();
6566
let mut unfilled_requests = Vec::new();
66-
let mut uninstalled = Vec::new();
67+
let mut uninstalled = FxHashSet::default();
6768
for (request, download_request) in requests.iter().zip(download_requests) {
6869
if matches!(requests.as_slice(), [PythonRequest::Default]) {
6970
writeln!(printer.stderr(), "Searching for Python installations")?;
@@ -89,7 +90,7 @@ pub(crate) async fn install(
8990
)?;
9091
}
9192
if reinstall {
92-
uninstalled.push(installation.key().clone());
93+
uninstalled.insert(installation.key());
9394
unfilled_requests.push(download_request);
9495
}
9596
} else {
@@ -155,7 +156,7 @@ pub(crate) async fn install(
155156
});
156157
}
157158

158-
let mut installed = vec![];
159+
let mut installed = FxHashSet::default();
159160
let mut errors = vec![];
160161
while let Some((key, result)) = tasks.next().await {
161162
match result {
@@ -166,7 +167,7 @@ pub(crate) async fn install(
166167
DownloadResult::Fetched(path) => path,
167168
};
168169

169-
installed.push(key.clone());
170+
installed.insert(key);
170171

171172
// Ensure the installations have externally managed markers
172173
let managed = ManagedPythonInstallation::new(path.clone())?;
@@ -180,7 +181,8 @@ pub(crate) async fn install(
180181
}
181182

182183
if !installed.is_empty() {
183-
if let [installed] = installed.as_slice() {
184+
if installed.len() == 1 {
185+
let installed = installed.iter().next().unwrap();
184186
// Ex) "Installed Python 3.9.7 in 1.68s"
185187
writeln!(
186188
printer.stderr(),
@@ -194,29 +196,38 @@ pub(crate) async fn install(
194196
)?;
195197
} else {
196198
// Ex) "Installed 2 versions in 1.68s"
197-
let s = if installed.len() == 1 { "" } else { "s" };
198199
writeln!(
199200
printer.stderr(),
200201
"{}",
201202
format!(
202203
"Installed {} {}",
203-
format!("{} version{s}", installed.len()).bold(),
204+
format!("{} versions", installed.len()).bold(),
204205
format!("in {}", elapsed(start.elapsed())).dimmed()
205206
)
206207
.dimmed()
207208
)?;
208209
}
209210

211+
let reinstalled = uninstalled
212+
.intersection(&installed)
213+
.copied()
214+
.collect::<FxHashSet<_>>();
215+
let uninstalled = uninstalled.difference(&reinstalled).copied();
216+
let installed = installed.difference(&reinstalled).copied();
217+
210218
for event in uninstalled
211-
.into_iter()
212219
.map(|key| ChangeEvent {
213-
key,
220+
key: key.clone(),
214221
kind: ChangeEventKind::Removed,
215222
})
216-
.chain(installed.into_iter().map(|key| ChangeEvent {
217-
key,
223+
.chain(installed.map(|key| ChangeEvent {
224+
key: key.clone(),
218225
kind: ChangeEventKind::Added,
219226
}))
227+
.chain(reinstalled.iter().map(|&key| ChangeEvent {
228+
key: key.clone(),
229+
kind: ChangeEventKind::Reinstalled,
230+
}))
220231
.sorted_unstable_by(|a, b| a.key.cmp(&b.key).then_with(|| a.kind.cmp(&b.kind)))
221232
{
222233
match event.kind {
@@ -226,6 +237,9 @@ pub(crate) async fn install(
226237
ChangeEventKind::Removed => {
227238
writeln!(printer.stderr(), " {} {}", "-".red(), event.key.bold())?;
228239
}
240+
ChangeEventKind::Reinstalled => {
241+
writeln!(printer.stderr(), " {} {}", "~".yellow(), event.key.bold(),)?;
242+
}
229243
}
230244
}
231245
}

crates/uv/src/commands/python/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub(super) enum ChangeEventKind {
1111
Removed,
1212
/// The Python version was installed.
1313
Added,
14+
/// The Python version was reinstalled.
15+
Reinstalled,
1416
}
1517

1618
#[derive(Debug)]

crates/uv/src/commands/python/uninstall.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,10 @@ async fn do_uninstall(
179179
.sorted_unstable_by(|a, b| a.key.cmp(&b.key).then_with(|| a.kind.cmp(&b.kind)))
180180
{
181181
match event.kind {
182-
ChangeEventKind::Added => {
183-
writeln!(printer.stderr(), " {} {}", "+".green(), event.key.bold())?;
184-
}
185182
ChangeEventKind::Removed => {
186183
writeln!(printer.stderr(), " {} {}", "-".red(), event.key.bold())?;
187184
}
185+
_ => unreachable!(),
188186
}
189187
}
190188
}

0 commit comments

Comments
 (0)