Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collapse unavailable packages in resolver errors #6154

Merged
merged 1 commit into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 116 additions & 8 deletions crates/uv-resolver/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,18 +222,24 @@ 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");
}
Comment on lines +228 to +230
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to see the tree before and after reduction when doing complex transforms


collapse_no_versions_of_workspace_members(&mut tree, &self.workspace_members);

if self.workspace_members.len() == 1 {
let project = self.workspace_members.iter().next().unwrap();
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);
Expand All @@ -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"));
}
}

Expand Down Expand Up @@ -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.
///
Expand Down
13 changes: 9 additions & 4 deletions crates/uv/tests/cache_prune.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay what this error was bad? Need to improve this separately

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #6156

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this says "and any of network connectivity is disabled"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`)

Expand Down
122 changes: 39 additions & 83 deletions crates/uv/tests/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be "all of"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's a little confusing. I think it's saying "any of a or b" instead of "all of a and b" — we don't use "and" or "or" so "all" and "any" do that. We can probably make this better somehow...

anyio==1.0.0
anyio==1.1.0
anyio==1.2.0
anyio==1.2.1
anyio==1.2.2
Comment on lines -1928 to +1933
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we enumerate all the versions in a single clause instead of having a clause for each! I think now we can simplify this range with the known available versions and it'll be way better.

Copy link
Member Author

@zanieb zanieb Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #6155

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:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be plural form "have no usable wheels" — will require more changes separately.

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`)
"###
Expand Down
Loading