Surface PubGrub derivation trees (#108)

I think the derivation trees could be stronger but this exposes
PubGrub's proof-like error messages.

Closes #102.
This commit is contained in:
Charlie Marsh 2023-10-16 14:14:36 -04:00 committed by GitHub
parent bae52d5edd
commit 5f5788e866
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 14 deletions

1
Cargo.lock generated
View file

@ -1826,6 +1826,7 @@ dependencies = [
"pep508_rs",
"platform-host",
"platform-tags",
"pubgrub",
"puffin-client",
"puffin-installer",
"puffin-interpreter",

View file

@ -14,6 +14,7 @@ pep440_rs = { path = "../pep440-rs" }
pep508_rs = { path = "../pep508-rs" }
platform-host = { path = "../platform-host" }
platform-tags = { path = "../platform-tags" }
pubgrub = { path = "../../vendor/pubgrub" }
puffin-client = { path = "../puffin-client" }
puffin-installer = { path = "../puffin-installer" }
puffin-interpreter = { path = "../puffin-interpreter" }

View file

@ -5,6 +5,7 @@ use std::path::Path;
use anyhow::Result;
use colored::Colorize;
use pubgrub::report::Reporter;
use tracing::debug;
use platform_host::Platform;
@ -62,7 +63,23 @@ pub(crate) async fn compile(
// Resolve the dependencies.
let resolver = puffin_resolver::Resolver::new(requirements, markers, &tags, &client);
let resolution = resolver.resolve().await?;
let resolution = match resolver.resolve().await {
Err(puffin_resolver::ResolveError::PubGrub(pubgrub::error::PubGrubError::NoSolution(
mut derivation_tree,
))) => {
derivation_tree.collapse_no_versions();
#[allow(clippy::print_stderr)]
{
eprintln!("{}: {}", "error".red().bold(), "no solution found".bold());
eprintln!(
"{}",
pubgrub::report::DefaultStringReporter::report(&derivation_tree)
);
}
return Ok(ExitStatus::Failure);
}
result => result,
}?;
let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(

View file

@ -1,11 +1,12 @@
use std::path::PathBuf;
use std::process::ExitCode;
use crate::commands::ExitStatus;
use clap::{Args, Parser, Subcommand};
use colored::Colorize;
use directories::ProjectDirs;
use crate::commands::ExitStatus;
mod commands;
mod logging;
mod printer;

View file

@ -1,3 +1,4 @@
pub use error::ResolveError;
pub use resolution::{PinnedPackage, Resolution};
pub use resolver::Resolver;
pub use wheel_finder::{Reporter, WheelFinder};

View file

@ -17,7 +17,7 @@ pub enum PubGrubPackage {
impl std::fmt::Display for PubGrubPackage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PubGrubPackage::Root => write!(f, "<root>"),
PubGrubPackage::Root => write!(f, "root"),
PubGrubPackage::Package(name, None) => write!(f, "{name}"),
PubGrubPackage::Package(name, Some(extra)) => {
write!(f, "{name}[{extra}]")

View file

@ -1,6 +1,7 @@
//! Given a set of requirements, find a set of compatible packages.
use std::borrow::Borrow;
use std::collections::hash_map::Entry;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::str::FromStr;
use std::sync::Arc;
@ -158,16 +159,13 @@ impl<'a> Resolver<'a> {
// Fetch the list of candidates.
let Some(potential_packages) = state.partial_solution.potential_packages() else {
let Some(selected_dependencies) = state.partial_solution.extract_solution() else {
let Some(selection) = state.partial_solution.extract_solution() else {
return Err(PubGrubError::Failure(
"How did we end up with no package to choose but no solution?".into(),
)
.into());
};
return Ok(PubGrubResolution {
selected_dependencies,
pins,
});
return Ok(PubGrubResolution { selection, pins });
};
// Choose a package version.
@ -415,12 +413,23 @@ impl<'a> Resolver<'a> {
for (package, version) in
iter_requirements(self.requirements.iter(), None, self.markers)
{
constraints.insert(package, version);
match constraints.entry(package) {
Entry::Occupied(mut entry) => {
entry.insert(entry.get().intersection(&version));
}
Entry::Vacant(entry) => {
entry.insert(version);
}
}
}
Ok(Dependencies::Known(constraints))
}
PubGrubPackage::Package(package_name, extra) => {
debug!("Fetching dependencies for {}[{:?}]", package_name, extra);
if let Some(extra) = extra.as_ref() {
debug!("Fetching dependencies for {}[{:?}]", package_name, extra);
} else {
debug!("Fetching dependencies for {}", package_name);
}
// Wait for the metadata to be available.
let versions = pins.get(package_name).unwrap();
@ -429,7 +438,6 @@ impl<'a> Resolver<'a> {
let metadata = entry.value();
let mut constraints = DependencyConstraints::default();
for (package, version) in
iter_requirements(metadata.requires_dist.iter(), extra.as_ref(), self.markers)
{
@ -443,7 +451,14 @@ impl<'a> Resolver<'a> {
}
// Add it to the constraints.
constraints.insert(package, version);
match constraints.entry(package) {
Entry::Occupied(mut entry) => {
entry.insert(entry.get().intersection(&version));
}
Entry::Vacant(entry) => {
entry.insert(version);
}
}
}
if let Some(extra) = extra {
@ -512,7 +527,7 @@ enum Dependencies {
#[derive(Debug)]
struct PubGrubResolution {
/// The selected dependencies.
selected_dependencies: SelectedDependencies<PubGrubPackage, PubGrubVersion>,
selection: SelectedDependencies<PubGrubPackage, PubGrubVersion>,
/// The selected file (source or built distribution) for each package.
pins: HashMap<PackageName, HashMap<pep440_rs::Version, File>>,
}
@ -520,7 +535,7 @@ struct PubGrubResolution {
impl From<PubGrubResolution> for Resolution {
fn from(value: PubGrubResolution) -> Self {
let mut packages = BTreeMap::new();
for (package, version) in value.selected_dependencies {
for (package, version) in value.selection {
let PubGrubPackage::Package(package_name, None) = package else {
continue;
};