Make missing METADATA file a recoverable error (#4247)

## Summary

I don't have a great way to test it, but this makes the error described
in https://github.com/astral-sh/uv/issues/4246 an incompatibility rather
than a fatal error.

Closes https://github.com/astral-sh/uv/issues/4246.
This commit is contained in:
Charlie Marsh 2024-06-11 12:49:38 -07:00 committed by GitHub
parent 8cfe202e4e
commit 6dae1920af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 67 additions and 13 deletions

1
Cargo.lock generated
View file

@ -4544,7 +4544,6 @@ dependencies = [
"serde",
"serde_json",
"sys-info",
"tempfile",
"thiserror",
"tl",
"tokio",

View file

@ -38,7 +38,6 @@ rmp-serde = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sys-info = { workspace = true }
tempfile = { workspace = true }
thiserror = { workspace = true }
tl = { workspace = true }
tokio = { workspace = true }

View file

@ -162,10 +162,6 @@ pub enum ErrorKind {
#[error("Metadata file `{0}` was not found in {1}")]
MetadataNotFound(WheelFilename, String),
/// The metadata file was not found in the registry.
#[error("File `{0}` was not found in the registry at {1}.")]
FileNotFound(String, #[source] BetterReqwestError),
/// A generic request error happened while making a request. Refer to the
/// error message for more details.
#[error(transparent)]
@ -185,9 +181,6 @@ pub enum ErrorKind {
#[error(transparent)]
AsyncHttpRangeReader(#[from] AsyncHttpRangeReaderError),
#[error("Expected a single .dist-info directory in {0}, found {1}")]
InvalidDistInfo(WheelFilename, String),
#[error("{0} is not a valid wheel filename")]
WheelFilename(#[source] WheelFilenameError),
@ -212,10 +205,6 @@ pub enum ErrorKind {
#[error("Cache serialization failed")]
Encode(#[source] rmp_serde::encode::Error),
/// An [`io::Error`] with a filename attached
#[error(transparent)]
Persist(#[from] tempfile::PersistError),
#[error("Missing `Content-Type` header for {0}")]
MissingContentType(Url),

View file

@ -476,6 +476,11 @@ impl PubGrubReportFormatter<'_> {
Some(UnavailablePackage::Offline) => {
hints.insert(PubGrubHint::Offline);
}
Some(UnavailablePackage::MissingMetadata) => {
hints.insert(PubGrubHint::MissingPackageMetadata {
package: package.clone(),
});
}
Some(UnavailablePackage::InvalidMetadata(reason)) => {
hints.insert(PubGrubHint::InvalidPackageMetadata {
package: package.clone(),
@ -500,6 +505,12 @@ impl PubGrubReportFormatter<'_> {
IncompletePackage::Offline => {
hints.insert(PubGrubHint::Offline);
}
IncompletePackage::MissingMetadata => {
hints.insert(PubGrubHint::MissingVersionMetadata {
package: package.clone(),
version: version.clone(),
});
}
IncompletePackage::InvalidMetadata(reason) => {
hints.insert(PubGrubHint::InvalidVersionMetadata {
package: package.clone(),
@ -601,6 +612,8 @@ pub(crate) enum PubGrubHint {
NoIndex,
/// A package was not found in the registry, but network access was disabled.
Offline,
/// Metadata for a package could not be found.
MissingPackageMetadata { package: PubGrubPackage },
/// Metadata for a package could not be parsed.
InvalidPackageMetadata {
package: PubGrubPackage,
@ -613,6 +626,12 @@ pub(crate) enum PubGrubHint {
#[derivative(PartialEq = "ignore", Hash = "ignore")]
reason: String,
},
/// Metadata for a package version could not be found.
MissingVersionMetadata {
package: PubGrubPackage,
#[derivative(PartialEq = "ignore", Hash = "ignore")]
version: Version,
},
/// Metadata for a package version could not be parsed.
InvalidVersionMetadata {
package: PubGrubPackage,
@ -689,6 +708,15 @@ impl std::fmt::Display for PubGrubHint {
":".bold(),
)
}
Self::MissingPackageMetadata { package } => {
write!(
f,
"{}{} Metadata for {} could not be found, as the wheel is missing a `METADATA` file",
"hint".bold().cyan(),
":".bold(),
package.bold()
)
}
Self::InvalidPackageMetadata { package, reason } => {
write!(
f,
@ -709,6 +737,16 @@ impl std::fmt::Display for PubGrubHint {
textwrap::indent(reason, " ")
)
}
Self::MissingVersionMetadata { package, version } => {
write!(
f,
"{}{} Metadata for {}=={} could not be found, as the wheel is missing a `METADATA` file",
"hint".bold().cyan(),
":".bold(),
package.bold(),
version.bold(),
)
}
Self::InvalidVersionMetadata {
package,
version,

View file

@ -30,6 +30,8 @@ impl Display for UnavailableReason {
pub(crate) enum UnavailableVersion {
/// Version is incompatible because it has no usable distributions
IncompatibleDist(IncompatibleDist),
/// The wheel metadata was not found.
MissingMetadata,
/// The wheel metadata was found, but could not be parsed.
InvalidMetadata,
/// The wheel metadata was found, but the metadata was inconsistent.
@ -46,6 +48,9 @@ impl Display for UnavailableVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
UnavailableVersion::IncompatibleDist(invalid_dist) => Display::fmt(invalid_dist, f),
UnavailableVersion::MissingMetadata => {
f.write_str("does not include a `METADATA` file")
}
UnavailableVersion::InvalidMetadata => f.write_str("has invalid metadata"),
UnavailableVersion::InconsistentMetadata => f.write_str("has inconsistent metadata"),
UnavailableVersion::InvalidStructure => f.write_str("has an invalid package format"),
@ -66,6 +71,8 @@ pub(crate) enum UnavailablePackage {
Offline,
/// The package was not found in the registry.
NotFound,
/// The package metadata was not found.
MissingMetadata,
/// The package metadata was found, but could not be parsed.
InvalidMetadata(String),
/// The package has an invalid structure.
@ -78,6 +85,7 @@ impl UnavailablePackage {
UnavailablePackage::NoIndex => "was not found in the provided package locations",
UnavailablePackage::Offline => "was not found in the cache",
UnavailablePackage::NotFound => "was not found in the package registry",
UnavailablePackage::MissingMetadata => "does not include a `METADATA` file",
UnavailablePackage::InvalidMetadata(_) => "has invalid metadata",
UnavailablePackage::InvalidStructure(_) => "has an invalid package format",
}
@ -95,6 +103,8 @@ impl Display for UnavailablePackage {
pub(crate) enum IncompletePackage {
/// Network requests were disabled (i.e., `--offline`), and the wheel metadata was not found in the cache.
Offline,
/// The wheel metadata was not found.
MissingMetadata,
/// The wheel metadata was found, but could not be parsed.
InvalidMetadata(String),
/// The wheel metadata was found, but the metadata was inconsistent.

View file

@ -749,6 +749,11 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
.insert(name.clone(), UnavailablePackage::Offline);
return Ok(None);
}
MetadataResponse::MissingMetadata => {
self.unavailable_packages
.insert(name.clone(), UnavailablePackage::MissingMetadata);
return Ok(None);
}
MetadataResponse::InvalidMetadata(err) => {
self.unavailable_packages.insert(
name.clone(),
@ -1042,6 +1047,15 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
.insert(version.clone(), IncompletePackage::Offline);
return Ok(Dependencies::Unavailable(UnavailableVersion::Offline));
}
MetadataResponse::MissingMetadata => {
self.incomplete_packages
.entry(name.clone())
.or_default()
.insert(version.clone(), IncompletePackage::MissingMetadata);
return Ok(Dependencies::Unavailable(
UnavailableVersion::MissingMetadata,
));
}
MetadataResponse::InvalidMetadata(err) => {
warn!("Unable to extract metadata for {name}: {err}");
self.incomplete_packages

View file

@ -33,6 +33,8 @@ pub enum VersionsResponse {
pub enum MetadataResponse {
/// The wheel metadata was found and parsed successfully.
Found(ArchiveMetadata),
/// The wheel metadata was not found.
MissingMetadata,
/// The wheel metadata was found, but could not be parsed.
InvalidMetadata(Box<pypi_types::MetadataError>),
/// The wheel metadata was found, but the metadata was inconsistent.
@ -184,6 +186,9 @@ impl<'a, Context: BuildContext> ResolverProvider for DefaultResolverProvider<'a,
Err(err) => match err {
uv_distribution::Error::Client(client) => match client.into_kind() {
uv_client::ErrorKind::Offline(_) => Ok(MetadataResponse::Offline),
uv_client::ErrorKind::MetadataNotFound(_, _) => {
Ok(MetadataResponse::MissingMetadata)
}
uv_client::ErrorKind::MetadataParseError(_, _, err) => {
Ok(MetadataResponse::InvalidMetadata(err))
}