mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-27 14:15:49 +00:00
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:
parent
999d653404
commit
f2c9e88f3e
5 changed files with 68 additions and 13 deletions
|
|
@ -1,15 +1,17 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use cache_key::CanonicalUrl;
|
||||||
use futures::stream::FuturesUnordered;
|
use futures::stream::FuturesUnordered;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use distribution_types::{Dist, LocalEditable};
|
use distribution_types::{Dist, DistributionMetadata, LocalEditable};
|
||||||
use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl};
|
use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl};
|
||||||
use pypi_types::Metadata23;
|
use pypi_types::Metadata23;
|
||||||
use uv_client::RegistryClient;
|
use uv_client::RegistryClient;
|
||||||
use uv_distribution::{DistributionDatabase, Reporter};
|
use uv_distribution::{DistributionDatabase, Reporter};
|
||||||
|
use uv_resolver::InMemoryIndex;
|
||||||
use uv_types::{BuildContext, Constraints, Overrides, RequestedRequirements};
|
use uv_types::{BuildContext, Constraints, Overrides, RequestedRequirements};
|
||||||
|
|
||||||
/// A resolver for resolving lookahead requirements from direct URLs.
|
/// A resolver for resolving lookahead requirements from direct URLs.
|
||||||
|
|
@ -37,6 +39,8 @@ pub struct LookaheadResolver<'a, Context: BuildContext + Send + Sync> {
|
||||||
overrides: &'a Overrides,
|
overrides: &'a Overrides,
|
||||||
/// The editable requirements for the project.
|
/// The editable requirements for the project.
|
||||||
editables: &'a [(LocalEditable, Metadata23)],
|
editables: &'a [(LocalEditable, Metadata23)],
|
||||||
|
/// The in-memory index for resolving dependencies.
|
||||||
|
index: &'a InMemoryIndex,
|
||||||
/// The database for fetching and building distributions.
|
/// The database for fetching and building distributions.
|
||||||
database: DistributionDatabase<'a, Context>,
|
database: DistributionDatabase<'a, Context>,
|
||||||
}
|
}
|
||||||
|
|
@ -50,12 +54,14 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
|
||||||
editables: &'a [(LocalEditable, Metadata23)],
|
editables: &'a [(LocalEditable, Metadata23)],
|
||||||
context: &'a Context,
|
context: &'a Context,
|
||||||
client: &'a RegistryClient,
|
client: &'a RegistryClient,
|
||||||
|
index: &'a InMemoryIndex,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
requirements,
|
requirements,
|
||||||
constraints,
|
constraints,
|
||||||
overrides,
|
overrides,
|
||||||
editables,
|
editables,
|
||||||
|
index,
|
||||||
database: DistributionDatabase::new(client, context),
|
database: DistributionDatabase::new(client, context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,8 +132,14 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
|
||||||
// Convert to a buildable distribution.
|
// Convert to a buildable distribution.
|
||||||
let dist = Dist::from_url(requirement.name, url.clone())?;
|
let dist = Dist::from_url(requirement.name, url.clone())?;
|
||||||
|
|
||||||
|
// 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.
|
// Run the PEP 517 build process to extract metadata from the source distribution.
|
||||||
let (metadata, _precise) = self
|
let (metadata, precise) = self
|
||||||
.database
|
.database
|
||||||
.get_or_build_wheel_metadata(&dist)
|
.get_or_build_wheel_metadata(&dist)
|
||||||
.await
|
.await
|
||||||
|
|
@ -136,6 +148,19 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
|
||||||
Dist::Source(source) => format!("Failed to download and build: {source}"),
|
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.
|
// Consider the dependencies to be "direct" if the requirement is a local source tree.
|
||||||
let direct = if let Dist::Source(source_dist) = &dist {
|
let direct = if let Dist::Source(source_dist) = &dist {
|
||||||
source_dist.as_path().is_some_and(std::path::Path::is_dir)
|
source_dist.as_path().is_some_and(std::path::Path::is_dir)
|
||||||
|
|
@ -146,7 +171,7 @@ impl<'a, Context: BuildContext + Send + Sync> LookaheadResolver<'a, Context> {
|
||||||
// Return the requirements from the metadata.
|
// Return the requirements from the metadata.
|
||||||
Ok(Some(RequestedRequirements::new(
|
Ok(Some(RequestedRequirements::new(
|
||||||
requirement.extras,
|
requirement.extras,
|
||||||
metadata.requires_dist,
|
requires_dist,
|
||||||
direct,
|
direct,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use cache_key::CanonicalUrl;
|
use cache_key::CanonicalUrl;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use distribution_types::PackageId;
|
use distribution_types::PackageId;
|
||||||
|
|
@ -24,3 +25,30 @@ pub struct InMemoryIndex {
|
||||||
/// `git+https://github.com/pallets/flask.git@c2f65dd1cfff0672b902fd5b30815f0b4137214c`.
|
/// `git+https://github.com/pallets/flask.git@c2f65dd1cfff0672b902fd5b30815f0b4137214c`.
|
||||||
pub(crate) redirects: DashMap<CanonicalUrl, Url>,
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,7 @@ pub(crate) async fn pip_compile(
|
||||||
&editables,
|
&editables,
|
||||||
&build_dispatch,
|
&build_dispatch,
|
||||||
&client,
|
&client,
|
||||||
|
&top_level_index,
|
||||||
)
|
)
|
||||||
.with_reporter(ResolverReporter::from(printer))
|
.with_reporter(ResolverReporter::from(printer))
|
||||||
.resolve(&markers)
|
.resolve(&markers)
|
||||||
|
|
|
||||||
|
|
@ -546,6 +546,7 @@ async fn resolve(
|
||||||
&editables,
|
&editables,
|
||||||
build_dispatch,
|
build_dispatch,
|
||||||
client,
|
client,
|
||||||
|
index,
|
||||||
)
|
)
|
||||||
.with_reporter(ResolverReporter::from(printer))
|
.with_reporter(ResolverReporter::from(printer))
|
||||||
.resolve(markers)
|
.resolve(markers)
|
||||||
|
|
|
||||||
|
|
@ -1584,8 +1584,8 @@ fn conflicting_transitive_url_dependency() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because flask==3.0.0 depends on werkzeug>=3.0.0 and only werkzeug<3.0.0
|
╰─▶ Because only werkzeug<3.0.0 is available and flask==3.0.0 depends on
|
||||||
is available, we can conclude that flask==3.0.0 cannot be used.
|
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
|
And because you require flask==3.0.0, we can conclude that the
|
||||||
requirements are unsatisfiable.
|
requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue