mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-16 13:30:52 +00:00
Add error trace to invalid package format (#15626)
In https://github.com/astral-sh/uv/issues/11636, we're getting reports for installation flakes that report an invalid package format for what appears to be a network problem. Since we're cutting the error reporting to the first error message in the chain, we're not reporting the actual network error underneath it. This PR displays the whole error chain for invalid package format errors, so we can debug and eventually catch-and-retry https://github.com/astral-sh/uv/issues/11636.
This commit is contained in:
parent
d70ea34d45
commit
19e19d5795
4 changed files with 56 additions and 23 deletions
|
@ -25,7 +25,8 @@ use crate::prerelease::AllowPrerelease;
|
|||
use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner, PubGrubPython};
|
||||
use crate::python_requirement::{PythonRequirement, PythonRequirementSource};
|
||||
use crate::resolver::{
|
||||
MetadataUnavailable, UnavailablePackage, UnavailableReason, UnavailableVersion,
|
||||
MetadataUnavailable, UnavailableErrorChain, UnavailablePackage, UnavailableReason,
|
||||
UnavailableVersion,
|
||||
};
|
||||
use crate::{Flexibility, InMemoryIndex, Options, ResolverEnvironment, VersionsResponse};
|
||||
|
||||
|
@ -1022,13 +1023,13 @@ pub(crate) enum PubGrubHint {
|
|||
InvalidPackageMetadata {
|
||||
package: PackageName,
|
||||
// excluded from `PartialEq` and `Hash`
|
||||
reason: String,
|
||||
reason: UnavailableErrorChain,
|
||||
},
|
||||
/// The structure of a package was invalid (e.g., multiple `.dist-info` directories).
|
||||
InvalidPackageStructure {
|
||||
package: PackageName,
|
||||
// excluded from `PartialEq` and `Hash`
|
||||
reason: String,
|
||||
reason: UnavailableErrorChain,
|
||||
},
|
||||
/// Metadata for a package version could not be parsed.
|
||||
InvalidVersionMetadata {
|
||||
|
@ -1344,21 +1345,21 @@ impl std::fmt::Display for PubGrubHint {
|
|||
Self::InvalidPackageMetadata { package, reason } => {
|
||||
write!(
|
||||
f,
|
||||
"{}{} Metadata for `{}` could not be parsed:\n{}",
|
||||
"{}{} Metadata for `{}` could not be parsed.\n{}",
|
||||
"hint".bold().cyan(),
|
||||
":".bold(),
|
||||
package.cyan(),
|
||||
textwrap::indent(reason, " ")
|
||||
textwrap::indent(reason.to_string().as_str(), " ")
|
||||
)
|
||||
}
|
||||
Self::InvalidPackageStructure { package, reason } => {
|
||||
write!(
|
||||
f,
|
||||
"{}{} The structure of `{}` was invalid:\n{}",
|
||||
"{}{} The structure of `{}` was invalid\n{}",
|
||||
"hint".bold().cyan(),
|
||||
":".bold(),
|
||||
package.cyan(),
|
||||
textwrap::indent(reason, " ")
|
||||
textwrap::indent(reason.to_string().as_str(), " ")
|
||||
)
|
||||
}
|
||||
Self::InvalidVersionMetadata {
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::resolver::{MetadataUnavailable, VersionFork};
|
||||
use uv_distribution_types::IncompatibleDist;
|
||||
use uv_pep440::{Version, VersionSpecifiers};
|
||||
use uv_platform_tags::{AbiTag, Tags};
|
||||
|
||||
use crate::resolver::{MetadataUnavailable, VersionFork};
|
||||
|
||||
/// The reason why a package or a version cannot be used.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum UnavailableReason {
|
||||
|
@ -119,6 +122,29 @@ impl From<&MetadataUnavailable> for UnavailableVersion {
|
|||
}
|
||||
}
|
||||
|
||||
/// Display the error chain for unavailable packages.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnavailableErrorChain(Arc<dyn std::error::Error + Send + Sync + 'static>);
|
||||
|
||||
impl Display for UnavailableErrorChain {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
for source in iter::successors(Some(&self.0 as &dyn std::error::Error), |&err| err.source())
|
||||
{
|
||||
writeln!(f, "Caused by: {}", source.to_string().trim())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for UnavailableErrorChain {
|
||||
/// Whether we can collapse two reasons into one because they would be rendered the same.
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.to_string() == other.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for UnavailableErrorChain {}
|
||||
|
||||
/// The package is unavailable and cannot be used.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum UnavailablePackage {
|
||||
|
@ -129,9 +155,9 @@ pub enum UnavailablePackage {
|
|||
/// The package was not found in the registry.
|
||||
NotFound,
|
||||
/// The package metadata was found, but could not be parsed.
|
||||
InvalidMetadata(String),
|
||||
InvalidMetadata(UnavailableErrorChain),
|
||||
/// The package has an invalid structure.
|
||||
InvalidStructure(String),
|
||||
InvalidStructure(UnavailableErrorChain),
|
||||
}
|
||||
|
||||
impl UnavailablePackage {
|
||||
|
@ -166,11 +192,15 @@ impl From<&MetadataUnavailable> for UnavailablePackage {
|
|||
fn from(reason: &MetadataUnavailable) -> Self {
|
||||
match reason {
|
||||
MetadataUnavailable::Offline => Self::Offline,
|
||||
MetadataUnavailable::InvalidMetadata(err) => Self::InvalidMetadata(err.to_string()),
|
||||
MetadataUnavailable::InconsistentMetadata(err) => {
|
||||
Self::InvalidMetadata(err.to_string())
|
||||
MetadataUnavailable::InvalidMetadata(err) => {
|
||||
Self::InvalidMetadata(UnavailableErrorChain(err.clone()))
|
||||
}
|
||||
MetadataUnavailable::InconsistentMetadata(err) => {
|
||||
Self::InvalidMetadata(UnavailableErrorChain(err.clone()))
|
||||
}
|
||||
MetadataUnavailable::InvalidStructure(err) => {
|
||||
Self::InvalidStructure(UnavailableErrorChain(err.clone()))
|
||||
}
|
||||
MetadataUnavailable::InvalidStructure(err) => Self::InvalidStructure(err.to_string()),
|
||||
MetadataUnavailable::RequiresPython(..) => {
|
||||
unreachable!("`requires-python` is only known upfront for registry distributions")
|
||||
}
|
||||
|
|
|
@ -56,7 +56,8 @@ use crate::python_requirement::PythonRequirement;
|
|||
use crate::resolution::ResolverOutput;
|
||||
use crate::resolution_mode::ResolutionStrategy;
|
||||
pub(crate) use crate::resolver::availability::{
|
||||
ResolverVersion, UnavailablePackage, UnavailableReason, UnavailableVersion,
|
||||
ResolverVersion, UnavailableErrorChain, UnavailablePackage, UnavailableReason,
|
||||
UnavailableVersion,
|
||||
};
|
||||
use crate::resolver::batch_prefetch::BatchPrefetcher;
|
||||
pub use crate::resolver::derivation::DerivationChainBuilder;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue