diff --git a/crates/uv-distribution-types/src/derivation.rs b/crates/uv-distribution-types/src/derivation.rs index 6084a9f0b056..5e974b52d7fe 100644 --- a/crates/uv-distribution-types/src/derivation.rs +++ b/crates/uv-distribution-types/src/derivation.rs @@ -1,4 +1,4 @@ -use uv_normalize::PackageName; +use uv_normalize::{ExtraName, GroupName, PackageName}; use uv_pep440::Version; use version_ranges::Ranges; @@ -65,6 +65,10 @@ impl IntoIterator for DerivationChain { pub struct DerivationStep { /// The name of the package. pub name: PackageName, + /// The enabled extra of the package, if any. + pub extra: Option, + /// The enabled dependency group of the package, if any. + pub group: Option, /// The version of the package. pub version: Version, /// The constraints applied to the subsequent package in the chain. @@ -73,9 +77,17 @@ pub struct DerivationStep { impl DerivationStep { /// Create a [`DerivationStep`] from a package name and version. - pub fn new(name: PackageName, version: Version, range: Ranges) -> Self { + pub fn new( + name: PackageName, + extra: Option, + group: Option, + version: Version, + range: Ranges, + ) -> Self { Self { name, + extra, + group, version, range, } @@ -84,6 +96,12 @@ impl DerivationStep { impl std::fmt::Display for DerivationStep { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}=={}", self.name, self.version) + if let Some(extra) = &self.extra { + write!(f, "{}[{}]=={}", self.name, extra, self.version) + } else if let Some(group) = &self.group { + write!(f, "{}:{}=={}", self.name, group, self.version) + } else { + write!(f, "{}=={}", self.name, self.version) + } } } diff --git a/crates/uv-resolver/src/pubgrub/package.rs b/crates/uv-resolver/src/pubgrub/package.rs index 9a6f4e4c2e49..c802dc020c92 100644 --- a/crates/uv-resolver/src/pubgrub/package.rs +++ b/crates/uv-resolver/src/pubgrub/package.rs @@ -186,6 +186,24 @@ impl PubGrubPackage { } } + /// Returns the group name associated with this PubGrub package, if it has + /// one. + pub(crate) fn group(&self) -> Option<&GroupName> { + match &**self { + // A root can never be a dependency of another package, and a `Python` pubgrub + // package is never returned by `get_dependencies`. So these cases never occur. + PubGrubPackageInner::Root(_) + | PubGrubPackageInner::Python(_) + | PubGrubPackageInner::Package { dev: None, .. } + | PubGrubPackageInner::Extra { .. } + | PubGrubPackageInner::Marker { .. } => None, + PubGrubPackageInner::Package { + dev: Some(ref dev), .. + } + | PubGrubPackageInner::Dev { ref dev, .. } => Some(dev), + } + } + /// Extracts a possible conflicting group from this package. /// /// If this package can't possibly be classified as a conflicting group, diff --git a/crates/uv-resolver/src/resolver/derivation.rs b/crates/uv-resolver/src/resolver/derivation.rs index bd1f4c684563..905037134743 100644 --- a/crates/uv-resolver/src/resolver/derivation.rs +++ b/crates/uv-resolver/src/resolver/derivation.rs @@ -1,11 +1,12 @@ use std::collections::VecDeque; +use petgraph::visit::EdgeRef; use petgraph::Direction; use pubgrub::{Kind, Range, SelectedDependencies, State}; use rustc_hash::FxHashSet; use uv_distribution_types::{ - DerivationChain, DerivationStep, DistRef, Name, Node, Resolution, ResolvedDist, + DerivationChain, DerivationStep, DistRef, Edge, Name, Node, Resolution, ResolvedDist, }; use uv_pep440::Version; @@ -40,11 +41,11 @@ impl DerivationChainBuilder { // Perform a BFS to find the shortest path to the root. let mut queue = VecDeque::new(); - queue.push_back((target, Vec::new())); + queue.push_back((target, None, None, Vec::new())); // TODO(charlie): Consider respecting markers here. let mut seen = FxHashSet::default(); - while let Some((node, mut path)) = queue.pop_front() { + while let Some((node, extra, group, mut path)) = queue.pop_front() { if !seen.insert(node) { continue; } @@ -55,16 +56,25 @@ impl DerivationChainBuilder { return Some(DerivationChain::from_iter(path)); } Node::Dist { dist, .. } => { - path.push(DerivationStep::new( - dist.name().clone(), - dist.version().clone(), - Range::empty(), - )); - for neighbor in resolution - .graph() - .neighbors_directed(node, Direction::Incoming) - { - queue.push_back((neighbor, path.clone())); + for edge in resolution.graph().edges_directed(node, Direction::Incoming) { + let mut path = path.clone(); + path.push(DerivationStep::new( + dist.name().clone(), + extra.clone(), + group.clone(), + dist.version().clone(), + Range::empty(), + )); + let target = edge.source(); + let extra = match edge.weight() { + Edge::Optional(extra, ..) => Some(extra.clone()), + _ => None, + }; + let group = match edge.weight() { + Edge::Dev(group, ..) => Some(group.clone()), + _ => None, + }; + queue.push_back((target, extra, group, path)); } } } @@ -109,6 +119,8 @@ impl DerivationChainBuilder { // Add to the current path. path.push(DerivationStep::new( name.clone(), + p1.extra().cloned(), + p1.group().cloned(), version.clone(), v2.clone(), )); diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 4c5b2828c4f3..44cf304c6933 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -19337,7 +19337,7 @@ fn lock_derivation_chain_extra() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? - help: `wsgiref` was included because `project==0.1.0` depends on `wsgiref (>=0.1)` + help: `wsgiref` was included because `project[wsgi]==0.1.0` depends on `wsgiref (>=0.1)` "###); Ok(()) @@ -19397,7 +19397,7 @@ fn lock_derivation_chain_group() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? - help: `wsgiref` was included because `project==0.1.0` depends on `wsgiref (*)` + help: `wsgiref` was included because `project:wsgi==0.1.0` depends on `wsgiref (*)` "###); Ok(()) diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index 11e593f6192e..f1f2ab72961e 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -4398,7 +4398,7 @@ fn sync_derivation_chain_extra() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? - help: `wsgiref` was included because `project==0.1.0` depends on `wsgiref` + help: `wsgiref` was included because `project[wsgi]==0.1.0` depends on `wsgiref` "###); Ok(()) @@ -4464,7 +4464,7 @@ fn sync_derivation_chain_group() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? - help: `wsgiref` was included because `project==0.1.0` depends on `wsgiref` + help: `wsgiref` was included because `project:wsgi==0.1.0` depends on `wsgiref` "###); Ok(())