Skip to content

Commit 556f71f

Browse files
committed
Avoid generating unused hashes during uv lock
1 parent bbf9558 commit 556f71f

File tree

8 files changed

+64
-39
lines changed

8 files changed

+64
-39
lines changed

crates/uv-distribution-types/src/hash.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub enum HashPolicy<'a> {
55
/// No hash policy is specified.
66
None,
77
/// Hashes should be generated (specifically, a SHA-256 hash), but not validated.
8-
Generate,
8+
Generate(HashGeneration),
99
/// Hashes should be validated against a pre-defined list of hashes. If necessary, hashes should
1010
/// be generated so as to ensure that the archive is valid.
1111
Validate(&'a [HashDigest]),
@@ -17,21 +17,28 @@ impl HashPolicy<'_> {
1717
matches!(self, Self::None)
1818
}
1919

20-
/// Returns `true` if the hash policy is `Generate`.
21-
pub fn is_generate(&self) -> bool {
22-
matches!(self, Self::Generate)
23-
}
24-
2520
/// Returns `true` if the hash policy is `Validate`.
2621
pub fn is_validate(&self) -> bool {
2722
matches!(self, Self::Validate(_))
2823
}
2924

25+
/// Returns `true` if the hash policy indicates that hashes should be generated.
26+
pub fn is_generate(&self, dist: &crate::BuiltDist) -> bool {
27+
match self {
28+
HashPolicy::Generate(HashGeneration::Url) => dist.file().is_none(),
29+
HashPolicy::Generate(HashGeneration::All) => {
30+
dist.file().map_or(true, |file| file.hashes.is_empty())
31+
}
32+
HashPolicy::Validate(_) => false,
33+
HashPolicy::None => false,
34+
}
35+
}
36+
3037
/// Return the algorithms used in the hash policy.
3138
pub fn algorithms(&self) -> Vec<HashAlgorithm> {
3239
match self {
3340
Self::None => vec![],
34-
Self::Generate => vec![HashAlgorithm::Sha256],
41+
Self::Generate(_) => vec![HashAlgorithm::Sha256],
3542
Self::Validate(hashes) => {
3643
let mut algorithms = hashes.iter().map(HashDigest::algorithm).collect::<Vec<_>>();
3744
algorithms.sort();
@@ -45,12 +52,22 @@ impl HashPolicy<'_> {
4552
pub fn digests(&self) -> &[HashDigest] {
4653
match self {
4754
Self::None => &[],
48-
Self::Generate => &[],
55+
Self::Generate(_) => &[],
4956
Self::Validate(hashes) => hashes,
5057
}
5158
}
5259
}
5360

61+
/// The context in which hashes should be generated.
62+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63+
pub enum HashGeneration {
64+
/// Generate hashes for direct URL distributions.
65+
Url,
66+
/// Generate hashes for direct URL distributions, along with any distributions that are hosted
67+
/// on a registry that does _not_ provide hashes.
68+
All,
69+
}
70+
5471
pub trait Hashed {
5572
/// Return the [`HashDigest`]s for the archive.
5673
fn hashes(&self) -> &[HashDigest];
@@ -59,7 +76,7 @@ pub trait Hashed {
5976
fn satisfies(&self, hashes: HashPolicy) -> bool {
6077
match hashes {
6178
HashPolicy::None => true,
62-
HashPolicy::Generate => self
79+
HashPolicy::Generate(_) => self
6380
.hashes()
6481
.iter()
6582
.any(|hash| hash.algorithm == HashAlgorithm::Sha256),
@@ -71,7 +88,7 @@ pub trait Hashed {
7188
fn has_digests(&self, hashes: HashPolicy) -> bool {
7289
match hashes {
7390
HashPolicy::None => true,
74-
HashPolicy::Generate => self
91+
HashPolicy::Generate(_) => self
7592
.hashes()
7693
.iter()
7794
.any(|hash| hash.algorithm == HashAlgorithm::Sha256),

crates/uv-distribution/src/distribution_database.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -405,22 +405,28 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
405405
return Ok(ArchiveMetadata::from_metadata23(metadata.clone()));
406406
}
407407

408-
// If hash generation is enabled, and the distribution isn't hosted on an index, get the
408+
// If hash generation is enabled, and the distribution isn't hosted on a registry, get the
409409
// entire wheel to ensure that the hashes are included in the response. If the distribution
410410
// is hosted on an index, the hashes will be included in the simple metadata response.
411411
// For hash _validation_, callers are expected to enforce the policy when retrieving the
412412
// wheel.
413+
//
414+
// Historically, for `uv pip compile --universal`, we also generate hashes for
415+
// registry-based distributions when the relevant registry doesn't provide them. This was
416+
// motivated by `--find-links`. We continue that behavior (under `HashGeneration::All`) for
417+
// backwards compatibility, but it's a little dubious, since we're only hashing _one_
418+
// distribution here (as opposed to hashing all distributions for the version), and it may
419+
// not even be a compatible distribution!
420+
//
413421
// TODO(charlie): Request the hashes via a separate method, to reduce the coupling in this API.
414-
if hashes.is_generate() {
415-
if dist.file().map_or(true, |file| file.hashes.is_empty()) {
416-
let wheel = self.get_wheel(dist, hashes).await?;
417-
let metadata = wheel.metadata()?;
418-
let hashes = wheel.hashes;
419-
return Ok(ArchiveMetadata {
420-
metadata: Metadata::from_metadata23(metadata),
421-
hashes,
422-
});
423-
}
422+
if hashes.is_generate(dist) {
423+
let wheel = self.get_wheel(dist, hashes).await?;
424+
let metadata = wheel.metadata()?;
425+
let hashes = wheel.hashes;
426+
return Ok(ArchiveMetadata {
427+
metadata: Metadata::from_metadata23(metadata),
428+
hashes,
429+
});
424430
}
425431

426432
let result = self

crates/uv-requirements/src/source_tree.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use url::Url;
1313
use uv_configuration::ExtrasSpecification;
1414
use uv_distribution::{DistributionDatabase, Reporter, RequiresDist};
1515
use uv_distribution_types::{
16-
BuildableSource, DirectorySourceUrl, HashPolicy, SourceUrl, VersionId,
16+
BuildableSource, DirectorySourceUrl, HashGeneration, HashPolicy, SourceUrl, VersionId,
1717
};
1818
use uv_fs::Simplified;
1919
use uv_normalize::{ExtraName, PackageName};
@@ -213,8 +213,8 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
213213
// manual match.
214214
let hashes = match self.hasher {
215215
HashStrategy::None => HashPolicy::None,
216-
HashStrategy::Generate => HashPolicy::Generate,
217-
HashStrategy::Verify(_) => HashPolicy::Generate,
216+
HashStrategy::Generate(mode) => HashPolicy::Generate(*mode),
217+
HashStrategy::Verify(_) => HashPolicy::Generate(HashGeneration::All),
218218
HashStrategy::Require(_) => {
219219
return Err(anyhow::anyhow!(
220220
"Hash-checking is not supported for local directories: {}",

crates/uv-types/src/hash.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use url::Url;
55

66
use uv_configuration::HashCheckingMode;
77
use uv_distribution_types::{
8-
DistributionMetadata, HashPolicy, Name, Resolution, UnresolvedRequirement, VersionId,
8+
DistributionMetadata, HashGeneration, HashPolicy, Name, Resolution, UnresolvedRequirement,
9+
VersionId,
910
};
1011
use uv_normalize::PackageName;
1112
use uv_pep440::Version;
@@ -19,7 +20,7 @@ pub enum HashStrategy {
1920
#[default]
2021
None,
2122
/// Hashes should be generated (specifically, a SHA-256 hash), but not validated.
22-
Generate,
23+
Generate(HashGeneration),
2324
/// Hashes should be validated, if present, but ignored if absent.
2425
///
2526
/// If necessary, hashes should be generated to ensure that the archive is valid.
@@ -35,7 +36,7 @@ impl HashStrategy {
3536
pub fn get<T: DistributionMetadata>(&self, distribution: &T) -> HashPolicy {
3637
match self {
3738
Self::None => HashPolicy::None,
38-
Self::Generate => HashPolicy::Generate,
39+
Self::Generate(mode) => HashPolicy::Generate(*mode),
3940
Self::Verify(hashes) => {
4041
if let Some(hashes) = hashes.get(&distribution.version_id()) {
4142
HashPolicy::Validate(hashes.as_slice())
@@ -56,7 +57,7 @@ impl HashStrategy {
5657
pub fn get_package(&self, name: &PackageName, version: &Version) -> HashPolicy {
5758
match self {
5859
Self::None => HashPolicy::None,
59-
Self::Generate => HashPolicy::Generate,
60+
Self::Generate(mode) => HashPolicy::Generate(*mode),
6061
Self::Verify(hashes) => {
6162
if let Some(hashes) =
6263
hashes.get(&VersionId::from_registry(name.clone(), version.clone()))
@@ -79,7 +80,7 @@ impl HashStrategy {
7980
pub fn get_url(&self, url: &Url) -> HashPolicy {
8081
match self {
8182
Self::None => HashPolicy::None,
82-
Self::Generate => HashPolicy::Generate,
83+
Self::Generate(mode) => HashPolicy::Generate(*mode),
8384
Self::Verify(hashes) => {
8485
if let Some(hashes) = hashes.get(&VersionId::from_url(url)) {
8586
HashPolicy::Validate(hashes.as_slice())
@@ -100,7 +101,7 @@ impl HashStrategy {
100101
pub fn allows_package(&self, name: &PackageName, version: &Version) -> bool {
101102
match self {
102103
Self::None => true,
103-
Self::Generate => true,
104+
Self::Generate(_) => true,
104105
Self::Verify(_) => true,
105106
Self::Require(hashes) => {
106107
hashes.contains_key(&VersionId::from_registry(name.clone(), version.clone()))
@@ -112,7 +113,7 @@ impl HashStrategy {
112113
pub fn allows_url(&self, url: &Url) -> bool {
113114
match self {
114115
Self::None => true,
115-
Self::Generate => true,
116+
Self::Generate(_) => true,
116117
Self::Verify(_) => true,
117118
Self::Require(hashes) => hashes.contains_key(&VersionId::from_url(url)),
118119
}

crates/uv/src/commands/pip/compile.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use uv_configuration::{
1717
use uv_configuration::{KeyringProviderType, TargetTriple};
1818
use uv_dispatch::{BuildDispatch, SharedState};
1919
use uv_distribution_types::{
20-
DependencyMetadata, Index, IndexLocations, NameRequirementSpecification, Origin,
21-
UnresolvedRequirementSpecification, Verbatim,
20+
DependencyMetadata, HashGeneration, Index, IndexLocations, NameRequirementSpecification,
21+
Origin, UnresolvedRequirementSpecification, Verbatim,
2222
};
2323
use uv_fs::Simplified;
2424
use uv_install_wheel::linker::LinkMode;
@@ -266,7 +266,7 @@ pub(crate) async fn pip_compile(
266266

267267
// Generate, but don't enforce hashes for the requirements.
268268
let hasher = if generate_hashes {
269-
HashStrategy::Generate
269+
HashStrategy::Generate(HashGeneration::All)
270270
} else {
271271
HashStrategy::None
272272
};

crates/uv/src/commands/project/lock.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use uv_configuration::{
1717
use uv_dispatch::{BuildDispatch, SharedState};
1818
use uv_distribution::DistributionDatabase;
1919
use uv_distribution_types::{
20-
DependencyMetadata, Index, IndexLocations, NameRequirementSpecification,
20+
DependencyMetadata, HashGeneration, Index, IndexLocations, NameRequirementSpecification,
2121
UnresolvedRequirementSpecification,
2222
};
2323
use uv_git::ResolvedRepositoryReference;
@@ -472,7 +472,7 @@ async fn do_lock(
472472
.index_strategy(index_strategy)
473473
.build_options(build_options.clone())
474474
.build();
475-
let hasher = HashStrategy::Generate;
475+
let hasher = HashStrategy::Generate(HashGeneration::Url);
476476

477477
// TODO(charlie): These are all default values. We should consider whether we want to make them
478478
// optional on the downstream APIs.

crates/uv/tests/it/lock.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9307,7 +9307,7 @@ fn lock_find_links_local_wheel() -> Result<()> {
93079307
----- stderr -----
93089308
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
93099309
Creating virtual environment at: .venv
9310-
Prepared 1 package in [TIME]
9310+
Prepared 2 packages in [TIME]
93119311
Installed 2 packages in [TIME]
93129312
+ project==0.1.0 (from file://[TEMP_DIR]/workspace)
93139313
+ tqdm==1000.0.0
@@ -9518,7 +9518,7 @@ fn lock_find_links_http_wheel() -> Result<()> {
95189518
----- stdout -----
95199519

95209520
----- stderr -----
9521-
Prepared 1 package in [TIME]
9521+
Prepared 2 packages in [TIME]
95229522
Installed 2 packages in [TIME]
95239523
+ packaging==23.2
95249524
+ project==0.1.0 (from file://[TEMP_DIR]/)
@@ -9744,7 +9744,7 @@ fn lock_local_index() -> Result<()> {
97449744
----- stdout -----
97459745

97469746
----- stderr -----
9747-
Prepared 1 package in [TIME]
9747+
Prepared 2 packages in [TIME]
97489748
Installed 2 packages in [TIME]
97499749
+ project==0.1.0 (from file://[TEMP_DIR]/)
97509750
+ tqdm==1000.0.0

crates/uv/tests/it/sync.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5870,6 +5870,7 @@ fn sync_build_tag() -> Result<()> {
58705870
----- stdout -----
58715871
58725872
----- stderr -----
5873+
Prepared 1 package in [TIME]
58735874
Installed 1 package in [TIME]
58745875
+ build-tag==1.0.0
58755876
"###);

0 commit comments

Comments
 (0)