Read cached registry distributions when --config-settings are present (#10578)

## Summary

Closes https://github.com/astral-sh/uv/issues/10577.
This commit is contained in:
Charlie Marsh 2025-01-13 16:25:12 -05:00 committed by GitHub
parent 53d3d5e3b8
commit b6aa40b29d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 104 additions and 6 deletions

View file

@ -3,6 +3,8 @@ use std::collections::hash_map::Entry;
use rustc_hash::{FxHashMap, FxHashSet};
use uv_cache::{Cache, CacheBucket, WheelCache};
use uv_cache_key::cache_digest;
use uv_configuration::ConfigSettings;
use uv_distribution_types::{CachedRegistryDist, Hashed, Index, IndexLocations, IndexUrl};
use uv_fs::{directories, files, symlinks};
use uv_normalize::PackageName;
@ -31,6 +33,7 @@ pub struct RegistryWheelIndex<'a> {
index_locations: &'a IndexLocations,
hasher: &'a HashStrategy,
index: FxHashMap<&'a PackageName, Vec<IndexEntry<'a>>>,
build_configuration: &'a ConfigSettings,
}
impl<'a> RegistryWheelIndex<'a> {
@ -40,12 +43,14 @@ impl<'a> RegistryWheelIndex<'a> {
tags: &'a Tags,
index_locations: &'a IndexLocations,
hasher: &'a HashStrategy,
build_configuration: &'a ConfigSettings,
) -> Self {
Self {
cache,
tags,
index_locations,
hasher,
build_configuration,
index: FxHashMap::default(),
}
}
@ -67,6 +72,7 @@ impl<'a> RegistryWheelIndex<'a> {
self.tags,
self.index_locations,
self.hasher,
self.build_configuration,
)),
};
versions
@ -79,6 +85,7 @@ impl<'a> RegistryWheelIndex<'a> {
tags: &Tags,
index_locations: &'index IndexLocations,
hasher: &HashStrategy,
build_configuration: &ConfigSettings,
) -> Vec<IndexEntry<'index>> {
let mut entries = vec![];
@ -162,9 +169,8 @@ impl<'a> RegistryWheelIndex<'a> {
WheelCache::Index(index.url()).wheel_dir(package.to_string()),
);
// For registry wheels, the cache structure is: `<index>/<package-name>/<version>/`.
// For registry source distributions, the cache structure is: `<index>/<package-name>/<version>/`.
for shard in directories(&cache_shard) {
// Read the existing metadata from the cache, if it exists.
let cache_shard = cache_shard.shard(shard);
// Read the revision from the cache.
@ -190,7 +196,16 @@ impl<'a> RegistryWheelIndex<'a> {
};
if let Some(revision) = revision {
for wheel_dir in symlinks(cache_shard.join(revision.id())) {
let cache_shard = cache_shard.shard(revision.id());
// If there are build settings, we need to scope to a cache shard.
let cache_shard = if build_configuration.is_empty() {
cache_shard
} else {
cache_shard.shard(cache_digest(build_configuration))
};
for wheel_dir in symlinks(cache_shard) {
if let Some(wheel) = CachedWheel::from_built_source(wheel_dir) {
if wheel.filename.compatibility(tags).is_compatible() {
// Enforce hash-checking based on the source distribution.

View file

@ -56,7 +56,8 @@ impl<'a> Planner<'a> {
tags: &Tags,
) -> Result<Plan> {
// Index all the already-downloaded wheels in the cache.
let mut registry_index = RegistryWheelIndex::new(cache, tags, index_locations, hasher);
let mut registry_index =
RegistryWheelIndex::new(cache, tags, index_locations, hasher, config_settings);
let built_index = BuiltWheelIndex::new(cache, tags, hasher, config_settings);
let mut cached = vec![];
@ -248,7 +249,7 @@ impl<'a> Planner<'a> {
}
Some(&entry.dist)
}) {
debug!("Requirement already cached: {distribution}");
debug!("Registry requirement already cached: {distribution}");
cached.push(CachedDist::Registry(distribution.clone()));
continue;
}

View file

@ -3251,7 +3251,89 @@ fn launcher_with_symlink() -> Result<()> {
}
#[test]
fn config_settings() {
fn config_settings_registry() {
let context = TestContext::new("3.12");
// Install with a `-C` flag. In this case, the flag has no impact on the build, but uv should
// respect it anyway.
uv_snapshot!(context.filters(), context.pip_install()
.arg("iniconfig")
.arg("--no-binary")
.arg("iniconfig")
.arg("-C=global-option=build_ext"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
"###
);
// Uninstall the package.
uv_snapshot!(context.filters(), context.pip_uninstall()
.arg("iniconfig"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Uninstalled 1 package in [TIME]
- iniconfig==2.0.0
"###);
// Re-install the package, with the same flag. We should read from the cache.
uv_snapshot!(context.filters(), context.pip_install()
.arg("iniconfig")
.arg("--no-binary")
.arg("iniconfig")
.arg("-C=global-option=build_ext"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
"###
);
// Uninstall the package.
uv_snapshot!(context.filters(), context.pip_uninstall()
.arg("iniconfig"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Uninstalled 1 package in [TIME]
- iniconfig==2.0.0
"###);
// Re-install the package, without the flag. We should build it from source.
uv_snapshot!(context.filters(), context.pip_install()
.arg("iniconfig")
.arg("--no-binary")
.arg("iniconfig"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
"###
);
}
#[test]
fn config_settings_path() {
let context = TestContext::new("3.12");
// Install the editable package.