mirror of https://github.com/astral-sh/uv
Persist cache info when re-installing cached wheels (#15274)
## Summary I noticed that these paths aren't returning the cache information, so if you install through these paths, we actually don't write `uv_cache.json` at all. I'm not sure how a user would actually end up here, because assuming there are no bugs, we don't really ever use this path? The install plan indexes the cached wheels and marks the wheel as installed, which means it's typically a mistake if we're asking the `DistributionDatabase` for a wheel that's already available in the cache... But I did verify that if I _skip_ the install plan's cache lookup, we write a wheel without `uv_cache.json`, so this is definitely more correct.
This commit is contained in:
parent
c4e5984258
commit
7cdb2d08d9
|
|
@ -27,6 +27,40 @@ pub(crate) struct BuiltWheelMetadata {
|
|||
}
|
||||
|
||||
impl BuiltWheelMetadata {
|
||||
/// Create a [`BuiltWheelMetadata`] from a [`BuiltWheelFile`].
|
||||
pub(crate) fn from_file(
|
||||
file: BuiltWheelFile,
|
||||
hashes: HashDigests,
|
||||
cache_info: CacheInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
path: file.path,
|
||||
target: file.target,
|
||||
filename: file.filename,
|
||||
hashes,
|
||||
cache_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashed for BuiltWheelMetadata {
|
||||
fn hashes(&self) -> &[HashDigest] {
|
||||
self.hashes.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
/// The path to a built wheel file, along with its parsed filename.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BuiltWheelFile {
|
||||
/// The path to the built wheel.
|
||||
pub(crate) path: Box<Path>,
|
||||
/// The expected path to the downloaded wheel's entry in the cache.
|
||||
pub(crate) target: Box<Path>,
|
||||
/// The parsed filename.
|
||||
pub(crate) filename: WheelFilename,
|
||||
}
|
||||
|
||||
impl BuiltWheelFile {
|
||||
/// Find a compatible wheel in the cache.
|
||||
pub(crate) fn find_in_cache(
|
||||
tags: &Tags,
|
||||
|
|
@ -51,26 +85,12 @@ impl BuiltWheelMetadata {
|
|||
target: cache_shard.join(filename.stem()).into_boxed_path(),
|
||||
path: path.into_boxed_path(),
|
||||
filename,
|
||||
cache_info: CacheInfo::default(),
|
||||
hashes: HashDigests::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_hashes(mut self, hashes: HashDigests) -> Self {
|
||||
self.hashes = hashes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns `true` if the wheel matches the given package name and version.
|
||||
pub(crate) fn matches(&self, name: Option<&PackageName>, version: Option<&Version>) -> bool {
|
||||
name.is_none_or(|name| self.filename.name == *name)
|
||||
&& version.is_none_or(|version| self.filename.version == *version)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashed for BuiltWheelMetadata {
|
||||
fn hashes(&self) -> &[HashDigest] {
|
||||
self.hashes.as_slice()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ use uv_workspace::pyproject::ToolUvSources;
|
|||
use crate::distribution_database::ManagedClient;
|
||||
use crate::error::Error;
|
||||
use crate::metadata::{ArchiveMetadata, GitWorkspaceMember, Metadata};
|
||||
use crate::source::built_wheel_metadata::BuiltWheelMetadata;
|
||||
use crate::source::built_wheel_metadata::{BuiltWheelFile, BuiltWheelMetadata};
|
||||
use crate::source::revision::Revision;
|
||||
use crate::{Reporter, RequiresDist};
|
||||
|
||||
|
|
@ -454,6 +454,10 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
let cache_shard = cache_shard.shard(revision.id());
|
||||
let source_dist_entry = cache_shard.entry(SOURCE);
|
||||
|
||||
// We don't track any cache information for URL-based source distributions; they're assumed
|
||||
// to be immutable.
|
||||
let cache_info = CacheInfo::default();
|
||||
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.name());
|
||||
|
|
@ -472,12 +476,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
if let Some(built_wheel) = BuiltWheelMetadata::find_in_cache(tags, &cache_shard)
|
||||
if let Some(file) = BuiltWheelFile::find_in_cache(tags, &cache_shard)
|
||||
.ok()
|
||||
.flatten()
|
||||
.filter(|built_wheel| built_wheel.matches(source.name(), source.version()))
|
||||
.filter(|file| file.matches(source.name(), source.version()))
|
||||
{
|
||||
return Ok(built_wheel.with_hashes(revision.into_hashes()));
|
||||
return Ok(BuiltWheelMetadata::from_file(
|
||||
file,
|
||||
revision.into_hashes(),
|
||||
cache_info,
|
||||
));
|
||||
}
|
||||
|
||||
// Otherwise, we need to build a wheel. Before building, ensure that the source is present.
|
||||
|
|
@ -540,7 +548,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
target: cache_shard.join(wheel_filename.stem()).into_boxed_path(),
|
||||
filename: wheel_filename,
|
||||
hashes: revision.into_hashes(),
|
||||
cache_info: CacheInfo::default(),
|
||||
cache_info,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -879,12 +887,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
if let Some(built_wheel) = BuiltWheelMetadata::find_in_cache(tags, &cache_shard)
|
||||
if let Some(file) = BuiltWheelFile::find_in_cache(tags, &cache_shard)
|
||||
.ok()
|
||||
.flatten()
|
||||
.filter(|built_wheel| built_wheel.matches(source.name(), source.version()))
|
||||
.filter(|file| file.matches(source.name(), source.version()))
|
||||
{
|
||||
return Ok(built_wheel);
|
||||
return Ok(BuiltWheelMetadata::from_file(
|
||||
file,
|
||||
revision.into_hashes(),
|
||||
cache_info,
|
||||
));
|
||||
}
|
||||
|
||||
// Otherwise, we need to build a wheel, which requires a source distribution.
|
||||
|
|
@ -1201,12 +1213,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
if let Some(built_wheel) = BuiltWheelMetadata::find_in_cache(tags, &cache_shard)
|
||||
if let Some(file) = BuiltWheelFile::find_in_cache(tags, &cache_shard)
|
||||
.ok()
|
||||
.flatten()
|
||||
.filter(|built_wheel| built_wheel.matches(source.name(), source.version()))
|
||||
.filter(|file| file.matches(source.name(), source.version()))
|
||||
{
|
||||
return Ok(built_wheel);
|
||||
return Ok(BuiltWheelMetadata::from_file(
|
||||
file,
|
||||
revision.into_hashes(),
|
||||
cache_info,
|
||||
));
|
||||
}
|
||||
|
||||
// Otherwise, we need to build a wheel.
|
||||
|
|
@ -1594,6 +1610,14 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
// Acquire the advisory lock.
|
||||
let _lock = cache_shard.lock().await.map_err(Error::CacheWrite)?;
|
||||
|
||||
// We don't track any cache information for Git-based source distributions; they're assumed
|
||||
// to be immutable.
|
||||
let cache_info = CacheInfo::default();
|
||||
|
||||
// We don't compute hashes for Git-based source distributions, since the Git commit SHA is
|
||||
// used as the identifier.
|
||||
let hashes = HashDigests::empty();
|
||||
|
||||
// If there are build settings or extra build dependencies, we need to scope to a cache shard.
|
||||
let config_settings = self.config_settings_for(source.name());
|
||||
let extra_build_deps = self.extra_build_dependencies_for(source.name());
|
||||
|
|
@ -1612,12 +1636,12 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
};
|
||||
|
||||
// If the cache contains a compatible wheel, return it.
|
||||
if let Some(built_wheel) = BuiltWheelMetadata::find_in_cache(tags, &cache_shard)
|
||||
if let Some(file) = BuiltWheelFile::find_in_cache(tags, &cache_shard)
|
||||
.ok()
|
||||
.flatten()
|
||||
.filter(|built_wheel| built_wheel.matches(source.name(), source.version()))
|
||||
.filter(|file| file.matches(source.name(), source.version()))
|
||||
{
|
||||
return Ok(built_wheel);
|
||||
return Ok(BuiltWheelMetadata::from_file(file, hashes, cache_info));
|
||||
}
|
||||
|
||||
let task = self
|
||||
|
|
@ -1650,8 +1674,8 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
path: cache_shard.join(&disk_filename).into_boxed_path(),
|
||||
target: cache_shard.join(filename.stem()).into_boxed_path(),
|
||||
filename,
|
||||
hashes: HashDigests::empty(),
|
||||
cache_info: CacheInfo::default(),
|
||||
hashes,
|
||||
cache_info,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue