@@ -20,6 +20,7 @@ use uv_static::EnvVars;
20
20
use crate :: candidate_selector:: CandidateSelector ;
21
21
use crate :: dependency_provider:: UvDependencyProvider ;
22
22
use crate :: fork_urls:: ForkUrls ;
23
+ use crate :: prerelease:: AllowPrerelease ;
23
24
use crate :: pubgrub:: { PubGrubPackage , PubGrubPackageInner , PubGrubReportFormatter } ;
24
25
use crate :: python_requirement:: PythonRequirement ;
25
26
use crate :: resolution:: ConflictingDistributionError ;
@@ -333,10 +334,17 @@ impl std::fmt::Display for NoSolutionError {
333
334
}
334
335
335
336
collapse_unavailable_versions ( & mut tree) ;
336
- collapse_redundant_no_versions ( & mut tree) ;
337
337
collapse_redundant_depends_on_no_versions ( & mut tree) ;
338
338
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) ;
340
348
341
349
while collapse_redundant_no_versions_tree ( & mut tree) {
342
350
// Continue collapsing until no more redundant nodes are found
@@ -456,15 +464,17 @@ fn collapse_redundant_no_versions(
456
464
// First, always recursively visit the other side of the tree
457
465
collapse_redundant_no_versions ( other) ;
458
466
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)
461
472
} ;
462
473
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 {
466
475
return ;
467
476
} ;
477
+
468
478
let versions = versions. complement ( ) ;
469
479
470
480
// If we're disqualifying a single version, this is important to retain, e.g,
@@ -473,11 +483,13 @@ fn collapse_redundant_no_versions(
473
483
return ;
474
484
}
475
485
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 {
477
490
return ;
478
491
}
479
492
480
- // Replace this node with the other tree
481
493
* tree = other. clone ( ) ;
482
494
}
483
495
// If not, just recurse
@@ -1088,44 +1100,94 @@ impl std::fmt::Display for NoSolutionHeader {
1088
1100
fn simplify_derivation_tree_ranges (
1089
1101
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
1090
1102
available_versions : & FxHashMap < PackageName , BTreeSet < Version > > ,
1103
+ candidate_selector : & CandidateSelector ,
1104
+ resolver_environment : & ResolverEnvironment ,
1091
1105
) {
1092
1106
match tree {
1093
1107
DerivationTree :: External ( external) => match external {
1094
1108
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
+ ) {
1096
1116
* versions1 = simplified;
1097
1117
}
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
+ ) {
1099
1125
* versions2 = simplified;
1100
1126
}
1101
1127
}
1102
1128
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
+ ) {
1104
1136
* versions = simplified;
1105
1137
}
1106
1138
}
1107
1139
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
+ ) {
1109
1147
* versions = simplified;
1110
1148
}
1111
1149
}
1112
1150
External :: NotRoot ( ..) => ( ) ,
1113
1151
} ,
1114
1152
DerivationTree :: Derived ( derived) => {
1115
1153
// 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
+ ) ;
1118
1166
1119
1167
// Simplify the terms
1120
1168
derived. terms = std:: mem:: take ( & mut derived. terms )
1121
1169
. into_iter ( )
1122
1170
. map ( |( pkg, term) | {
1123
1171
let term = match term {
1124
1172
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) ,
1126
1181
) ,
1127
1182
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) ,
1129
1191
) ,
1130
1192
} ;
1131
1193
( pkg, term)
@@ -1142,6 +1204,8 @@ fn simplify_range(
1142
1204
range : & Range < Version > ,
1143
1205
package : & PubGrubPackage ,
1144
1206
available_versions : & FxHashMap < PackageName , BTreeSet < Version > > ,
1207
+ candidate_selector : & CandidateSelector ,
1208
+ resolver_environment : & ResolverEnvironment ,
1145
1209
) -> Option < Range < Version > > {
1146
1210
// If there's not a package name or available versions, we can't simplify anything
1147
1211
let name = package. name ( ) ?;
@@ -1159,6 +1223,39 @@ fn simplify_range(
1159
1223
}
1160
1224
}
1161
1225
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
+
1162
1246
// 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
+ } ) ) )
1164
1261
}
0 commit comments