diff --git a/crates/uv-resolver/src/pubgrub/dependencies.rs b/crates/uv-resolver/src/pubgrub/dependencies.rs index fced9adb5..bfc774b0b 100644 --- a/crates/uv-resolver/src/pubgrub/dependencies.rs +++ b/crates/uv-resolver/src/pubgrub/dependencies.rs @@ -15,76 +15,59 @@ use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner}; use crate::resolver::{Locals, Urls}; use crate::{PubGrubSpecifier, ResolveError}; -#[derive(Debug)] -pub struct PubGrubDependencies(Vec<(PubGrubPackage, Range)>); +#[derive(Clone, Debug)] +pub(crate) struct PubGrubDependency { + pub(crate) package: PubGrubPackage, + pub(crate) version: Range, +} -impl PubGrubDependencies { - /// Generate a set of PubGrub dependencies from a set of requirements. - #[allow(clippy::too_many_arguments)] - pub(crate) fn from_requirements( - flattened_requirements: &[&Requirement], - source_name: Option<&PackageName>, - urls: &Urls, - locals: &Locals, - git: &GitResolver, - ) -> Result { - let mut dependencies = Vec::new(); - for requirement in flattened_requirements { - // Add the package, plus any extra variants. - for result in std::iter::once(PubGrubRequirement::from_requirement( - requirement, - None, - urls, - locals, - git, - )) - .chain(requirement.extras.clone().into_iter().map(|extra| { - PubGrubRequirement::from_requirement(requirement, Some(extra), urls, locals, git) - })) { - let PubGrubRequirement { package, version } = result?; +impl PubGrubDependency { + pub(crate) fn from_requirement<'a>( + requirement: &'a Requirement, + source_name: Option<&'a PackageName>, + urls: &'a Urls, + locals: &'a Locals, + git: &'a GitResolver, + ) -> impl Iterator> + 'a { + // Add the package, plus any extra variants. + std::iter::once(None) + .chain(requirement.extras.clone().into_iter().map(Some)) + .map(|extra| { + PubGrubRequirement::from_requirement(requirement, extra, urls, locals, git) + }) + .filter_map_ok(move |pubgrub_requirement| { + let PubGrubRequirement { package, version } = pubgrub_requirement; match &*package { PubGrubPackageInner::Package { name, .. } => { // Detect self-dependencies. if source_name.is_some_and(|source_name| source_name == name) { warn!("{name} has a dependency on itself"); - continue; + return None; } - dependencies.push((package.clone(), version.clone())); - } - PubGrubPackageInner::Marker { .. } => { - dependencies.push((package.clone(), version.clone())); + Some(PubGrubDependency { + package: package.clone(), + version: version.clone(), + }) } + PubGrubPackageInner::Marker { .. } => Some(PubGrubDependency { + package: package.clone(), + version: version.clone(), + }), PubGrubPackageInner::Extra { name, .. } => { debug_assert!( !source_name.is_some_and(|source_name| source_name == name), "extras not flattened for {name}" ); - dependencies.push((package.clone(), version.clone())); + Some(PubGrubDependency { + package: package.clone(), + version: version.clone(), + }) } - _ => {} + _ => None, } - } - } - Ok(Self(dependencies)) - } - - /// Add a [`PubGrubPackage`] and [`PubGrubVersion`] range into the dependencies. - pub(crate) fn push(&mut self, package: PubGrubPackage, version: Range) { - self.0.push((package, version)); - } - - /// Iterate over the dependencies. - pub(crate) fn iter(&self) -> impl Iterator)> { - self.0.iter() - } -} - -/// Convert a [`PubGrubDependencies`] to a [`DependencyConstraints`]. -impl From for Vec<(PubGrubPackage, Range)> { - fn from(dependencies: PubGrubDependencies) -> Self { - dependencies.0 + }) } } diff --git a/crates/uv-resolver/src/pubgrub/mod.rs b/crates/uv-resolver/src/pubgrub/mod.rs index b6d48cea9..587de7440 100644 --- a/crates/uv-resolver/src/pubgrub/mod.rs +++ b/crates/uv-resolver/src/pubgrub/mod.rs @@ -1,4 +1,4 @@ -pub(crate) use crate::pubgrub::dependencies::PubGrubDependencies; +pub(crate) use crate::pubgrub::dependencies::PubGrubDependency; pub(crate) use crate::pubgrub::distribution::PubGrubDistribution; pub(crate) use crate::pubgrub::package::{PubGrubPackage, PubGrubPackageInner, PubGrubPython}; pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority}; diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 8e8a5aa39..45dad72a0 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -44,8 +44,8 @@ use crate::manifest::Manifest; use crate::pins::FilePins; use crate::preferences::Preferences; use crate::pubgrub::{ - PubGrubDependencies, PubGrubDistribution, PubGrubPackage, PubGrubPackageInner, - PubGrubPriorities, PubGrubPython, PubGrubSpecifier, + PubGrubDependency, PubGrubDistribution, PubGrubPackage, PubGrubPackageInner, PubGrubPriorities, + PubGrubPython, PubGrubSpecifier, }; use crate::python_requirement::PythonRequirement; use crate::resolution::ResolutionGraph; @@ -943,13 +943,18 @@ impl ResolverState, _>>()?; (dependencies, None) } @@ -1075,13 +1080,18 @@ impl ResolverState, _>>()?; // If a package has metadata for an enabled dependency group, // add a dependency from it to the same package with the group // enabled. @@ -1090,15 +1100,15 @@ impl ResolverState ResolverState ResolverState ResolverState)>, + dependencies: Vec, prefetcher: &BatchPrefetcher, ) -> Result<(), ResolveError> { if dependencies .iter() - .any(|(dependency, _)| dependency == &self.next) + .any(|dependency| dependency.package == self.next) { if enabled!(Level::DEBUG) { prefetcher.log_tried_versions(); @@ -1667,7 +1676,10 @@ impl SolveState { self.pubgrub.add_package_version_dependencies( self.next.clone(), version.clone(), - dependencies, + dependencies.into_iter().map(|dependency| { + let PubGrubDependency { package, version } = dependency; + (package, version) + }), ); Ok(()) } @@ -1994,7 +2006,6 @@ enum Response { /// This effectively distills the dependency metadata of a package down into /// its pubgrub specific constituent parts: each dependency package has a range /// of possible versions. -#[derive(Clone)] enum Dependencies { /// Package dependencies are not available. Unavailable(UnavailableVersion), @@ -2003,7 +2014,7 @@ enum Dependencies { /// Note that in universal mode, it is possible and allowed for multiple /// `PubGrubPackage` values in this list to have the same package name. /// These conflicts are resolved via `Dependencies::fork`. - Available(Vec<(PubGrubPackage, Range)>), + Available(Vec), } impl Dependencies { @@ -2022,13 +2033,16 @@ impl Dependencies { }; let mut by_name: FxHashMap<&PackageName, PossibleForks> = FxHashMap::default(); - for (index, (ref pkg, _)) in deps.iter().enumerate() { + for (index, dependency) in deps.iter().enumerate() { // A root can never be a dependency of another package, // and a `Python` pubgrub package is never returned by // `get_dependencies`. So a pubgrub package always has a // name in this context. - let name = pkg.name().expect("dependency always has a name"); - let marker = pkg.marker(); + let name = dependency + .package + .name() + .expect("dependency always has a name"); + let marker = dependency.package.marker(); let Some(marker) = marker else { // When no marker is found, it implies there is a dependency on // this package that is unconditional with respect to marker @@ -2157,7 +2171,7 @@ enum ForkedDependencies { /// No forking occurred. /// /// This is the same as `Dependencies::Available`. - Unforked(Vec<(PubGrubPackage, Range)>), + Unforked(Vec), /// Forked containers for all available package versions. /// /// Note that there is always at least two forks. If there would @@ -2189,7 +2203,7 @@ struct Fork { /// they should use `add_forked_package` or `add_nonfork_package`. Namely, /// it should be impossible for a package with a marker expression that is /// disjoint from the marker expression on this fork to be added. - dependencies: Vec<(PubGrubPackage, Range)>, + dependencies: Vec, /// The markers that provoked this fork. /// /// So in the example above, the `a<2` fork would have @@ -2215,15 +2229,16 @@ impl Fork { /// can cause the resolver to explore otherwise impossible resolutions, /// and also run into conflicts (and thus a failed resolution) that don't /// actually exist. - fn add_forked_package(&mut self, (pkg, range): (PubGrubPackage, Range)) { + fn add_forked_package(&mut self, dependency: PubGrubDependency) { // OK because a package without a marker is unconditional and // thus can never provoke a fork. - let marker = pkg + let marker = dependency + .package .marker() .cloned() .expect("forked package always has a marker"); self.remove_disjoint_packages(&marker); - self.dependencies.push((pkg, range)); + self.dependencies.push(dependency); // Each marker expression in a single fork is, // by construction, overlapping with at least // one other marker expression in this fork. @@ -2239,14 +2254,15 @@ impl Fork { /// /// It is only added if the markers on the given package are not disjoint /// with this fork's markers. - fn add_nonfork_package(&mut self, (pkg, range): (PubGrubPackage, Range)) { + fn add_nonfork_package(&mut self, dependency: PubGrubDependency) { use crate::marker::is_disjoint; - if pkg + if dependency + .package .marker() .map_or(true, |marker| !is_disjoint(marker, &self.markers)) { - self.dependencies.push((pkg, range)); + self.dependencies.push(dependency); } } @@ -2255,8 +2271,10 @@ impl Fork { fn remove_disjoint_packages(&mut self, fork_marker: &MarkerTree) { use crate::marker::is_disjoint; - self.dependencies.retain(|(pkg, _)| { - pkg.marker() + self.dependencies.retain(|dependency| { + dependency + .package + .marker() .map_or(true, |pkg_marker| !is_disjoint(pkg_marker, fork_marker)) }); }