From 210c3a7bf8abd89451dd92c1a713964a0d3019c9 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 13 Dec 2024 14:51:06 -0600 Subject: [PATCH] Fix redundant enumeration of all package versions in some resolver errors --- crates/uv-resolver/src/error.rs | 57 +++++++++++++++++++++++++++++++ crates/uv/tests/it/pip_compile.rs | 11 +----- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index b371608ce697..477a9c58c48b 100644 --- a/crates/uv-resolver/src/error.rs +++ b/crates/uv-resolver/src/error.rs @@ -336,6 +336,7 @@ impl std::fmt::Display for NoSolutionError { } collapse_unavailable_versions(&mut tree); + collapse_redundant_no_versions(&mut tree); collapse_redundant_depends_on_no_versions(&mut tree); if should_display_tree { @@ -422,6 +423,62 @@ fn display_tree_inner( } } +fn collapse_redundant_no_versions( + tree: &mut DerivationTree, UnavailableReason>, +) { + match tree { + DerivationTree::External(_) => {} + DerivationTree::Derived(derived) => { + match ( + Arc::make_mut(&mut derived.cause1), + Arc::make_mut(&mut derived.cause2), + ) { + // If we have a node for a package with no versions... + ( + DerivationTree::External(External::NoVersions(package, versions)), + ref mut other, + ) + | ( + ref mut other, + DerivationTree::External(External::NoVersions(package, versions)), + ) => { + // First, always recursively visit the other side of the tree + collapse_redundant_no_versions(other); + + let DerivationTree::Derived(derived) = other else { + return; + }; + + // If the range in the conclusion (terms) matches the range of no versions, + // then we'll drop this node + let Some(Term::Positive(term)) = derived.terms.get(package) else { + return; + }; + let versions = versions.complement(); + + // If we're disqualifying a single version, this is important to retain, e.g, + // for `only foo==1.0.0 is available` + if versions.as_singleton().is_some() { + return; + } + + if *term != versions { + return; + } + + // Replace this node with the other tree + *tree = other.clone(); + } + // If not, just recurse + _ => { + collapse_redundant_no_versions(Arc::make_mut(&mut derived.cause1)); + collapse_redundant_no_versions(Arc::make_mut(&mut derived.cause2)); + } + } + } + } +} + /// Given a [`DerivationTree`], collapse any `NoVersion` incompatibilities for workspace members /// to avoid saying things like "only ==0.1.0 is available". fn collapse_no_versions_of_workspace_members( diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 988117212f4b..bf212332e683 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -13107,16 +13107,7 @@ fn compile_enumerate_no_versions() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: ╰─▶ Because the current Python version (3.10.15) does not satisfy Python>=3.11,<4.0 and all versions of rooster-blue depend on Python>=3.11,<4.0, we can conclude that all versions of rooster-blue cannot be used. - And because only the following versions of rooster-blue are available: - rooster-blue==0.0.1 - rooster-blue==0.0.2 - rooster-blue==0.0.3 - rooster-blue==0.0.4 - rooster-blue==0.0.5 - rooster-blue==0.0.6 - rooster-blue==0.0.7 - rooster-blue==0.0.8 - and you require rooster-blue, we can conclude that your requirements are unsatisfiable. + And because you require rooster-blue, we can conclude that your requirements are unsatisfiable. "###); Ok(())