Force cache indexes to set hash digests and cache info (#15291)

## Summary

Making it harder to accidentally omit these.
This commit is contained in:
Charlie Marsh 2025-08-14 23:28:56 +01:00 committed by GitHub
parent bcfa8443da
commit 7eb076aaef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 94 additions and 77 deletions

View File

@ -10,10 +10,11 @@ use uv_distribution_types::{
};
use uv_normalize::PackageName;
use uv_platform_tags::Tags;
use uv_pypi_types::HashDigests;
use uv_types::HashStrategy;
use crate::Error;
use crate::index::cached_wheel::CachedWheel;
use crate::index::cached_wheel::{CachedWheel, ResolvedWheel};
use crate::source::{HTTP_REVISION, HttpRevisionPointer, LOCAL_REVISION, LocalRevisionPointer};
/// A local index of built distributions for a specific source distribution.
@ -92,7 +93,9 @@ impl<'a> BuiltWheelIndex<'a> {
)))
};
Ok(self.find(&cache_shard))
Ok(self.find(&cache_shard).map(|wheel| {
CachedWheel::from_entry(wheel, revision.into_hashes(), CacheInfo::default())
}))
}
/// Return the most compatible [`CachedWheel`] for a given source distribution at a local path.
pub fn path(&self, source_dist: &PathSourceDist) -> Result<Option<CachedWheel>, Error> {
@ -141,7 +144,7 @@ impl<'a> BuiltWheelIndex<'a> {
Ok(self
.find(&cache_shard)
.map(|wheel| wheel.with_cache_info(cache_info)))
.map(|wheel| CachedWheel::from_entry(wheel, revision.into_hashes(), cache_info)))
}
/// Return the most compatible [`CachedWheel`] for a given source distribution built from a
@ -199,7 +202,7 @@ impl<'a> BuiltWheelIndex<'a> {
Ok(self
.find(&cache_shard)
.map(|wheel| wheel.with_cache_info(cache_info)))
.map(|wheel| CachedWheel::from_entry(wheel, revision.into_hashes(), cache_info)))
}
/// Return the most compatible [`CachedWheel`] for a given source distribution at a git URL.
@ -234,6 +237,7 @@ impl<'a> BuiltWheelIndex<'a> {
};
self.find(&cache_shard)
.map(|wheel| CachedWheel::from_entry(wheel, HashDigests::empty(), CacheInfo::default()))
}
/// Find the "best" distribution in the index for a given source distribution.
@ -252,8 +256,8 @@ impl<'a> BuiltWheelIndex<'a> {
/// ```
///
/// The `shard` should be `built-wheels-v0/pypi/django-allauth-0.51.0.tar.gz`.
fn find(&self, shard: &CacheShard) -> Option<CachedWheel> {
let mut candidate: Option<CachedWheel> = None;
fn find(&self, shard: &CacheShard) -> Option<ResolvedWheel> {
let mut candidate: Option<ResolvedWheel> = None;
// Unzipped wheels are stored as symlinks into the archive directory.
for wheel_dir in uv_fs::entries(shard).ok().into_iter().flatten() {
@ -265,7 +269,7 @@ impl<'a> BuiltWheelIndex<'a> {
continue;
}
match CachedWheel::from_built_source(&wheel_dir, self.cache) {
match ResolvedWheel::from_built_source(&wheel_dir, self.cache) {
None => {}
Some(dist_info) => {
// Pick the wheel with the highest priority

View File

@ -12,6 +12,30 @@ use uv_pypi_types::{HashDigest, HashDigests, VerbatimParsedUrl};
use crate::archive::Archive;
use crate::{HttpArchivePointer, LocalArchivePointer};
#[derive(Debug, Clone)]
pub struct ResolvedWheel {
/// The filename of the wheel.
pub filename: WheelFilename,
/// The [`CacheEntry`] for the wheel.
pub entry: CacheEntry,
}
impl ResolvedWheel {
/// Try to parse a distribution from a cached directory name (like `typing-extensions-4.8.0-py3-none-any`).
pub fn from_built_source(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> {
let path = path.as_ref();
// Determine the wheel filename.
let filename = path.file_name()?.to_str()?;
let filename = WheelFilename::from_stem(filename).ok()?;
// Convert to a cached wheel.
let archive = cache.resolve_link(path).ok()?;
let entry = CacheEntry::from_path(archive);
Some(Self { filename, entry })
}
}
#[derive(Debug, Clone)]
pub struct CachedWheel {
/// The filename of the wheel.
@ -25,21 +49,62 @@ pub struct CachedWheel {
}
impl CachedWheel {
/// Try to parse a distribution from a cached directory name (like `typing-extensions-4.8.0-py3-none-any`).
pub fn from_built_source(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> {
/// Create a [`CachedWheel`] from a [`ResolvedWheel`].
pub fn from_entry(wheel: ResolvedWheel, hashes: HashDigests, cache_info: CacheInfo) -> Self {
Self {
filename: wheel.filename,
entry: wheel.entry,
hashes,
cache_info,
}
}
/// Read a cached wheel from a `.http` pointer
pub fn from_http_pointer(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> {
let path = path.as_ref();
// Determine the wheel filename.
let filename = path.file_name()?.to_str()?;
let filename = WheelFilename::from_stem(filename).ok()?;
// Read the pointer.
let pointer = HttpArchivePointer::read_from(path).ok()??;
let cache_info = pointer.to_cache_info();
let archive = pointer.into_archive();
// Ignore stale pointers.
if !archive.exists(cache) {
return None;
}
let Archive { id, hashes, .. } = archive;
let entry = cache.entry(CacheBucket::Archive, "", id);
// Convert to a cached wheel.
let archive = cache.resolve_link(path).ok()?;
let entry = CacheEntry::from_path(archive);
let hashes = HashDigests::empty();
let cache_info = CacheInfo::default();
Some(Self {
filename,
filename: archive.filename,
entry,
hashes,
cache_info,
})
}
/// Read a cached wheel from a `.rev` pointer
pub fn from_local_pointer(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> {
let path = path.as_ref();
// Read the pointer.
let pointer = LocalArchivePointer::read_from(path).ok()??;
let cache_info = pointer.to_cache_info();
let archive = pointer.into_archive();
// Ignore stale pointers.
if !archive.exists(cache) {
return None;
}
let Archive { id, hashes, .. } = archive;
let entry = cache.entry(CacheBucket::Archive, "", id);
// Convert to a cached wheel.
Some(Self {
filename: archive.filename,
entry,
hashes,
cache_info,
@ -115,64 +180,6 @@ impl CachedWheel {
cache_info: self.cache_info,
}
}
/// Read a cached wheel from a `.http` pointer
pub fn from_http_pointer(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> {
let path = path.as_ref();
// Read the pointer.
let pointer = HttpArchivePointer::read_from(path).ok()??;
let cache_info = pointer.to_cache_info();
let archive = pointer.into_archive();
// Ignore stale pointers.
if !archive.exists(cache) {
return None;
}
let Archive { id, hashes, .. } = archive;
let entry = cache.entry(CacheBucket::Archive, "", id);
// Convert to a cached wheel.
Some(Self {
filename: archive.filename,
entry,
hashes,
cache_info,
})
}
/// Read a cached wheel from a `.rev` pointer
pub fn from_local_pointer(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> {
let path = path.as_ref();
// Read the pointer.
let pointer = LocalArchivePointer::read_from(path).ok()??;
let cache_info = pointer.to_cache_info();
let archive = pointer.into_archive();
// Ignore stale pointers.
if !archive.exists(cache) {
return None;
}
let Archive { id, hashes, .. } = archive;
let entry = cache.entry(CacheBucket::Archive, "", id);
// Convert to a cached wheel.
Some(Self {
filename: archive.filename,
entry,
hashes,
cache_info,
})
}
#[must_use]
pub fn with_cache_info(mut self, cache_info: CacheInfo) -> Self {
self.cache_info = cache_info;
self
}
}
impl Hashed for CachedWheel {

View File

@ -4,6 +4,7 @@ use std::collections::hash_map::Entry;
use rustc_hash::{FxHashMap, FxHashSet};
use uv_cache::{Cache, CacheBucket, WheelCache};
use uv_cache_info::CacheInfo;
use uv_cache_key::cache_digest;
use uv_distribution_types::{
BuildVariables, CachedRegistryDist, ConfigSettings, ExtraBuildRequirement, ExtraBuildRequires,
@ -14,7 +15,7 @@ use uv_normalize::PackageName;
use uv_platform_tags::Tags;
use uv_types::HashStrategy;
use crate::index::cached_wheel::CachedWheel;
use crate::index::cached_wheel::{CachedWheel, ResolvedWheel};
use crate::source::{HTTP_REVISION, HttpRevisionPointer, LOCAL_REVISION, LocalRevisionPointer};
/// An entry in the [`RegistryWheelIndex`].
@ -247,13 +248,18 @@ impl<'a> RegistryWheelIndex<'a> {
continue;
}
if let Some(wheel) = CachedWheel::from_built_source(wheel_dir, cache) {
if let Some(wheel) = ResolvedWheel::from_built_source(wheel_dir, cache) {
if wheel.filename.compatibility(tags).is_compatible() {
// Enforce hash-checking based on the source distribution.
if revision.satisfies(
hasher
.get_package(&wheel.filename.name, &wheel.filename.version),
) {
let wheel = CachedWheel::from_entry(
wheel,
revision.hashes().into(),
CacheInfo::default(),
);
entries.push(IndexEntry {
dist: wheel.into_registry_dist(),
index,