Use version in RegistryIndex (#543)

When building up the `RegistryIndex`, index by both package name and
version to fix #537.
This commit is contained in:
konsti 2023-12-04 17:26:14 +01:00 committed by GitHub
parent 37ca2e2928
commit e9c9e9718e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 26 deletions

View file

@ -87,10 +87,22 @@ impl InstallPlan {
// Identify any locally-available distributions that satisfy the requirement. // Identify any locally-available distributions that satisfy the requirement.
match requirement.version_or_url.as_ref() { match requirement.version_or_url.as_ref() {
None | Some(VersionOrUrl::VersionSpecifier(_)) => { None => {
if let Some(distribution) = registry_index if let Some((_version, distribution)) =
.get(&requirement.name) registry_index.by_name(&requirement.name).next()
.filter(|dist| requirement.is_satisfied_by(&dist.filename.version)) {
debug!("Requirement already cached: {distribution}");
local.push(CachedDist::Registry(distribution.clone()));
continue;
}
}
Some(VersionOrUrl::VersionSpecifier(specifier)) => {
if let Some((_version, distribution)) = registry_index
.by_name(&requirement.name)
.find(|(version, dist)| {
specifier.contains(version)
&& requirement.is_satisfied_by(&dist.filename.version)
})
{ {
debug!("Requirement already cached: {distribution}"); debug!("Requirement already cached: {distribution}");
local.push(CachedDist::Registry(distribution.clone())); local.push(CachedDist::Registry(distribution.clone()));

View file

@ -1,9 +1,10 @@
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
use fs_err as fs; use fs_err as fs;
use tracing::warn; use tracing::warn;
use distribution_types::{CachedRegistryDist, Metadata}; use distribution_types::{CachedRegistryDist, Metadata};
use pep440_rs::Version;
use platform_tags::Tags; use platform_tags::Tags;
use puffin_cache::{Cache, CacheBucket, WheelCache}; use puffin_cache::{Cache, CacheBucket, WheelCache};
use puffin_normalize::PackageName; use puffin_normalize::PackageName;
@ -11,12 +12,12 @@ use pypi_types::IndexUrls;
/// A local index of distributions that originate from a registry, like `PyPI`. /// A local index of distributions that originate from a registry, like `PyPI`.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct RegistryIndex(HashMap<PackageName, CachedRegistryDist>); pub struct RegistryIndex(HashMap<PackageName, BTreeMap<Version, CachedRegistryDist>>);
impl RegistryIndex { impl RegistryIndex {
/// Build an index of cached distributions from a directory. /// Build an index of cached distributions from a directory.
pub fn try_from_directory(cache: &Cache, tags: &Tags, index_urls: &IndexUrls) -> Self { pub fn try_from_directory(cache: &Cache, tags: &Tags, index_urls: &IndexUrls) -> Self {
let mut index: HashMap<PackageName, CachedRegistryDist> = HashMap::new(); let mut index: HashMap<PackageName, BTreeMap<Version, CachedRegistryDist>> = HashMap::new();
for index_url in index_urls { for index_url in index_urls {
let wheel_dir = cache let wheel_dir = cache
@ -28,34 +29,36 @@ impl RegistryIndex {
}; };
for entry in dir { for entry in dir {
let (path, file_type) = let path = match entry.map(|entry| entry.path()) {
match entry.and_then(|entry| Ok((entry.path(), entry.file_type()?))) { Ok(path) => path,
Ok((path, file_type)) => (path, file_type), Err(err) => {
Err(err) => { warn!(
warn!( "Failed to read entry of cache at {}: {}",
"Failed to read entry of cache at {}: {}", cache.root().display(),
cache.root().display(), err
err );
); continue;
continue; }
} };
};
if !file_type.is_dir() {
continue;
}
match CachedRegistryDist::try_from_path(&path) { match CachedRegistryDist::try_from_path(&path) {
Ok(None) => {} Ok(None) => {}
Ok(Some(dist_info)) => { Ok(Some(dist_info)) => {
// Pick the wheel with the highest priority // Pick the wheel with the highest priority
let compatibility = dist_info.filename.compatibility(tags); let compatibility = dist_info.filename.compatibility(tags);
if let Some(existing) = index.get_mut(dist_info.name()) { if let Some(existing) = index
.get_mut(dist_info.name())
.and_then(|package| package.get_mut(&dist_info.filename.version))
{
// Override if we have better compatibility // Override if we have better compatibility
if compatibility > existing.filename.compatibility(tags) { if compatibility > existing.filename.compatibility(tags) {
*existing = dist_info; *existing = dist_info;
} }
} else if compatibility.is_some() { } else if compatibility.is_some() {
index.insert(dist_info.name().clone(), dist_info); index
.entry(dist_info.name().clone())
.or_default()
.insert(dist_info.filename.version.clone(), dist_info);
} }
} }
Err(err) => { Err(err) => {
@ -76,7 +79,13 @@ impl RegistryIndex {
} }
/// Returns a distribution from the index, if it exists. /// Returns a distribution from the index, if it exists.
pub fn get(&self, name: &PackageName) -> Option<&CachedRegistryDist> { pub fn by_name(
self.0.get(name) &self,
name: &PackageName,
) -> impl Iterator<Item = (&Version, &CachedRegistryDist)> {
// Using static to extend the lifetime
static DEFAULT_MAP: BTreeMap<Version, CachedRegistryDist> = BTreeMap::new();
// We should only query this
self.0.get(name).unwrap_or(&DEFAULT_MAP).iter().rev()
} }
} }