mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-06 18:50:36 +00:00
Use upgrade-specific output for tool upgrade (#5997)
## Summary Closes https://github.com/astral-sh/uv/issues/5949.
This commit is contained in:
parent
f5110f7b5e
commit
ec8248ff93
6 changed files with 222 additions and 76 deletions
|
@ -124,6 +124,24 @@ pub enum InstalledVersion<'a> {
|
|||
Url(&'a Url, &'a Version),
|
||||
}
|
||||
|
||||
impl<'a> InstalledVersion<'a> {
|
||||
/// If it is a URL, return its value.
|
||||
pub fn url(&self) -> Option<&Url> {
|
||||
match self {
|
||||
InstalledVersion::Version(_) => None,
|
||||
InstalledVersion::Url(url, _) => Some(url),
|
||||
}
|
||||
}
|
||||
|
||||
/// If it is a version, return its value.
|
||||
pub fn version(&self) -> &Version {
|
||||
match self {
|
||||
InstalledVersion::Version(version) => version,
|
||||
InstalledVersion::Url(_, version) => version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InstalledVersion<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
|
||||
use itertools::Itertools;
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
use distribution_types::{CachedDist, InstalledDist, InstalledMetadata, LocalDist, Name};
|
||||
|
||||
use crate::commands::{elapsed, ChangeEvent, ChangeEventKind};
|
||||
use crate::printer::Printer;
|
||||
use distribution_types::{CachedDist, InstalledDist, InstalledMetadata, LocalDist, Name};
|
||||
use itertools::Itertools;
|
||||
use owo_colors::OwoColorize;
|
||||
use pep440_rs::Version;
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
/// A trait to handle logging during install operations.
|
||||
pub(crate) trait InstallLogger {
|
||||
|
@ -223,6 +225,172 @@ impl InstallLogger for SummaryInstallLogger {
|
|||
}
|
||||
}
|
||||
|
||||
/// A logger that shows special output for the modification of the given target.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct UpgradeInstallLogger {
|
||||
target: PackageName,
|
||||
}
|
||||
|
||||
impl UpgradeInstallLogger {
|
||||
/// Create a new logger for the given target.
|
||||
pub(crate) fn new(target: PackageName) -> Self {
|
||||
Self { target }
|
||||
}
|
||||
}
|
||||
|
||||
impl InstallLogger for UpgradeInstallLogger {
|
||||
fn on_audit(
|
||||
&self,
|
||||
_count: usize,
|
||||
_start: std::time::Instant,
|
||||
_printer: Printer,
|
||||
) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_prepare(
|
||||
&self,
|
||||
_count: usize,
|
||||
_start: std::time::Instant,
|
||||
_printer: Printer,
|
||||
) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_uninstall(
|
||||
&self,
|
||||
_count: usize,
|
||||
_start: std::time::Instant,
|
||||
_printer: Printer,
|
||||
) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_install(
|
||||
&self,
|
||||
_count: usize,
|
||||
_start: std::time::Instant,
|
||||
_printer: Printer,
|
||||
) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_complete(
|
||||
&self,
|
||||
installed: Vec<CachedDist>,
|
||||
reinstalled: Vec<InstalledDist>,
|
||||
uninstalled: Vec<InstalledDist>,
|
||||
printer: Printer,
|
||||
) -> fmt::Result {
|
||||
// Index the removals by package name.
|
||||
let removals: FxHashMap<&PackageName, BTreeSet<Version>> =
|
||||
reinstalled.iter().chain(uninstalled.iter()).fold(
|
||||
FxHashMap::with_capacity_and_hasher(
|
||||
reinstalled.len() + uninstalled.len(),
|
||||
FxBuildHasher,
|
||||
),
|
||||
|mut acc, distribution| {
|
||||
acc.entry(distribution.name())
|
||||
.or_default()
|
||||
.insert(distribution.installed_version().version().clone());
|
||||
acc
|
||||
},
|
||||
);
|
||||
|
||||
// Index the additions by package name.
|
||||
let additions: FxHashMap<&PackageName, BTreeSet<Version>> = installed.iter().fold(
|
||||
FxHashMap::with_capacity_and_hasher(installed.len(), FxBuildHasher),
|
||||
|mut acc, distribution| {
|
||||
acc.entry(distribution.name())
|
||||
.or_default()
|
||||
.insert(distribution.installed_version().version().clone());
|
||||
acc
|
||||
},
|
||||
);
|
||||
|
||||
// Summarize the change for the target.
|
||||
match (removals.get(&self.target), additions.get(&self.target)) {
|
||||
(Some(removals), Some(additions)) => {
|
||||
if removals == additions {
|
||||
let reinstalls = additions
|
||||
.iter()
|
||||
.map(|version| format!("v{version}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{} {} {}",
|
||||
"Reinstalled".yellow().bold(),
|
||||
&self.target,
|
||||
reinstalls
|
||||
)?;
|
||||
} else {
|
||||
let removals = removals
|
||||
.iter()
|
||||
.map(|version| format!("v{version}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let additions = additions
|
||||
.iter()
|
||||
.map(|version| format!("v{version}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{} {} {} -> {}",
|
||||
"Updated".green().bold(),
|
||||
&self.target,
|
||||
removals,
|
||||
additions
|
||||
)?;
|
||||
}
|
||||
}
|
||||
(Some(removals), None) => {
|
||||
let removals = removals
|
||||
.iter()
|
||||
.map(|version| format!("v{version}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{} {} {}",
|
||||
"Removed".red().bold(),
|
||||
&self.target,
|
||||
removals
|
||||
)?;
|
||||
}
|
||||
(None, Some(additions)) => {
|
||||
let additions = additions
|
||||
.iter()
|
||||
.map(|version| format!("v{version}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{} {} {}",
|
||||
"Added".green().bold(),
|
||||
&self.target,
|
||||
additions
|
||||
)?;
|
||||
}
|
||||
(None, None) => {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{} {} {}",
|
||||
"Modified".dimmed(),
|
||||
&self.target.dimmed().bold(),
|
||||
"environment".dimmed()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Follow-up with a detailed summary of all changes.
|
||||
DefaultInstallLogger.on_complete(installed, reinstalled, uninstalled, printer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to handle logging during resolve operations.
|
||||
pub(crate) trait ResolveLogger {
|
||||
/// Log the completion of the operation.
|
||||
|
|
|
@ -61,13 +61,6 @@ pub(crate) fn remove_entrypoints(tool: &Tool) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents the action to be performed on executables: update or install.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum InstallAction {
|
||||
Update,
|
||||
Install,
|
||||
}
|
||||
|
||||
/// Installs tool executables for a given package and handles any conflicts.
|
||||
pub(crate) fn install_executables(
|
||||
environment: &PythonEnvironment,
|
||||
|
@ -77,7 +70,6 @@ pub(crate) fn install_executables(
|
|||
force: bool,
|
||||
python: Option<String>,
|
||||
requirements: Vec<Requirement>,
|
||||
action: InstallAction,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<ExitStatus> {
|
||||
let site_packages = SitePackages::from_environment(environment)?;
|
||||
|
@ -102,8 +94,7 @@ pub(crate) fn install_executables(
|
|||
installed_dist.version(),
|
||||
)?;
|
||||
|
||||
// Determine the entry points targets
|
||||
// Use a sorted collection for deterministic output
|
||||
// Determine the entry points targets. Use a sorted collection for deterministic output.
|
||||
let target_entry_points = entry_points
|
||||
.into_iter()
|
||||
.map(|(name, source_path)| {
|
||||
|
@ -180,13 +171,9 @@ pub(crate) fn install_executables(
|
|||
} else {
|
||||
"s"
|
||||
};
|
||||
let install_message = match action {
|
||||
InstallAction::Install => "Installed",
|
||||
InstallAction::Update => "Updated",
|
||||
};
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{install_message} {} executable{s}: {}",
|
||||
"Installed {} executable{s}: {}",
|
||||
target_entry_points.len(),
|
||||
target_entry_points
|
||||
.iter()
|
||||
|
@ -194,7 +181,7 @@ pub(crate) fn install_executables(
|
|||
.join(", ")
|
||||
)?;
|
||||
|
||||
debug!("Adding receipt for tool `{}`", name);
|
||||
debug!("Adding receipt for tool `{name}`");
|
||||
let tool = Tool::new(
|
||||
requirements.into_iter().collect(),
|
||||
python,
|
||||
|
|
|
@ -20,11 +20,10 @@ use uv_warnings::{warn_user, warn_user_once};
|
|||
|
||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
|
||||
|
||||
use crate::commands::tool::common::remove_entrypoints;
|
||||
use crate::commands::{
|
||||
project::{resolve_environment, resolve_names, sync_environment, update_environment},
|
||||
tool::common::InstallAction,
|
||||
use crate::commands::project::{
|
||||
resolve_environment, resolve_names, sync_environment, update_environment,
|
||||
};
|
||||
use crate::commands::tool::common::remove_entrypoints;
|
||||
use crate::commands::{reporters::PythonDownloadReporter, tool::common::install_executables};
|
||||
use crate::commands::{ExitStatus, SharedState};
|
||||
use crate::printer::Printer;
|
||||
|
@ -352,7 +351,6 @@ pub(crate) async fn install(
|
|||
force || invalid_tool_receipt,
|
||||
python,
|
||||
requirements,
|
||||
InstallAction::Install,
|
||||
printer,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,12 +4,6 @@ use anyhow::Result;
|
|||
use owo_colors::OwoColorize;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
|
||||
use crate::commands::project::update_environment;
|
||||
use crate::commands::tool::common::{remove_entrypoints, InstallAction};
|
||||
use crate::commands::{tool::common::install_executables, ExitStatus, SharedState};
|
||||
use crate::printer::Printer;
|
||||
use crate::settings::ResolverInstallerSettings;
|
||||
use uv_cache::Cache;
|
||||
use uv_client::Connectivity;
|
||||
use uv_configuration::{Concurrency, PreviewMode};
|
||||
|
@ -19,6 +13,13 @@ use uv_settings::{Combine, ResolverInstallerOptions, ToolOptions};
|
|||
use uv_tool::InstalledTools;
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
use crate::commands::pip::loggers::{SummaryResolveLogger, UpgradeInstallLogger};
|
||||
use crate::commands::project::update_environment;
|
||||
use crate::commands::tool::common::remove_entrypoints;
|
||||
use crate::commands::{tool::common::install_executables, ExitStatus, SharedState};
|
||||
use crate::printer::Printer;
|
||||
use crate::settings::ResolverInstallerSettings;
|
||||
|
||||
/// Upgrade a tool.
|
||||
pub(crate) async fn upgrade(
|
||||
name: Option<PackageName>,
|
||||
|
@ -127,8 +128,8 @@ pub(crate) async fn upgrade(
|
|||
spec,
|
||||
&settings,
|
||||
&state,
|
||||
Box::new(DefaultResolveLogger),
|
||||
Box::new(DefaultInstallLogger),
|
||||
Box::new(SummaryResolveLogger),
|
||||
Box::new(UpgradeInstallLogger::new(name.clone())),
|
||||
preview,
|
||||
connectivity,
|
||||
concurrency,
|
||||
|
@ -150,7 +151,6 @@ pub(crate) async fn upgrade(
|
|||
true,
|
||||
existing_tool_receipt.python().to_owned(),
|
||||
requirements.to_vec(),
|
||||
InstallAction::Update,
|
||||
printer,
|
||||
)?;
|
||||
}
|
||||
|
|
|
@ -50,14 +50,11 @@ fn test_tool_upgrade_name() {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
Updated babel v2.6.0 -> v2.14.0
|
||||
- babel==2.6.0
|
||||
+ babel==2.14.0
|
||||
- pytz==2018.5
|
||||
Updated 1 executable: pybabel
|
||||
Installed 1 executable: pybabel
|
||||
"###);
|
||||
}
|
||||
|
||||
|
@ -126,21 +123,15 @@ fn test_tool_upgrade_all() {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
Updated babel v2.6.0 -> v2.14.0
|
||||
- babel==2.6.0
|
||||
+ babel==2.14.0
|
||||
- pytz==2018.5
|
||||
Updated 1 executable: pybabel
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
Installed 1 executable: pybabel
|
||||
Updated python-dotenv v0.10.2.post2 -> v1.0.1
|
||||
- python-dotenv==0.10.2.post2
|
||||
+ python-dotenv==1.0.1
|
||||
Updated 1 executable: dotenv
|
||||
Installed 1 executable: dotenv
|
||||
"###);
|
||||
}
|
||||
|
||||
|
@ -228,9 +219,7 @@ fn test_tool_upgrade_settings() {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Audited [N] packages in [TIME]
|
||||
Updated 2 executables: black, blackd
|
||||
Installed 2 executables: black, blackd
|
||||
"###);
|
||||
|
||||
// Upgrade `black`, but override the resolution.
|
||||
|
@ -246,13 +235,10 @@ fn test_tool_upgrade_settings() {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
Updated black v23.1.0 -> v24.3.0
|
||||
- black==23.1.0
|
||||
+ black==24.3.0
|
||||
Updated 2 executables: black, blackd
|
||||
Installed 2 executables: black, blackd
|
||||
"###);
|
||||
}
|
||||
|
||||
|
@ -300,15 +286,12 @@ fn test_tool_upgrade_respect_constraints() {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
Updated babel v2.6.0 -> v2.9.1
|
||||
- babel==2.6.0
|
||||
+ babel==2.9.1
|
||||
- pytz==2018.5
|
||||
+ pytz==2024.1
|
||||
Updated 1 executable: pybabel
|
||||
Installed 1 executable: pybabel
|
||||
"###);
|
||||
}
|
||||
|
||||
|
@ -358,15 +341,12 @@ fn test_tool_upgrade_constraint() {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
Updated babel v2.6.0 -> v2.13.1
|
||||
- babel==2.6.0
|
||||
+ babel==2.13.1
|
||||
- pytz==2018.5
|
||||
+ setuptools==69.2.0
|
||||
Updated 1 executable: pybabel
|
||||
Installed 1 executable: pybabel
|
||||
"###);
|
||||
|
||||
// Upgrade `babel` without a constraint.
|
||||
|
@ -383,14 +363,11 @@ fn test_tool_upgrade_constraint() {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Uninstalled [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
Updated babel v2.13.1 -> v2.14.0
|
||||
- babel==2.13.1
|
||||
+ babel==2.14.0
|
||||
- setuptools==69.2.0
|
||||
Updated 1 executable: pybabel
|
||||
Installed 1 executable: pybabel
|
||||
"###);
|
||||
|
||||
// Passing `--upgrade` explicitly should warn.
|
||||
|
@ -409,8 +386,6 @@ fn test_tool_upgrade_constraint() {
|
|||
----- stderr -----
|
||||
warning: `--upgrade` is enabled by default on `uv tool upgrade`
|
||||
warning: `uv tool upgrade` is experimental and may change without warning
|
||||
Resolved [N] packages in [TIME]
|
||||
Audited [N] packages in [TIME]
|
||||
Updated 1 executable: pybabel
|
||||
Installed 1 executable: pybabel
|
||||
"###);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue