diff --git a/crates/distribution-types/src/diagnostic.rs b/crates/distribution-types/src/diagnostic.rs new file mode 100644 index 000000000..177dcd03c --- /dev/null +++ b/crates/distribution-types/src/diagnostic.rs @@ -0,0 +1,9 @@ +use uv_normalize::PackageName; + +pub trait Diagnostic { + /// Convert the diagnostic into a user-facing message. + fn message(&self) -> String; + + /// Returns `true` if the [`PackageName`] is involved in this diagnostic. + fn includes(&self, name: &PackageName) -> bool; +} diff --git a/crates/distribution-types/src/lib.rs b/crates/distribution-types/src/lib.rs index cf072ff6c..cc9025ac5 100644 --- a/crates/distribution-types/src/lib.rs +++ b/crates/distribution-types/src/lib.rs @@ -49,6 +49,7 @@ pub use crate::annotation::*; pub use crate::any::*; pub use crate::buildable::*; pub use crate::cached::*; +pub use crate::diagnostic::*; pub use crate::editable::*; pub use crate::error::*; pub use crate::file::*; @@ -68,6 +69,7 @@ mod annotation; mod any; mod buildable; mod cached; +mod diagnostic; mod editable; mod error; mod file; diff --git a/crates/distribution-types/src/resolution.rs b/crates/distribution-types/src/resolution.rs index a590054e4..463a5f4d9 100644 --- a/crates/distribution-types/src/resolution.rs +++ b/crates/distribution-types/src/resolution.rs @@ -4,22 +4,22 @@ use pep508_rs::VerbatimUrl; use uv_normalize::{ExtraName, PackageName}; use crate::{ - BuiltDist, DirectorySourceDist, Dist, InstalledDirectUrlDist, InstalledDist, LocalEditable, - Name, Requirement, RequirementSource, ResolvedDist, SourceDist, + BuiltDist, Diagnostic, DirectorySourceDist, Dist, InstalledDirectUrlDist, InstalledDist, + LocalEditable, Name, Requirement, RequirementSource, ResolvedDist, SourceDist, }; /// A set of packages pinned at specific versions. #[derive(Debug, Default, Clone)] pub struct Resolution { packages: BTreeMap, - diagnostics: Vec, + diagnostics: Vec, } impl Resolution { /// Create a new resolution from the given pinned packages. pub fn new( packages: BTreeMap, - diagnostics: Vec, + diagnostics: Vec, ) -> Self { Self { packages, @@ -63,8 +63,8 @@ impl Resolution { self.packages.values().map(Requirement::from) } - /// Return the [`Diagnostic`]s that were produced during resolution. - pub fn diagnostics(&self) -> &[Diagnostic] { + /// Return the [`ResolutionDiagnostic`]s that were produced during resolution. + pub fn diagnostics(&self) -> &[ResolutionDiagnostic] { &self.diagnostics } @@ -99,7 +99,7 @@ impl Resolution { } #[derive(Debug, Clone)] -pub enum Diagnostic { +pub enum ResolutionDiagnostic { MissingExtra { /// The distribution that was requested with a non-existent extra. For example, /// `black==23.10.0`. @@ -115,9 +115,9 @@ pub enum Diagnostic { }, } -impl Diagnostic { +impl Diagnostic for ResolutionDiagnostic { /// Convert the diagnostic into a user-facing message. - pub fn message(&self) -> String { + fn message(&self) -> String { match self { Self::MissingExtra { dist, extra } => { format!("The package `{dist}` does not have an extra named `{extra}`.") @@ -133,7 +133,7 @@ impl Diagnostic { } /// Returns `true` if the [`PackageName`] is involved in this diagnostic. - pub fn includes(&self, name: &PackageName) -> bool { + fn includes(&self, name: &PackageName) -> bool { match self { Self::MissingExtra { dist, .. } => name == dist.name(), Self::YankedVersion { dist, .. } => name == dist.name(), diff --git a/crates/uv-installer/src/lib.rs b/crates/uv-installer/src/lib.rs index e4fe8e8ca..effa69ca9 100644 --- a/crates/uv-installer/src/lib.rs +++ b/crates/uv-installer/src/lib.rs @@ -3,7 +3,7 @@ pub use downloader::{Downloader, Reporter as DownloadReporter}; pub use editable::{is_dynamic, BuiltEditable, InstalledEditable, ResolvedEditable}; pub use installer::{Installer, Reporter as InstallReporter}; pub use plan::{Plan, Planner}; -pub use site_packages::{Diagnostic, SatisfiesResult, SitePackages}; +pub use site_packages::{SatisfiesResult, SitePackages, SitePackagesDiagnostic}; pub use uninstall::{uninstall, UninstallError}; mod compile; diff --git a/crates/uv-installer/src/site_packages.rs b/crates/uv-installer/src/site_packages.rs index d33edfbcd..38e4db8ee 100644 --- a/crates/uv-installer/src/site_packages.rs +++ b/crates/uv-installer/src/site_packages.rs @@ -8,7 +8,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use url::Url; use distribution_types::{ - InstalledDist, Name, Requirement, UnresolvedRequirement, UnresolvedRequirementSpecification, + Diagnostic, InstalledDist, Name, Requirement, UnresolvedRequirement, + UnresolvedRequirementSpecification, }; use pep440_rs::{Version, VersionSpecifiers}; use requirements_txt::EditableRequirement; @@ -188,7 +189,7 @@ impl SitePackages { } /// Validate the installed packages in the virtual environment. - pub fn diagnostics(&self) -> Result> { + pub fn diagnostics(&self) -> Result> { let mut diagnostics = Vec::new(); for (package, indexes) in &self.by_name { @@ -201,7 +202,7 @@ impl SitePackages { if let Some(conflict) = distributions.next() { // There are multiple installed distributions for the same package. - diagnostics.push(Diagnostic::DuplicatePackage { + diagnostics.push(SitePackagesDiagnostic::DuplicatePackage { package: package.clone(), paths: std::iter::once(distribution.path().to_owned()) .chain(std::iter::once(conflict.path().to_owned())) @@ -218,7 +219,7 @@ impl SitePackages { // Determine the dependencies for the given package. let Ok(metadata) = distribution.metadata() else { - diagnostics.push(Diagnostic::IncompletePackage { + diagnostics.push(SitePackagesDiagnostic::IncompletePackage { package: package.clone(), path: distribution.path().to_owned(), }); @@ -228,7 +229,7 @@ impl SitePackages { // Verify that the package is compatible with the current Python version. if let Some(requires_python) = metadata.requires_python.as_ref() { if !requires_python.contains(self.venv.interpreter().python_version()) { - diagnostics.push(Diagnostic::IncompatiblePythonVersion { + diagnostics.push(SitePackagesDiagnostic::IncompatiblePythonVersion { package: package.clone(), version: self.venv.interpreter().python_version().clone(), requires_python: requires_python.clone(), @@ -246,7 +247,7 @@ impl SitePackages { match installed.as_slice() { [] => { // No version installed. - diagnostics.push(Diagnostic::MissingDependency { + diagnostics.push(SitePackagesDiagnostic::MissingDependency { package: package.clone(), requirement: dependency.clone(), }); @@ -261,11 +262,13 @@ impl SitePackages { )) => { // The installed version doesn't satisfy the requirement. if !version_specifier.contains(installed.version()) { - diagnostics.push(Diagnostic::IncompatibleDependency { - package: package.clone(), - version: installed.version().clone(), - requirement: dependency.clone(), - }); + diagnostics.push( + SitePackagesDiagnostic::IncompatibleDependency { + package: package.clone(), + version: installed.version().clone(), + requirement: dependency.clone(), + }, + ); } } } @@ -449,7 +452,7 @@ impl IntoIterator for SitePackages { } #[derive(Debug)] -pub enum Diagnostic { +pub enum SitePackagesDiagnostic { IncompletePackage { /// The package that is missing metadata. package: PackageName, @@ -486,9 +489,9 @@ pub enum Diagnostic { }, } -impl Diagnostic { +impl Diagnostic for SitePackagesDiagnostic { /// Convert the diagnostic into a user-facing message. - pub fn message(&self) -> String { + fn message(&self) -> String { match self { Self::IncompletePackage { package, path } => format!( "The package `{package}` is broken or incomplete (unable to read `METADATA`). Consider recreating the virtualenv, or removing the package directory at: {}.", path.display(), @@ -525,7 +528,7 @@ impl Diagnostic { } /// Returns `true` if the [`PackageName`] is involved in this diagnostic. - pub fn includes(&self, name: &PackageName) -> bool { + fn includes(&self, name: &PackageName) -> bool { match self { Self::IncompletePackage { package, .. } => name == package, Self::IncompatiblePythonVersion { package, .. } => name == package, diff --git a/crates/uv-resolver/src/resolution/graph.rs b/crates/uv-resolver/src/resolution/graph.rs index ce7d7c0d7..c40aba193 100644 --- a/crates/uv-resolver/src/resolution/graph.rs +++ b/crates/uv-resolver/src/resolution/graph.rs @@ -7,8 +7,8 @@ use pubgrub::type_aliases::SelectedDependencies; use rustc_hash::{FxHashMap, FxHashSet}; use distribution_types::{ - Diagnostic, Dist, DistributionMetadata, Name, ParsedUrlError, Requirement, ResolvedDist, - VersionId, VersionOrUrlRef, + Dist, DistributionMetadata, Name, ParsedUrlError, Requirement, ResolutionDiagnostic, + ResolvedDist, VersionId, VersionOrUrlRef, }; use pep440_rs::{Version, VersionSpecifier}; use pep508_rs::MarkerEnvironment; @@ -37,7 +37,7 @@ pub struct ResolutionGraph { /// The set of editable requirements in this resolution. pub(crate) editables: Editables, /// Any diagnostics that were encountered while building the graph. - pub(crate) diagnostics: Vec, + pub(crate) diagnostics: Vec, } impl ResolutionGraph { @@ -90,7 +90,7 @@ impl ResolutionGraph { .unwrap_or_else(|| panic!("Every package should be pinned: {name:?}")) .clone(); - diagnostics.push(Diagnostic::MissingExtra { + diagnostics.push(ResolutionDiagnostic::MissingExtra { dist, extra: extra.clone(), }); @@ -111,7 +111,7 @@ impl ResolutionGraph { } else { let dist = Dist::from_editable(name.clone(), editable.built.clone())?; - diagnostics.push(Diagnostic::MissingExtra { + diagnostics.push(ResolutionDiagnostic::MissingExtra { dist: dist.into(), extra: extra.clone(), }); @@ -141,7 +141,7 @@ impl ResolutionGraph { } else { let dist = Dist::from_url(name.clone(), url_to_precise(url.clone()))?; - diagnostics.push(Diagnostic::MissingExtra { + diagnostics.push(ResolutionDiagnostic::MissingExtra { dist: dist.into(), extra: extra.clone(), }); @@ -177,13 +177,13 @@ impl ResolutionGraph { match dist.yanked() { None | Some(Yanked::Bool(false)) => {} Some(Yanked::Bool(true)) => { - diagnostics.push(Diagnostic::YankedVersion { + diagnostics.push(ResolutionDiagnostic::YankedVersion { dist: dist.clone(), reason: None, }); } Some(Yanked::Reason(reason)) => { - diagnostics.push(Diagnostic::YankedVersion { + diagnostics.push(ResolutionDiagnostic::YankedVersion { dist: dist.clone(), reason: Some(reason.clone()), }); @@ -411,8 +411,8 @@ impl ResolutionGraph { .map(|node| node.weight.dist) } - /// Return the [`Diagnostic`]s that were encountered while building the graph. - pub fn diagnostics(&self) -> &[Diagnostic] { + /// Return the [`ResolutionDiagnostic`]s that were encountered while building the graph. + pub fn diagnostics(&self) -> &[ResolutionDiagnostic] { &self.diagnostics } diff --git a/crates/uv/src/commands/pip/check.rs b/crates/uv/src/commands/pip/check.rs index 86248c1e1..63be05927 100644 --- a/crates/uv/src/commands/pip/check.rs +++ b/crates/uv/src/commands/pip/check.rs @@ -5,10 +5,10 @@ use anyhow::Result; use owo_colors::OwoColorize; use tracing::debug; -use distribution_types::InstalledDist; +use distribution_types::{Diagnostic, InstalledDist}; use uv_cache::Cache; use uv_fs::Simplified; -use uv_installer::{Diagnostic, SitePackages}; +use uv_installer::{SitePackages, SitePackagesDiagnostic}; use uv_interpreter::{PythonEnvironment, SystemPython}; use crate::commands::{elapsed, ExitStatus}; @@ -53,7 +53,8 @@ pub(crate) fn pip_check( .dimmed() )?; - let diagnostics: Vec = site_packages.diagnostics()?.into_iter().collect(); + let diagnostics: Vec = + site_packages.diagnostics()?.into_iter().collect(); if diagnostics.is_empty() { writeln!( diff --git a/crates/uv/src/commands/pip/freeze.rs b/crates/uv/src/commands/pip/freeze.rs index ff59ed8d9..f9d7db32c 100644 --- a/crates/uv/src/commands/pip/freeze.rs +++ b/crates/uv/src/commands/pip/freeze.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use owo_colors::OwoColorize; use tracing::debug; -use distribution_types::{InstalledDist, Name}; +use distribution_types::{Diagnostic, InstalledDist, Name}; use uv_cache::Cache; use uv_fs::Simplified; use uv_installer::SitePackages; diff --git a/crates/uv/src/commands/pip/list.rs b/crates/uv/src/commands/pip/list.rs index 1aa17ee9c..504ae90c3 100644 --- a/crates/uv/src/commands/pip/list.rs +++ b/crates/uv/src/commands/pip/list.rs @@ -8,7 +8,7 @@ use serde::Serialize; use tracing::debug; use unicode_width::UnicodeWidthStr; -use distribution_types::{InstalledDist, Name}; +use distribution_types::{Diagnostic, InstalledDist, Name}; use uv_cache::Cache; use uv_fs::Simplified; use uv_installer::SitePackages; diff --git a/crates/uv/src/commands/pip/operations.rs b/crates/uv/src/commands/pip/operations.rs index b0cf0caba..80a095aca 100644 --- a/crates/uv/src/commands/pip/operations.rs +++ b/crates/uv/src/commands/pip/operations.rs @@ -9,7 +9,8 @@ use owo_colors::OwoColorize; use tracing::debug; use distribution_types::{ - CachedDist, Diagnostic, InstalledDist, Requirement, UnresolvedRequirementSpecification, + CachedDist, Diagnostic, InstalledDist, Requirement, ResolutionDiagnostic, + UnresolvedRequirementSpecification, }; use distribution_types::{ DistributionMetadata, IndexLocations, InstalledMetadata, InstalledVersion, LocalDist, Name, @@ -708,7 +709,7 @@ pub(crate) fn report_modifications( /// Report any diagnostics on resolved distributions. pub(crate) fn diagnose_resolution( - diagnostics: &[Diagnostic], + diagnostics: &[ResolutionDiagnostic], printer: Printer, ) -> Result<(), Error> { for diagnostic in diagnostics { diff --git a/crates/uv/src/commands/pip/show.rs b/crates/uv/src/commands/pip/show.rs index c98da09d6..6df022ddc 100644 --- a/crates/uv/src/commands/pip/show.rs +++ b/crates/uv/src/commands/pip/show.rs @@ -6,7 +6,7 @@ use owo_colors::OwoColorize; use rustc_hash::FxHashMap; use tracing::debug; -use distribution_types::Name; +use distribution_types::{Diagnostic, Name}; use uv_cache::Cache; use uv_fs::Simplified; use uv_installer::SitePackages;