Make cache robust to removed archives (#6284)

## Summary

Closes https://github.com/astral-sh/uv/issues/6147.

## Test Plan

- `cargo run pip install flask --no-binary flask --cache-dir foo
--reinstall`
- `rm -rf foo/archive-v0`
- `cargo run pip install flask --no-binary flask --cache-dir foo
--reinstall`
This commit is contained in:
Charlie Marsh
2024-08-20 19:56:23 -04:00
committed by GitHub
parent 9892a4ab50
commit d954a76cb6
2 changed files with 19 additions and 6 deletions
+6 -1
View File
@@ -1,6 +1,6 @@
use distribution_types::Hashed;
use pypi_types::HashDigest;
use uv_cache::ArchiveId;
use uv_cache::{ArchiveId, Cache};
/// An archive (unzipped wheel) that exists in the local cache.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -16,6 +16,11 @@ impl Archive {
pub(crate) fn new(id: ArchiveId, hashes: Vec<HashDigest>) -> Self {
Self { id, hashes }
}
/// Returns `true` if the archive exists in the cache.
pub(crate) fn exists(&self, cache: &Cache) -> bool {
cache.archive(&self.id).exists()
}
}
impl Hashed for Archive {
@@ -596,8 +596,12 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
CachedClientError::Client(err) => Error::Client(err),
})?;
// If the archive is missing the required hashes, force a refresh.
let archive = if archive.has_digests(hashes) {
// If the archive is missing the required hashes, or has since been removed, force a refresh.
let archive = Some(archive)
.filter(|archive| archive.has_digests(hashes))
.filter(|archive| archive.exists(self.build_context.cache()));
let archive = if let Some(archive) = archive {
archive
} else {
self.client
@@ -746,12 +750,16 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
CachedClientError::Client(err) => Error::Client(err),
})?;
// If the archive is missing the required hashes, force a refresh.
let archive = if archive.has_digests(hashes) {
// If the archive is missing the required hashes, or has since been removed, force a refresh.
let archive = Some(archive)
.filter(|archive| archive.has_digests(hashes))
.filter(|archive| archive.exists(self.build_context.cache()));
let archive = if let Some(archive) = archive {
archive
} else {
self.client
.managed(|client| async move {
.managed(|client| async {
client
.cached_client()
.skip_cache(self.request(url)?, &http_entry, download)