Populate the in-memory index when resolving lookahead URLs (#2761)

## Summary

Just ensures that we don't have to go back to the cache when resolving.
This commit is contained in:
Charlie Marsh 2024-04-01 18:07:54 -04:00 committed by GitHub
parent 999d653404
commit f2c9e88f3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 68 additions and 13 deletions

View file

@ -1,15 +1,17 @@
use std::collections::VecDeque;
use anyhow::{Context, Result};
use cache_key::CanonicalUrl;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use rustc_hash::FxHashSet;
use distribution_types::{Dist, LocalEditable};
use distribution_types::{Dist, DistributionMetadata, LocalEditable};
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_types::{BuildContext, Constraints, Overrides, RequestedRequirements};
/// A resolver for resolving lookahead requirements from direct URLs.
@ -37,6 +39,8 @@ pub struct LookaheadResolver<'a, Context: BuildContext + Send + Sync> {
overrides: &'a Overrides,
/// The editable requirements for the project.
editables: &'a [(LocalEditable, Metadata23)],
/// The in-memory index for resolving dependencies.
index: &'a InMemoryIndex,
/// The database for fetching and building distributions.
database: DistributionDatabase<'a, Context>,
}
@ -50,12 +54,14 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
editables: &'a [(LocalEditable, Metadata23)],
context: &'a Context,
client: &'a RegistryClient,
index: &'a InMemoryIndex,
) -> Self {
Self {
requirements,
constraints,
overrides,
editables,
index,
database: DistributionDatabase::new(client, context),
}
}
@ -126,15 +132,34 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
// Convert to a buildable distribution.
let dist = Dist::from_url(requirement.name, url.clone())?;
// Run the PEP 517 build process to extract metadata from the source distribution.
let (metadata, _precise) = self
.database
.get_or_build_wheel_metadata(&dist)
.await
.with_context(|| match &dist {
Dist::Built(built) => format!("Failed to download: {built}"),
Dist::Source(source) => format!("Failed to download and build: {source}"),
})?;
// Fetch the metadata for the distribution.
let requires_dist = {
// If the metadata is already in the index, return it.
if let Some(metadata) = self.index.get_metadata(&dist.package_id()) {
metadata.requires_dist.clone()
} else {
// Run the PEP 517 build process to extract metadata from the source distribution.
let (metadata, precise) = self
.database
.get_or_build_wheel_metadata(&dist)
.await
.with_context(|| match &dist {
Dist::Built(built) => format!("Failed to download: {built}"),
Dist::Source(source) => format!("Failed to download and build: {source}"),
})?;
// Insert the metadata into the index.
self.index
.insert_metadata(dist.package_id(), metadata.clone());
// Insert the redirect into the index.
if let Some(precise) = precise {
self.index.insert_redirect(CanonicalUrl::new(url), precise);
}
metadata.requires_dist
}
};
// Consider the dependencies to be "direct" if the requirement is a local source tree.
let direct = if let Dist::Source(source_dist) = &dist {
@ -146,7 +171,7 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
// Return the requirements from the metadata.
Ok(Some(RequestedRequirements::new(
requirement.extras,
metadata.requires_dist,
requires_dist,
direct,
)))
}

View file

@ -1,5 +1,6 @@
use cache_key::CanonicalUrl;
use dashmap::DashMap;
use std::sync::Arc;
use url::Url;
use distribution_types::PackageId;
@ -24,3 +25,30 @@ pub struct InMemoryIndex {
/// `git+https://github.com/pallets/flask.git@c2f65dd1cfff0672b902fd5b30815f0b4137214c`.
pub(crate) redirects: DashMap<CanonicalUrl, Url>,
}
impl InMemoryIndex {
/// Insert a [`VersionsResponse`] into the index.
pub fn insert_package(&self, package_name: PackageName, metadata: VersionsResponse) {
self.packages.done(package_name, metadata);
}
/// Insert a [`Metadata23`] into the index.
pub fn insert_metadata(&self, package_id: PackageId, metadata: Metadata23) {
self.distributions.done(package_id, metadata);
}
/// Insert a redirect from a source URL to a target URL.
pub fn insert_redirect(&self, source: CanonicalUrl, target: Url) {
self.redirects.insert(source, target);
}
/// 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>> {
self.distributions.get(package_id)
}
}

View file

@ -345,6 +345,7 @@ pub(crate) async fn pip_compile(
&editables,
&build_dispatch,
&client,
&top_level_index,
)
.with_reporter(ResolverReporter::from(printer))
.resolve(&markers)

View file

@ -546,6 +546,7 @@ async fn resolve(
&editables,
build_dispatch,
client,
index,
)
.with_reporter(ResolverReporter::from(printer))
.resolve(markers)

View file

@ -1584,8 +1584,8 @@ fn conflicting_transitive_url_dependency() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because flask==3.0.0 depends on werkzeug>=3.0.0 and only werkzeug<3.0.0
is available, we can conclude that flask==3.0.0 cannot be used.
Because only werkzeug<3.0.0 is available and flask==3.0.0 depends on
werkzeug>=3.0.0, we can conclude that flask==3.0.0 cannot be used.
And because you require flask==3.0.0, we can conclude that the
requirements are unsatisfiable.
"###