Description
Hey!
I'm sort of losing my mind digging around on this one, hopefully I'm missing something obvious.
I recently noticed this confusing error message, in which we say:
Because only the following versions of open3d are available:
open3d==0.8.0.0
open3d==0.9.0.0
open3d==0.10.0.0
open3d==0.10.0.1
open3d==0.11.0
open3d==0.11.1
open3d==0.11.2
open3d==0.12.0
open3d==0.13.0
open3d==0.14.1
open3d==0.15.1
open3d==0.15.2
open3d==0.16.0
open3d==0.16.1
open3d==0.17.0
open3d==0.18.0
and open3d<=0.15.2 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.9.0.0 cannot be used.
And because open3d>=0.16.0 has no wheels with a matching platform tag and you require open3d, we can conclude that your requirements are unsatisfiable.
There are a few problems here, like, I'm transforming the derivation tree to collapse the "no wheels" incompatibilities and things get out of order so we make a conclusion about the usable versions before we have enumerated all the incompatibilities (tracking that in astral-sh/uv#9861)
But I want to focus on open3d<0.9.0.0
, which doesn't make any sense here. If I display the complement of the range and turn of simplification based on the available versions, we get this instead:
Because only the following versions of open3d are available:
open3d==0.8.0.0
open3d==0.9.0.0
open3d==0.10.0.0
open3d==0.10.0.1
open3d==0.11.0
open3d==0.11.1
open3d==0.11.2
open3d==0.12.0
open3d==0.13.0
open3d==0.14.1
open3d==0.15.1
open3d==0.15.2
open3d==0.16.0
open3d==0.16.1
open3d==0.17.0
open3d==0.18.0
and open3d<=0.15.2 has no wheels with a matching Python ABI tag, we can conclude that all of:
open3d==0.9.0.0
open3d==0.10.0.0
open3d==0.10.0.1
open3d==0.11.0
open3d==0.11.1
open3d==0.11.2
open3d==0.12.0
open3d==0.13.0
open3d==0.14.1
open3d==0.15.1
open3d==0.15.2
open3d==0.16.0
open3d==0.16.1
open3d==0.17.0
open3d==0.18.0
cannot be used.
And because open3d>=0.16.0 has no wheels with a matching platform tag and you require open3d, we can conclude that your requirements are unsatisfiable.
Now we're in a place that makes way more sense. We enumerate all the available versions, then say open3d<=0.15.2
is incompatible and open3d>=0.16.0
is incompatible so all available versions are incompatible and there's no solution. There's one problem though:open3d==0.8.0.0
is missing from the conclusion.
I have this little utility to display the derivation tree for debug purposes:
#[allow(clippy::print_stderr)]
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(EnvVars::UV_INTERNAL__SHOW_DERIVATION_TREE).is_some() {
eprintln!("{name}\n{}", lines.join("\n"));
} else {
trace!("{name}\n{}", lines.join("\n"));
}
}
fn display_tree_inner(
error: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
lines: &mut Vec<String>,
depth: usize,
) {
let prefix = " ".repeat(depth).to_string();
match error {
DerivationTree::Derived(derived) => {
display_tree_inner(&derived.cause1, lines, depth + 1);
display_tree_inner(&derived.cause2, lines, depth + 1);
for (package, term) in derived.terms.iter() {
match term {
Term::Positive(versions) => {
lines.push(format!("{prefix}term {package}{versions}"));
}
Term::Negative(versions) => {
lines.push(format!("{prefix}term not {package}{versions}"));
}
}
}
}
DerivationTree::External(external) => 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}"));
}
},
}
}
Which gives us
term root==0a0.dev0
root==0a0.dev0 depends on open3d*
term open3d*
open3d==0.18.0 no wheels with a matching platform tag
term open3d<0.18.0 | >0.18.0
open3d==0.17.0 no wheels with a matching platform tag
term open3d<0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.16.1 no wheels with a matching platform tag
term open3d<0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.16.0 no wheels with a matching platform tag
term open3d<0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.15.2 no wheels with a matching Python ABI tag
term open3d<0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.15.1 no wheels with a matching Python ABI tag
term open3d<0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.14.1 no wheels with a matching Python ABI tag
term open3d<0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.13.0 no wheels with a matching Python ABI tag
term open3d<0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.12.0 no wheels with a matching Python ABI tag
term open3d<0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.11.2 no wheels with a matching Python ABI tag
term open3d<0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.11.1 no wheels with a matching Python ABI tag
term open3d<0.11.1 | >0.11.1, <0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.11.0 no wheels with a matching Python ABI tag
term open3d<0.11.0 | >0.11.0, <0.11.1 | >0.11.1, <0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.10.0.1 no wheels with a matching Python ABI tag
term open3d<0.10.0.1 | >0.10.0.1, <0.11.0 | >0.11.0, <0.11.1 | >0.11.1, <0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.10.0.0 no wheels with a matching Python ABI tag
term open3d<0.10.0.0 | >0.10.0.0, <0.10.0.1 | >0.10.0.1, <0.11.0 | >0.11.0, <0.11.1 | >0.11.1, <0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.9.0.0 no wheels with a matching Python ABI tag
term open3d<0.9.0.0 | >0.9.0.0, <0.10.0.0 | >0.10.0.0, <0.10.0.1 | >0.10.0.1, <0.11.0 | >0.11.0, <0.11.1 | >0.11.1, <0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
open3d==0.8.0.0 no wheels with a matching Python ABI tag
no versions of open3d<0.8.0.0 | >0.8.0.0, <0.9.0.0 | >0.9.0.0, <0.10.0.0 | >0.10.0.0, <0.10.0.1 | >0.10.0.1, <0.11.0 | >0.11.0, <0.11.1 | >0.11.1, <0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
Notice we create the incompatibility open3d==0.8.0.0 no wheels with a matching Python ABI tag
but it's never reflected in the terms — so it's missing from the conclusion.
I added a debug log for the term_intersection
in the no_versions
case in the resolver here and see
open3d<0.8.0.0 | >0.8.0.0, <0.9.0.0 | >0.9.0.0, <0.10.0.0 | >0.10.0.0, <0.10.0.1 | >0.10.0.1, <0.11.0 | >0.11.0, <0.11.1 | >0.11.1, <0.11.2 | >0.11.2, <0.12.0 | >0.12.0, <0.13.0 | >0.13.0, <0.14.1 | >0.14.1, <0.15.1 | >0.15.1, <0.15.2 | >0.15.2, <0.16.0 | >0.16.0, <0.16.1 | >0.16.1, <0.17.0 | >0.17.0, <0.18.0 | >0.18.0
So.. we do construct this term, it just doesn't make it into the tree? Is this expected? It seems incorrect? Does this reflect a bug in PubGrub or in our resolver implementation? I don't think it's possible to construct the correct error message without it.
You can reproduce this with
$ echo "open3d" | uv pip compile --no-cache --exclude-newer 2024-03-25T00:00:00Z --python-platform linux --python 3.10 -
For what it's worth, if I drop our transformation to merge incompatibilities you get
× No solution found when resolving dependencies:
╰─▶ Because only the following versions of open3d are available:
open3d==0.8.0.0
open3d==0.9.0.0
open3d==0.10.0.0
open3d==0.10.0.1
open3d==0.11.0
open3d==0.11.1
open3d==0.11.2
open3d==0.12.0
open3d==0.13.0
open3d==0.14.1
open3d==0.15.1
open3d==0.15.2
open3d==0.16.0
open3d==0.16.1
open3d==0.17.0
open3d==0.18.0
and open3d==0.8.0.0 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.9.0.0 cannot be used.
And because open3d==0.9.0.0 has no wheels with a matching Python ABI tag and open3d==0.10.0.0 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.10.0.1 cannot be used.
And because open3d==0.10.0.1 has no wheels with a matching Python ABI tag and open3d==0.11.0 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.11.1 cannot be used.
And because open3d==0.11.1 has no wheels with a matching Python ABI tag and open3d==0.11.2 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.12.0 cannot be used.
And because open3d==0.12.0 has no wheels with a matching Python ABI tag and open3d==0.13.0 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.14.1 cannot be used.
And because open3d==0.14.1 has no wheels with a matching Python ABI tag and open3d==0.15.1 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.15.2 cannot be used.
And because open3d==0.15.2 has no wheels with a matching Python ABI tag and open3d==0.16.0 has no wheels with a matching platform tag, we can conclude that open3d<0.16.1 cannot be used.
And because open3d==0.16.1 has no wheels with a matching platform tag and open3d==0.17.0 has no wheels with a matching platform tag, we can conclude that open3d<0.18.0 cannot be used.
And because open3d==0.18.0 has no wheels with a matching platform tag and you require open3d, we can conclude that your requirements are unsatisfiable.
Which handles the 0.8.0.0
conclusion with and open3d==0.8.0.0 has no wheels with a matching Python ABI tag, we can conclude that open3d<0.9.0.0 cannot be used.
The entire conclusion is quite logical, so perhaps we just need to revisit how were merging these. It's here for reference. That transformation may be entirely unsound and PubGrub is working as intended here.