mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 04:17:37 +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
|
||||
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);
|
||||
|
||||
if self.workspace_members.len() == 1 {
|
||||
|
|
@ -229,11 +236,10 @@ impl std::fmt::Display for NoSolutionError {
|
|||
drop_root_dependency_on_project(&mut tree, project);
|
||||
}
|
||||
|
||||
// Display the tree if enabled
|
||||
if std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some()
|
||||
|| tracing::enabled!(tracing::Level::TRACE)
|
||||
{
|
||||
display_tree(&tree);
|
||||
collapse_unavailable_versions(&mut tree);
|
||||
|
||||
if should_display_tree {
|
||||
display_tree(&tree, "Resolver derivation tree after reduction");
|
||||
}
|
||||
|
||||
let report = DefaultStringReporter::report_with_formatter(&tree, &formatter);
|
||||
|
|
@ -257,15 +263,18 @@ impl std::fmt::Display for NoSolutionError {
|
|||
}
|
||||
|
||||
#[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();
|
||||
display_tree_inner(error, &mut lines, 0);
|
||||
lines.reverse();
|
||||
|
||||
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 {
|
||||
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
|
||||
/// to the project.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -241,10 +241,15 @@ fn prune_unzipped() -> Result<()> {
|
|||
╰─▶ Because only the following versions of iniconfig are available:
|
||||
iniconfig<=0.1
|
||||
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 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.
|
||||
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.
|
||||
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.
|
||||
and any of:
|
||||
iniconfig==0.1
|
||||
iniconfig==1.0.0
|
||||
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`)
|
||||
|
||||
|
|
|
|||
|
|
@ -1925,95 +1925,51 @@ fn install_only_binary_all_and_no_binary_all() {
|
|||
anyio>=3.0.0,<=3.6.2
|
||||
anyio>=3.7.0,<=3.7.1
|
||||
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.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.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:
|
||||
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.
|
||||
And because 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`)
|
||||
"###
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue