make `ErrorTree` for `NoSolutionError` externally accessible (#14444)

Hey, are you okay with exposing the `ErrorTree` for library consumers?

We have a use case that needs more information on conflicts. We need the
tree-structure of the conflict and be able to traverse it in particular.

Signed-off-by: Simon Sure <ssure@palantir.com>
This commit is contained in:
Simon Sure 2025-07-03 17:43:59 +01:00 committed by GitHub
parent a1cda6213c
commit 8afbd86f03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 15 additions and 10 deletions

View File

@ -156,7 +156,7 @@ impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ResolveError {
} }
} }
pub(crate) type ErrorTree = DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>; pub 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.
pub struct NoSolutionError { pub struct NoSolutionError {
@ -367,6 +367,11 @@ impl NoSolutionError {
NoSolutionHeader::new(self.env.clone()) NoSolutionHeader::new(self.env.clone())
} }
/// Get the conflict derivation tree for external analysis
pub fn derivation_tree(&self) -> &ErrorTree {
&self.error
}
/// Hint at limiting the resolver environment if universal resolution failed for a target /// Hint at limiting the resolver environment if universal resolution failed for a target
/// that is not the current platform or not the current Python version. /// that is not the current platform or not the current Python version.
fn hint_disjoint_targets(&self, f: &mut Formatter) -> std::fmt::Result { fn hint_disjoint_targets(&self, f: &mut Formatter) -> std::fmt::Result {

View File

@ -1,5 +1,5 @@
pub use dependency_mode::DependencyMode; pub use dependency_mode::DependencyMode;
pub use error::{NoSolutionError, NoSolutionHeader, ResolveError, SentinelRange}; pub use error::{ErrorTree, NoSolutionError, NoSolutionHeader, ResolveError, SentinelRange};
pub use exclude_newer::ExcludeNewer; pub use exclude_newer::ExcludeNewer;
pub use exclusions::Exclusions; pub use exclusions::Exclusions;
pub use flat_index::{FlatDistributions, FlatIndex}; pub use flat_index::{FlatDistributions, FlatIndex};
@ -54,7 +54,7 @@ mod options;
mod pins; mod pins;
mod preferences; mod preferences;
mod prerelease; mod prerelease;
mod pubgrub; pub mod pubgrub;
mod python_requirement; mod python_requirement;
mod redirect; mod redirect;
mod resolution; mod resolution;

View File

@ -1,6 +1,6 @@
pub(crate) use crate::pubgrub::dependencies::PubGrubDependency; pub(crate) use crate::pubgrub::dependencies::PubGrubDependency;
pub(crate) use crate::pubgrub::distribution::PubGrubDistribution; pub(crate) use crate::pubgrub::distribution::PubGrubDistribution;
pub(crate) use crate::pubgrub::package::{PubGrubPackage, PubGrubPackageInner, PubGrubPython}; pub use crate::pubgrub::package::{PubGrubPackage, PubGrubPackageInner, PubGrubPython};
pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority, PubGrubTiebreaker}; pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority, PubGrubTiebreaker};
pub(crate) use crate::pubgrub::report::PubGrubReportFormatter; pub(crate) use crate::pubgrub::report::PubGrubReportFormatter;

View File

@ -9,7 +9,7 @@ use crate::python_requirement::PythonRequirement;
/// [`Arc`] wrapper around [`PubGrubPackageInner`] to make cloning (inside PubGrub) cheap. /// [`Arc`] wrapper around [`PubGrubPackageInner`] to make cloning (inside PubGrub) cheap.
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) struct PubGrubPackage(Arc<PubGrubPackageInner>); pub struct PubGrubPackage(Arc<PubGrubPackageInner>);
impl Deref for PubGrubPackage { impl Deref for PubGrubPackage {
type Target = PubGrubPackageInner; type Target = PubGrubPackageInner;
@ -39,7 +39,7 @@ impl From<PubGrubPackageInner> for PubGrubPackage {
/// package (e.g., `black[colorama]`), and mark it as a dependency of the real package (e.g., /// package (e.g., `black[colorama]`), and mark it as a dependency of the real package (e.g.,
/// `black`). We then discard the virtual packages at the end of the resolution process. /// `black`). We then discard the virtual packages at the end of the resolution process.
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) enum PubGrubPackageInner { pub enum PubGrubPackageInner {
/// The root package, which is used to start the resolution process. /// The root package, which is used to start the resolution process.
Root(Option<PackageName>), Root(Option<PackageName>),
/// A Python version. /// A Python version.
@ -295,7 +295,7 @@ impl PubGrubPackage {
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)] #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)]
pub(crate) enum PubGrubPython { pub enum PubGrubPython {
/// The Python version installed in the current environment. /// The Python version installed in the current environment.
Installed, Installed,
/// The Python version for which dependencies are being resolved. /// The Python version for which dependencies are being resolved.

View File

@ -7,7 +7,7 @@ use uv_platform_tags::{AbiTag, Tags};
/// The reason why a package or a version cannot be used. /// The reason why a package or a version cannot be used.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailableReason { pub enum UnavailableReason {
/// The entire package cannot be used. /// The entire package cannot be used.
Package(UnavailablePackage), Package(UnavailablePackage),
/// A single version cannot be used. /// A single version cannot be used.
@ -29,7 +29,7 @@ impl Display for UnavailableReason {
/// Most variant are from [`MetadataResponse`] without the error source, since we don't format /// Most variant are from [`MetadataResponse`] without the error source, since we don't format
/// the source and we want to merge unavailable messages across versions. /// the source and we want to merge unavailable messages across versions.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailableVersion { pub enum UnavailableVersion {
/// Version is incompatible because it has no usable distributions /// Version is incompatible because it has no usable distributions
IncompatibleDist(IncompatibleDist), IncompatibleDist(IncompatibleDist),
/// The wheel metadata was found, but could not be parsed. /// The wheel metadata was found, but could not be parsed.
@ -123,7 +123,7 @@ impl From<&MetadataUnavailable> for UnavailableVersion {
/// The package is unavailable and cannot be used. /// The package is unavailable and cannot be used.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailablePackage { pub enum UnavailablePackage {
/// Index lookups were disabled (i.e., `--no-index`) and the package was not found in a flat index (i.e. from `--find-links`). /// Index lookups were disabled (i.e., `--no-index`) and the package was not found in a flat index (i.e. from `--find-links`).
NoIndex, NoIndex,
/// Network requests were disabled (i.e., `--offline`), and the package was not found in the cache. /// Network requests were disabled (i.e., `--offline`), and the package was not found in the cache.