mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-25 05:33:43 +00:00
Fall back to distributions without hashes in resolver (#2949)
## Summary This represents a change to `--require-hashes` in the event that we don't find a matching hash from the registry. The behavior in this PR is closer to pip's. Prior to this PR, if a distribution had no reported hash, or only mismatched hashes, we would mark it as incompatible. Now, we mark it as compatible, but we use the hash-agreement as part of the ordering, such that we prefer any distribution with a matching hash, then any distribution with no hash, then any distribution with a mismatched hash. As a result, if an index reports incorrect hashes, but the user provides the correct one, resolution now succeeds, where it would've failed. Similarly, if an index omits hashes altogether, but the user provides the correct one, resolution now succeeds, where it would've failed. If we end up picking a distribution whose hash ultimately doesn't match, we'll reject it later, after resolution.
This commit is contained in:
parent
1f3b5bb093
commit
c18551fd3c
5 changed files with 321 additions and 185 deletions
|
|
@ -8,11 +8,11 @@ use tracing::instrument;
|
|||
|
||||
use distribution_filename::{DistFilename, WheelFilename};
|
||||
use distribution_types::{
|
||||
Dist, IncompatibleSource, IncompatibleWheel, IndexUrl, PrioritizedDist,
|
||||
Dist, Hash, IncompatibleSource, IncompatibleWheel, IndexUrl, PrioritizedDist,
|
||||
SourceDistCompatibility, WheelCompatibility,
|
||||
};
|
||||
use pep440_rs::{Version, VersionSpecifiers};
|
||||
use platform_tags::Tags;
|
||||
use platform_tags::{TagCompatibility, Tags};
|
||||
use pypi_types::{HashDigest, Yanked};
|
||||
use uv_client::{OwnedArchive, SimpleMetadata, VersionFiles};
|
||||
use uv_configuration::{NoBinary, NoBuild};
|
||||
|
|
@ -456,19 +456,6 @@ impl VersionMapLazy {
|
|||
));
|
||||
}
|
||||
|
||||
// Check if hashes line up
|
||||
if !self.required_hashes.is_empty() {
|
||||
if hashes.is_empty() {
|
||||
return SourceDistCompatibility::Incompatible(IncompatibleSource::MissingHash);
|
||||
}
|
||||
if !hashes
|
||||
.iter()
|
||||
.any(|hash| self.required_hashes.contains(hash))
|
||||
{
|
||||
return SourceDistCompatibility::Incompatible(IncompatibleSource::MismatchedHash);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if yanked
|
||||
if let Some(yanked) = yanked {
|
||||
if yanked.is_yanked() && !self.allowed_yanks.contains(version) {
|
||||
|
|
@ -489,7 +476,23 @@ impl VersionMapLazy {
|
|||
}
|
||||
}
|
||||
|
||||
SourceDistCompatibility::Compatible
|
||||
// Check if hashes line up. If hashes aren't required, they're considered matching.
|
||||
let hash = if self.required_hashes.is_empty() {
|
||||
Hash::Matched
|
||||
} else {
|
||||
if hashes.is_empty() {
|
||||
Hash::Missing
|
||||
} else if hashes
|
||||
.iter()
|
||||
.any(|hash| self.required_hashes.contains(hash))
|
||||
{
|
||||
Hash::Matched
|
||||
} else {
|
||||
Hash::Mismatched
|
||||
}
|
||||
};
|
||||
|
||||
SourceDistCompatibility::Compatible(hash)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
|
@ -513,19 +516,6 @@ impl VersionMapLazy {
|
|||
return WheelCompatibility::Incompatible(IncompatibleWheel::ExcludeNewer(upload_time));
|
||||
}
|
||||
|
||||
// Check if hashes line up
|
||||
if !self.required_hashes.is_empty() {
|
||||
if hashes.is_empty() {
|
||||
return WheelCompatibility::Incompatible(IncompatibleWheel::MissingHash);
|
||||
}
|
||||
if !hashes
|
||||
.iter()
|
||||
.any(|hash| self.required_hashes.contains(hash))
|
||||
{
|
||||
return WheelCompatibility::Incompatible(IncompatibleWheel::MismatchedHash);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if yanked
|
||||
if let Some(yanked) = yanked {
|
||||
if yanked.is_yanked() && !self.allowed_yanks.contains(version) {
|
||||
|
|
@ -542,8 +532,31 @@ impl VersionMapLazy {
|
|||
}
|
||||
}
|
||||
|
||||
// Determine a compatibility for the wheel based on tags
|
||||
WheelCompatibility::from(filename.compatibility(&self.tags))
|
||||
// Determine a compatibility for the wheel based on tags.
|
||||
let priority = match filename.compatibility(&self.tags) {
|
||||
TagCompatibility::Incompatible(tag) => {
|
||||
return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag))
|
||||
}
|
||||
TagCompatibility::Compatible(priority) => priority,
|
||||
};
|
||||
|
||||
// Check if hashes line up. If hashes aren't required, they're considered matching.
|
||||
let hash = if self.required_hashes.is_empty() {
|
||||
Hash::Matched
|
||||
} else {
|
||||
if hashes.is_empty() {
|
||||
Hash::Missing
|
||||
} else if hashes
|
||||
.iter()
|
||||
.any(|hash| self.required_hashes.contains(hash))
|
||||
{
|
||||
Hash::Matched
|
||||
} else {
|
||||
Hash::Mismatched
|
||||
}
|
||||
};
|
||||
|
||||
WheelCompatibility::Compatible(hash, priority)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue