mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-17 22:07:47 +00:00
Refactor unavailable metadata to shrink the resolver (#9769)
The resolver methods are already too large and complex, especially `choose_version*`, so i wanted to shrink and simplify them a bit before adding new methods to them. I've split `MetadataResponse` into three variants: success, non-fatal error (reported through pubgrub), fatal error (reported as error trace). The resulting non-fatal `MetadataUnavailable` type is equivalent to the `IncompletePackage` type, so they are now merged. (`UnavailableVersion` is a bit different since, besides the extra `IncompatibleDist` variant, it have no error source attached). This shows that the missing metadata variant was unused, which I removed. Tagging as error messages for the logging format changes.
This commit is contained in:
parent
edf875e306
commit
b751648bfe
6 changed files with 138 additions and 256 deletions
|
@ -24,7 +24,7 @@ use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner, PubGrubReportFormatter
|
|||
use crate::python_requirement::PythonRequirement;
|
||||
use crate::resolution::ConflictingDistributionError;
|
||||
use crate::resolver::{
|
||||
IncompletePackage, ResolverEnvironment, UnavailablePackage, UnavailableReason,
|
||||
MetadataUnavailable, ResolverEnvironment, UnavailablePackage, UnavailableReason,
|
||||
};
|
||||
use crate::Options;
|
||||
|
||||
|
@ -145,7 +145,7 @@ pub struct NoSolutionError {
|
|||
index_locations: IndexLocations,
|
||||
index_capabilities: IndexCapabilities,
|
||||
unavailable_packages: FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, MetadataUnavailable>>,
|
||||
fork_urls: ForkUrls,
|
||||
env: ResolverEnvironment,
|
||||
workspace_members: BTreeSet<PackageName>,
|
||||
|
@ -163,7 +163,7 @@ impl NoSolutionError {
|
|||
index_locations: IndexLocations,
|
||||
index_capabilities: IndexCapabilities,
|
||||
unavailable_packages: FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, MetadataUnavailable>>,
|
||||
fork_urls: ForkUrls,
|
||||
env: ResolverEnvironment,
|
||||
workspace_members: BTreeSet<PackageName>,
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::error::ErrorTree;
|
|||
use crate::fork_urls::ForkUrls;
|
||||
use crate::prerelease::AllowPrerelease;
|
||||
use crate::python_requirement::{PythonRequirement, PythonRequirementSource};
|
||||
use crate::resolver::{IncompletePackage, UnavailablePackage, UnavailableReason};
|
||||
use crate::resolver::{MetadataUnavailable, UnavailablePackage, UnavailableReason};
|
||||
use crate::{Flexibility, Options, RequiresPython, ResolverEnvironment};
|
||||
|
||||
use super::{PubGrubPackage, PubGrubPackageInner, PubGrubPython};
|
||||
|
@ -548,7 +548,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
index_capabilities: &IndexCapabilities,
|
||||
available_indexes: &FxHashMap<PackageName, BTreeSet<IndexUrl>>,
|
||||
unavailable_packages: &FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: &FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
incomplete_packages: &FxHashMap<PackageName, BTreeMap<Version, MetadataUnavailable>>,
|
||||
fork_urls: &ForkUrls,
|
||||
env: &ResolverEnvironment,
|
||||
workspace_members: &BTreeSet<PackageName>,
|
||||
|
@ -679,7 +679,7 @@ impl PubGrubReportFormatter<'_> {
|
|||
index_capabilities: &IndexCapabilities,
|
||||
available_indexes: &FxHashMap<PackageName, BTreeSet<IndexUrl>>,
|
||||
unavailable_packages: &FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: &FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
incomplete_packages: &FxHashMap<PackageName, BTreeMap<Version, MetadataUnavailable>>,
|
||||
hints: &mut IndexSet<PubGrubHint>,
|
||||
) {
|
||||
let no_find_links = index_locations.flat_indexes().peekable().peek().is_none();
|
||||
|
@ -694,11 +694,6 @@ 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(),
|
||||
|
@ -720,37 +715,31 @@ impl PubGrubReportFormatter<'_> {
|
|||
for (version, incomplete) in versions.iter().rev() {
|
||||
if set.contains(version) {
|
||||
match incomplete {
|
||||
IncompletePackage::Offline => {
|
||||
MetadataUnavailable::Offline => {
|
||||
hints.insert(PubGrubHint::Offline);
|
||||
}
|
||||
IncompletePackage::MissingMetadata => {
|
||||
hints.insert(PubGrubHint::MissingVersionMetadata {
|
||||
package: package.clone(),
|
||||
version: version.clone(),
|
||||
});
|
||||
}
|
||||
IncompletePackage::InvalidMetadata(reason) => {
|
||||
MetadataUnavailable::InvalidMetadata(reason) => {
|
||||
hints.insert(PubGrubHint::InvalidVersionMetadata {
|
||||
package: package.clone(),
|
||||
version: version.clone(),
|
||||
reason: reason.clone(),
|
||||
reason: reason.to_string(),
|
||||
});
|
||||
}
|
||||
IncompletePackage::InconsistentMetadata(reason) => {
|
||||
MetadataUnavailable::InconsistentMetadata(reason) => {
|
||||
hints.insert(PubGrubHint::InconsistentVersionMetadata {
|
||||
package: package.clone(),
|
||||
version: version.clone(),
|
||||
reason: reason.clone(),
|
||||
reason: reason.to_string(),
|
||||
});
|
||||
}
|
||||
IncompletePackage::InvalidStructure(reason) => {
|
||||
MetadataUnavailable::InvalidStructure(reason) => {
|
||||
hints.insert(PubGrubHint::InvalidVersionStructure {
|
||||
package: package.clone(),
|
||||
version: version.clone(),
|
||||
reason: reason.clone(),
|
||||
reason: reason.to_string(),
|
||||
});
|
||||
}
|
||||
IncompletePackage::RequiresPython(requires_python, python_version) => {
|
||||
MetadataUnavailable::RequiresPython(requires_python, python_version) => {
|
||||
hints.insert(PubGrubHint::IncompatibleBuildRequirement {
|
||||
package: package.clone(),
|
||||
version: version.clone(),
|
||||
|
@ -882,8 +871,6 @@ 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,
|
||||
|
@ -896,12 +883,6 @@ pub(crate) enum PubGrubHint {
|
|||
// excluded from `PartialEq` and `Hash`
|
||||
reason: String,
|
||||
},
|
||||
/// Metadata for a package version could not be found.
|
||||
MissingVersionMetadata {
|
||||
package: PubGrubPackage,
|
||||
// excluded from `PartialEq` and `Hash`
|
||||
version: Version,
|
||||
},
|
||||
/// Metadata for a package version could not be parsed.
|
||||
InvalidVersionMetadata {
|
||||
package: PubGrubPackage,
|
||||
|
@ -992,18 +973,12 @@ enum PubGrubHintCore {
|
|||
},
|
||||
NoIndex,
|
||||
Offline,
|
||||
MissingPackageMetadata {
|
||||
package: PubGrubPackage,
|
||||
},
|
||||
InvalidPackageMetadata {
|
||||
package: PubGrubPackage,
|
||||
},
|
||||
InvalidPackageStructure {
|
||||
package: PubGrubPackage,
|
||||
},
|
||||
MissingVersionMetadata {
|
||||
package: PubGrubPackage,
|
||||
},
|
||||
InvalidVersionMetadata {
|
||||
package: PubGrubPackage,
|
||||
},
|
||||
|
@ -1052,18 +1027,12 @@ impl From<PubGrubHint> for PubGrubHintCore {
|
|||
}
|
||||
PubGrubHint::NoIndex => Self::NoIndex,
|
||||
PubGrubHint::Offline => Self::Offline,
|
||||
PubGrubHint::MissingPackageMetadata { package, .. } => {
|
||||
Self::MissingPackageMetadata { package }
|
||||
}
|
||||
PubGrubHint::InvalidPackageMetadata { package, .. } => {
|
||||
Self::InvalidPackageMetadata { package }
|
||||
}
|
||||
PubGrubHint::InvalidPackageStructure { package, .. } => {
|
||||
Self::InvalidPackageStructure { package }
|
||||
}
|
||||
PubGrubHint::MissingVersionMetadata { package, .. } => {
|
||||
Self::MissingVersionMetadata { package }
|
||||
}
|
||||
PubGrubHint::InvalidVersionMetadata { package, .. } => {
|
||||
Self::InvalidVersionMetadata { package }
|
||||
}
|
||||
|
@ -1162,15 +1131,6 @@ 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,
|
||||
|
@ -1191,16 +1151,6 @@ 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.cyan(),
|
||||
format!("v{version}").cyan(),
|
||||
)
|
||||
}
|
||||
Self::InvalidVersionMetadata {
|
||||
package,
|
||||
version,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use crate::resolver::MetadataUnavailable;
|
||||
use uv_distribution_types::IncompatibleDist;
|
||||
use uv_pep440::{Version, VersionSpecifiers};
|
||||
|
||||
|
@ -21,17 +22,15 @@ impl Display for UnavailableReason {
|
|||
}
|
||||
}
|
||||
|
||||
/// The package version is unavailable and cannot be used. Unlike [`PackageUnavailable`], this
|
||||
/// The package version is unavailable and cannot be used. Unlike [`MetadataUnavailable`], this
|
||||
/// applies to a single version of the package.
|
||||
///
|
||||
/// Most variant are from [`MetadataResponse`] without the error source (since we don't format
|
||||
/// the source).
|
||||
/// Most variant are from [`MetadataResponse`] without the error source, since we don't format
|
||||
/// the source and we want to merge unavailable messages across versions.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
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.
|
||||
|
@ -49,7 +48,6 @@ impl UnavailableVersion {
|
|||
pub(crate) fn message(&self) -> String {
|
||||
match self {
|
||||
UnavailableVersion::IncompatibleDist(invalid_dist) => format!("{invalid_dist}"),
|
||||
UnavailableVersion::MissingMetadata => "not include a `METADATA` file".into(),
|
||||
UnavailableVersion::InvalidMetadata => "invalid metadata".into(),
|
||||
UnavailableVersion::InconsistentMetadata => "inconsistent metadata".into(),
|
||||
UnavailableVersion::InvalidStructure => "an invalid package format".into(),
|
||||
|
@ -63,7 +61,6 @@ impl UnavailableVersion {
|
|||
pub(crate) fn singular_message(&self) -> String {
|
||||
match self {
|
||||
UnavailableVersion::IncompatibleDist(invalid_dist) => invalid_dist.singular_message(),
|
||||
UnavailableVersion::MissingMetadata => format!("does {self}"),
|
||||
UnavailableVersion::InvalidMetadata => format!("has {self}"),
|
||||
UnavailableVersion::InconsistentMetadata => format!("has {self}"),
|
||||
UnavailableVersion::InvalidStructure => format!("has {self}"),
|
||||
|
@ -75,7 +72,6 @@ impl UnavailableVersion {
|
|||
pub(crate) fn plural_message(&self) -> String {
|
||||
match self {
|
||||
UnavailableVersion::IncompatibleDist(invalid_dist) => invalid_dist.plural_message(),
|
||||
UnavailableVersion::MissingMetadata => format!("do {self}"),
|
||||
UnavailableVersion::InvalidMetadata => format!("have {self}"),
|
||||
UnavailableVersion::InconsistentMetadata => format!("have {self}"),
|
||||
UnavailableVersion::InvalidStructure => format!("have {self}"),
|
||||
|
@ -91,6 +87,22 @@ impl Display for UnavailableVersion {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&MetadataUnavailable> for UnavailableVersion {
|
||||
fn from(reason: &MetadataUnavailable) -> Self {
|
||||
match reason {
|
||||
MetadataUnavailable::Offline => UnavailableVersion::Offline,
|
||||
MetadataUnavailable::InvalidMetadata(_) => UnavailableVersion::InvalidMetadata,
|
||||
MetadataUnavailable::InconsistentMetadata(_) => {
|
||||
UnavailableVersion::InconsistentMetadata
|
||||
}
|
||||
MetadataUnavailable::InvalidStructure(_) => UnavailableVersion::InvalidStructure,
|
||||
MetadataUnavailable::RequiresPython(requires_python, _python_version) => {
|
||||
UnavailableVersion::RequiresPython(requires_python.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The package is unavailable and cannot be used.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum UnavailablePackage {
|
||||
|
@ -100,8 +112,6 @@ 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.
|
||||
|
@ -114,7 +124,6 @@ impl UnavailablePackage {
|
|||
UnavailablePackage::NoIndex => "not found in the provided package locations",
|
||||
UnavailablePackage::Offline => "not found in the cache",
|
||||
UnavailablePackage::NotFound => "not found in the package registry",
|
||||
UnavailablePackage::MissingMetadata => "not include a `METADATA` file",
|
||||
UnavailablePackage::InvalidMetadata(_) => "invalid metadata",
|
||||
UnavailablePackage::InvalidStructure(_) => "an invalid package format",
|
||||
}
|
||||
|
@ -125,7 +134,6 @@ impl UnavailablePackage {
|
|||
UnavailablePackage::NoIndex => format!("was {self}"),
|
||||
UnavailablePackage::Offline => format!("was {self}"),
|
||||
UnavailablePackage::NotFound => format!("was {self}"),
|
||||
UnavailablePackage::MissingMetadata => format!("does {self}"),
|
||||
UnavailablePackage::InvalidMetadata(_) => format!("has {self}"),
|
||||
UnavailablePackage::InvalidStructure(_) => format!("has {self}"),
|
||||
}
|
||||
|
@ -138,22 +146,20 @@ impl Display for UnavailablePackage {
|
|||
}
|
||||
}
|
||||
|
||||
/// The package is unavailable at specific versions.
|
||||
#[derive(Debug, Clone)]
|
||||
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.
|
||||
InconsistentMetadata(String),
|
||||
/// The wheel has an invalid structure.
|
||||
InvalidStructure(String),
|
||||
/// The source distribution has a `requires-python` requirement that is not met by the installed
|
||||
/// Python version (and static metadata is not available).
|
||||
RequiresPython(VersionSpecifiers, Version),
|
||||
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::InvalidStructure(err) => Self::InvalidStructure(err.to_string()),
|
||||
MetadataUnavailable::RequiresPython(..) => {
|
||||
unreachable!("`requires-python` is only known upfront for registry distributions")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -55,7 +55,7 @@ use crate::python_requirement::PythonRequirement;
|
|||
use crate::resolution::ResolverOutput;
|
||||
use crate::resolution_mode::ResolutionStrategy;
|
||||
pub(crate) use crate::resolver::availability::{
|
||||
IncompletePackage, ResolverVersion, UnavailablePackage, UnavailableReason, UnavailableVersion,
|
||||
ResolverVersion, UnavailablePackage, UnavailableReason, UnavailableVersion,
|
||||
};
|
||||
use crate::resolver::batch_prefetch::BatchPrefetcher;
|
||||
pub use crate::resolver::derivation::DerivationChainBuilder;
|
||||
|
@ -64,6 +64,7 @@ pub use crate::resolver::environment::ResolverEnvironment;
|
|||
pub(crate) use crate::resolver::fork_map::{ForkMap, ForkSet};
|
||||
pub(crate) use crate::resolver::urls::Urls;
|
||||
use crate::universal_marker::{ConflictMarker, UniversalMarker};
|
||||
pub(crate) use provider::MetadataUnavailable;
|
||||
|
||||
pub use crate::resolver::index::InMemoryIndex;
|
||||
use crate::resolver::indexes::Indexes;
|
||||
|
@ -118,7 +119,7 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
|
|||
/// Incompatibilities for packages that are entirely unavailable.
|
||||
unavailable_packages: DashMap<PackageName, UnavailablePackage>,
|
||||
/// Incompatibilities for packages that are unavailable at specific versions.
|
||||
incomplete_packages: DashMap<PackageName, DashMap<Version, IncompletePackage>>,
|
||||
incomplete_packages: DashMap<PackageName, DashMap<Version, MetadataUnavailable>>,
|
||||
/// The options that were used to configure this resolver.
|
||||
options: Options,
|
||||
/// The reporter to use for this resolver.
|
||||
|
@ -354,6 +355,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
state.priorities.get(&state.pubgrub.package_store[id])
|
||||
})
|
||||
else {
|
||||
// All packages have been assigned, the fork has been successfully resolved
|
||||
if tracing::enabled!(Level::DEBUG) {
|
||||
prefetcher.log_tried_versions();
|
||||
}
|
||||
|
@ -919,40 +921,11 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
// If we failed to fetch the metadata for a URL, we can't proceed.
|
||||
let metadata = match &*response {
|
||||
MetadataResponse::Found(archive) => &archive.metadata,
|
||||
MetadataResponse::Offline => {
|
||||
MetadataResponse::Unavailable(reason) => {
|
||||
self.unavailable_packages
|
||||
.insert(name.clone(), UnavailablePackage::Offline);
|
||||
.insert(name.clone(), reason.into());
|
||||
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(),
|
||||
UnavailablePackage::InvalidMetadata(err.to_string()),
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
MetadataResponse::InconsistentMetadata(err) => {
|
||||
self.unavailable_packages.insert(
|
||||
name.clone(),
|
||||
UnavailablePackage::InvalidMetadata(err.to_string()),
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
MetadataResponse::InvalidStructure(err) => {
|
||||
self.unavailable_packages.insert(
|
||||
name.clone(),
|
||||
UnavailablePackage::InvalidStructure(err.to_string()),
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
MetadataResponse::RequiresPython(..) => {
|
||||
unreachable!("`requires-python` is only known upfront for registry distributions")
|
||||
}
|
||||
MetadataResponse::Error(dist, err) => {
|
||||
// TODO(charlie): Add derivation chain for URL dependencies. In practice, this isn't
|
||||
// critical since we fetch URL dependencies _prior_ to invoking the resolver.
|
||||
|
@ -1318,82 +1291,20 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
|
||||
let metadata = match &*response {
|
||||
MetadataResponse::Found(archive) => &archive.metadata,
|
||||
MetadataResponse::Offline => {
|
||||
MetadataResponse::Unavailable(reason) => {
|
||||
let unavailable_version = UnavailableVersion::from(reason);
|
||||
let message = unavailable_version.singular_message();
|
||||
if let Some(err) = reason.source() {
|
||||
// Show the detailed error for metadata parse errors.
|
||||
warn!("{name} {message}: {err}");
|
||||
} else {
|
||||
warn!("{name} {message}");
|
||||
}
|
||||
self.incomplete_packages
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.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
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.insert(
|
||||
version.clone(),
|
||||
IncompletePackage::InvalidMetadata(err.to_string()),
|
||||
);
|
||||
return Ok(Dependencies::Unavailable(
|
||||
UnavailableVersion::InvalidMetadata,
|
||||
));
|
||||
}
|
||||
MetadataResponse::InconsistentMetadata(err) => {
|
||||
warn!("Unable to extract metadata for {name}: {err}");
|
||||
self.incomplete_packages
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.insert(
|
||||
version.clone(),
|
||||
IncompletePackage::InconsistentMetadata(err.to_string()),
|
||||
);
|
||||
return Ok(Dependencies::Unavailable(
|
||||
UnavailableVersion::InconsistentMetadata,
|
||||
));
|
||||
}
|
||||
MetadataResponse::InvalidStructure(err) => {
|
||||
warn!("Unable to extract metadata for {name}: {err}");
|
||||
self.incomplete_packages
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.insert(
|
||||
version.clone(),
|
||||
IncompletePackage::InvalidStructure(err.to_string()),
|
||||
);
|
||||
return Ok(Dependencies::Unavailable(
|
||||
UnavailableVersion::InvalidStructure,
|
||||
));
|
||||
}
|
||||
MetadataResponse::RequiresPython(requires_python, python_version) => {
|
||||
warn!(
|
||||
"Unable to extract metadata for {name}: {}",
|
||||
uv_distribution::Error::RequiresPython(
|
||||
requires_python.clone(),
|
||||
python_version.clone()
|
||||
)
|
||||
);
|
||||
self.incomplete_packages
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.insert(
|
||||
version.clone(),
|
||||
IncompletePackage::RequiresPython(
|
||||
requires_python.clone(),
|
||||
python_version.clone(),
|
||||
),
|
||||
);
|
||||
return Ok(Dependencies::Unavailable(
|
||||
UnavailableVersion::RequiresPython(requires_python.clone()),
|
||||
));
|
||||
.insert(version.clone(), reason.clone());
|
||||
return Ok(Dependencies::Unavailable(unavailable_version));
|
||||
}
|
||||
MetadataResponse::Error(dist, err) => {
|
||||
let chain = DerivationChainBuilder::from_state(id, version, pubgrub)
|
||||
|
@ -1856,37 +1767,20 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
))),
|
||||
);
|
||||
}
|
||||
Some(Response::Dist {
|
||||
dist: Dist::Built(dist),
|
||||
metadata,
|
||||
}) => {
|
||||
trace!("Received built distribution metadata for: {dist}");
|
||||
match &metadata {
|
||||
MetadataResponse::InvalidMetadata(err) => {
|
||||
warn!("Unable to extract metadata for {dist}: {err}");
|
||||
Some(Response::Dist { dist, metadata }) => {
|
||||
let dist_kind = match dist {
|
||||
Dist::Built(_) => "built",
|
||||
Dist::Source(_) => "source",
|
||||
};
|
||||
trace!("Received {dist_kind} distribution metadata for: {dist}");
|
||||
if let MetadataResponse::Unavailable(reason) = &metadata {
|
||||
let message = UnavailableVersion::from(reason).singular_message();
|
||||
if let Some(err) = reason.source() {
|
||||
// Show the detailed error for metadata parse errors.
|
||||
warn!("{dist} {message}: {err}");
|
||||
} else {
|
||||
warn!("{dist} {message}");
|
||||
}
|
||||
MetadataResponse::InvalidStructure(err) => {
|
||||
warn!("Unable to extract metadata for {dist}: {err}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.index
|
||||
.distributions()
|
||||
.done(dist.version_id(), Arc::new(metadata));
|
||||
}
|
||||
Some(Response::Dist {
|
||||
dist: Dist::Source(dist),
|
||||
metadata,
|
||||
}) => {
|
||||
trace!("Received source distribution metadata for: {dist}");
|
||||
match &metadata {
|
||||
MetadataResponse::InvalidMetadata(err) => {
|
||||
warn!("Unable to extract metadata for {dist}: {err}");
|
||||
}
|
||||
MetadataResponse::InvalidStructure(err) => {
|
||||
warn!("Unable to extract metadata for {dist}: {err}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.index
|
||||
.distributions()
|
||||
|
|
|
@ -34,21 +34,43 @@ 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<uv_pypi_types::MetadataError>),
|
||||
/// The wheel metadata was found, but the metadata was inconsistent.
|
||||
InconsistentMetadata(Box<uv_distribution::Error>),
|
||||
/// The wheel has an invalid structure.
|
||||
InvalidStructure(Box<uv_metadata::Error>),
|
||||
/// A non-fatal error.
|
||||
Unavailable(MetadataUnavailable),
|
||||
/// The distribution could not be built or downloaded, a fatal error.
|
||||
Error(Box<Dist>, Arc<uv_distribution::Error>),
|
||||
}
|
||||
|
||||
/// Non-fatal metadata fetching error.
|
||||
///
|
||||
/// This is also the unavailability reasons for a package, while version unavailability is separate
|
||||
/// in [`UnavailableVersion`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MetadataUnavailable {
|
||||
/// The wheel metadata was not found in the cache and the network is not available.
|
||||
Offline,
|
||||
/// The wheel metadata was found, but could not be parsed.
|
||||
InvalidMetadata(Arc<uv_pypi_types::MetadataError>),
|
||||
/// The wheel metadata was found, but the metadata was inconsistent.
|
||||
InconsistentMetadata(Arc<uv_distribution::Error>),
|
||||
/// The wheel has an invalid structure.
|
||||
InvalidStructure(Arc<uv_metadata::Error>),
|
||||
/// The source distribution has a `requires-python` requirement that is not met by the installed
|
||||
/// Python version (and static metadata is not available).
|
||||
RequiresPython(VersionSpecifiers, Version),
|
||||
/// The distribution could not be built or downloaded.
|
||||
Error(Box<Dist>, Arc<uv_distribution::Error>),
|
||||
}
|
||||
|
||||
impl MetadataUnavailable {
|
||||
/// Like [`std::error::Error::source`], but we don't want to derive the std error since our
|
||||
/// formatting system is more custom.
|
||||
pub(crate) fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
MetadataUnavailable::Offline => None,
|
||||
MetadataUnavailable::InvalidMetadata(err) => Some(err),
|
||||
MetadataUnavailable::InconsistentMetadata(err) => Some(err),
|
||||
MetadataUnavailable::InvalidStructure(err) => Some(err),
|
||||
MetadataUnavailable::RequiresPython(_, _) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResolverProvider {
|
||||
|
@ -189,29 +211,39 @@ impl<'a, Context: BuildContext> ResolverProvider for DefaultResolverProvider<'a,
|
|||
Ok(metadata) => Ok(MetadataResponse::Found(metadata)),
|
||||
Err(err) => match err {
|
||||
uv_distribution::Error::Client(client) => match client.into_kind() {
|
||||
uv_client::ErrorKind::Offline(_) => Ok(MetadataResponse::Offline),
|
||||
uv_client::ErrorKind::Offline(_) => {
|
||||
Ok(MetadataResponse::Unavailable(MetadataUnavailable::Offline))
|
||||
}
|
||||
uv_client::ErrorKind::MetadataParseError(_, _, err) => {
|
||||
Ok(MetadataResponse::InvalidMetadata(err))
|
||||
}
|
||||
uv_client::ErrorKind::Metadata(_, err) => {
|
||||
Ok(MetadataResponse::InvalidStructure(Box::new(err)))
|
||||
Ok(MetadataResponse::Unavailable(
|
||||
MetadataUnavailable::InvalidMetadata(Arc::new(*err)),
|
||||
))
|
||||
}
|
||||
uv_client::ErrorKind::Metadata(_, err) => Ok(MetadataResponse::Unavailable(
|
||||
MetadataUnavailable::InvalidStructure(Arc::new(err)),
|
||||
)),
|
||||
kind => Err(uv_client::Error::from(kind).into()),
|
||||
},
|
||||
uv_distribution::Error::WheelMetadataVersionMismatch { .. } => {
|
||||
Ok(MetadataResponse::InconsistentMetadata(Box::new(err)))
|
||||
Ok(MetadataResponse::Unavailable(
|
||||
MetadataUnavailable::InconsistentMetadata(Arc::new(err)),
|
||||
))
|
||||
}
|
||||
uv_distribution::Error::WheelMetadataNameMismatch { .. } => {
|
||||
Ok(MetadataResponse::InconsistentMetadata(Box::new(err)))
|
||||
}
|
||||
uv_distribution::Error::Metadata(err) => {
|
||||
Ok(MetadataResponse::InvalidMetadata(Box::new(err)))
|
||||
}
|
||||
uv_distribution::Error::WheelMetadata(_, err) => {
|
||||
Ok(MetadataResponse::InvalidStructure(err))
|
||||
Ok(MetadataResponse::Unavailable(
|
||||
MetadataUnavailable::InconsistentMetadata(Arc::new(err)),
|
||||
))
|
||||
}
|
||||
uv_distribution::Error::Metadata(err) => Ok(MetadataResponse::Unavailable(
|
||||
MetadataUnavailable::InvalidMetadata(Arc::new(err)),
|
||||
)),
|
||||
uv_distribution::Error::WheelMetadata(_, err) => Ok(MetadataResponse::Unavailable(
|
||||
MetadataUnavailable::InvalidStructure(Arc::new(*err)),
|
||||
)),
|
||||
uv_distribution::Error::RequiresPython(requires_python, version) => {
|
||||
Ok(MetadataResponse::RequiresPython(requires_python, version))
|
||||
Ok(MetadataResponse::Unavailable(
|
||||
MetadataUnavailable::RequiresPython(requires_python, version),
|
||||
))
|
||||
}
|
||||
err => Ok(MetadataResponse::Error(
|
||||
Box::new(dist.clone()),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue