This commit is contained in:
Charlie Marsh 2025-12-16 21:08:25 -05:00 committed by GitHub
commit 6493b30e5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 64 additions and 35 deletions

View File

@ -16,7 +16,7 @@ use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_platform_tags::Tags; use uv_platform_tags::Tags;
use uv_pypi_types::ResolverMarkerEnvironment; use uv_pypi_types::ResolverMarkerEnvironment;
use crate::lock::{LockErrorKind, Package, TagPolicy}; use crate::lock::{HashedDist, LockErrorKind, Package, TagPolicy};
use crate::{Lock, LockError}; use crate::{Lock, LockError};
pub trait Installable<'lock> { pub trait Installable<'lock> {
@ -527,18 +527,14 @@ pub trait Installable<'lock> {
marker_env: &ResolverMarkerEnvironment, marker_env: &ResolverMarkerEnvironment,
build_options: &BuildOptions, build_options: &BuildOptions,
) -> Result<Node, LockError> { ) -> Result<Node, LockError> {
let dist = package.to_dist( let tag_policy = TagPolicy::Required(tags);
self.install_path(), let HashedDist { dist, hashes } =
TagPolicy::Required(tags), package.to_dist(self.install_path(), tag_policy, build_options, marker_env)?;
build_options,
marker_env,
)?;
let version = package.version().cloned(); let version = package.version().cloned();
let dist = ResolvedDist::Installable { let dist = ResolvedDist::Installable {
dist: Arc::new(dist), dist: Arc::new(dist),
version, version,
}; };
let hashes = package.hashes();
Ok(Node::Dist { Ok(Node::Dist {
dist, dist,
hashes, hashes,
@ -553,7 +549,7 @@ pub trait Installable<'lock> {
tags: &Tags, tags: &Tags,
marker_env: &ResolverMarkerEnvironment, marker_env: &ResolverMarkerEnvironment,
) -> Result<Node, LockError> { ) -> Result<Node, LockError> {
let dist = package.to_dist( let HashedDist { dist, .. } = package.to_dist(
self.install_path(), self.install_path(),
TagPolicy::Preferred(tags), TagPolicy::Preferred(tags),
&BuildOptions::default(), &BuildOptions::default(),

View File

@ -171,6 +171,15 @@ static ANDROID_X86_MARKERS: LazyLock<UniversalMarker> = LazyLock::new(|| {
marker marker
}); });
/// A distribution with its associated hash.
///
/// This pairs a [`Dist`] with the [`HashDigests`] for the specific wheel or
/// sdist that would be installed.
pub(crate) struct HashedDist {
pub(crate) dist: Dist,
pub(crate) hashes: HashDigests,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
#[serde(try_from = "LockWire")] #[serde(try_from = "LockWire")]
pub struct Lock { pub struct Lock {
@ -1761,7 +1770,7 @@ impl Lock {
if let Some(version) = package.id.version.as_ref() { if let Some(version) = package.id.version.as_ref() {
// For a non-dynamic package, fetch the metadata from the distribution database. // For a non-dynamic package, fetch the metadata from the distribution database.
let dist = package.to_dist( let HashedDist { dist, .. } = package.to_dist(
root, root,
TagPolicy::Preferred(tags), TagPolicy::Preferred(tags),
&BuildOptions::default(), &BuildOptions::default(),
@ -1912,7 +1921,7 @@ impl Lock {
// exactly. For example, `hatchling` will flatten any recursive (or self-referential) // exactly. For example, `hatchling` will flatten any recursive (or self-referential)
// extras, while `setuptools` will not. // extras, while `setuptools` will not.
if !satisfied { if !satisfied {
let dist = package.to_dist( let HashedDist { dist, .. } = package.to_dist(
root, root,
TagPolicy::Preferred(tags), TagPolicy::Preferred(tags),
&BuildOptions::default(), &BuildOptions::default(),
@ -2579,20 +2588,26 @@ impl Package {
Ok(()) Ok(())
} }
/// Convert the [`Package`] to a [`Dist`] that can be used in installation. /// Convert the [`Package`] to a [`Dist`] that can be used in installation, along with its hash.
fn to_dist( fn to_dist(
&self, &self,
workspace_root: &Path, workspace_root: &Path,
tag_policy: TagPolicy<'_>, tag_policy: TagPolicy<'_>,
build_options: &BuildOptions, build_options: &BuildOptions,
markers: &MarkerEnvironment, markers: &MarkerEnvironment,
) -> Result<Dist, LockError> { ) -> Result<HashedDist, LockError> {
let no_binary = build_options.no_binary_package(&self.id.name); let no_binary = build_options.no_binary_package(&self.id.name);
let no_build = build_options.no_build_package(&self.id.name); let no_build = build_options.no_build_package(&self.id.name);
if !no_binary { if !no_binary {
if let Some(best_wheel_index) = self.find_best_wheel(tag_policy) { if let Some(best_wheel_index) = self.find_best_wheel(tag_policy) {
return match &self.id.source { let hashes = self.wheels[best_wheel_index]
.hash
.as_ref()
.map(|hash| HashDigests::from(vec![hash.0.clone()]))
.unwrap_or_else(|| HashDigests::from(vec![]));
let dist = match &self.id.source {
Source::Registry(source) => { Source::Registry(source) => {
let wheels = self let wheels = self
.wheels .wheels
@ -2604,7 +2619,7 @@ impl Package {
best_wheel_index, best_wheel_index,
sdist: None, sdist: None,
}; };
Ok(Dist::Built(BuiltDist::Registry(reg_built_dist))) Dist::Built(BuiltDist::Registry(reg_built_dist))
} }
Source::Path(path) => { Source::Path(path) => {
let filename: WheelFilename = let filename: WheelFilename =
@ -2616,7 +2631,7 @@ impl Package {
install_path: absolute_path(workspace_root, path)?.into_boxed_path(), install_path: absolute_path(workspace_root, path)?.into_boxed_path(),
}; };
let built_dist = BuiltDist::Path(path_dist); let built_dist = BuiltDist::Path(path_dist);
Ok(Dist::Built(built_dist)) Dist::Built(built_dist)
} }
Source::Direct(url, direct) => { Source::Direct(url, direct) => {
let filename: WheelFilename = let filename: WheelFilename =
@ -2632,29 +2647,39 @@ impl Package {
url: VerbatimUrl::from_url(url), url: VerbatimUrl::from_url(url),
}; };
let built_dist = BuiltDist::DirectUrl(direct_dist); let built_dist = BuiltDist::DirectUrl(direct_dist);
Ok(Dist::Built(built_dist)) Dist::Built(built_dist)
} }
Source::Git(_, _) => Err(LockErrorKind::InvalidWheelSource { Source::Git(_, _) => {
return Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(), id: self.id.clone(),
source_type: "Git", source_type: "Git",
} }
.into()), .into());
Source::Directory(_) => Err(LockErrorKind::InvalidWheelSource { }
Source::Directory(_) => {
return Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(), id: self.id.clone(),
source_type: "directory", source_type: "directory",
} }
.into()), .into());
Source::Editable(_) => Err(LockErrorKind::InvalidWheelSource { }
Source::Editable(_) => {
return Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(), id: self.id.clone(),
source_type: "editable", source_type: "editable",
} }
.into()), .into());
Source::Virtual(_) => Err(LockErrorKind::InvalidWheelSource { }
Source::Virtual(_) => {
return Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(), id: self.id.clone(),
source_type: "virtual", source_type: "virtual",
} }
.into()), .into());
}
}; };
return Ok(HashedDist { dist, hashes });
} }
} }
@ -2663,7 +2688,16 @@ impl Package {
// any local source tree, or at least editable source trees, which we allow in // any local source tree, or at least editable source trees, which we allow in
// `uv pip`.) // `uv pip`.)
if !no_build || sdist.is_virtual() { if !no_build || sdist.is_virtual() {
return Ok(Dist::Source(sdist)); let hashes = self
.sdist
.as_ref()
.and_then(|s| s.hash())
.map(|hash| HashDigests::from(vec![hash.0.clone()]))
.unwrap_or_else(|| HashDigests::from(vec![]));
return Ok(HashedDist {
dist: Dist::Source(sdist),
hashes,
});
} }
} }

View File

@ -8231,7 +8231,6 @@ fn lock_invalid_hash() -> Result<()> {
Hash mismatch for `idna==3.6` Hash mismatch for `idna==3.6`
Expected: Expected:
sha256:aecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca
sha256:d05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f sha256:d05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
Computed: Computed: