5
5
import itertools
6
6
import logging
7
7
import re
8
+ import sys
8
9
from dataclasses import dataclass
9
10
from typing import TYPE_CHECKING , FrozenSet , Iterable , List , Optional , Set , Tuple , Union
10
11
36
37
from pip ._internal .utils .misc import build_netloc
37
38
from pip ._internal .utils .packaging import check_requires_python
38
39
from pip ._internal .utils .unpacking import SUPPORTED_EXTENSIONS
39
- from pip ._internal .utils .variant import VariantJson
40
+ from pip ._internal .utils .variant import (
41
+ VariantJson ,
42
+ get_cached_variant_hashes_by_priority ,
43
+ get_variants_json_filename ,
44
+ )
40
45
41
46
if TYPE_CHECKING :
42
47
from pip ._vendor .typing_extensions import TypeGuard
@@ -105,6 +110,7 @@ class LinkType(enum.Enum):
105
110
format_invalid = enum .auto ()
106
111
platform_mismatch = enum .auto ()
107
112
requires_python_mismatch = enum .auto ()
113
+ variant_unsupported = enum .auto ()
108
114
109
115
110
116
class LinkEvaluator :
@@ -154,7 +160,7 @@ def __init__(
154
160
self ._target_python = target_python
155
161
156
162
self .project_name = project_name
157
- self .variants_json = None
163
+ self .variants_json = {}
158
164
159
165
def evaluate_link (self , link : Link ) -> Tuple [LinkType , str , Optional [str ]]:
160
166
"""
@@ -204,9 +210,7 @@ def evaluate_link(self, link: Link) -> Tuple[LinkType, str, Optional[str]]:
204
210
return (LinkType .different_project , reason , None )
205
211
206
212
variant_hash = wheel .variant_hash
207
- supported_tags = self ._target_python .get_unsorted_tags (
208
- variants_json = self .variants_json
209
- )
213
+ supported_tags = self ._target_python .get_unsorted_tags ()
210
214
if not wheel .supported (supported_tags ):
211
215
# Include the wheel's tags in the reason string to
212
216
# simplify troubleshooting compatibility issues.
@@ -217,6 +221,20 @@ def evaluate_link(self, link: Link) -> Tuple[LinkType, str, Optional[str]]:
217
221
)
218
222
return (LinkType .platform_mismatch , reason , None )
219
223
224
+ supported_variants = set (
225
+ get_cached_variant_hashes_by_priority (
226
+ self .variants_json .get (
227
+ get_variants_json_filename (wheel )
228
+ )
229
+ )
230
+ )
231
+ if wheel .variant_hash not in supported_variants :
232
+ reason = (
233
+ f"variant { wheel .variant_hash } is not compatible with "
234
+ f"the system"
235
+ )
236
+ return (LinkType .variant_unsupported , reason , None )
237
+
220
238
version = wheel .version
221
239
222
240
# This should be up by the self.ok_binary check, but see issue 2700.
@@ -384,7 +402,7 @@ def create(
384
402
allow_all_prereleases : bool = False ,
385
403
specifier : Optional [specifiers .BaseSpecifier ] = None ,
386
404
hashes : Optional [Hashes ] = None ,
387
- variants_json : Optional [VariantJson ] = None ,
405
+ variants_json : dict [VariantJson ] = {} ,
388
406
) -> "CandidateEvaluator" :
389
407
"""Create a CandidateEvaluator object.
390
408
@@ -401,9 +419,7 @@ def create(
401
419
if specifier is None :
402
420
specifier = specifiers .SpecifierSet ()
403
421
404
- supported_tags = target_python .get_sorted_tags (
405
- variants_json = variants_json ,
406
- )
422
+ supported_tags = target_python .get_sorted_tags ()
407
423
408
424
return cls (
409
425
project_name = project_name ,
@@ -412,6 +428,7 @@ def create(
412
428
prefer_binary = prefer_binary ,
413
429
allow_all_prereleases = allow_all_prereleases ,
414
430
hashes = hashes ,
431
+ variants_json = variants_json ,
415
432
)
416
433
417
434
def __init__ (
@@ -422,6 +439,7 @@ def __init__(
422
439
prefer_binary : bool = False ,
423
440
allow_all_prereleases : bool = False ,
424
441
hashes : Optional [Hashes ] = None ,
442
+ variants_json : dict [VariantJson ] = [],
425
443
) -> None :
426
444
"""
427
445
:param supported_tags: The PEP 425 tags supported by the target
@@ -433,6 +451,7 @@ def __init__(
433
451
self ._project_name = project_name
434
452
self ._specifier = specifier
435
453
self ._supported_tags = supported_tags
454
+ self ._variants_json = variants_json
436
455
# Since the index of the tag in the _supported_tags list is used
437
456
# as a priority, precompute a map from tag to index/priority to be
438
457
# used in wheel.find_most_preferred_tag.
@@ -513,12 +532,20 @@ def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey:
513
532
if link .is_wheel :
514
533
# can raise InvalidWheelFilename
515
534
wheel = Wheel (link .filename )
535
+
536
+ supported_variants = get_cached_variant_hashes_by_priority (
537
+ self ._variants_json .get (
538
+ get_variants_json_filename (wheel )
539
+ )
540
+ )
541
+
516
542
try :
517
543
pri = - (
518
544
wheel .find_most_preferred_tag (
519
545
valid_tags , self ._wheel_tag_preferences
520
546
)
521
547
)
548
+ variant_pri = - supported_variants .index (wheel .variant_hash )
522
549
except ValueError :
523
550
raise UnsupportedWheel (
524
551
f"{ wheel .filename } is not a supported wheel for this platform. It "
@@ -533,13 +560,15 @@ def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey:
533
560
build_tag = (int (build_tag_groups [0 ]), build_tag_groups [1 ])
534
561
else : # sdist
535
562
pri = - (support_num )
563
+ variant_pri = - sys .maxsize
536
564
has_allowed_hash = int (link .is_hash_allowed (self ._hashes ))
537
565
yank_value = - 1 * int (link .is_yanked ) # -1 for yanked.
538
566
return (
539
567
has_allowed_hash ,
540
568
yank_value ,
541
569
binary_preference ,
542
570
candidate .version ,
571
+ variant_pri ,
543
572
pri ,
544
573
build_tag ,
545
574
)
@@ -741,7 +770,7 @@ def _sort_links(self, links: Iterable[Link]) -> List[Link]:
741
770
for link in links :
742
771
if link not in seen :
743
772
seen .add (link )
744
- if link .filename == " variants.json" :
773
+ if link .filename . endswith ( "- variants.json") :
745
774
variants_json .append (link )
746
775
elif link .egg_fragment :
747
776
eggs .append (link )
@@ -784,10 +813,6 @@ def get_install_candidate(
784
813
except InvalidVersion :
785
814
return None
786
815
787
- @functools .cache
788
- def get_variants_json (self , link : Link ) -> dict :
789
- return VariantJson (self ._link_collector .session .request ("GET" , link .url ).json ())
790
-
791
816
def evaluate_links (
792
817
self , link_evaluator : LinkEvaluator , links : Iterable [Link ]
793
818
) -> List [InstallationCandidate ]:
@@ -796,8 +821,11 @@ def evaluate_links(
796
821
"""
797
822
candidates = []
798
823
for link in self ._sort_links (links ):
799
- if link .filename == "variants.json" :
800
- link_evaluator .variants_json = self .get_variants_json (link )
824
+ if link .filename .endswith ("-variants.json" ):
825
+ link_evaluator .variants_json [link .filename ] = VariantJson (
826
+ link .url ,
827
+ lambda url : self ._link_collector .session .request ("GET" , url ).json (),
828
+ )
801
829
continue
802
830
803
831
candidate = self .get_install_candidate (link_evaluator , link )
0 commit comments