17228ba04e
## 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.
154 lines
4.8 KiB
Rust
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,
|
|
},
|
|
}
|
|
}
|
|
}
|