Skip to content

Commit

Permalink
Add some text decoration to toolchain CLI (#4882)
Browse files Browse the repository at this point in the history
## Summary

Attempts to make the CLI output a little more consistent with the `pip`
interface. I opted to make the Python versions, requests, and filenames
blue, and the keys green, but open to opinions on that. (We use blue for
filenames elsewhere.)

Closes #4813.
Closes #4814.

![Screenshot 2024-07-07 at 9 18
48 PM](https://github.com/astral-sh/uv/assets/1309177/8518b559-196b-4cd0-bc16-8e79e66460bb)
  • Loading branch information
charliermarsh committed Jul 8, 2024
1 parent 57cfe1e commit 2d651fe
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 46 deletions.
44 changes: 20 additions & 24 deletions crates/uv/src/commands/python/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use anyhow::Result;
use fs_err as fs;
use futures::StreamExt;
use itertools::Itertools;
use owo_colors::OwoColorize;
use uv_cache::Cache;
use uv_client::Connectivity;
use uv_configuration::PreviewMode;
Expand All @@ -15,7 +16,7 @@ use uv_python::{requests_from_version_file, PythonRequest};
use uv_warnings::warn_user_once;

use crate::commands::reporters::PythonDownloadReporter;
use crate::commands::ExitStatus;
use crate::commands::{elapsed, ExitStatus};
use crate::printer::Printer;

/// Download and install Python versions.
Expand Down Expand Up @@ -62,22 +63,24 @@ pub(crate) async fn install(
for (request, download_request) in requests.iter().zip(download_requests) {
writeln!(
printer.stderr(),
"Looking for installation {request} ({download_request})"
"Searching for Python versions matching: {}",
request.cyan()
)?;
if let Some(installation) = installed_installations
.iter()
.find(|installation| download_request.satisfied_by_key(installation.key()))
{
writeln!(
printer.stderr(),
"Found existing installation `{}` that satisfies {request}",
installation.key()
"Found existing installation for {}: {}",
request.cyan(),
installation.key().green(),
)?;
if force {
writeln!(
printer.stderr(),
"Removing existing installation `{}`",
installation.key()
"Uninstalling {}",
installation.key().green()
)?;
fs::remove_dir_all(installation.path())?;
unfilled_requests.push(download_request);
Expand All @@ -94,12 +97,7 @@ pub(crate) async fn install(
"Python is already available. Use `uv python install <request>` to install a specific version.",
)?;
} else if requests.len() > 1 {
writeln!(
printer.stderr(),
"All requested versions already installed."
)?;
} else {
writeln!(printer.stderr(), "Requested versions already installed.")?;
writeln!(printer.stderr(), "All requested versions already installed")?;
}
return Ok(ExitStatus::Success);
}
Expand All @@ -117,13 +115,6 @@ pub(crate) async fn install(
.unique_by(|download| download.key())
.collect::<Vec<_>>();

let s = if downloads.len() == 1 { "" } else { "s" };
writeln!(
printer.stderr(),
"Found {} version{s} requiring installation",
downloads.len()
)?;

// Construct a client
let client = uv_client::BaseClientBuilder::new()
.connectivity(connectivity)
Expand All @@ -150,8 +141,9 @@ pub(crate) async fn install(
DownloadResult::Fetched(path) => {
writeln!(
printer.stderr(),
"Installed Python {version} to {}",
path.user_display()
"Installed {} to: {}",
format!("Python {version}").cyan(),
path.user_display().cyan()
)?;
path
}
Expand All @@ -165,9 +157,13 @@ pub(crate) async fn install(
let s = if downloads.len() == 1 { "" } else { "s" };
writeln!(
printer.stderr(),
"Installed {} version{s} in {}s",
downloads.len(),
start.elapsed().as_secs()
"{}",
format!(
"Installed {} {}",
format!("{} version{s}", downloads.len()).bold(),
format!("in {}", elapsed(start.elapsed())).dimmed()
)
.dimmed()
)?;

Ok(ExitStatus::Success)
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/python/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub(crate) async fn list(
.collect::<Result<Vec<Result<PythonInstallation, PythonNotFound>>, DiscoveryError>>()?
.into_iter()
// Drop any "missing" installations
.filter_map(std::result::Result::ok);
.filter_map(Result::ok);

let mut output = BTreeSet::new();
for installation in installed {
Expand Down
48 changes: 27 additions & 21 deletions crates/uv/src/commands/python/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ use std::fmt::Write;
use anyhow::Result;
use futures::StreamExt;
use itertools::Itertools;
use owo_colors::OwoColorize;

use uv_configuration::PreviewMode;
use uv_python::downloads::{self, PythonDownloadRequest};
use uv_python::managed::ManagedPythonInstallations;
use uv_python::PythonRequest;
use uv_warnings::warn_user_once;

use crate::commands::ExitStatus;
use crate::commands::{elapsed, ExitStatus};
use crate::printer::Printer;

/// Uninstall managed Python versions.
Expand All @@ -24,6 +25,8 @@ pub(crate) async fn uninstall(
warn_user_once!("`uv python uninstall` is experimental and may change without warning.");
}

let start = std::time::Instant::now();

let installations = ManagedPythonInstallations::from_settings()?.init()?;
let _lock = installations.acquire_lock()?;

Expand All @@ -43,7 +46,8 @@ pub(crate) async fn uninstall(
for (request, download_request) in requests.iter().zip(download_requests) {
writeln!(
printer.stderr(),
"Looking for Python installations matching {request} ({download_request})"
"Searching for Python versions matching: {}",
request.cyan()
)?;
let mut found = false;
for installation in installed_installations
Expand All @@ -54,31 +58,28 @@ pub(crate) async fn uninstall(
if matching_installations.insert(installation.clone()) {
writeln!(
printer.stderr(),
"Found installation `{}` that matches {request}",
installation.key()
"Found existing installation for {}: {}",
request.cyan(),
installation.key().green(),
)?;
}
}
if !found {
writeln!(
printer.stderr(),
"No installations found matching {request}"
"No existing installations found for: {}",
request.cyan()
)?;
}
}

if matching_installations.is_empty() {
if matches!(requests.as_slice(), [PythonRequest::Any]) {
writeln!(printer.stderr(), "No installed installations found")?;
writeln!(printer.stderr(), "No Python installations found")?;
} else if requests.len() > 1 {
writeln!(
printer.stderr(),
"No installations found matching the requests"
)?;
} else {
writeln!(
printer.stderr(),
"No installations found matching the request"
"No Python installations found matching the requests"
)?;
}
return Ok(ExitStatus::Failure);
Expand All @@ -98,18 +99,19 @@ pub(crate) async fn uninstall(
for (key, result) in results.iter().sorted_by_key(|(key, _)| key) {
if let Err(err) = result {
failed = true;
writeln!(printer.stderr(), "Failed to uninstall `{key}`: {err}")?;
writeln!(
printer.stderr(),
"Failed to uninstall {}: {err}",
key.green()
)?;
} else {
writeln!(printer.stderr(), "Uninstalled `{key}`")?;
writeln!(printer.stderr(), "Uninstalled {}", key.green())?;
}
}

if failed {
if matching_installations.len() > 1 {
writeln!(
printer.stderr(),
"Failed to remove some Python installations"
)?;
writeln!(printer.stderr(), "Failed to uninstall some Python versions")?;
}
return Ok(ExitStatus::Failure);
}
Expand All @@ -119,11 +121,15 @@ pub(crate) async fn uninstall(
} else {
"s"
};

writeln!(
printer.stderr(),
"Removed {} Python installation{s}",
matching_installations.len()
"{}",
format!(
"Uninstalled {} {}",
format!("{} version{s}", matching_installations.len()).bold(),
format!("in {}", elapsed(start.elapsed())).dimmed()
)
.dimmed()
)?;

Ok(ExitStatus::Success)
Expand Down

0 comments on commit 2d651fe

Please sign in to comment.