mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-27 12:39:09 +00:00
Add a progress bar to uv tree --outdated
and uv pip list --outdated
(#9284)
## Summary Closes https://github.com/astral-sh/uv/issues/9282.
This commit is contained in:
parent
a0de83001c
commit
b19ccb6b97
3 changed files with 125 additions and 60 deletions
|
@ -27,6 +27,7 @@ use uv_resolver::{ExcludeNewer, PrereleaseMode, RequiresPython};
|
||||||
|
|
||||||
use crate::commands::pip::latest::LatestClient;
|
use crate::commands::pip::latest::LatestClient;
|
||||||
use crate::commands::pip::operations::report_target_environment;
|
use crate::commands::pip::operations::report_target_environment;
|
||||||
|
use crate::commands::reporters::LatestVersionReporter;
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ pub(crate) async fn pip_list(
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
// Determine the latest version for each package.
|
// Determine the latest version for each package.
|
||||||
let latest = if outdated {
|
let latest = if outdated && !results.is_empty() {
|
||||||
let capabilities = IndexCapabilities::default();
|
let capabilities = IndexCapabilities::default();
|
||||||
|
|
||||||
// Initialize the registry client.
|
// Initialize the registry client.
|
||||||
|
@ -110,6 +111,8 @@ pub(crate) async fn pip_list(
|
||||||
requires_python: &requires_python,
|
requires_python: &requires_python,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let reporter = LatestVersionReporter::from(printer).with_length(results.len() as u64);
|
||||||
|
|
||||||
// Fetch the latest version for each package.
|
// Fetch the latest version for each package.
|
||||||
let mut fetches = futures::stream::iter(&results)
|
let mut fetches = futures::stream::iter(&results)
|
||||||
.map(|dist| async {
|
.map(|dist| async {
|
||||||
|
@ -120,8 +123,14 @@ pub(crate) async fn pip_list(
|
||||||
|
|
||||||
let mut map = FxHashMap::default();
|
let mut map = FxHashMap::default();
|
||||||
while let Some((package, version)) = fetches.next().await.transpose()? {
|
while let Some((package, version)) = fetches.next().await.transpose()? {
|
||||||
|
if let Some(version) = version.as_ref() {
|
||||||
|
reporter.on_fetch_version(package, version.version());
|
||||||
|
} else {
|
||||||
|
reporter.on_fetch_progress();
|
||||||
|
}
|
||||||
map.insert(package, version);
|
map.insert(package, version);
|
||||||
}
|
}
|
||||||
|
reporter.on_fetch_complete();
|
||||||
map
|
map
|
||||||
} else {
|
} else {
|
||||||
FxHashMap::default()
|
FxHashMap::default()
|
||||||
|
|
|
@ -24,6 +24,7 @@ use crate::commands::project::lock::{do_safe_lock, LockMode};
|
||||||
use crate::commands::project::{
|
use crate::commands::project::{
|
||||||
default_dependency_groups, DependencyGroupsTarget, ProjectError, ProjectInterpreter,
|
default_dependency_groups, DependencyGroupsTarget, ProjectError, ProjectInterpreter,
|
||||||
};
|
};
|
||||||
|
use crate::commands::reporters::LatestVersionReporter;
|
||||||
use crate::commands::{diagnostics, ExitStatus, SharedState};
|
use crate::commands::{diagnostics, ExitStatus, SharedState};
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
use crate::settings::ResolverSettings;
|
use crate::settings::ResolverSettings;
|
||||||
|
@ -142,6 +143,23 @@ pub(crate) async fn tree(
|
||||||
|
|
||||||
// If necessary, look up the latest version of each package.
|
// If necessary, look up the latest version of each package.
|
||||||
let latest = if outdated {
|
let latest = if outdated {
|
||||||
|
// Filter to packages that are derived from a registry.
|
||||||
|
let packages = lock
|
||||||
|
.packages()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|package| {
|
||||||
|
let index = match package.index(workspace.install_path()) {
|
||||||
|
Ok(Some(index)) => index,
|
||||||
|
Ok(None) => return None,
|
||||||
|
Err(err) => return Some(Err(err)),
|
||||||
|
};
|
||||||
|
Some(Ok((package, index)))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
if packages.is_empty() {
|
||||||
|
PackageMap::default()
|
||||||
|
} else {
|
||||||
let ResolverSettings {
|
let ResolverSettings {
|
||||||
index_locations: _,
|
index_locations: _,
|
||||||
index_strategy: _,
|
index_strategy: _,
|
||||||
|
@ -162,8 +180,9 @@ pub(crate) async fn tree(
|
||||||
let capabilities = IndexCapabilities::default();
|
let capabilities = IndexCapabilities::default();
|
||||||
|
|
||||||
// Initialize the registry client.
|
// Initialize the registry client.
|
||||||
let client =
|
let client = RegistryClientBuilder::new(
|
||||||
RegistryClientBuilder::new(cache.clone().with_refresh(Refresh::All(Timestamp::now())))
|
cache.clone().with_refresh(Refresh::All(Timestamp::now())),
|
||||||
|
)
|
||||||
.native_tls(native_tls)
|
.native_tls(native_tls)
|
||||||
.connectivity(connectivity)
|
.connectivity(connectivity)
|
||||||
.keyring(*keyring_provider)
|
.keyring(*keyring_provider)
|
||||||
|
@ -180,35 +199,33 @@ pub(crate) async fn tree(
|
||||||
tags: None,
|
tags: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let reporter = LatestVersionReporter::from(printer).with_length(packages.len() as u64);
|
||||||
|
|
||||||
// Fetch the latest version for each package.
|
// Fetch the latest version for each package.
|
||||||
let mut fetches = futures::stream::iter(lock.packages().iter().filter_map(|package| {
|
let mut fetches = futures::stream::iter(packages)
|
||||||
// Filter to packages that are derived from a registry.
|
.map(|(package, index)| async move {
|
||||||
let index = match package.index(workspace.install_path()) {
|
let Some(filename) = client.find_latest(package.name(), Some(&index)).await?
|
||||||
Ok(Some(index)) => index,
|
else {
|
||||||
Ok(None) => return None,
|
|
||||||
Err(err) => return Some(Err(err)),
|
|
||||||
};
|
|
||||||
Some(Ok((package, index)))
|
|
||||||
}))
|
|
||||||
.map(|result| async move {
|
|
||||||
let (package, index) = result?;
|
|
||||||
let Some(filename) = client.find_latest(package.name(), Some(&index)).await? else {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
if filename.version() == package.version() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
Ok::<Option<_>, Error>(Some((package, filename.into_version())))
|
Ok::<Option<_>, Error>(Some((package, filename.into_version())))
|
||||||
})
|
})
|
||||||
.buffer_unordered(concurrency.downloads);
|
.buffer_unordered(concurrency.downloads);
|
||||||
|
|
||||||
let mut map = PackageMap::default();
|
let mut map = PackageMap::default();
|
||||||
while let Some(entry) = fetches.next().await.transpose()? {
|
while let Some(entry) = fetches.next().await.transpose()? {
|
||||||
if let Some((package, version)) = entry {
|
let Some((package, version)) = entry else {
|
||||||
|
reporter.on_fetch_progress();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
reporter.on_fetch_version(package.name(), &version);
|
||||||
|
if version > *package.version() {
|
||||||
map.insert(package.clone(), version);
|
map.insert(package.clone(), version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reporter.on_fetch_complete();
|
||||||
map
|
map
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
PackageMap::default()
|
PackageMap::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ use uv_distribution_types::{
|
||||||
BuildableSource, CachedDist, DistributionMetadata, Name, SourceDist, VersionOrUrlRef,
|
BuildableSource, CachedDist, DistributionMetadata, Name, SourceDist, VersionOrUrlRef,
|
||||||
};
|
};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
use uv_pep440::Version;
|
||||||
use uv_python::PythonInstallationKey;
|
use uv_python::PythonInstallationKey;
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
|
@ -528,6 +529,44 @@ impl uv_publish::Reporter for PublishReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct LatestVersionReporter {
|
||||||
|
progress: ProgressBar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Printer> for LatestVersionReporter {
|
||||||
|
fn from(printer: Printer) -> Self {
|
||||||
|
let progress = ProgressBar::with_draw_target(None, printer.target());
|
||||||
|
progress.set_style(
|
||||||
|
ProgressStyle::with_template("{bar:20} [{pos}/{len}] {wide_msg:.dim}").unwrap(),
|
||||||
|
);
|
||||||
|
progress.set_message("Fetching latest versions...");
|
||||||
|
Self { progress }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LatestVersionReporter {
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn with_length(self, length: u64) -> Self {
|
||||||
|
self.progress.set_length(length);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn on_fetch_progress(&self) {
|
||||||
|
self.progress.inc(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn on_fetch_version(&self, name: &PackageName, version: &Version) {
|
||||||
|
self.progress.set_message(format!("{name} v{version}"));
|
||||||
|
self.progress.inc(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn on_fetch_complete(&self) {
|
||||||
|
self.progress.set_message("");
|
||||||
|
self.progress.finish_and_clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct CleaningDirectoryReporter {
|
pub(crate) struct CleaningDirectoryReporter {
|
||||||
bar: ProgressBar,
|
bar: ProgressBar,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue