mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Surface request stream errors in the resolver (#107)
Closes https://github.com/astral-sh/puffin/issues/105.
This commit is contained in:
parent
7e8ffeb2df
commit
bae52d5edd
3 changed files with 72 additions and 49 deletions
|
@ -10,12 +10,18 @@ pub enum ResolveError {
|
||||||
#[error("Failed to find a version of {0} that satisfies the requirement")]
|
#[error("Failed to find a version of {0} that satisfies the requirement")]
|
||||||
NotFound(Requirement),
|
NotFound(Requirement),
|
||||||
|
|
||||||
|
#[error("The request stream terminated unexpectedly")]
|
||||||
|
StreamTermination,
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Client(#[from] puffin_client::PypiClientError),
|
Client(#[from] puffin_client::PypiClientError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TrySend(#[from] futures::channel::mpsc::SendError),
|
TrySend(#[from] futures::channel::mpsc::SendError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Join(#[from] tokio::task::JoinError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PubGrub(#[from] pubgrub::error::PubGrubError<PubGrubPackage, PubGrubVersion>),
|
PubGrub(#[from] pubgrub::error::PubGrubError<PubGrubPackage, PubGrubVersion>),
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,6 @@ use once_cell::sync::Lazy;
|
||||||
pub struct PubGrubVersion(pep440_rs::Version);
|
pub struct PubGrubVersion(pep440_rs::Version);
|
||||||
|
|
||||||
impl PubGrubVersion {
|
impl PubGrubVersion {
|
||||||
/// Returns `true` if this is a pre-release version.
|
|
||||||
pub fn is_prerelease(&self) -> bool {
|
|
||||||
self.0.pre.is_some() || self.0.dev.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the smallest PEP 440 version that is larger than `self`.
|
/// Returns the smallest PEP 440 version that is larger than `self`.
|
||||||
pub fn next(&self) -> PubGrubVersion {
|
pub fn next(&self) -> PubGrubVersion {
|
||||||
let mut next = self.clone();
|
let mut next = self.clone();
|
||||||
|
|
|
@ -7,11 +7,12 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::future::Either;
|
use futures::future::Either;
|
||||||
use futures::{StreamExt, TryFutureExt};
|
use futures::{pin_mut, FutureExt, StreamExt, TryFutureExt};
|
||||||
use pubgrub::error::PubGrubError;
|
use pubgrub::error::PubGrubError;
|
||||||
use pubgrub::range::Range;
|
use pubgrub::range::Range;
|
||||||
use pubgrub::solver::{DependencyConstraints, Incompatibility, State};
|
use pubgrub::solver::{DependencyConstraints, Incompatibility, State};
|
||||||
use pubgrub::type_aliases::SelectedDependencies;
|
use pubgrub::type_aliases::SelectedDependencies;
|
||||||
|
use tokio::select;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
use waitmap::WaitMap;
|
use waitmap::WaitMap;
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ impl<'a> Resolver<'a> {
|
||||||
// A channel to fetch package metadata (e.g., given `flask`, fetch all versions) and version
|
// A channel to fetch package metadata (e.g., given `flask`, fetch all versions) and version
|
||||||
// metadata (e.g., given `flask==1.0.0`, fetch the metadata for that version).
|
// metadata (e.g., given `flask==1.0.0`, fetch the metadata for that version).
|
||||||
let (request_sink, request_stream) = futures::channel::mpsc::unbounded();
|
let (request_sink, request_stream) = futures::channel::mpsc::unbounded();
|
||||||
tokio::spawn({
|
let requests_fut = tokio::spawn({
|
||||||
let cache = cache.clone();
|
let cache = cache.clone();
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
async move {
|
async move {
|
||||||
|
@ -101,7 +102,7 @@ impl<'a> Resolver<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), ResolveError>(())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,52 +113,39 @@ impl<'a> Resolver<'a> {
|
||||||
request_sink.unbounded_send(Request::Package(package_name))?;
|
request_sink.unbounded_send(Request::Package(package_name))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track the packages and versions that we've requested.
|
// Run the solver.
|
||||||
let mut requested_packages = HashSet::new();
|
let resolve_fut = self.solve(&cache, &request_sink);
|
||||||
let mut requested_versions = HashSet::new();
|
|
||||||
let mut pins = HashMap::new();
|
|
||||||
|
|
||||||
let selected_dependencies = self
|
let requests_fut = requests_fut.fuse();
|
||||||
.solve(
|
let resolve_fut = resolve_fut.fuse();
|
||||||
&cache,
|
pin_mut!(requests_fut, resolve_fut);
|
||||||
&mut pins,
|
|
||||||
&mut requested_packages,
|
|
||||||
&mut requested_versions,
|
|
||||||
&request_sink,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Map to our own internal resolution type.
|
let resolution = select! {
|
||||||
let mut resolution = BTreeMap::new();
|
result = requests_fut => {
|
||||||
for (package, version) in selected_dependencies {
|
result??;
|
||||||
let PubGrubPackage::Package(package_name, None) = package else {
|
return Err(ResolveError::StreamTermination);
|
||||||
continue;
|
}
|
||||||
};
|
resolution = resolve_fut => {
|
||||||
|
resolution?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let version = pep440_rs::Version::from(version);
|
Ok(resolution.into())
|
||||||
let file = pins
|
|
||||||
.get(&package_name)
|
|
||||||
.and_then(|versions| versions.get(&version))
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
let pinned_package = PinnedPackage::new(package_name.clone(), version, file);
|
|
||||||
resolution.insert(package_name, pinned_package);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Resolution::new(resolution))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the `PubGrub` solver.
|
/// Run the `PubGrub` solver.
|
||||||
async fn solve(
|
async fn solve(
|
||||||
&self,
|
&self,
|
||||||
cache: &SolverCache,
|
cache: &SolverCache,
|
||||||
pins: &mut HashMap<PackageName, HashMap<pep440_rs::Version, File>>,
|
|
||||||
requested_packages: &mut HashSet<PackageName>,
|
|
||||||
requested_versions: &mut HashSet<String>,
|
|
||||||
request_sink: &futures::channel::mpsc::UnboundedSender<Request>,
|
request_sink: &futures::channel::mpsc::UnboundedSender<Request>,
|
||||||
) -> Result<SelectedDependencies<PubGrubPackage, PubGrubVersion>, ResolveError> {
|
) -> Result<PubGrubResolution, ResolveError> {
|
||||||
let root = PubGrubPackage::Root;
|
let root = PubGrubPackage::Root;
|
||||||
|
|
||||||
|
// Keep track of the packages for which we've requested metadata.
|
||||||
|
let mut requested_packages = HashSet::new();
|
||||||
|
let mut requested_versions = HashSet::new();
|
||||||
|
let mut pins = HashMap::new();
|
||||||
|
|
||||||
// Start the solve.
|
// Start the solve.
|
||||||
let mut state = State::init(root.clone(), MIN_VERSION.clone());
|
let mut state = State::init(root.clone(), MIN_VERSION.clone());
|
||||||
let mut added_dependencies: HashMap<PubGrubPackage, HashSet<PubGrubVersion>> =
|
let mut added_dependencies: HashMap<PubGrubPackage, HashSet<PubGrubVersion>> =
|
||||||
|
@ -170,11 +158,15 @@ impl<'a> Resolver<'a> {
|
||||||
|
|
||||||
// Fetch the list of candidates.
|
// Fetch the list of candidates.
|
||||||
let Some(potential_packages) = state.partial_solution.potential_packages() else {
|
let Some(potential_packages) = state.partial_solution.potential_packages() else {
|
||||||
return state.partial_solution.extract_solution().ok_or_else(|| {
|
let Some(selected_dependencies) = state.partial_solution.extract_solution() else {
|
||||||
PubGrubError::Failure(
|
return Err(PubGrubError::Failure(
|
||||||
"How did we end up with no package to choose but no solution?".into(),
|
"How did we end up with no package to choose but no solution?".into(),
|
||||||
)
|
)
|
||||||
.into()
|
.into());
|
||||||
|
};
|
||||||
|
return Ok(PubGrubResolution {
|
||||||
|
selected_dependencies,
|
||||||
|
pins,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -184,8 +176,8 @@ impl<'a> Resolver<'a> {
|
||||||
.choose_package_version(
|
.choose_package_version(
|
||||||
potential_packages,
|
potential_packages,
|
||||||
cache,
|
cache,
|
||||||
pins,
|
&mut pins,
|
||||||
requested_versions,
|
&mut requested_versions,
|
||||||
request_sink,
|
request_sink,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -218,8 +210,8 @@ impl<'a> Resolver<'a> {
|
||||||
package,
|
package,
|
||||||
&version,
|
&version,
|
||||||
cache,
|
cache,
|
||||||
pins,
|
&mut pins,
|
||||||
requested_packages,
|
&mut requested_packages,
|
||||||
request_sink,
|
request_sink,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
|
@ -516,3 +508,33 @@ enum Dependencies {
|
||||||
/// Container for all available package versions.
|
/// Container for all available package versions.
|
||||||
Known(DependencyConstraints<PubGrubPackage, PubGrubVersion>),
|
Known(DependencyConstraints<PubGrubPackage, PubGrubVersion>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PubGrubResolution {
|
||||||
|
/// The selected dependencies.
|
||||||
|
selected_dependencies: SelectedDependencies<PubGrubPackage, PubGrubVersion>,
|
||||||
|
/// The selected file (source or built distribution) for each package.
|
||||||
|
pins: HashMap<PackageName, HashMap<pep440_rs::Version, File>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PubGrubResolution> for Resolution {
|
||||||
|
fn from(value: PubGrubResolution) -> Self {
|
||||||
|
let mut packages = BTreeMap::new();
|
||||||
|
for (package, version) in value.selected_dependencies {
|
||||||
|
let PubGrubPackage::Package(package_name, None) = package else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let version = pep440_rs::Version::from(version);
|
||||||
|
let file = value
|
||||||
|
.pins
|
||||||
|
.get(&package_name)
|
||||||
|
.and_then(|versions| versions.get(&version))
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
let pinned_package = PinnedPackage::new(package_name.clone(), version, file);
|
||||||
|
packages.insert(package_name, pinned_package);
|
||||||
|
}
|
||||||
|
Resolution::new(packages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue