Skip to content

Commit 651a074

Browse files
committed
Add chain inference for errors
More Simplify Starting to come together Reverts Trying to get the lock info from sync Rewrite resolution into a graph Add version to ResolvedDist Fix up graphs Attach to install everywhere Attach to install everywhere
1 parent 18317fb commit 651a074

File tree

23 files changed

+1088
-178
lines changed

23 files changed

+1088
-178
lines changed

crates/uv-configuration/src/install_options.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ impl InstallOptions {
2727
}
2828
}
2929

30+
/// Returns `true` if the options indicate that all packages should be installed.
31+
pub fn all(&self) -> bool {
32+
!self.no_install_project && !self.no_install_workspace && self.no_install_package.is_empty()
33+
}
34+
3035
/// Returns `true` if a package passes the install filters.
3136
pub fn include_package(
3237
&self,

crates/uv-dispatch/src/lib.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages};
2929
use uv_pypi_types::{ConflictingGroupList, Requirement};
3030
use uv_python::{Interpreter, PythonEnvironment};
3131
use uv_resolver::{
32-
ExcludeNewer, FlatIndex, Flexibility, InMemoryIndex, Manifest, OptionsBuilder,
33-
PythonRequirement, Resolver, ResolverEnvironment,
32+
DerivationChainBuilder, ExcludeNewer, FlatIndex, Flexibility, InMemoryIndex, Manifest,
33+
OptionsBuilder, PythonRequirement, Resolver, ResolverEnvironment,
3434
};
3535
use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
3636

@@ -278,7 +278,33 @@ impl<'a> BuildContext for BuildDispatch<'a> {
278278
remote.iter().map(ToString::to_string).join(", ")
279279
);
280280

281-
preparer.prepare(remote, self.in_flight).await?
281+
preparer
282+
.prepare(remote, self.in_flight)
283+
.await
284+
.map_err(|err| match err {
285+
uv_installer::PrepareError::DownloadAndBuild(dist, chain, err) => {
286+
debug_assert!(chain.is_empty());
287+
let chain =
288+
DerivationChainBuilder::from_resolution(resolution, (&*dist).into())
289+
.unwrap_or_default();
290+
uv_installer::PrepareError::DownloadAndBuild(dist, chain, err)
291+
}
292+
uv_installer::PrepareError::Download(dist, chain, err) => {
293+
debug_assert!(chain.is_empty());
294+
let chain =
295+
DerivationChainBuilder::from_resolution(resolution, (&*dist).into())
296+
.unwrap_or_default();
297+
uv_installer::PrepareError::Download(dist, chain, err)
298+
}
299+
uv_installer::PrepareError::Build(dist, chain, err) => {
300+
debug_assert!(chain.is_empty());
301+
let chain =
302+
DerivationChainBuilder::from_resolution(resolution, (&*dist).into())
303+
.unwrap_or_default();
304+
uv_installer::PrepareError::Build(dist, chain, err)
305+
}
306+
_ => err,
307+
})?
282308
};
283309

284310
// Remove any unnecessary packages.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use uv_normalize::PackageName;
2+
use uv_pep440::Version;
3+
4+
/// A chain of derivation steps from the root package to the current package, to explain why a
5+
/// package is included in the resolution.
6+
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
7+
pub struct DerivationChain(Vec<DerivationStep>);
8+
9+
impl FromIterator<DerivationStep> for DerivationChain {
10+
fn from_iter<T: IntoIterator<Item = DerivationStep>>(iter: T) -> Self {
11+
Self(iter.into_iter().collect())
12+
}
13+
}
14+
15+
impl DerivationChain {
16+
/// Returns the length of the derivation chain.
17+
pub fn len(&self) -> usize {
18+
self.0.len()
19+
}
20+
21+
/// Returns `true` if the derivation chain is empty.
22+
pub fn is_empty(&self) -> bool {
23+
self.0.is_empty()
24+
}
25+
26+
/// Returns an iterator over the steps in the derivation chain.
27+
pub fn iter(&self) -> std::slice::Iter<DerivationStep> {
28+
self.0.iter()
29+
}
30+
}
31+
32+
impl std::fmt::Display for DerivationChain {
33+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34+
for (idx, step) in self.0.iter().enumerate() {
35+
if idx > 0 {
36+
write!(f, " -> ")?;
37+
}
38+
write!(f, "{}=={}", step.name, step.version)?;
39+
}
40+
Ok(())
41+
}
42+
}
43+
44+
impl<'chain> IntoIterator for &'chain DerivationChain {
45+
type Item = &'chain DerivationStep;
46+
type IntoIter = std::slice::Iter<'chain, DerivationStep>;
47+
48+
fn into_iter(self) -> Self::IntoIter {
49+
self.0.iter()
50+
}
51+
}
52+
53+
impl IntoIterator for DerivationChain {
54+
type Item = DerivationStep;
55+
type IntoIter = std::vec::IntoIter<DerivationStep>;
56+
57+
fn into_iter(self) -> Self::IntoIter {
58+
self.0.into_iter()
59+
}
60+
}
61+
62+
/// A step in a derivation chain.
63+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
64+
pub struct DerivationStep {
65+
/// The name of the package.
66+
name: PackageName,
67+
/// The version of the package.
68+
version: Version,
69+
}
70+
71+
impl DerivationStep {
72+
/// Create a [`DerivationStep`] from a package name and version.
73+
pub fn new(name: PackageName, version: Version) -> Self {
74+
Self { name, version }
75+
}
76+
}
77+
78+
impl std::fmt::Display for DerivationStep {
79+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80+
write!(f, "{}=={}", self.name, self.version)
81+
}
82+
}

crates/uv-distribution-types/src/file.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub enum FileConversionError {
2121
}
2222

2323
/// Internal analog to [`uv_pypi_types::File`].
24-
#[derive(Debug, Clone, Hash, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
24+
#[derive(Debug, Clone, PartialEq, Eq, Hash, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
2525
#[rkyv(derive(Debug))]
2626
pub struct File {
2727
pub dist_info_metadata: bool,
@@ -66,7 +66,7 @@ impl File {
6666
}
6767

6868
/// While a registry file is generally a remote URL, it can also be a file if it comes from a directory flat indexes.
69-
#[derive(Debug, Clone, Hash, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
69+
#[derive(Debug, Clone, PartialEq, Eq, Hash, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
7070
#[rkyv(derive(Debug))]
7171
pub enum FileLocation {
7272
/// URL relative to the base URL.

crates/uv-distribution-types/src/lib.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub use crate::any::*;
5252
pub use crate::buildable::*;
5353
pub use crate::cached::*;
5454
pub use crate::dependency_metadata::*;
55+
pub use crate::derivation::*;
5556
pub use crate::diagnostic::*;
5657
pub use crate::error::*;
5758
pub use crate::file::*;
@@ -74,6 +75,7 @@ mod any;
7475
mod buildable;
7576
mod cached;
7677
mod dependency_metadata;
78+
mod derivation;
7779
mod diagnostic;
7880
mod error;
7981
mod file;
@@ -166,14 +168,21 @@ impl std::fmt::Display for InstalledVersion<'_> {
166168
/// Either a built distribution, a wheel, or a source distribution that exists at some location.
167169
///
168170
/// The location can be an index, URL or path (wheel), or index, URL, path or Git repository (source distribution).
169-
#[derive(Debug, Clone, Hash)]
171+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
170172
pub enum Dist {
171173
Built(BuiltDist),
172174
Source(SourceDist),
173175
}
174176

177+
/// A reference to a built or source distribution.
178+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
179+
pub enum DistRef<'a> {
180+
Built(&'a BuiltDist),
181+
Source(&'a SourceDist),
182+
}
183+
175184
/// A wheel, with its three possible origins (index, url, path)
176-
#[derive(Debug, Clone, Hash)]
185+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
177186
#[allow(clippy::large_enum_variant)]
178187
pub enum BuiltDist {
179188
Registry(RegistryBuiltDist),
@@ -182,7 +191,7 @@ pub enum BuiltDist {
182191
}
183192

184193
/// A source distribution, with its possible origins (index, url, path, git)
185-
#[derive(Debug, Clone, Hash)]
194+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
186195
#[allow(clippy::large_enum_variant)]
187196
pub enum SourceDist {
188197
Registry(RegistrySourceDist),
@@ -193,15 +202,15 @@ pub enum SourceDist {
193202
}
194203

195204
/// A built distribution (wheel) that exists in a registry, like `PyPI`.
196-
#[derive(Debug, Clone, Hash)]
205+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
197206
pub struct RegistryBuiltWheel {
198207
pub filename: WheelFilename,
199208
pub file: Box<File>,
200209
pub index: IndexUrl,
201210
}
202211

203212
/// A built distribution (wheel) that exists in a registry, like `PyPI`.
204-
#[derive(Debug, Clone, Hash)]
213+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
205214
pub struct RegistryBuiltDist {
206215
/// All wheels associated with this distribution. It is guaranteed
207216
/// that there is at least one wheel.
@@ -231,7 +240,7 @@ pub struct RegistryBuiltDist {
231240
}
232241

233242
/// A built distribution (wheel) that exists at an arbitrary URL.
234-
#[derive(Debug, Clone, Hash)]
243+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
235244
pub struct DirectUrlBuiltDist {
236245
/// We require that wheel urls end in the full wheel filename, e.g.
237246
/// `https://example.org/packages/flask-3.0.0-py3-none-any.whl`
@@ -243,7 +252,7 @@ pub struct DirectUrlBuiltDist {
243252
}
244253

245254
/// A built distribution (wheel) that exists in a local directory.
246-
#[derive(Debug, Clone, Hash)]
255+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
247256
pub struct PathBuiltDist {
248257
pub filename: WheelFilename,
249258
/// The absolute path to the wheel which we use for installing.
@@ -253,7 +262,7 @@ pub struct PathBuiltDist {
253262
}
254263

255264
/// A source distribution that exists in a registry, like `PyPI`.
256-
#[derive(Debug, Clone, Hash)]
265+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
257266
pub struct RegistrySourceDist {
258267
pub name: PackageName,
259268
pub version: Version,
@@ -272,7 +281,7 @@ pub struct RegistrySourceDist {
272281
}
273282

274283
/// A source distribution that exists at an arbitrary URL.
275-
#[derive(Debug, Clone, Hash)]
284+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
276285
pub struct DirectUrlSourceDist {
277286
/// Unlike [`DirectUrlBuiltDist`], we can't require a full filename with a version here, people
278287
/// like using e.g. `foo @ https://github.com/org/repo/archive/master.zip`
@@ -288,7 +297,7 @@ pub struct DirectUrlSourceDist {
288297
}
289298

290299
/// A source distribution that exists in a Git repository.
291-
#[derive(Debug, Clone, Hash)]
300+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
292301
pub struct GitSourceDist {
293302
pub name: PackageName,
294303
/// The URL without the revision and subdirectory fragment.
@@ -300,7 +309,7 @@ pub struct GitSourceDist {
300309
}
301310

302311
/// A source distribution that exists in a local archive (e.g., a `.tar.gz` file).
303-
#[derive(Debug, Clone, Hash)]
312+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
304313
pub struct PathSourceDist {
305314
pub name: PackageName,
306315
/// The absolute path to the distribution which we use for installing.
@@ -312,7 +321,7 @@ pub struct PathSourceDist {
312321
}
313322

314323
/// A source distribution that exists in a local directory.
315-
#[derive(Debug, Clone, Hash)]
324+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
316325
pub struct DirectorySourceDist {
317326
pub name: PackageName,
318327
/// The absolute path to the distribution which we use for installing.
@@ -512,12 +521,33 @@ impl Dist {
512521
}
513522
}
514523

524+
/// Returns the version of the distribution, if it is known.
515525
pub fn version(&self) -> Option<&Version> {
516526
match self {
517527
Self::Built(wheel) => Some(wheel.version()),
518528
Self::Source(source_dist) => source_dist.version(),
519529
}
520530
}
531+
532+
/// Convert this distribution into a reference.
533+
pub fn as_ref(&self) -> DistRef {
534+
match self {
535+
Self::Built(dist) => DistRef::Built(dist),
536+
Self::Source(dist) => DistRef::Source(dist),
537+
}
538+
}
539+
}
540+
541+
impl<'a> From<&'a SourceDist> for DistRef<'a> {
542+
fn from(dist: &'a SourceDist) -> Self {
543+
DistRef::Source(dist)
544+
}
545+
}
546+
547+
impl<'a> From<&'a BuiltDist> for DistRef<'a> {
548+
fn from(dist: &'a BuiltDist) -> Self {
549+
DistRef::Built(dist)
550+
}
521551
}
522552

523553
impl BuiltDist {

crates/uv-distribution-types/src/resolution.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,6 @@ impl Diagnostic for ResolutionDiagnostic {
174174

175175
/// A node in the resolution, along with whether its been filtered out.
176176
///
177-
/// This is similar to [`ResolutionGraph`], but represents a resolution for a single platform.
178-
///
179177
/// We retain filtered nodes as we still need to be able to trace dependencies through the graph
180178
/// (e.g., to determine why a package was install in the resolution).
181179
#[derive(Debug, Clone)]

0 commit comments

Comments
 (0)