-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## 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.
- Loading branch information
1 parent
88331e7
commit 29e1b15
Showing
14 changed files
with
328 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
use uv_client::{RegistryClient, VersionFiles}; | ||
use uv_distribution_filename::DistFilename; | ||
use uv_distribution_types::{IndexCapabilities, IndexUrl}; | ||
use uv_normalize::PackageName; | ||
use uv_platform_tags::Tags; | ||
use uv_resolver::{ExcludeNewer, PrereleaseMode, RequiresPython}; | ||
use uv_warnings::warn_user_once; | ||
|
||
/// A client to fetch the latest version of a package from an index. | ||
/// | ||
/// The returned distribution is guaranteed to be compatible with the provided tags and Python | ||
/// requirement. | ||
#[derive(Debug)] | ||
pub(crate) struct LatestClient<'env> { | ||
pub(crate) client: &'env RegistryClient, | ||
pub(crate) capabilities: &'env IndexCapabilities, | ||
pub(crate) prerelease: PrereleaseMode, | ||
pub(crate) exclude_newer: Option<ExcludeNewer>, | ||
pub(crate) tags: Option<&'env Tags>, | ||
pub(crate) requires_python: &'env RequiresPython, | ||
} | ||
|
||
impl<'env> LatestClient<'env> { | ||
/// Find the latest version of a package from an index. | ||
pub(crate) async fn find_latest( | ||
&self, | ||
package: &PackageName, | ||
index: Option<&IndexUrl>, | ||
) -> anyhow::Result<Option<DistFilename>, uv_client::Error> { | ||
let mut latest: Option<DistFilename> = None; | ||
for (_, archive) in self | ||
.client | ||
.simple(package, index, self.capabilities) | ||
.await? | ||
{ | ||
for datum in archive.iter().rev() { | ||
// Find the first compatible distribution. | ||
let files = rkyv::deserialize::<VersionFiles, rkyv::rancor::Error>(&datum.files) | ||
.expect("archived version files always deserializes"); | ||
|
||
// Determine whether there's a compatible wheel and/or source distribution. | ||
let mut best = None; | ||
|
||
for (filename, file) in files.all() { | ||
// Skip distributions uploaded after the cutoff. | ||
if let Some(exclude_newer) = self.exclude_newer { | ||
match file.upload_time_utc_ms.as_ref() { | ||
Some(&upload_time) | ||
if upload_time >= exclude_newer.timestamp_millis() => | ||
{ | ||
continue; | ||
} | ||
None => { | ||
warn_user_once!( | ||
"{} is missing an upload date, but user provided: {exclude_newer}", | ||
file.filename, | ||
); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
// Skip pre-release distributions. | ||
if !filename.version().is_stable() { | ||
if !matches!(self.prerelease, PrereleaseMode::Allow) { | ||
continue; | ||
} | ||
} | ||
|
||
// Skip distributions that are yanked. | ||
if file.yanked.is_some_and(|yanked| yanked.is_yanked()) { | ||
continue; | ||
} | ||
|
||
// Skip distributions that are incompatible with the Python requirement. | ||
if file | ||
.requires_python | ||
.as_ref() | ||
.is_some_and(|requires_python| { | ||
!self.requires_python.is_contained_by(requires_python) | ||
}) | ||
{ | ||
continue; | ||
} | ||
|
||
// Skip distributions that are incompatible with the current platform. | ||
if let DistFilename::WheelFilename(filename) = &filename { | ||
if self | ||
.tags | ||
.is_some_and(|tags| !filename.compatibility(tags).is_compatible()) | ||
{ | ||
continue; | ||
} | ||
} | ||
|
||
match filename { | ||
DistFilename::WheelFilename(_) => { | ||
best = Some(filename); | ||
break; | ||
} | ||
DistFilename::SourceDistFilename(_) => { | ||
if best.is_none() { | ||
best = Some(filename); | ||
} | ||
} | ||
} | ||
} | ||
|
||
match (latest.as_ref(), best) { | ||
(Some(current), Some(best)) => { | ||
if best.version() > current.version() { | ||
latest = Some(best); | ||
} | ||
} | ||
(None, Some(best)) => { | ||
latest = Some(best); | ||
} | ||
_ => {} | ||
} | ||
} | ||
} | ||
Ok(latest) | ||
} | ||
} |
Oops, something went wrong.