mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-12 00:45:35 +00:00
Remove double-proxy nodes in error reporting (#5738)
## Summary If _both_ nodes in the derivation tree are proxies, we need to remove the _entire_ node. So, the function now returns an `Option<Tree>` instead of taking `&mut Tree`. Closes https://github.com/astral-sh/uv/issues/5618.
This commit is contained in:
parent
097aa929b7
commit
b26794bf6f
5 changed files with 54 additions and 47 deletions
|
|
@ -2,7 +2,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pubgrub::{DefaultStringReporter, DerivationTree, External, Range, Reporter};
|
use pubgrub::{DefaultStringReporter, DerivationTree, Derived, External, Range, Reporter};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use distribution_types::{BuiltDist, IndexLocations, InstalledDist, SourceDist};
|
use distribution_types::{BuiltDist, IndexLocations, InstalledDist, SourceDist};
|
||||||
|
|
@ -13,9 +13,7 @@ use uv_normalize::PackageName;
|
||||||
use crate::candidate_selector::CandidateSelector;
|
use crate::candidate_selector::CandidateSelector;
|
||||||
use crate::dependency_provider::UvDependencyProvider;
|
use crate::dependency_provider::UvDependencyProvider;
|
||||||
use crate::fork_urls::ForkUrls;
|
use crate::fork_urls::ForkUrls;
|
||||||
use crate::pubgrub::{
|
use crate::pubgrub::{PubGrubPackage, PubGrubReportFormatter, PubGrubSpecifierError};
|
||||||
PubGrubPackage, PubGrubPackageInner, PubGrubReportFormatter, PubGrubSpecifierError,
|
|
||||||
};
|
|
||||||
use crate::python_requirement::PythonRequirement;
|
use crate::python_requirement::PythonRequirement;
|
||||||
use crate::resolver::{IncompletePackage, ResolverMarkers, UnavailablePackage, UnavailableReason};
|
use crate::resolver::{IncompletePackage, ResolverMarkers, UnavailablePackage, UnavailableReason};
|
||||||
|
|
||||||
|
|
@ -113,6 +111,8 @@ impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ResolveError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type ErrorTree = DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>;
|
||||||
|
|
||||||
/// A wrapper around [`pubgrub::error::NoSolutionError`] that displays a resolution failure report.
|
/// A wrapper around [`pubgrub::error::NoSolutionError`] that displays a resolution failure report.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NoSolutionError {
|
pub struct NoSolutionError {
|
||||||
|
|
@ -165,49 +165,46 @@ impl NoSolutionError {
|
||||||
|
|
||||||
/// Given a [`DerivationTree`], collapse any [`External::FromDependencyOf`] incompatibilities
|
/// Given a [`DerivationTree`], collapse any [`External::FromDependencyOf`] incompatibilities
|
||||||
/// wrap an [`PubGrubPackageInner::Extra`] package.
|
/// wrap an [`PubGrubPackageInner::Extra`] package.
|
||||||
pub(crate) fn collapse_proxies(
|
pub(crate) fn collapse_proxies(derivation_tree: ErrorTree) -> ErrorTree {
|
||||||
derivation_tree: &mut DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
|
fn collapse(derivation_tree: ErrorTree) -> Option<ErrorTree> {
|
||||||
) {
|
|
||||||
match derivation_tree {
|
match derivation_tree {
|
||||||
DerivationTree::External(_) => {}
|
|
||||||
DerivationTree::Derived(derived) => {
|
DerivationTree::Derived(derived) => {
|
||||||
match (
|
match (&*derived.cause1, &*derived.cause2) {
|
||||||
Arc::make_mut(&mut derived.cause1),
|
(
|
||||||
Arc::make_mut(&mut derived.cause2),
|
DerivationTree::External(External::FromDependencyOf(package1, ..)),
|
||||||
) {
|
DerivationTree::External(External::FromDependencyOf(package2, ..)),
|
||||||
|
) if package1.is_proxy() && package2.is_proxy() => None,
|
||||||
(
|
(
|
||||||
DerivationTree::External(External::FromDependencyOf(package, ..)),
|
DerivationTree::External(External::FromDependencyOf(package, ..)),
|
||||||
ref mut cause,
|
cause,
|
||||||
) if matches!(
|
) if package.is_proxy() => collapse(cause.clone()),
|
||||||
&**package,
|
|
||||||
PubGrubPackageInner::Extra { .. }
|
|
||||||
| PubGrubPackageInner::Marker { .. }
|
|
||||||
| PubGrubPackageInner::Dev { .. }
|
|
||||||
) =>
|
|
||||||
{
|
|
||||||
Self::collapse_proxies(cause);
|
|
||||||
*derivation_tree = cause.clone();
|
|
||||||
}
|
|
||||||
(
|
(
|
||||||
ref mut cause,
|
cause,
|
||||||
DerivationTree::External(External::FromDependencyOf(package, ..)),
|
DerivationTree::External(External::FromDependencyOf(package, ..)),
|
||||||
) if matches!(
|
) if package.is_proxy() => collapse(cause.clone()),
|
||||||
&**package,
|
(cause1, cause2) => {
|
||||||
PubGrubPackageInner::Extra { .. }
|
let cause1 = collapse(cause1.clone());
|
||||||
| PubGrubPackageInner::Marker { .. }
|
let cause2 = collapse(cause2.clone());
|
||||||
| PubGrubPackageInner::Dev { .. }
|
match (cause1, cause2) {
|
||||||
) =>
|
(Some(cause1), Some(cause2)) => {
|
||||||
{
|
Some(DerivationTree::Derived(Derived {
|
||||||
Self::collapse_proxies(cause);
|
cause1: Arc::new(cause1),
|
||||||
*derivation_tree = cause.clone();
|
cause2: Arc::new(cause2),
|
||||||
|
..derived
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
_ => {
|
(Some(cause), None) | (None, Some(cause)) => Some(cause),
|
||||||
Self::collapse_proxies(Arc::make_mut(&mut derived.cause1));
|
_ => None,
|
||||||
Self::collapse_proxies(Arc::make_mut(&mut derived.cause2));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DerivationTree::External(_) => Some(derivation_tree),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse(derivation_tree)
|
||||||
|
.expect("derivation tree should contain at least one external term")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,16 @@ impl PubGrubPackage {
|
||||||
PubGrubPackageInner::Marker { marker, .. } => Some(marker),
|
PubGrubPackageInner::Marker { marker, .. } => Some(marker),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this PubGrub package is a proxy package.
|
||||||
|
pub fn is_proxy(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
&**self,
|
||||||
|
PubGrubPackageInner::Extra { .. }
|
||||||
|
| PubGrubPackageInner::Dev { .. }
|
||||||
|
| PubGrubPackageInner::Marker { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)]
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use pep440_rs::Version;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
use crate::candidate_selector::CandidateSelector;
|
use crate::candidate_selector::CandidateSelector;
|
||||||
|
use crate::error::ErrorTree;
|
||||||
use crate::fork_urls::ForkUrls;
|
use crate::fork_urls::ForkUrls;
|
||||||
use crate::prerelease::AllowPrerelease;
|
use crate::prerelease::AllowPrerelease;
|
||||||
use crate::python_requirement::{PythonRequirement, PythonTarget};
|
use crate::python_requirement::{PythonRequirement, PythonTarget};
|
||||||
|
|
@ -399,7 +400,7 @@ impl PubGrubReportFormatter<'_> {
|
||||||
/// their requirements.
|
/// their requirements.
|
||||||
pub(crate) fn hints(
|
pub(crate) fn hints(
|
||||||
&self,
|
&self,
|
||||||
derivation_tree: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
|
derivation_tree: &ErrorTree,
|
||||||
selector: &CandidateSelector,
|
selector: &CandidateSelector,
|
||||||
index_locations: &IndexLocations,
|
index_locations: &IndexLocations,
|
||||||
unavailable_packages: &FxHashMap<PackageName, UnavailablePackage>,
|
unavailable_packages: &FxHashMap<PackageName, UnavailablePackage>,
|
||||||
|
|
|
||||||
|
|
@ -1905,7 +1905,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
visited: &FxHashSet<PackageName>,
|
visited: &FxHashSet<PackageName>,
|
||||||
index_locations: &IndexLocations,
|
index_locations: &IndexLocations,
|
||||||
) -> ResolveError {
|
) -> ResolveError {
|
||||||
NoSolutionError::collapse_proxies(&mut err);
|
err = NoSolutionError::collapse_proxies(err);
|
||||||
|
|
||||||
let mut unavailable_packages = FxHashMap::default();
|
let mut unavailable_packages = FxHashMap::default();
|
||||||
for package in err.packages() {
|
for package in err.packages() {
|
||||||
|
|
|
||||||
|
|
@ -6803,8 +6803,7 @@ fn universal_multi_version() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because iniconfig{python_version > '3.12'}==2.0.0 depends on iniconfig==2.0.0 and you require iniconfig{python_version > '3.12'}==2.0.0, we can conclude that your requirements and iniconfig{python_version == '3.12'}==1.0.0 are incompatible.
|
╰─▶ Because you require iniconfig{python_version > '3.12'}==2.0.0 and iniconfig{python_version == '3.12'}==1.0.0, we can conclude that the requirements are unsatisfiable.
|
||||||
And because you require iniconfig{python_version == '3.12'}==1.0.0, we can conclude that the requirements are unsatisfiable.
|
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue