Show available pre-releases in error hints (#844)

## Summary

If pre-releases are available for a package that we otherwise couldn't
resolve, we now show a hint that includes one of the example versions.

Closes https://github.com/astral-sh/puffin/issues/811.
This commit is contained in:
Charlie Marsh 2024-01-09 09:58:38 -05:00 committed by GitHub
parent b1edecdf1f
commit ee3a6431c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 30 deletions

View file

@ -522,6 +522,8 @@ fn requires_package_only_prereleases_in_range() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because there are no versions of a>0.1.0 and root depends on a>0.1.0, version solving failed.
hint: Pre-releases are available for a in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
});
@ -1116,6 +1118,8 @@ fn requires_transitive_package_only_prereleases_in_range() -> Result<()> {
× No solution found when resolving dependencies:
Because there are no versions of b>0.1 and a==0.1.0 depends on b>0.1, a==0.1.0 is forbidden.
And because there are no versions of a<0.1.0 | >0.1.0 and root depends on a, version solving failed.
hint: Pre-releases are available for b in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
});

View file

@ -158,39 +158,57 @@ impl PubGrubReportFormatter<'_> {
derivation_tree: &DerivationTree<PubGrubPackage, Range<PubGrubVersion>>,
selector: &CandidateSelector,
) -> FxHashSet<PubGrubHint> {
/// Returns `true` if pre-releases were allowed for a package.
fn allowed_prerelease(package: &PubGrubPackage, selector: &CandidateSelector) -> bool {
match selector.prerelease_strategy() {
PreReleaseStrategy::Disallow => false,
PreReleaseStrategy::Allow => true,
PreReleaseStrategy::IfNecessary => false,
PreReleaseStrategy::Explicit(packages) => {
if let PubGrubPackage::Package(package, ..) = package {
packages.contains(package)
} else {
false
}
}
PreReleaseStrategy::IfNecessaryOrExplicit(packages) => {
if let PubGrubPackage::Package(package, ..) = package {
packages.contains(package)
} else {
false
}
}
}
}
let mut hints = FxHashSet::default();
match derivation_tree {
DerivationTree::External(external) => match external {
External::NoVersions(package, set) => {
// Determine whether a pre-release marker appeared in the version requirements.
if set.bounds().any(PubGrubVersion::any_prerelease) {
// Determine whether pre-releases were allowed for this package.
let allowed_prerelease = match selector.prerelease_strategy() {
PreReleaseStrategy::Disallow => false,
PreReleaseStrategy::Allow => true,
PreReleaseStrategy::IfNecessary => false,
PreReleaseStrategy::Explicit(packages) => {
if let PubGrubPackage::Package(package, ..) = package {
packages.contains(package)
} else {
false
}
}
PreReleaseStrategy::IfNecessaryOrExplicit(packages) => {
if let PubGrubPackage::Package(package, ..) = package {
packages.contains(package)
} else {
false
}
}
};
if !allowed_prerelease {
hints.insert(PubGrubHint::NoVersionsWithPreRelease {
// A pre-release marker appeared in the version requirements.
if !allowed_prerelease(package, selector) {
hints.insert(PubGrubHint::PreReleaseRequested {
package: package.clone(),
range: self.simplify_set(set, package).into_owned(),
});
}
} else if let Some(version) =
self.available_versions.get(package).and_then(|versions| {
versions
.iter()
.rev()
.filter(|version| version.any_prerelease())
.find(|version| set.contains(version))
})
{
// There are pre-release versions available for the package.
if !allowed_prerelease(package, selector) {
hints.insert(PubGrubHint::PreReleaseAvailable {
package: package.clone(),
version: version.clone(),
});
}
}
}
External::NotRoot(..) => {}
@ -210,9 +228,17 @@ impl PubGrubReportFormatter<'_> {
#[derive(Derivative, Debug, Clone)]
#[derivative(Hash, PartialEq, Eq)]
pub(crate) enum PubGrubHint {
/// A package was requested with a pre-release marker, but pre-releases weren't enabled for
/// that package.
NoVersionsWithPreRelease {
/// There are pre-release versions available for a package, but pre-releases weren't enabled
/// for that package.
///
PreReleaseAvailable {
package: PubGrubPackage,
#[derivative(PartialEq = "ignore", Hash = "ignore")]
version: PubGrubVersion,
},
/// A requirement included a pre-release marker, but pre-releases weren't enabled for that
/// package.
PreReleaseRequested {
package: PubGrubPackage,
#[derivative(PartialEq = "ignore", Hash = "ignore")]
range: Range<PubGrubVersion>,
@ -222,7 +248,17 @@ pub(crate) enum PubGrubHint {
impl std::fmt::Display for PubGrubHint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PubGrubHint::NoVersionsWithPreRelease { package, range } => {
PubGrubHint::PreReleaseAvailable { package, version } => {
write!(
f,
"{}{} Pre-releases are available for {} in the requested range (e.g., {}), but pre-releases weren't enabled (try: `--prerelease=allow`)",
"hint".bold().cyan(),
":".bold(),
package.bold(),
version.bold()
)
}
PubGrubHint::PreReleaseRequested { package, range } => {
write!(
f,
"{}{} {} was requested with a pre-release marker (e.g., {}), but pre-releases weren't enabled (try: `--prerelease=allow`)",

View file

@ -493,7 +493,11 @@ async fn black_disallow_prerelease() -> Result<()> {
.await
.unwrap_err();
assert_snapshot!(err, @"Because there are no versions of black<=20.0 and root depends on black<=20.0, version solving failed.");
assert_snapshot!(err, @r###"
Because there are no versions of black<=20.0 and root depends on black<=20.0, version solving failed.
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
Ok(())
}
@ -511,7 +515,11 @@ async fn black_allow_prerelease_if_necessary() -> Result<()> {
.await
.unwrap_err();
assert_snapshot!(err, @"Because there are no versions of black<=20.0 and root depends on black<=20.0, version solving failed.");
assert_snapshot!(err, @r###"
Because there are no versions of black<=20.0 and root depends on black<=20.0, version solving failed.
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
Ok(())
}