Add context and diagnostics for missing METADATA (#761)

Closes https://github.com/astral-sh/puffin/issues/717.
This commit is contained in:
Charlie Marsh 2024-01-03 20:09:23 -04:00 committed by GitHub
parent 1f2112191f
commit 2d1d6ac0dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,4 +1,5 @@
use std::hash::BuildHasherDefault; use std::hash::BuildHasherDefault;
use std::path::PathBuf;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use fs_err as fs; use fs_err as fs;
@ -169,7 +170,13 @@ impl<'a> SitePackages<'a> {
let distribution = &self.distributions[*index]; let distribution = &self.distributions[*index];
// Determine the dependencies for the given package. // Determine the dependencies for the given package.
let metadata = distribution.metadata()?; let Ok(metadata) = distribution.metadata() else {
diagnostics.push(Diagnostic::IncompletePackage {
package: package.clone(),
path: distribution.path().to_owned(),
});
continue;
};
// Verify that the package is compatible with the current Python version. // Verify that the package is compatible with the current Python version.
if let Some(requires_python) = metadata.requires_python.as_ref() { if let Some(requires_python) = metadata.requires_python.as_ref() {
@ -243,7 +250,10 @@ impl<'a> SitePackages<'a> {
}; };
// Recurse into the dependencies. // Recurse into the dependencies.
requirements.extend(distribution.metadata()?.requires_dist); let metadata = distribution
.metadata()
.with_context(|| format!("Failed to read metadata for: {distribution}"))?;
requirements.extend(metadata.requires_dist);
} }
// Verify that all non-editable requirements are met. // Verify that all non-editable requirements are met.
@ -291,7 +301,10 @@ impl<'a> SitePackages<'a> {
// Recurse into the dependencies. // Recurse into the dependencies.
if seen.insert(requirement) { if seen.insert(requirement) {
requirements.extend(distribution.metadata()?.requires_dist); let metadata = distribution
.metadata()
.with_context(|| format!("Failed to read metadata for: {distribution}"))?;
requirements.extend(metadata.requires_dist);
} }
} }
@ -310,6 +323,12 @@ impl IntoIterator for SitePackages<'_> {
#[derive(Debug)] #[derive(Debug)]
pub enum Diagnostic { pub enum Diagnostic {
IncompletePackage {
/// The package that is missing metadata.
package: PackageName,
/// The path to the package.
path: PathBuf,
},
IncompatiblePythonVersion { IncompatiblePythonVersion {
/// The package that requires a different version of Python. /// The package that requires a different version of Python.
package: PackageName, package: PackageName,
@ -338,6 +357,9 @@ impl Diagnostic {
/// Convert the diagnostic into a user-facing message. /// Convert the diagnostic into a user-facing message.
pub fn message(&self) -> String { pub fn message(&self) -> String {
match self { match self {
Self::IncompletePackage { package, path } => format!(
"The package `{package}` is broken or incomplete (unable to read `METADATA`). Consider recreating the virtual environment, or removing the package directory at: {}.", path.display(),
),
Self::IncompatiblePythonVersion { Self::IncompatiblePythonVersion {
package, package,
version, version,
@ -364,6 +386,7 @@ impl Diagnostic {
/// Returns `true` if the [`PackageName`] is involved in this diagnostic. /// Returns `true` if the [`PackageName`] is involved in this diagnostic.
pub fn includes(&self, name: &PackageName) -> bool { pub fn includes(&self, name: &PackageName) -> bool {
match self { match self {
Self::IncompletePackage { package, .. } => name == package,
Self::IncompatiblePythonVersion { package, .. } => name == package, Self::IncompatiblePythonVersion { package, .. } => name == package,
Self::MissingDependency { package, .. } => name == package, Self::MissingDependency { package, .. } => name == package,
Self::IncompatibleDependency { Self::IncompatibleDependency {