-
Notifications
You must be signed in to change notification settings - Fork 901
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
107ab3d
commit 7c88abd
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.