mirror of https://github.com/astral-sh/uv
Allow displaying the derivation tree (#6124)
I need this for debugging error messages.
I used an environment variable instead of a trace log so you can do
`UV_INTERNAL__SHOW_DERIVATION_TREE=1` and run a test to see the tree in
the test snapshot without further changes.
e.g.
```rust
// Resolving should fail.
uv_snapshot!(context.filters(), context.lock().arg("--preview").current_dir(&workspace), @r###"
success: false
exit_code: 1
----- stdout -----
UV_INTERNAL__SHOW_DERIVATION_TREE
root==0a0.dev0 depends on foo*
root==0a0.dev0 depends on bar[some-extra]*
foo==0.1.0 depends on anyio==4.1.0
bar[some-extra]==0.1.0 depends on anyio==4.2.0
no versions of bar[some-extra]<0.1.0 | >0.1.0
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
× No solution found when resolving dependencies:
╰─▶ Because only bar[some-extra]==0.1.0 is available and bar[some-extra] depends on anyio==4.2.0, we can conclude that all versions of bar[some-extra] depend on anyio==4.2.0.
And because foo depends on anyio==4.1.0, we can conclude that foo and all versions of bar[some-extra] are incompatible.
And because your workspace requires bar[some-extra] and foo, we can conclude that your workspace's requirements are unsatisfiable.
"###
);
```
This commit is contained in:
parent
f2d6718038
commit
d7abe827d6
|
|
@ -8,6 +8,7 @@ use rustc_hash::FxHashMap;
|
||||||
use distribution_types::{BuiltDist, IndexLocations, InstalledDist, SourceDist};
|
use distribution_types::{BuiltDist, IndexLocations, InstalledDist, SourceDist};
|
||||||
use pep440_rs::Version;
|
use pep440_rs::Version;
|
||||||
use pep508_rs::MarkerTree;
|
use pep508_rs::MarkerTree;
|
||||||
|
use tracing::trace;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
use crate::candidate_selector::CandidateSelector;
|
use crate::candidate_selector::CandidateSelector;
|
||||||
|
|
@ -228,6 +229,13 @@ 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
|
||||||
|
if std::env::var_os("UV_INTERNAL__SHOW_DERIVATION_TREE").is_some()
|
||||||
|
|| tracing::enabled!(tracing::Level::TRACE)
|
||||||
|
{
|
||||||
|
display_tree(&tree);
|
||||||
|
}
|
||||||
|
|
||||||
let report = DefaultStringReporter::report_with_formatter(&tree, &formatter);
|
let report = DefaultStringReporter::report_with_formatter(&tree, &formatter);
|
||||||
write!(f, "{report}")?;
|
write!(f, "{report}")?;
|
||||||
|
|
||||||
|
|
@ -248,6 +256,56 @@ impl std::fmt::Display for NoSolutionError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::print_stderr)]
|
||||||
|
fn display_tree(error: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>) {
|
||||||
|
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"));
|
||||||
|
} else {
|
||||||
|
trace!("Resolver error derivation tree\n{}", lines.join("\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_tree_inner(
|
||||||
|
error: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
|
||||||
|
lines: &mut Vec<String>,
|
||||||
|
depth: usize,
|
||||||
|
) {
|
||||||
|
match error {
|
||||||
|
DerivationTree::Derived(derived) => {
|
||||||
|
display_tree_inner(&derived.cause1, lines, depth + 1);
|
||||||
|
display_tree_inner(&derived.cause2, lines, depth + 1);
|
||||||
|
}
|
||||||
|
DerivationTree::External(external) => {
|
||||||
|
let prefix = " ".repeat(depth).to_string();
|
||||||
|
match external {
|
||||||
|
External::FromDependencyOf(package, version, dependency, dependency_version) => {
|
||||||
|
lines.push(format!(
|
||||||
|
"{prefix}{package}{version} depends on {dependency}{dependency_version}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
External::Custom(package, versions, reason) => match reason {
|
||||||
|
UnavailableReason::Package(_) => {
|
||||||
|
lines.push(format!("{prefix}{package} {reason}"));
|
||||||
|
}
|
||||||
|
UnavailableReason::Version(_) => {
|
||||||
|
lines.push(format!("{prefix}{package}{versions} {reason}"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
External::NoVersions(package, versions) => {
|
||||||
|
lines.push(format!("{prefix}no versions of {package}{versions}"));
|
||||||
|
}
|
||||||
|
External::NotRoot(package, versions) => {
|
||||||
|
lines.push(format!("{prefix}not root {package}{versions}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a [`DerivationTree`], collapse any `NoVersion` incompatibilities for workspace members
|
/// Given a [`DerivationTree`], collapse any `NoVersion` incompatibilities for workspace members
|
||||||
/// to avoid saying things like "only <workspace-member>==0.1.0 is available".
|
/// to avoid saying things like "only <workspace-member>==0.1.0 is available".
|
||||||
fn collapse_no_versions_of_workspace_members(
|
fn collapse_no_versions_of_workspace_members(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue