@@ -31,7 +31,7 @@ use uv_distribution_types::{
31
31
use uv_git:: GitResolver ;
32
32
use uv_normalize:: { ExtraName , GroupName , PackageName } ;
33
33
use uv_pep440:: { release_specifiers_to_ranges, Version , VersionSpecifiers , MIN_VERSION } ;
34
- use uv_pep508:: { MarkerExpression , MarkerOperator , MarkerTree , MarkerValueString } ;
34
+ use uv_pep508:: MarkerTree ;
35
35
use uv_platform_tags:: Tags ;
36
36
use uv_pypi_types:: {
37
37
ConflictItem , ConflictItemRef , Conflicts , Requirement , ResolutionMetadata , VerbatimParsedUrl ,
@@ -1339,88 +1339,108 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
1339
1339
return Ok ( None ) ;
1340
1340
} ;
1341
1341
1342
- for platform in [
1343
- ResolverPlatform :: Linux ,
1344
- ResolverPlatform :: Windows ,
1345
- ResolverPlatform :: MacOS ,
1346
- ] {
1347
- let marker = platform. marker ( ) ;
1348
-
1349
- // If the platform is relevant to the current environment...
1350
- if !env. included_by_marker ( marker) {
1351
- continue ;
1352
- }
1342
+ debug ! (
1343
+ "Looking at local version: {}=={}" ,
1344
+ name,
1345
+ candidate. version( )
1346
+ ) ;
1353
1347
1354
- // But isn't supported by the distribution ...
1355
- if !dist . implied_markers ( ) . is_disjoint ( marker ) {
1356
- continue ;
1357
- }
1348
+ // If there's a non-local version ...
1349
+ let range = range . clone ( ) . intersection ( & Range :: singleton (
1350
+ candidate . version ( ) . clone ( ) . without_local ( ) ,
1351
+ ) ) ;
1358
1352
1359
- // And there's a non-local version that _does_ support the platform.
1360
- let range = range. clone ( ) . intersection ( & Range :: singleton (
1361
- candidate. version ( ) . clone ( ) . without_local ( ) ,
1362
- ) ) ;
1353
+ let Some ( base_candidate) = self . selector . select (
1354
+ name,
1355
+ & range,
1356
+ version_maps,
1357
+ preferences,
1358
+ & self . installed_packages ,
1359
+ & self . exclusions ,
1360
+ index,
1361
+ env,
1362
+ ) else {
1363
+ return Ok ( None ) ;
1364
+ } ;
1365
+ let CandidateDist :: Compatible ( base_dist) = base_candidate. dist ( ) else {
1366
+ return Ok ( None ) ;
1367
+ } ;
1363
1368
1364
- let Some ( platform_candidate) = self . selector . select (
1365
- name,
1366
- & range,
1367
- version_maps,
1368
- preferences,
1369
- & self . installed_packages ,
1370
- & self . exclusions ,
1371
- index,
1372
- env,
1373
- ) else {
1374
- continue ;
1375
- } ;
1376
- let CandidateDist :: Compatible ( platform_dist) = platform_candidate. dist ( ) else {
1377
- continue ;
1378
- } ;
1369
+ // ...and the non-local version has greater platform support...
1370
+ let remainder = {
1371
+ let mut remainder = base_dist. implied_markers ( ) ;
1372
+ remainder. and ( dist. implied_markers ( ) . negate ( ) ) ;
1373
+ remainder
1374
+ } ;
1375
+ if remainder. is_false ( ) {
1376
+ return Ok ( None ) ;
1377
+ }
1379
1378
1380
- if platform_dist. implied_markers ( ) . is_disjoint ( marker) {
1381
- continue ;
1382
- } ;
1379
+ // If the remainder isn't relevant to the current environment, there's no need to fork.
1380
+ // For example, if we're solving for `sys_platform == 'darwin'` but the remainder is
1381
+ // `sys_platform == 'linux'`, we don't need to fork.
1382
+ if !env. included_by_marker ( remainder) {
1383
+ return Ok ( None ) ;
1384
+ }
1383
1385
1384
- // Then we need to fork.
1385
- let Some ( ( platform_env, env) ) = fork_version_by_marker ( env, marker) else {
1386
- continue ;
1386
+ // Similarly, if the local distribution is incompatible with the current environment, then
1387
+ // use the base distribution instead (but don't fork).
1388
+ if !env. included_by_marker ( dist. implied_markers ( ) ) {
1389
+ let filename = match dist. for_installation ( ) {
1390
+ ResolvedDistRef :: InstallableRegistrySourceDist { sdist, .. } => sdist
1391
+ . filename ( )
1392
+ . unwrap_or ( Cow :: Borrowed ( "unknown filename" ) ) ,
1393
+ ResolvedDistRef :: InstallableRegistryBuiltDist { wheel, .. } => wheel
1394
+ . filename ( )
1395
+ . unwrap_or ( Cow :: Borrowed ( "unknown filename" ) ) ,
1396
+ ResolvedDistRef :: Installed { .. } => Cow :: Borrowed ( "installed" ) ,
1387
1397
} ;
1388
1398
1389
1399
debug ! (
1390
- "Forking platform for {}=={} ({})" ,
1400
+ "Preferring non-local candidate: {}=={} [{}] ({})" ,
1391
1401
name,
1392
- candidate. version( ) ,
1393
- [ & platform_env, & env]
1394
- . iter( )
1395
- . map( ToString :: to_string)
1396
- . collect:: <Vec <_>>( )
1397
- . join( ", " )
1402
+ base_candidate. version( ) ,
1403
+ base_candidate. choice_kind( ) ,
1404
+ filename,
1398
1405
) ;
1399
- self . visit_candidate ( candidate, dist, package, pins, request_sink) ?;
1400
- self . visit_candidate (
1401
- & platform_candidate,
1402
- platform_dist,
1403
- package,
1404
- pins,
1405
- request_sink,
1406
- ) ?;
1407
-
1408
- let forks = vec ! [
1409
- VersionFork {
1410
- env: env. clone( ) ,
1411
- id,
1412
- version: Some ( candidate. version( ) . clone( ) ) ,
1413
- } ,
1414
- VersionFork {
1415
- env: platform_env. clone( ) ,
1416
- id,
1417
- version: Some ( platform_candidate. version( ) . clone( ) ) ,
1418
- } ,
1419
- ] ;
1420
- return Ok ( Some ( ResolverVersion :: Forked ( forks) ) ) ;
1406
+ self . visit_candidate ( & base_candidate, base_dist, package, pins, request_sink) ?;
1407
+
1408
+ return Ok ( Some ( ResolverVersion :: Unforked (
1409
+ base_candidate. version ( ) . clone ( ) ,
1410
+ ) ) ) ;
1421
1411
}
1422
1412
1423
- Ok ( None )
1413
+ // Otherwise, we need to fork.
1414
+ let Some ( ( base_env, local_env) ) = fork_version_by_marker ( env, remainder) else {
1415
+ return Ok ( None ) ;
1416
+ } ;
1417
+
1418
+ debug ! (
1419
+ "Forking platform for {}=={} ({})" ,
1420
+ name,
1421
+ candidate. version( ) ,
1422
+ [ & base_env, & local_env]
1423
+ . iter( )
1424
+ . map( ToString :: to_string)
1425
+ . collect:: <Vec <_>>( )
1426
+ . join( ", " )
1427
+ ) ;
1428
+ self . visit_candidate ( candidate, dist, package, pins, request_sink) ?;
1429
+ self . visit_candidate ( & base_candidate, base_dist, package, pins, request_sink) ?;
1430
+
1431
+ let forks = vec ! [
1432
+ VersionFork {
1433
+ env: base_env. clone( ) ,
1434
+ id,
1435
+ version: Some ( base_candidate. version( ) . clone( ) ) ,
1436
+ } ,
1437
+ VersionFork {
1438
+ env: local_env. clone( ) ,
1439
+ id,
1440
+ version: Some ( candidate. version( ) . clone( ) ) ,
1441
+ } ,
1442
+ ] ;
1443
+ Ok ( Some ( ResolverVersion :: Forked ( forks) ) )
1424
1444
}
1425
1445
1426
1446
/// Visit a selected candidate.
@@ -3536,41 +3556,3 @@ struct ConflictTracker {
3536
3556
/// Distilled from `culprit` for fast checking in the hot loop.
3537
3557
deprioritize : Vec < Id < PubGrubPackage > > ,
3538
3558
}
3539
-
3540
- /// A platform for which the resolver is solving.
3541
- #[ derive( Debug , Clone , Copy ) ]
3542
- enum ResolverPlatform {
3543
- Linux ,
3544
- Windows ,
3545
- MacOS ,
3546
- }
3547
-
3548
- impl ResolverPlatform {
3549
- /// Return the platform's `sys.platform` value.
3550
- fn sys_platform ( self ) -> & ' static str {
3551
- match self {
3552
- ResolverPlatform :: Linux => "linux" ,
3553
- ResolverPlatform :: Windows => "win32" ,
3554
- ResolverPlatform :: MacOS => "darwin" ,
3555
- }
3556
- }
3557
-
3558
- /// Return a [`MarkerTree`] for the platform.
3559
- fn marker ( self ) -> MarkerTree {
3560
- MarkerTree :: expression ( MarkerExpression :: String {
3561
- key : MarkerValueString :: SysPlatform ,
3562
- operator : MarkerOperator :: Equal ,
3563
- value : self . sys_platform ( ) . to_string ( ) ,
3564
- } )
3565
- }
3566
- }
3567
-
3568
- impl Display for ResolverPlatform {
3569
- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
3570
- match self {
3571
- ResolverPlatform :: Linux => write ! ( f, "Linux" ) ,
3572
- ResolverPlatform :: Windows => write ! ( f, "Windows" ) ,
3573
- ResolverPlatform :: MacOS => write ! ( f, "macOS" ) ,
3574
- }
3575
- }
3576
- }
0 commit comments