Skip to content

Commit 515d72c

Browse files
zaniebkonstin
andauthored
Improve display of ranges when pre-releases are not allowed (#9944)
Closes #9891 There are two changes here 1. We now exclude pre-releases (if they are not allowed) from the available versions set when simplifying ranges, this means the simplified range reflects the _allowed_ available versions — which is what we want. We no longer segment ranges into arbitrary looking segments.. 2. We improve on #9885, expanding the scope to avoid regressions where we would now otherwise enumerate a bunch of versions --------- Co-authored-by: konsti <[email protected]>
1 parent 3fae533 commit 515d72c

File tree

3 files changed

+117
-49
lines changed

3 files changed

+117
-49
lines changed

crates/uv-resolver/src/error.rs

Lines changed: 115 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use uv_static::EnvVars;
2020
use crate::candidate_selector::CandidateSelector;
2121
use crate::dependency_provider::UvDependencyProvider;
2222
use crate::fork_urls::ForkUrls;
23+
use crate::prerelease::AllowPrerelease;
2324
use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner, PubGrubReportFormatter};
2425
use crate::python_requirement::PythonRequirement;
2526
use crate::resolution::ConflictingDistributionError;
@@ -333,10 +334,17 @@ impl std::fmt::Display for NoSolutionError {
333334
}
334335

335336
collapse_unavailable_versions(&mut tree);
336-
collapse_redundant_no_versions(&mut tree);
337337
collapse_redundant_depends_on_no_versions(&mut tree);
338338

339-
simplify_derivation_tree_ranges(&mut tree, &self.available_versions);
339+
simplify_derivation_tree_ranges(
340+
&mut tree,
341+
&self.available_versions,
342+
&self.selector,
343+
&self.env,
344+
);
345+
346+
// This needs to be applied _after_ simplification of the ranges
347+
collapse_redundant_no_versions(&mut tree);
340348

341349
while collapse_redundant_no_versions_tree(&mut tree) {
342350
// Continue collapsing until no more redundant nodes are found
@@ -456,15 +464,17 @@ fn collapse_redundant_no_versions(
456464
// First, always recursively visit the other side of the tree
457465
collapse_redundant_no_versions(other);
458466

459-
let DerivationTree::Derived(derived) = other else {
460-
return;
467+
// Retrieve the nearest terms, either alongside this node or from the parent.
468+
let package_terms = if let DerivationTree::Derived(derived) = other {
469+
derived.terms.get(package)
470+
} else {
471+
derived.terms.get(package)
461472
};
462473

463-
// If the range in the conclusion (terms) matches the range of no versions,
464-
// then we'll drop this node
465-
let Some(Term::Positive(term)) = derived.terms.get(package) else {
474+
let Some(Term::Positive(term)) = package_terms else {
466475
return;
467476
};
477+
468478
let versions = versions.complement();
469479

470480
// If we're disqualifying a single version, this is important to retain, e.g,
@@ -473,11 +483,13 @@ fn collapse_redundant_no_versions(
473483
return;
474484
}
475485

476-
if *term != versions {
486+
// If the range in the conclusion (terms) matches the range of no versions,
487+
// then we'll drop this node. If the range is "all versions", then there's no
488+
// also no need to enumerate the available versions.
489+
if *term != Range::full() && *term != versions {
477490
return;
478491
}
479492

480-
// Replace this node with the other tree
481493
*tree = other.clone();
482494
}
483495
// If not, just recurse
@@ -1088,44 +1100,94 @@ impl std::fmt::Display for NoSolutionHeader {
10881100
fn simplify_derivation_tree_ranges(
10891101
tree: &mut DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
10901102
available_versions: &FxHashMap<PackageName, BTreeSet<Version>>,
1103+
candidate_selector: &CandidateSelector,
1104+
resolver_environment: &ResolverEnvironment,
10911105
) {
10921106
match tree {
10931107
DerivationTree::External(external) => match external {
10941108
External::FromDependencyOf(package1, versions1, package2, versions2) => {
1095-
if let Some(simplified) = simplify_range(versions1, package1, available_versions) {
1109+
if let Some(simplified) = simplify_range(
1110+
versions1,
1111+
package1,
1112+
available_versions,
1113+
candidate_selector,
1114+
resolver_environment,
1115+
) {
10961116
*versions1 = simplified;
10971117
}
1098-
if let Some(simplified) = simplify_range(versions2, package2, available_versions) {
1118+
if let Some(simplified) = simplify_range(
1119+
versions2,
1120+
package2,
1121+
available_versions,
1122+
candidate_selector,
1123+
resolver_environment,
1124+
) {
10991125
*versions2 = simplified;
11001126
}
11011127
}
11021128
External::NoVersions(package, versions) => {
1103-
if let Some(simplified) = simplify_range(versions, package, available_versions) {
1129+
if let Some(simplified) = simplify_range(
1130+
versions,
1131+
package,
1132+
available_versions,
1133+
candidate_selector,
1134+
resolver_environment,
1135+
) {
11041136
*versions = simplified;
11051137
}
11061138
}
11071139
External::Custom(package, versions, _) => {
1108-
if let Some(simplified) = simplify_range(versions, package, available_versions) {
1140+
if let Some(simplified) = simplify_range(
1141+
versions,
1142+
package,
1143+
available_versions,
1144+
candidate_selector,
1145+
resolver_environment,
1146+
) {
11091147
*versions = simplified;
11101148
}
11111149
}
11121150
External::NotRoot(..) => (),
11131151
},
11141152
DerivationTree::Derived(derived) => {
11151153
// Recursively simplify both sides of the tree
1116-
simplify_derivation_tree_ranges(Arc::make_mut(&mut derived.cause1), available_versions);
1117-
simplify_derivation_tree_ranges(Arc::make_mut(&mut derived.cause2), available_versions);
1154+
simplify_derivation_tree_ranges(
1155+
Arc::make_mut(&mut derived.cause1),
1156+
available_versions,
1157+
candidate_selector,
1158+
resolver_environment,
1159+
);
1160+
simplify_derivation_tree_ranges(
1161+
Arc::make_mut(&mut derived.cause2),
1162+
available_versions,
1163+
candidate_selector,
1164+
resolver_environment,
1165+
);
11181166

11191167
// Simplify the terms
11201168
derived.terms = std::mem::take(&mut derived.terms)
11211169
.into_iter()
11221170
.map(|(pkg, term)| {
11231171
let term = match term {
11241172
Term::Positive(versions) => Term::Positive(
1125-
simplify_range(&versions, &pkg, available_versions).unwrap_or(versions),
1173+
simplify_range(
1174+
&versions,
1175+
&pkg,
1176+
available_versions,
1177+
candidate_selector,
1178+
resolver_environment,
1179+
)
1180+
.unwrap_or(versions),
11261181
),
11271182
Term::Negative(versions) => Term::Negative(
1128-
simplify_range(&versions, &pkg, available_versions).unwrap_or(versions),
1183+
simplify_range(
1184+
&versions,
1185+
&pkg,
1186+
available_versions,
1187+
candidate_selector,
1188+
resolver_environment,
1189+
)
1190+
.unwrap_or(versions),
11291191
),
11301192
};
11311193
(pkg, term)
@@ -1142,6 +1204,8 @@ fn simplify_range(
11421204
range: &Range<Version>,
11431205
package: &PubGrubPackage,
11441206
available_versions: &FxHashMap<PackageName, BTreeSet<Version>>,
1207+
candidate_selector: &CandidateSelector,
1208+
resolver_environment: &ResolverEnvironment,
11451209
) -> Option<Range<Version>> {
11461210
// If there's not a package name or available versions, we can't simplify anything
11471211
let name = package.name()?;
@@ -1159,6 +1223,39 @@ fn simplify_range(
11591223
}
11601224
}
11611225

1226+
// Check if pre-releases are allowed
1227+
let prereleases_not_allowed = candidate_selector
1228+
.prerelease_strategy()
1229+
.allows(name, resolver_environment)
1230+
!= AllowPrerelease::Yes;
1231+
1232+
let any_prerelease = range.iter().any(|(start, end)| {
1233+
let is_pre1 = match start {
1234+
Bound::Included(version) => version.any_prerelease(),
1235+
Bound::Excluded(version) => version.any_prerelease(),
1236+
Bound::Unbounded => false,
1237+
};
1238+
let is_pre2 = match end {
1239+
Bound::Included(version) => version.any_prerelease(),
1240+
Bound::Excluded(version) => version.any_prerelease(),
1241+
Bound::Unbounded => false,
1242+
};
1243+
is_pre1 || is_pre2
1244+
});
1245+
11621246
// Simplify the range, as implemented in PubGrub
1163-
Some(range.simplify(versions.iter()))
1247+
Some(range.simplify(versions.iter().filter(|version| {
1248+
// If there are pre-releases in the range segments, we need to include pre-releases
1249+
if any_prerelease {
1250+
return true;
1251+
}
1252+
1253+
// If pre-releases are not allowed, filter out pre-releases
1254+
if prereleases_not_allowed && version.any_prerelease() {
1255+
return false;
1256+
}
1257+
1258+
// Otherwise, include the version
1259+
true
1260+
})))
11641261
}

crates/uv/tests/it/cache_prune.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -257,17 +257,7 @@ fn prune_unzipped() -> Result<()> {
257257
258258
----- stderr -----
259259
× No solution found when resolving dependencies:
260-
╰─▶ Because only the following versions of iniconfig are available:
261-
iniconfig<=0.1
262-
iniconfig>=1.0.0
263-
and all of:
264-
iniconfig<=0.1
265-
iniconfig>=1.0.0
266-
need to be downloaded from a registry, we can conclude that all of:
267-
iniconfig<=0.1
268-
iniconfig>=1.0.0
269-
cannot be used.
270-
And because you require iniconfig, we can conclude that your requirements are unsatisfiable.
260+
╰─▶ Because all versions of iniconfig need to be downloaded from a registry and you require iniconfig, we can conclude that your requirements are unsatisfiable.
271261
272262
hint: Pre-releases are available for `iniconfig` in the requested range (e.g., 0.2.dev0), but pre-releases weren't enabled (try: `--prerelease=allow`)
273263

crates/uv/tests/it/pip_install.rs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,26 +2172,7 @@ fn install_only_binary_all_and_no_binary_all() {
21722172
21732173
----- stderr -----
21742174
× No solution found when resolving dependencies:
2175-
╰─▶ Because only the following versions of anyio are available:
2176-
anyio>=1.0.0,<=1.4.0
2177-
anyio>=2.0.0,<=2.2.0
2178-
anyio>=3.0.0,<=3.6.2
2179-
anyio>=3.7.0,<=3.7.1
2180-
anyio>=4.0.0
2181-
and all of:
2182-
anyio>=1.0.0,<=1.4.0
2183-
anyio>=2.0.0,<=2.2.0
2184-
anyio>=3.0.0,<=3.6.2
2185-
anyio>=3.7.0,<=3.7.1
2186-
anyio>=4.0.0
2187-
have no usable wheels, we can conclude that all of:
2188-
anyio>=1.0.0,<=1.4.0
2189-
anyio>=2.0.0,<=2.2.0
2190-
anyio>=3.0.0,<=3.6.2
2191-
anyio>=3.7.0,<=3.7.1
2192-
anyio>=4.0.0
2193-
cannot be used.
2194-
And because you require anyio, we can conclude that your requirements are unsatisfiable.
2175+
╰─▶ Because all versions of anyio have no usable wheels and you require anyio, we can conclude that your requirements are unsatisfiable.
21952176
21962177
hint: Pre-releases are available for `anyio` in the requested range (e.g., 4.0.0rc1), but pre-releases weren't enabled (try: `--prerelease=allow`)
21972178

0 commit comments

Comments
 (0)