Skip to content

Commit 91396f9

Browse files
committed
Collapse unavailable packages in resolver errors
1 parent d7abe82 commit 91396f9

File tree

3 files changed

+164
-98
lines changed

3 files changed

+164
-98
lines changed

crates/uv-resolver/src/error.rs

+116-11
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,24 @@ impl std::fmt::Display for NoSolutionError {
222222

223223
// Transform the error tree for reporting
224224
let mut tree = self.error.clone();
225+
let should_display_tree = std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some()
226+
|| tracing::enabled!(tracing::Level::TRACE);
227+
228+
if should_display_tree {
229+
display_tree(&tree, "Resolver derivation tree before reduction");
230+
}
231+
225232
collapse_no_versions_of_workspace_members(&mut tree, &self.workspace_members);
226233

227234
if self.workspace_members.len() == 1 {
228235
let project = self.workspace_members.iter().next().unwrap();
229236
drop_root_dependency_on_project(&mut tree, project);
230237
}
231238

232-
// Display the tree if enabled
233-
if std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some()
234-
|| tracing::enabled!(tracing::Level::TRACE)
235-
{
236-
display_tree(&tree);
239+
collapse_unavailable_versions(&mut tree);
240+
241+
if should_display_tree {
242+
display_tree(&tree, "Resolver derivation tree after reduction");
237243
}
238244

239245
let report = DefaultStringReporter::report_with_formatter(&tree, &formatter);
@@ -257,15 +263,18 @@ impl std::fmt::Display for NoSolutionError {
257263
}
258264

259265
#[allow(clippy::print_stderr)]
260-
fn display_tree(error: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>) {
266+
fn display_tree(
267+
error: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
268+
name: &str,
269+
) {
261270
let mut lines = Vec::new();
262271
display_tree_inner(error, &mut lines, 0);
263272
lines.reverse();
264273

265274
if std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some() {
266-
eprintln!("Resolver error derivation tree\n{}", lines.join("\n"));
275+
eprintln!("{name}\n{}", lines.join("\n"));
267276
} else {
268-
trace!("Resolver error derivation tree\n{}", lines.join("\n"));
277+
trace!("{name}\n{}", lines.join("\n"));
269278
}
270279
}
271280

@@ -355,6 +364,105 @@ fn collapse_no_versions_of_workspace_members(
355364
}
356365
}
357366

367+
/// Given a [`DerivationTree`], collapse incompatibilities for versions of a package that are
368+
/// unavailable for the same reason to avoid repeating the same message for every unavailable
369+
/// version.
370+
fn collapse_unavailable_versions(
371+
tree: &mut DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
372+
) {
373+
match tree {
374+
DerivationTree::External(_) => {}
375+
DerivationTree::Derived(derived) => {
376+
match (
377+
Arc::make_mut(&mut derived.cause1),
378+
Arc::make_mut(&mut derived.cause2),
379+
) {
380+
// If we have a node for unavailable package versions
381+
(
382+
DerivationTree::External(External::Custom(package, versions, reason)),
383+
ref mut other,
384+
)
385+
| (
386+
ref mut other,
387+
DerivationTree::External(External::Custom(package, versions, reason)),
388+
) => {
389+
// First, recursively collapse the other side of the tree
390+
collapse_unavailable_versions(other);
391+
392+
// If it's not a derived tree, nothing to do.
393+
let DerivationTree::Derived(Derived {
394+
terms,
395+
shared_id,
396+
cause1,
397+
cause2,
398+
}) = other
399+
else {
400+
return;
401+
};
402+
403+
// If the other tree has an unavailable package...
404+
match (&**cause1, &**cause2) {
405+
// Note the following cases are the same, but we need two matches to retain
406+
// the ordering of the causes
407+
(
408+
_,
409+
DerivationTree::External(External::Custom(
410+
other_package,
411+
other_versions,
412+
other_reason,
413+
)),
414+
) => {
415+
// And the package and reason are the same...
416+
if package == other_package && reason == other_reason {
417+
// Collapse both into a new node, with a union of their ranges
418+
*tree = DerivationTree::Derived(Derived {
419+
terms: terms.clone(),
420+
shared_id: *shared_id,
421+
cause1: cause1.clone(),
422+
cause2: Arc::new(DerivationTree::External(External::Custom(
423+
package.clone(),
424+
versions.union(other_versions),
425+
reason.clone(),
426+
))),
427+
});
428+
}
429+
}
430+
(
431+
DerivationTree::External(External::Custom(
432+
other_package,
433+
other_versions,
434+
other_reason,
435+
)),
436+
_,
437+
) => {
438+
// And the package and reason are the same...
439+
if package == other_package && reason == other_reason {
440+
// Collapse both into a new node, with a union of their ranges
441+
*tree = DerivationTree::Derived(Derived {
442+
terms: terms.clone(),
443+
shared_id: *shared_id,
444+
cause1: Arc::new(DerivationTree::External(External::Custom(
445+
package.clone(),
446+
versions.union(other_versions),
447+
reason.clone(),
448+
))),
449+
cause2: cause2.clone(),
450+
});
451+
}
452+
}
453+
_ => {}
454+
}
455+
}
456+
// If not, just recurse
457+
_ => {
458+
collapse_unavailable_versions(Arc::make_mut(&mut derived.cause1));
459+
collapse_unavailable_versions(Arc::make_mut(&mut derived.cause2));
460+
}
461+
}
462+
}
463+
}
464+
}
465+
358466
/// Given a [`DerivationTree`], drop dependency incompatibilities from the root
359467
/// to the project.
360468
///
@@ -395,9 +503,6 @@ fn drop_root_dependency_on_project(
395503
return;
396504
}
397505

398-
// Recursively collapse the other side of the tree
399-
drop_root_dependency_on_project(other, project);
400-
401506
// Then, replace this node with the other tree
402507
*tree = other.clone();
403508
}

crates/uv/tests/cache_prune.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,15 @@ fn prune_unzipped() -> Result<()> {
241241
╰─▶ Because only the following versions of iniconfig are available:
242242
iniconfig<=0.1
243243
iniconfig>=1.0.0
244-
and iniconfig==0.1 network connectivity is disabled, but the metadata wasn't found in the cache, we can conclude that iniconfig<1.0.0 cannot be used.
245-
And because iniconfig==1.0.0 network connectivity is disabled, but the metadata wasn't found in the cache and iniconfig==1.0.1 network connectivity is disabled, but the metadata wasn't found in the cache, we can conclude that iniconfig<1.1.0 cannot be used.
246-
And because iniconfig==1.1.0 network connectivity is disabled, but the metadata wasn't found in the cache and iniconfig==1.1.1 network connectivity is disabled, but the metadata wasn't found in the cache, we can conclude that iniconfig<2.0.0 cannot be used.
247-
And because iniconfig==2.0.0 network connectivity is disabled, but the metadata wasn't found in the cache and you require iniconfig, we can conclude that your requirements are unsatisfiable.
244+
and any of:
245+
iniconfig==0.1
246+
iniconfig==1.0.0
247+
iniconfig==1.0.1
248+
iniconfig==1.1.0
249+
iniconfig==1.1.1
250+
iniconfig==2.0.0
251+
network connectivity is disabled, but the metadata wasn't found in the cache, we can conclude that iniconfig<1.0.0 cannot be used.
252+
And because you require iniconfig, we can conclude that your requirements are unsatisfiable.
248253
249254
hint: Pre-releases are available for iniconfig in the requested range (e.g., 0.2.dev0), but pre-releases weren't enabled (try: `--prerelease=allow`)
250255

crates/uv/tests/pip_install.rs

+39-83
Original file line numberDiff line numberDiff line change
@@ -1925,95 +1925,51 @@ fn install_only_binary_all_and_no_binary_all() {
19251925
anyio>=3.0.0,<=3.6.2
19261926
anyio>=3.7.0,<=3.7.1
19271927
anyio>=4.0.0
1928-
and anyio==1.0.0 has no usable wheels and building from source is disabled, we can conclude that any of:
1928+
and any of:
1929+
anyio==1.0.0
1930+
anyio==1.1.0
1931+
anyio==1.2.0
1932+
anyio==1.2.1
1933+
anyio==1.2.2
1934+
anyio==1.2.3
1935+
anyio==1.3.0
1936+
anyio==1.3.1
1937+
anyio==1.4.0
1938+
anyio==2.0.0
1939+
anyio==2.0.1
1940+
anyio==2.0.2
1941+
anyio==2.1.0
1942+
anyio==2.2.0
1943+
anyio==3.0.0
1944+
anyio==3.0.1
1945+
anyio==3.1.0
1946+
anyio==3.2.0
1947+
anyio==3.2.1
1948+
anyio==3.3.0
1949+
anyio==3.3.1
1950+
anyio==3.3.2
1951+
anyio==3.3.3
1952+
anyio==3.3.4
1953+
anyio==3.4.0
1954+
anyio==3.5.0
1955+
anyio==3.6.0
1956+
anyio==3.6.1
1957+
anyio==3.6.2
1958+
anyio==3.7.0
1959+
anyio==3.7.1
1960+
anyio==4.0.0
1961+
anyio==4.1.0
1962+
anyio==4.2.0
1963+
anyio==4.3.0
1964+
anyio==4.4.0
1965+
has no usable wheels and building from source is disabled, we can conclude that any of:
19291966
anyio<1.1.0
19301967
anyio>1.4.0,<2.0.0
19311968
anyio>2.2.0,<3.0.0
19321969
anyio>3.6.2,<3.7.0
19331970
anyio>3.7.1,<4.0.0
19341971
cannot be used.
1935-
And because anyio==1.1.0 has no usable wheels and building from source is disabled and anyio==1.2.0 has no usable wheels and building from source is disabled, we can conclude that any of:
1936-
anyio<1.2.1
1937-
anyio>1.4.0,<2.0.0
1938-
anyio>2.2.0,<3.0.0
1939-
anyio>3.6.2,<3.7.0
1940-
anyio>3.7.1,<4.0.0
1941-
cannot be used.
1942-
And because anyio==1.2.1 has no usable wheels and building from source is disabled and anyio==1.2.2 has no usable wheels and building from source is disabled, we can conclude that any of:
1943-
anyio<1.2.3
1944-
anyio>1.4.0,<2.0.0
1945-
anyio>2.2.0,<3.0.0
1946-
anyio>3.6.2,<3.7.0
1947-
anyio>3.7.1,<4.0.0
1948-
cannot be used.
1949-
And because anyio==1.2.3 has no usable wheels and building from source is disabled and anyio==1.3.0 has no usable wheels and building from source is disabled, we can conclude that any of:
1950-
anyio<1.3.1
1951-
anyio>1.4.0,<2.0.0
1952-
anyio>2.2.0,<3.0.0
1953-
anyio>3.6.2,<3.7.0
1954-
anyio>3.7.1,<4.0.0
1955-
cannot be used.
1956-
And because anyio==1.3.1 has no usable wheels and building from source is disabled and anyio==1.4.0 has no usable wheels and building from source is disabled, we can conclude that any of:
1957-
anyio<2.0.0
1958-
anyio>2.2.0,<3.0.0
1959-
anyio>3.6.2,<3.7.0
1960-
anyio>3.7.1,<4.0.0
1961-
cannot be used.
1962-
And because anyio==2.0.0 has no usable wheels and building from source is disabled and anyio==2.0.1 has no usable wheels and building from source is disabled, we can conclude that any of:
1963-
anyio<2.0.2
1964-
anyio>2.2.0,<3.0.0
1965-
anyio>3.6.2,<3.7.0
1966-
anyio>3.7.1,<4.0.0
1967-
cannot be used.
1968-
And because anyio==2.0.2 has no usable wheels and building from source is disabled and anyio==2.1.0 has no usable wheels and building from source is disabled, we can conclude that any of:
1969-
anyio<2.2.0
1970-
anyio>2.2.0,<3.0.0
1971-
anyio>3.6.2,<3.7.0
1972-
anyio>3.7.1,<4.0.0
1973-
cannot be used.
1974-
And because anyio==2.2.0 has no usable wheels and building from source is disabled and anyio==3.0.0 has no usable wheels and building from source is disabled, we can conclude that any of:
1975-
anyio<3.0.1
1976-
anyio>3.6.2,<3.7.0
1977-
anyio>3.7.1,<4.0.0
1978-
cannot be used.
1979-
And because anyio==3.0.1 has no usable wheels and building from source is disabled and anyio==3.1.0 has no usable wheels and building from source is disabled, we can conclude that any of:
1980-
anyio<3.2.0
1981-
anyio>3.6.2,<3.7.0
1982-
anyio>3.7.1,<4.0.0
1983-
cannot be used.
1984-
And because anyio==3.2.0 has no usable wheels and building from source is disabled and anyio==3.2.1 has no usable wheels and building from source is disabled, we can conclude that any of:
1985-
anyio<3.3.0
1986-
anyio>3.6.2,<3.7.0
1987-
anyio>3.7.1,<4.0.0
1988-
cannot be used.
1989-
And because anyio==3.3.0 has no usable wheels and building from source is disabled and anyio==3.3.1 has no usable wheels and building from source is disabled, we can conclude that any of:
1990-
anyio<3.3.2
1991-
anyio>3.6.2,<3.7.0
1992-
anyio>3.7.1,<4.0.0
1993-
cannot be used.
1994-
And because anyio==3.3.2 has no usable wheels and building from source is disabled and anyio==3.3.3 has no usable wheels and building from source is disabled, we can conclude that any of:
1995-
anyio<3.3.4
1996-
anyio>3.6.2,<3.7.0
1997-
anyio>3.7.1,<4.0.0
1998-
cannot be used.
1999-
And because anyio==3.3.4 has no usable wheels and building from source is disabled and anyio==3.4.0 has no usable wheels and building from source is disabled, we can conclude that any of:
2000-
anyio<3.5.0
2001-
anyio>3.6.2,<3.7.0
2002-
anyio>3.7.1,<4.0.0
2003-
cannot be used.
2004-
And because anyio==3.5.0 has no usable wheels and building from source is disabled and anyio==3.6.0 has no usable wheels and building from source is disabled, we can conclude that any of:
2005-
anyio<3.6.1
2006-
anyio>3.6.2,<3.7.0
2007-
anyio>3.7.1,<4.0.0
2008-
cannot be used.
2009-
And because anyio==3.6.1 has no usable wheels and building from source is disabled and anyio==3.6.2 has no usable wheels and building from source is disabled, we can conclude that any of:
2010-
anyio<3.7.0
2011-
anyio>3.7.1,<4.0.0
2012-
cannot be used.
2013-
And because anyio==3.7.0 has no usable wheels and building from source is disabled and anyio==3.7.1 has no usable wheels and building from source is disabled, we can conclude that anyio<4.0.0 cannot be used.
2014-
And because anyio==4.0.0 has no usable wheels and building from source is disabled and anyio==4.1.0 has no usable wheels and building from source is disabled, we can conclude that anyio<4.2.0 cannot be used.
2015-
And because anyio==4.2.0 has no usable wheels and building from source is disabled and anyio==4.3.0 has no usable wheels and building from source is disabled, we can conclude that anyio<4.4.0 cannot be used.
2016-
And because anyio==4.4.0 has no usable wheels and building from source is disabled and you require anyio, we can conclude that your requirements are unsatisfiable.
1972+
And because you require anyio, we can conclude that your requirements are unsatisfiable.
20171973
20181974
hint: Pre-releases are available for anyio in the requested range (e.g., 4.0.0rc1), but pre-releases weren't enabled (try: `--prerelease=allow`)
20191975
"###

0 commit comments

Comments
 (0)