Files
uv/crates/puffin-resolver/src/error.rs
T
Charlie Marsh 17228ba04e Add support for path dependencies (#471)
## Summary

This PR adds support for local path dependencies. The approach mostly
just falls out of our existing approach and infrastructure for Git and
URL dependencies.

Closes https://github.com/astral-sh/puffin/issues/436. (We'll open a
separate issue for editable installs.)

## Test Plan

Added `pip-compile` tests that pre-download a wheel or source
distribution, then install it via local path.
2023-11-21 11:49:42 +00:00

154 lines
4.8 KiB
Rust

use std::fmt::Formatter;
use pubgrub::range::Range;
use pubgrub::report::Reporter;
use thiserror::Error;
use url::Url;
use distribution_types::{BuiltDist, Dist, SourceDist};
use pep508_rs::Requirement;
use puffin_normalize::PackageName;
use crate::pubgrub::{PubGrubPackage, PubGrubVersion};
use crate::ResolutionFailureReporter;
#[derive(Error, Debug)]
pub enum ResolveError {
#[error("Failed to find a version of {0} that satisfies the requirement")]
NotFound(Requirement),
#[error("The request stream terminated unexpectedly")]
StreamTermination,
#[error(transparent)]
Client(#[from] puffin_client::Error),
#[error(transparent)]
TrySend(#[from] futures::channel::mpsc::SendError),
#[error(transparent)]
Join(#[from] tokio::task::JoinError),
#[error(transparent)]
PubGrub(#[from] RichPubGrubError),
#[error("Package metadata name `{metadata}` does not match given name `{given}`")]
NameMismatch {
given: PackageName,
metadata: PackageName,
},
#[error("~= operator requires at least two release segments: {0}")]
InvalidTildeEquals(pep440_rs::VersionSpecifier),
#[error("Conflicting URLs for package `{0}`:\n- {1}\n- {2}")]
ConflictingUrls(PackageName, String, String),
#[error("Conflicting versions for `{0}`: {1}")]
ConflictingVersions(String, String),
#[error("Package `{0}` attempted to resolve via URL: {1}. URL dependencies must be expressed as direct requirements or constraints. Consider adding `{0} @ {1}` to your dependencies or constraints file.")]
DisallowedUrl(PackageName, Url),
#[error(transparent)]
DistributionType(#[from] distribution_types::Error),
#[error("Failed to fetch wheel metadata from: {filename}")]
RegistryBuiltDist {
filename: String,
// TODO(konstin): Gives this a proper error type
#[source]
err: anyhow::Error,
},
#[error("Failed to fetch wheel metadata from: {url}")]
UrlBuiltDist {
url: Url,
// TODO(konstin): Gives this a proper error type
#[source]
err: anyhow::Error,
},
#[error("Failed to build distribution: {filename}")]
RegistrySourceDist {
filename: String,
// TODO(konstin): Gives this a proper error type
#[source]
err: anyhow::Error,
},
#[error("Failed to build distribution from URL: {url}")]
UrlSourceDist {
url: Url,
// TODO(konstin): Gives this a proper error type
#[source]
err: anyhow::Error,
},
}
impl<T> From<futures::channel::mpsc::TrySendError<T>> for ResolveError {
fn from(value: futures::channel::mpsc::TrySendError<T>) -> Self {
value.into_send_error().into()
}
}
/// A wrapper around [`pubgrub::error::PubGrubError`] that displays a resolution failure report.
#[derive(Debug)]
pub struct RichPubGrubError {
source: pubgrub::error::PubGrubError<PubGrubPackage, Range<PubGrubVersion>>,
}
impl std::error::Error for RichPubGrubError {}
impl std::fmt::Display for RichPubGrubError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let pubgrub::error::PubGrubError::NoSolution(derivation_tree) = &self.source {
let report = ResolutionFailureReporter::report(derivation_tree);
write!(f, "{report}")
} else {
write!(f, "{}", self.source)
}
}
}
impl From<pubgrub::error::PubGrubError<PubGrubPackage, Range<PubGrubVersion>>> for ResolveError {
fn from(value: pubgrub::error::PubGrubError<PubGrubPackage, Range<PubGrubVersion>>) -> Self {
ResolveError::PubGrub(RichPubGrubError { source: value })
}
}
impl ResolveError {
pub fn from_dist(dist: Dist, err: anyhow::Error) -> Self {
match dist {
Dist::Built(BuiltDist::Registry(wheel)) => Self::RegistryBuiltDist {
filename: wheel.file.filename.clone(),
err,
},
Dist::Built(BuiltDist::DirectUrl(wheel)) => Self::UrlBuiltDist {
url: wheel.url.clone(),
err,
},
Dist::Built(BuiltDist::Path(wheel)) => Self::UrlBuiltDist {
url: wheel.url.clone(),
err,
},
Dist::Source(SourceDist::Registry(sdist)) => Self::RegistrySourceDist {
filename: sdist.file.filename.clone(),
err,
},
Dist::Source(SourceDist::DirectUrl(sdist)) => Self::UrlSourceDist {
url: sdist.url.clone(),
err,
},
Dist::Source(SourceDist::Git(sdist)) => Self::UrlSourceDist {
url: sdist.url.clone(),
err,
},
Dist::Source(SourceDist::Path(sdist)) => Self::UrlBuiltDist {
url: sdist.url.clone(),
err,
},
}
}
}