Skip to content

Commit

Permalink
Backtrack on distributions with invalid metadata (#2834)
Browse files Browse the repository at this point in the history
## Summary

Closes #2821.
  • Loading branch information
charliermarsh authored Apr 5, 2024
1 parent f0b0e19 commit 0093404
Show file tree
Hide file tree
Showing 15 changed files with 229 additions and 73 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/uv-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub enum ErrorKind {

/// Dist-info error
#[error(transparent)]
InstallWheel(#[from] install_wheel_rs::Error),
DistInfo(#[from] install_wheel_rs::Error),

#[error("{0} isn't available locally, but making network requests to registries was banned.")]
NoIndex(String),
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-client/src/registry_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ async fn read_metadata_async_seek(
.enumerate()
.filter_map(|(index, entry)| Some((index, entry.filename().as_str().ok()?))),
)
.map_err(ErrorKind::InstallWheel)?;
.map_err(ErrorKind::DistInfo)?;

// Read the contents of the `METADATA` file.
let mut contents = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-client/src/remote_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub(crate) async fn wheel_metadata_from_remote_zip(
.enumerate()
.filter_map(|(idx, e)| Some(((idx, e), e.filename().as_str().ok()?))),
)
.map_err(ErrorKind::InstallWheel)?;
.map_err(ErrorKind::DistInfo)?;

let offset = metadata_entry.header_offset();
let size = metadata_entry.compressed_size()
Expand Down
18 changes: 15 additions & 3 deletions crates/uv-requirements/src/lookahead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl};
use pypi_types::Metadata23;
use uv_client::RegistryClient;
use uv_distribution::{DistributionDatabase, Reporter};
use uv_resolver::InMemoryIndex;
use uv_resolver::{InMemoryIndex, MetadataResponse};
use uv_types::{BuildContext, Constraints, Overrides, RequestedRequirements};

/// A resolver for resolving lookahead requirements from direct URLs.
Expand Down Expand Up @@ -134,7 +134,18 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
// Fetch the metadata for the distribution.
let requires_dist = {
let id = dist.package_id();
if let Some(metadata) = self.index.get_metadata(&id) {
if let Some(metadata) = self
.index
.get_metadata(&id)
.as_deref()
.and_then(|response| {
if let MetadataResponse::Found(metadata) = response {
Some(metadata)
} else {
None
}
})
{
// If the metadata is already in the index, return it.
metadata.requires_dist.clone()
} else {
Expand All @@ -151,7 +162,8 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
let requires_dist = metadata.requires_dist.clone();

// Insert the metadata into the index.
self.index.insert_metadata(id, metadata);
self.index
.insert_metadata(id, MetadataResponse::Found(metadata));

requires_dist
}
Expand Down
22 changes: 17 additions & 5 deletions crates/uv-requirements/src/source_tree.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::borrow::Cow;
use std::ops::Deref;

use std::path::{Path, PathBuf};

use anyhow::{Context, Result};
Expand All @@ -11,7 +11,7 @@ use pep508_rs::Requirement;
use uv_client::RegistryClient;
use uv_distribution::{DistributionDatabase, Reporter};
use uv_fs::Simplified;
use uv_resolver::InMemoryIndex;
use uv_resolver::{InMemoryIndex, MetadataResponse};
use uv_types::BuildContext;

use crate::ExtrasSpecification;
Expand Down Expand Up @@ -87,16 +87,28 @@ impl<'a, Context: BuildContext + Send + Sync> SourceTreeResolver<'a, Context> {
// Fetch the metadata for the distribution.
let metadata = {
let id = PackageId::from_url(source.url());
if let Some(metadata) = self.index.get_metadata(&id) {
if let Some(metadata) = self
.index
.get_metadata(&id)
.as_deref()
.and_then(|response| {
if let MetadataResponse::Found(metadata) = response {
Some(metadata)
} else {
None
}
})
{
// If the metadata is already in the index, return it.
metadata.deref().clone()
metadata.clone()
} else {
// Run the PEP 517 build process to extract metadata from the source distribution.
let source = BuildableSource::Url(source);
let metadata = self.database.build_wheel_metadata(&source).await?;

// Insert the metadata into the index.
self.index.insert_metadata(id, metadata.clone());
self.index
.insert_metadata(id, MetadataResponse::Found(metadata.clone()));

metadata
}
Expand Down
12 changes: 9 additions & 3 deletions crates/uv-requirements/src/unnamed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use pypi_types::Metadata10;
use uv_client::RegistryClient;
use uv_distribution::{DistributionDatabase, Reporter};
use uv_normalize::PackageName;
use uv_resolver::InMemoryIndex;
use uv_resolver::{InMemoryIndex, MetadataResponse};
use uv_types::BuildContext;

/// Like [`RequirementsSpecification`], but with concrete names for all requirements.
Expand Down Expand Up @@ -236,7 +236,13 @@ impl<'a, Context: BuildContext + Send + Sync> NamedRequirementsResolver<'a, Cont
// Fetch the metadata for the distribution.
let name = {
let id = PackageId::from_url(source.url());
if let Some(metadata) = index.get_metadata(&id) {
if let Some(metadata) = index.get_metadata(&id).as_deref().and_then(|response| {
if let MetadataResponse::Found(metadata) = response {
Some(metadata)
} else {
None
}
}) {
// If the metadata is already in the index, return it.
metadata.name.clone()
} else {
Expand All @@ -247,7 +253,7 @@ impl<'a, Context: BuildContext + Send + Sync> NamedRequirementsResolver<'a, Cont
let name = metadata.name.clone();

// Insert the metadata into the index.
index.insert_metadata(id, metadata);
index.insert_metadata(id, MetadataResponse::Found(metadata));

name
}
Expand Down
1 change: 1 addition & 0 deletions crates/uv-resolver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ workspace = true
cache-key = { workspace = true }
distribution-filename = { workspace = true, features = ["serde"] }
distribution-types = { workspace = true }
install-wheel-rs = { workspace = true }
once-map = { workspace = true }
pep440_rs = { workspace = true }
pep508_rs = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-resolver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use python_requirement::PythonRequirement;
pub use resolution::{AnnotationStyle, Diagnostic, DisplayResolutionGraph, ResolutionGraph};
pub use resolution_mode::ResolutionMode;
pub use resolver::{
BuildId, DefaultResolverProvider, InMemoryIndex, PackageVersionsResult,
BuildId, DefaultResolverProvider, InMemoryIndex, MetadataResponse, PackageVersionsResult,
Reporter as ResolverReporter, Resolver, ResolverProvider, VersionsResponse,
WheelMetadataResult,
};
Expand Down
32 changes: 26 additions & 6 deletions crates/uv-resolver/src/resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use distribution_types::{
use once_map::OnceMap;
use pep440_rs::Version;
use pep508_rs::MarkerEnvironment;
use pypi_types::{Hashes, Metadata23};
use pypi_types::Hashes;
use uv_distribution::to_precise;
use uv_normalize::{ExtraName, PackageName};

Expand All @@ -28,7 +28,7 @@ use crate::pins::FilePins;
use crate::preferences::Preferences;
use crate::pubgrub::{PubGrubDistribution, PubGrubPackage};
use crate::redirect::apply_redirect;
use crate::resolver::{InMemoryIndex, VersionsResponse};
use crate::resolver::{InMemoryIndex, MetadataResponse, VersionsResponse};
use crate::{Manifest, ResolveError};

/// Indicate the style of annotation comments, used to indicate the dependencies that requested each
Expand Down Expand Up @@ -66,7 +66,7 @@ impl ResolutionGraph {
selection: &SelectedDependencies<UvDependencyProvider>,
pins: &FilePins,
packages: &OnceMap<PackageName, VersionsResponse>,
distributions: &OnceMap<PackageId, Metadata23>,
distributions: &OnceMap<PackageId, MetadataResponse>,
state: &State<UvDependencyProvider>,
preferences: &Preferences,
editables: Editables,
Expand Down Expand Up @@ -164,13 +164,20 @@ impl ResolutionGraph {
});
}
} else {
let metadata = distributions.get(&dist.package_id()).unwrap_or_else(|| {
let response = distributions.get(&dist.package_id()).unwrap_or_else(|| {
panic!(
"Every package should have metadata: {:?}",
dist.package_id()
)
});

let MetadataResponse::Found(metadata) = &*response else {
panic!(
"Every package should have metadata: {:?}",
dist.package_id()
)
};

if metadata.provides_extras.contains(extra) {
extras
.entry(package_name.clone())
Expand Down Expand Up @@ -211,13 +218,20 @@ impl ResolutionGraph {
});
}
} else {
let metadata = distributions.get(&dist.package_id()).unwrap_or_else(|| {
let response = distributions.get(&dist.package_id()).unwrap_or_else(|| {
panic!(
"Every package should have metadata: {:?}",
dist.package_id()
)
});

let MetadataResponse::Found(metadata) = &*response else {
panic!(
"Every package should have metadata: {:?}",
dist.package_id()
)
};

if metadata.provides_extras.contains(extra) {
extras
.entry(package_name.clone())
Expand Down Expand Up @@ -417,10 +431,16 @@ impl ResolutionGraph {
}
VersionOrUrl::Url(verbatim_url) => PackageId::from_url(verbatim_url.raw()),
};
let md = index
let res = index
.distributions
.get(&package_id)
.expect("every package in resolution graph has metadata");
let MetadataResponse::Found(md) = &*res else {
panic!(
"Every package should have metadata: {:?}",
dist.package_id()
)
};
for req in manifest.apply(&md.requires_dist) {
let Some(ref marker_tree) = req.marker else {
continue;
Expand Down
17 changes: 8 additions & 9 deletions crates/uv-resolver/src/resolver/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ use std::sync::Arc;

use distribution_types::PackageId;
use once_map::OnceMap;
use pypi_types::Metadata23;
use uv_normalize::PackageName;

use crate::resolver::provider::VersionsResponse;
use crate::resolver::provider::{MetadataResponse, VersionsResponse};

/// In-memory index of package metadata.
#[derive(Default)]
Expand All @@ -15,27 +14,27 @@ pub struct InMemoryIndex {
pub(crate) packages: OnceMap<PackageName, VersionsResponse>,

/// A map from package ID to metadata for that distribution.
pub(crate) distributions: OnceMap<PackageId, Metadata23>,
pub(crate) distributions: OnceMap<PackageId, MetadataResponse>,
}

impl InMemoryIndex {
/// Insert a [`VersionsResponse`] into the index.
pub fn insert_package(&self, package_name: PackageName, metadata: VersionsResponse) {
self.packages.done(package_name, metadata);
pub fn insert_package(&self, package_name: PackageName, response: VersionsResponse) {
self.packages.done(package_name, response);
}

/// Insert a [`Metadata23`] into the index.
pub fn insert_metadata(&self, package_id: PackageId, metadata: Metadata23) {
self.distributions.done(package_id, metadata);
pub fn insert_metadata(&self, package_id: PackageId, response: MetadataResponse) {
self.distributions.done(package_id, response);
}

/// Get the [`VersionsResponse`] for a given package name, without waiting.
pub fn get_package(&self, package_name: &PackageName) -> Option<Arc<VersionsResponse>> {
self.packages.get(package_name)
}

/// Get the [`Metadata23`] for a given package ID, without waiting.
pub fn get_metadata(&self, package_id: &PackageId) -> Option<Arc<Metadata23>> {
/// Get the [`MetadataResponse`] for a given package ID, without waiting.
pub fn get_metadata(&self, package_id: &PackageId) -> Option<Arc<MetadataResponse>> {
self.distributions.get(package_id)
}
}
Loading

0 comments on commit 0093404

Please sign in to comment.