Consolidate UnusableDependencies into a generic Unavailable incompatibility (#1088)

Requires https://github.com/zanieb/pubgrub/pull/20

In short, `UnusableDependencies` can be generalized into `Unavailable`
which encompasses incompatibilities where a package range which is
unusable for some inherent reason as well as when its dependencies are
unusable. We can eventually use this to track more incompatibilities in
the solver. I made the reason string required because I can't see a case
where we should leave it out.

Additionally, this improves the display of conflicts in the root
requirements.
This commit is contained in:
Zanie Blue 2024-01-24 22:10:44 -06:00 committed by GitHub
parent 091f8e09ff
commit ed1ac640b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 54 additions and 49 deletions

View file

@ -49,10 +49,10 @@ pub enum ResolveError {
#[error("~= operator requires at least two release segments: {0}")]
InvalidTildeEquals(pep440_rs::VersionSpecifier),
#[error("Conflicting URLs for package `{0}`:\n- {1}\n- {2}")]
#[error("There are conflicting URLs for package `{0}`:\n- {1}\n- {2}")]
ConflictingUrls(PackageName, String, String),
#[error("Conflicting versions for `{0}`: {1}")]
#[error("There are conflicting versions for `{0}`: {1}")]
ConflictingVersions(String, String),
#[error("Package `{0}` attempted to resolve via URL: {1}. URL dependencies must be expressed as direct requirements or constraints. Consider adding `{0} @ {1}` to your dependencies or constraints file.")]

View file

@ -96,32 +96,18 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
}
}
}
External::UnavailableDependencies(package, set) => {
let set = self.simplify_set(set, package);
format!(
"dependencies of {}are unavailable",
Padded::new("", &PackageRange::compatibility(package, &set), " ")
)
}
External::UnusableDependencies(package, set, reason) => {
if let Some(reason) = reason {
if matches!(package, PubGrubPackage::Root(_)) {
format!("{package} dependencies are unusable: {reason}")
} else {
let set = self.simplify_set(set, package);
format!(
"dependencies of {}are unusable: {reason}",
Padded::new("", &PackageRange::compatibility(package, &set), " ")
)
}
} else {
let set = self.simplify_set(set, package);
format!(
"dependencies of {}are unusable",
Padded::new("", &PackageRange::compatibility(package, &set), " ")
)
External::Unavailable(package, set, reason) => match package {
PubGrubPackage::Root(Some(name)) => {
format!("{name} cannot be used because {reason}")
}
}
PubGrubPackage::Root(None) => {
format!("your requirements cannot be used because {reason}")
}
_ => format!(
"{}is unusable because {reason}",
Padded::new("", &PackageRange::compatibility(package, set), " ")
),
},
External::FromDependencyOf(package, package_set, dependency, dependency_set) => {
let package_set = self.simplify_set(package_set, package);
let dependency_set = self.simplify_set(dependency_set, dependency);
@ -403,8 +389,7 @@ impl PubGrubReportFormatter<'_> {
}
}
External::NotRoot(..) => {}
External::UnavailableDependencies(..) => {}
External::UnusableDependencies(..) => {}
External::Unavailable(..) => {}
External::FromDependencyOf(..) => {}
},
DerivationTree::Derived(derived) => {

View file

@ -335,22 +335,30 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
.get_dependencies(package, &version, &mut priorities, request_sink)
.await?
{
Dependencies::Unusable(reason) => {
state.add_incompatibility(Incompatibility::unusable_dependencies(
Dependencies::Unavailable(reason) => {
let message = {
if matches!(package, PubGrubPackage::Root(_)) {
// Including front-matter for the root package is redundant
reason.clone()
} else {
format!("its dependencies are unusable because {reason}")
}
};
state.add_incompatibility(Incompatibility::unavailable(
package.clone(),
version.clone(),
reason.clone(),
message,
));
continue;
}
Dependencies::Known(constraints) if constraints.contains_key(package) => {
Dependencies::Available(constraints) if constraints.contains_key(package) => {
return Err(PubGrubError::SelfDependency {
package: package.clone(),
version: version.clone(),
}
.into());
}
Dependencies::Known(constraints) => constraints,
Dependencies::Available(constraints) => constraints,
};
// Add that package and version if the dependencies are not problematic.
@ -588,7 +596,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
| ResolveError::ConflictingUrls(..)),
) = constraints
{
return Ok(Dependencies::Unusable(Some(err.to_string())));
return Ok(Dependencies::Unavailable(uncapitalize(err.to_string())));
}
let mut constraints = constraints?;
@ -610,10 +618,12 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
);
}
Ok(Dependencies::Known(constraints.into()))
Ok(Dependencies::Available(constraints.into()))
}
PubGrubPackage::Python(_) => Ok(Dependencies::Known(DependencyConstraints::default())),
PubGrubPackage::Python(_) => {
Ok(Dependencies::Available(DependencyConstraints::default()))
}
PubGrubPackage::Package(package_name, extra, url) => {
// Wait for the metadata to be available.
@ -640,7 +650,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
version.clone(),
);
constraints.insert(PubGrubPackage::Python(PubGrubPython::Target), version);
return Ok(Dependencies::Known(constraints));
return Ok(Dependencies::Available(constraints));
}
let entry = self.index.distributions.wait(&package_id).await?;
@ -670,7 +680,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
);
}
Ok(Dependencies::Known(constraints.into()))
Ok(Dependencies::Available(constraints.into()))
}
}
}
@ -877,8 +887,16 @@ enum Response {
/// For each [Package] there is a set of versions allowed as a dependency.
#[derive(Clone)]
enum Dependencies {
/// Package dependencies are not usable
Unusable(Option<String>),
/// Package dependencies are not available.
Unavailable(String),
/// Container for all available package versions.
Known(DependencyConstraints<PubGrubPackage, Range<Version>>),
Available(DependencyConstraints<PubGrubPackage, Range<Version>>),
}
fn uncapitalize<T: AsRef<str>>(string: T) -> String {
let mut chars = string.as_ref().chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_lowercase().chain(chars).collect(),
}
}