mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-02 12:59:45 +00:00
Collapse unavailable packages in resolver errors (#6154)
Uses my expanding tree reduction knowledge from #6092 to improve the long-standing issue of verbose messages for unavailable packages. Implements https://github.com/pubgrub-rs/pubgrub/issues/232, but post-resolution instead of during resolution. Partially addresses https://github.com/astral-sh/uv/issues/5046 Closes https://github.com/astral-sh/uv/issues/2519
This commit is contained in:
parent
d643e92d66
commit
05cceee523
3 changed files with 164 additions and 95 deletions
|
|
@ -222,6 +222,13 @@ impl std::fmt::Display for NoSolutionError {
|
||||||
|
|
||||||
// Transform the error tree for reporting
|
// Transform the error tree for reporting
|
||||||
let mut tree = self.error.clone();
|
let mut tree = self.error.clone();
|
||||||
|
let should_display_tree = std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some()
|
||||||
|
|| tracing::enabled!(tracing::Level::TRACE);
|
||||||
|
|
||||||
|
if should_display_tree {
|
||||||
|
display_tree(&tree, "Resolver derivation tree before reduction");
|
||||||
|
}
|
||||||
|
|
||||||
collapse_no_versions_of_workspace_members(&mut tree, &self.workspace_members);
|
collapse_no_versions_of_workspace_members(&mut tree, &self.workspace_members);
|
||||||
|
|
||||||
if self.workspace_members.len() == 1 {
|
if self.workspace_members.len() == 1 {
|
||||||
|
|
@ -229,11 +236,10 @@ impl std::fmt::Display for NoSolutionError {
|
||||||
drop_root_dependency_on_project(&mut tree, project);
|
drop_root_dependency_on_project(&mut tree, project);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the tree if enabled
|
collapse_unavailable_versions(&mut tree);
|
||||||
if std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some()
|
|
||||||
|| tracing::enabled!(tracing::Level::TRACE)
|
if should_display_tree {
|
||||||
{
|
display_tree(&tree, "Resolver derivation tree after reduction");
|
||||||
display_tree(&tree);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let report = DefaultStringReporter::report_with_formatter(&tree, &formatter);
|
let report = DefaultStringReporter::report_with_formatter(&tree, &formatter);
|
||||||
|
|
@ -257,15 +263,18 @@ impl std::fmt::Display for NoSolutionError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::print_stderr)]
|
#[allow(clippy::print_stderr)]
|
||||||
fn display_tree(error: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>) {
|
fn display_tree(
|
||||||
|
error: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
|
||||||
|
name: &str,
|
||||||
|
) {
|
||||||
let mut lines = Vec::new();
|
let mut lines = Vec::new();
|
||||||
display_tree_inner(error, &mut lines, 0);
|
display_tree_inner(error, &mut lines, 0);
|
||||||
lines.reverse();
|
lines.reverse();
|
||||||
|
|
||||||
if std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some() {
|
if std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some() {
|
||||||
eprintln!("Resolver error derivation tree\n{}", lines.join("\n"));
|
eprintln!("{name}\n{}", lines.join("\n"));
|
||||||
} else {
|
} else {
|
||||||
trace!("Resolver error derivation tree\n{}", lines.join("\n"));
|
trace!("{name}\n{}", lines.join("\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,6 +364,105 @@ fn collapse_no_versions_of_workspace_members(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a [`DerivationTree`], collapse incompatibilities for versions of a package that are
|
||||||
|
/// unavailable for the same reason to avoid repeating the same message for every unavailable
|
||||||
|
/// version.
|
||||||
|
fn collapse_unavailable_versions(
|
||||||
|
tree: &mut DerivationTree<PubGrubPackage, Range<Version>, 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 unavailable package versions
|
||||||
|
(
|
||||||
|
DerivationTree::External(External::Custom(package, versions, reason)),
|
||||||
|
ref mut other,
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
ref mut other,
|
||||||
|
DerivationTree::External(External::Custom(package, versions, reason)),
|
||||||
|
) => {
|
||||||
|
// First, recursively collapse the other side of the tree
|
||||||
|
collapse_unavailable_versions(other);
|
||||||
|
|
||||||
|
// If it's not a derived tree, nothing to do.
|
||||||
|
let DerivationTree::Derived(Derived {
|
||||||
|
terms,
|
||||||
|
shared_id,
|
||||||
|
cause1,
|
||||||
|
cause2,
|
||||||
|
}) = other
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the other tree has an unavailable package...
|
||||||
|
match (&**cause1, &**cause2) {
|
||||||
|
// Note the following cases are the same, but we need two matches to retain
|
||||||
|
// the ordering of the causes
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
DerivationTree::External(External::Custom(
|
||||||
|
other_package,
|
||||||
|
other_versions,
|
||||||
|
other_reason,
|
||||||
|
)),
|
||||||
|
) => {
|
||||||
|
// And the package and reason are the same...
|
||||||
|
if package == other_package && reason == other_reason {
|
||||||
|
// Collapse both into a new node, with a union of their ranges
|
||||||
|
*tree = DerivationTree::Derived(Derived {
|
||||||
|
terms: terms.clone(),
|
||||||
|
shared_id: *shared_id,
|
||||||
|
cause1: cause1.clone(),
|
||||||
|
cause2: Arc::new(DerivationTree::External(External::Custom(
|
||||||
|
package.clone(),
|
||||||
|
versions.union(other_versions),
|
||||||
|
reason.clone(),
|
||||||
|
))),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
DerivationTree::External(External::Custom(
|
||||||
|
other_package,
|
||||||
|
other_versions,
|
||||||
|
other_reason,
|
||||||
|
)),
|
||||||
|
_,
|
||||||
|
) => {
|
||||||
|
// And the package and reason are the same...
|
||||||
|
if package == other_package && reason == other_reason {
|
||||||
|
// Collapse both into a new node, with a union of their ranges
|
||||||
|
*tree = DerivationTree::Derived(Derived {
|
||||||
|
terms: terms.clone(),
|
||||||
|
shared_id: *shared_id,
|
||||||
|
cause1: Arc::new(DerivationTree::External(External::Custom(
|
||||||
|
package.clone(),
|
||||||
|
versions.union(other_versions),
|
||||||
|
reason.clone(),
|
||||||
|
))),
|
||||||
|
cause2: cause2.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If not, just recurse
|
||||||
|
_ => {
|
||||||
|
collapse_unavailable_versions(Arc::make_mut(&mut derived.cause1));
|
||||||
|
collapse_unavailable_versions(Arc::make_mut(&mut derived.cause2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a [`DerivationTree`], drop dependency incompatibilities from the root
|
/// Given a [`DerivationTree`], drop dependency incompatibilities from the root
|
||||||
/// to the project.
|
/// to the project.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -241,10 +241,15 @@ fn prune_unzipped() -> Result<()> {
|
||||||
╰─▶ Because only the following versions of iniconfig are available:
|
╰─▶ Because only the following versions of iniconfig are available:
|
||||||
iniconfig<=0.1
|
iniconfig<=0.1
|
||||||
iniconfig>=1.0.0
|
iniconfig>=1.0.0
|
||||||
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.
|
and any of:
|
||||||
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.
|
iniconfig==0.1
|
||||||
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.
|
iniconfig==1.0.0
|
||||||
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.
|
iniconfig==1.0.1
|
||||||
|
iniconfig==1.1.0
|
||||||
|
iniconfig==1.1.1
|
||||||
|
iniconfig==2.0.0
|
||||||
|
network connectivity is disabled, but the metadata wasn't found in the cache, we can conclude that iniconfig<1.0.0 cannot be used.
|
||||||
|
And because you require iniconfig, we can conclude that your requirements are unsatisfiable.
|
||||||
|
|
||||||
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`)
|
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`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1925,95 +1925,51 @@ fn install_only_binary_all_and_no_binary_all() {
|
||||||
anyio>=3.0.0,<=3.6.2
|
anyio>=3.0.0,<=3.6.2
|
||||||
anyio>=3.7.0,<=3.7.1
|
anyio>=3.7.0,<=3.7.1
|
||||||
anyio>=4.0.0
|
anyio>=4.0.0
|
||||||
and anyio==1.0.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
and any of:
|
||||||
|
anyio==1.0.0
|
||||||
|
anyio==1.1.0
|
||||||
|
anyio==1.2.0
|
||||||
|
anyio==1.2.1
|
||||||
|
anyio==1.2.2
|
||||||
|
anyio==1.2.3
|
||||||
|
anyio==1.3.0
|
||||||
|
anyio==1.3.1
|
||||||
|
anyio==1.4.0
|
||||||
|
anyio==2.0.0
|
||||||
|
anyio==2.0.1
|
||||||
|
anyio==2.0.2
|
||||||
|
anyio==2.1.0
|
||||||
|
anyio==2.2.0
|
||||||
|
anyio==3.0.0
|
||||||
|
anyio==3.0.1
|
||||||
|
anyio==3.1.0
|
||||||
|
anyio==3.2.0
|
||||||
|
anyio==3.2.1
|
||||||
|
anyio==3.3.0
|
||||||
|
anyio==3.3.1
|
||||||
|
anyio==3.3.2
|
||||||
|
anyio==3.3.3
|
||||||
|
anyio==3.3.4
|
||||||
|
anyio==3.4.0
|
||||||
|
anyio==3.5.0
|
||||||
|
anyio==3.6.0
|
||||||
|
anyio==3.6.1
|
||||||
|
anyio==3.6.2
|
||||||
|
anyio==3.7.0
|
||||||
|
anyio==3.7.1
|
||||||
|
anyio==4.0.0
|
||||||
|
anyio==4.1.0
|
||||||
|
anyio==4.2.0
|
||||||
|
anyio==4.3.0
|
||||||
|
anyio==4.4.0
|
||||||
|
has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||||
anyio<1.1.0
|
anyio<1.1.0
|
||||||
anyio>1.4.0,<2.0.0
|
anyio>1.4.0,<2.0.0
|
||||||
anyio>2.2.0,<3.0.0
|
anyio>2.2.0,<3.0.0
|
||||||
anyio>3.6.2,<3.7.0
|
anyio>3.6.2,<3.7.0
|
||||||
anyio>3.7.1,<4.0.0
|
anyio>3.7.1,<4.0.0
|
||||||
cannot be used.
|
cannot be used.
|
||||||
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:
|
And because you require anyio, we can conclude that your requirements are unsatisfiable.
|
||||||
anyio<1.2.1
|
|
||||||
anyio>1.4.0,<2.0.0
|
|
||||||
anyio>2.2.0,<3.0.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<1.2.3
|
|
||||||
anyio>1.4.0,<2.0.0
|
|
||||||
anyio>2.2.0,<3.0.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<1.3.1
|
|
||||||
anyio>1.4.0,<2.0.0
|
|
||||||
anyio>2.2.0,<3.0.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<2.0.0
|
|
||||||
anyio>2.2.0,<3.0.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<2.0.2
|
|
||||||
anyio>2.2.0,<3.0.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<2.2.0
|
|
||||||
anyio>2.2.0,<3.0.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.0.1
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.2.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.3.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.3.2
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.3.4
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.5.0
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.6.1
|
|
||||||
anyio>3.6.2,<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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:
|
|
||||||
anyio<3.7.0
|
|
||||||
anyio>3.7.1,<4.0.0
|
|
||||||
cannot be used.
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
|
|
||||||
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`)
|
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`)
|
||||||
"###
|
"###
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue