mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-03 18:38:21 +00:00
Add uv tree --outdated
(#8893)
## Summary Similar to `pip list --outdated`, but for `uv tree`. ## Test Plan Looks like: ``` foo v0.1.0 └── flask v2.0.0 (latest: v3.0.3) ├── click v8.1.7 ├── itsdangerous v2.2.0 ├── jinja2 v3.1.4 │ └── markupsafe v3.0.2 └── werkzeug v3.1.2 └── markupsafe v3.0.2 ``` With `(latest: v3.0.3)` in bold cyan.
This commit is contained in:
parent
88331e756e
commit
29e1b15473
14 changed files with 328 additions and 118 deletions
|
@ -4,8 +4,8 @@ pub use exclude_newer::ExcludeNewer;
|
|||
pub use exclusions::Exclusions;
|
||||
pub use flat_index::{FlatDistributions, FlatIndex};
|
||||
pub use lock::{
|
||||
InstallTarget, Lock, LockError, LockVersion, RequirementsTxtExport, ResolverManifest,
|
||||
SatisfiesResult, TreeDisplay, VERSION,
|
||||
InstallTarget, Lock, LockError, LockVersion, PackageMap, RequirementsTxtExport,
|
||||
ResolverManifest, SatisfiesResult, TreeDisplay, VERSION,
|
||||
};
|
||||
pub use manifest::Manifest;
|
||||
pub use options::{Flexibility, Options, OptionsBuilder};
|
||||
|
|
37
crates/uv-resolver/src/lock/map.rs
Normal file
37
crates/uv-resolver/src/lock/map.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::lock::{Package, PackageId};
|
||||
|
||||
/// A map from package to values, indexed by [`PackageId`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PackageMap<T>(FxHashMap<PackageId, T>);
|
||||
|
||||
impl<T> Default for PackageMap<T> {
|
||||
fn default() -> Self {
|
||||
Self(FxHashMap::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PackageMap<T> {
|
||||
/// Get a value by [`PackageId`].
|
||||
pub(crate) fn get(&self, package_id: &PackageId) -> Option<&T> {
|
||||
self.0.get(package_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromIterator<(Package, T)> for PackageMap<T> {
|
||||
fn from_iter<I: IntoIterator<Item = (Package, T)>>(iter: I) -> Self {
|
||||
Self(
|
||||
iter.into_iter()
|
||||
.map(|(package, value)| (package.id, value))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Extend<(Package, T)> for PackageMap<T> {
|
||||
fn extend<I: IntoIterator<Item = (Package, T)>>(&mut self, iter: I) {
|
||||
self.0
|
||||
.extend(iter.into_iter().map(|(package, value)| (package.id, value)));
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ use std::sync::{Arc, LazyLock};
|
|||
use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
|
||||
use url::Url;
|
||||
|
||||
pub use crate::lock::map::PackageMap;
|
||||
pub use crate::lock::requirements_txt::RequirementsTxtExport;
|
||||
pub use crate::lock::target::InstallTarget;
|
||||
pub use crate::lock::tree::TreeDisplay;
|
||||
|
@ -47,6 +48,7 @@ use uv_types::{BuildContext, HashStrategy};
|
|||
use uv_workspace::dependency_groups::DependencyGroupError;
|
||||
use uv_workspace::Workspace;
|
||||
|
||||
mod map;
|
||||
mod requirements_txt;
|
||||
mod target;
|
||||
mod tree;
|
||||
|
@ -2206,6 +2208,24 @@ impl Package {
|
|||
self.fork_markers.as_slice()
|
||||
}
|
||||
|
||||
/// Returns the [`IndexUrl`] for the package, if it is a registry source.
|
||||
pub fn index(&self, root: &Path) -> Result<Option<IndexUrl>, LockError> {
|
||||
match &self.id.source {
|
||||
Source::Registry(RegistrySource::Url(url)) => {
|
||||
let index = IndexUrl::from(VerbatimUrl::from_url(url.to_url()));
|
||||
Ok(Some(index))
|
||||
}
|
||||
Source::Registry(RegistrySource::Path(path)) => {
|
||||
let index = IndexUrl::from(
|
||||
VerbatimUrl::from_absolute_path(root.join(path))
|
||||
.map_err(LockErrorKind::RegistryVerbatimUrl)?,
|
||||
);
|
||||
Ok(Some(index))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all the hashes associated with this [`Package`].
|
||||
fn hashes(&self) -> Vec<HashDigest> {
|
||||
let mut hashes = Vec::new();
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::borrow::Cow;
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use itertools::Itertools;
|
||||
use owo_colors::OwoColorize;
|
||||
use petgraph::graph::{EdgeIndex, NodeIndex};
|
||||
use petgraph::prelude::EdgeRef;
|
||||
use petgraph::Direction;
|
||||
|
@ -9,10 +10,11 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||
|
||||
use uv_configuration::DevGroupsManifest;
|
||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||
use uv_pep440::Version;
|
||||
use uv_pypi_types::ResolverMarkerEnvironment;
|
||||
|
||||
use crate::lock::{Dependency, PackageId};
|
||||
use crate::Lock;
|
||||
use crate::{Lock, PackageMap};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TreeDisplay<'env> {
|
||||
|
@ -20,6 +22,8 @@ pub struct TreeDisplay<'env> {
|
|||
graph: petgraph::graph::Graph<&'env PackageId, Edge<'env>, petgraph::Directed>,
|
||||
/// The packages considered as roots of the dependency tree.
|
||||
roots: Vec<NodeIndex>,
|
||||
/// The latest known version of each package.
|
||||
latest: &'env PackageMap<Version>,
|
||||
/// Maximum display depth of the dependency tree.
|
||||
depth: usize,
|
||||
/// Whether to de-duplicate the displayed dependencies.
|
||||
|
@ -31,6 +35,7 @@ impl<'env> TreeDisplay<'env> {
|
|||
pub fn new(
|
||||
lock: &'env Lock,
|
||||
markers: Option<&'env ResolverMarkerEnvironment>,
|
||||
latest: &'env PackageMap<Version>,
|
||||
depth: usize,
|
||||
prune: &[PackageName],
|
||||
packages: &[PackageName],
|
||||
|
@ -242,6 +247,7 @@ impl<'env> TreeDisplay<'env> {
|
|||
Self {
|
||||
graph,
|
||||
roots,
|
||||
latest,
|
||||
depth,
|
||||
no_dedupe,
|
||||
}
|
||||
|
@ -306,6 +312,13 @@ impl<'env> TreeDisplay<'env> {
|
|||
}
|
||||
}
|
||||
|
||||
// Incorporate the latest version of the package, if known.
|
||||
let line = if let Some(version) = self.latest.get(package_id) {
|
||||
format!("{line} {}", format!("(latest: v{version})").bold().cyan())
|
||||
} else {
|
||||
line
|
||||
};
|
||||
|
||||
let mut dependencies = self
|
||||
.graph
|
||||
.edges_directed(cursor.node(), Direction::Outgoing)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue