@@ -4,55 +4,80 @@ load("//python/private:version.bzl", "version")
44load (":parse_whl_name.bzl" , "parse_whl_name" )
55load (":python_tag.bzl" , "PY_TAG_GENERIC" , "python_tag" )
66
7- def _get_priority (* , tags , values , allow_wildcard = True ):
7+ def _get_priority (* , tag , values , allow_wildcard = True ):
88 keys = []
99 for priority , wp in enumerate (values ):
10- for tag in tags .split ("." ):
11- head , sep , tail = wp .partition ("*" )
12- if "*" in tail :
13- fail ("only a single '*' can be present in the matcher" )
14- if not allow_wildcard and sep :
15- fail ("'*' is not allowed in the matcher" )
16-
17- if not sep and tag == head :
18- keys .append (priority )
19- elif sep and tag .startswith (head ) and tag .endswith (tail ):
20- keys .append (priority )
10+ head , sep , tail = wp .partition ("*" )
11+ if "*" in tail :
12+ fail ("only a single '*' can be present in the matcher" )
13+ if not allow_wildcard and sep :
14+ fail ("'*' is not allowed in the matcher" )
15+
16+ if not sep and tag == head :
17+ keys .append (priority )
18+ elif sep and tag .startswith (head ) and tag .endswith (tail ):
19+ keys .append (priority )
2120
2221 if not keys :
2322 return None
2423
2524 return max (keys )
2625
27- def _get_py_priority (* , tags , implementation , py_version ):
28- keys = []
29- for tag in tags .split ("." ):
30- if tag .startswith (PY_TAG_GENERIC ):
31- ver_str = tag [len (PY_TAG_GENERIC ):]
32- elif tag .startswith (implementation ):
33- ver_str = tag [len (implementation ):]
34- else :
35- continue
26+ def _get_py_priority (* , tag , implementation , py_version ):
27+ if tag .startswith (PY_TAG_GENERIC ):
28+ ver_str = tag [len (PY_TAG_GENERIC ):]
29+ elif tag .startswith (implementation ):
30+ ver_str = tag [len (implementation ):]
31+ else :
32+ return None
3633
37- # Add a 0 at the end in case it is a single digit
38- ver_str = "{}.{}" .format (ver_str [0 ], ver_str [1 :] or "0" )
34+ # Add a 0 at the end in case it is a single digit
35+ ver_str = "{}.{}" .format (ver_str [0 ], ver_str [1 :] or "0" )
3936
40- ver = version .parse (ver_str )
41- if not version .is_compatible (py_version , ver ):
42- continue
37+ ver = version .parse (ver_str )
38+ if not version .is_compatible (py_version , ver ):
39+ return None
4340
44- keys .append ((
45- tag .startswith (implementation ),
46- version .key (ver ),
47- # Prefer shorter py_tags, which will yield more specialized matches,
48- # like preferring py3 over py2.py3
49- - len (tags ),
50- ))
41+ return (
42+ tag .startswith (implementation ),
43+ version .key (ver ),
44+ )
5145
52- if not keys :
53- return None
46+ def _tag_sets (* , whls , implementation , py_version , whl_abi_tags , platforms ):
47+ ret = {}
48+ for whl in whls :
49+ parsed = parse_whl_name (whl .filename )
5450
55- return max (keys )
51+ # See https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#compressed-tag-sets
52+ for py in parsed .python_tag .split ("." ):
53+ py = _get_py_priority (
54+ tag = py ,
55+ implementation = implementation ,
56+ py_version = py_version ,
57+ )
58+ if py == None :
59+ ret [struct (py = py )] = whl
60+ continue
61+
62+ for abi in parsed .abi_tag .split ("." ):
63+ abi = _get_priority (
64+ tag = abi ,
65+ values = whl_abi_tags ,
66+ allow_wildcard = False ,
67+ )
68+ if py == None :
69+ ret [struct (py = py , abi = abi )] = whl
70+ continue
71+
72+ for p in parsed .platform_tag .split ("." ):
73+ platform = _get_priority (
74+ tag = p ,
75+ values = platforms ,
76+ )
77+
78+ ret [struct (py = py , abi = abi , platform = platform )] = whl
79+
80+ return ret
5681
5782def select_whl (* , whls , python_version , platforms , whl_abi_tags , implementation_name = "cpython" , limit = 1 , logger = None ):
5883 """Select a whl that is the most suitable for the given platform.
@@ -77,65 +102,41 @@ def select_whl(*, whls, python_version, platforms, whl_abi_tags, implementation_
77102 candidates = {}
78103 implementation = python_tag (implementation_name )
79104
80- for whl in whls :
81- parsed = parse_whl_name (whl .filename )
82-
83- if parsed .python_tag .startswith (PY_TAG_GENERIC ):
84- pass
85- elif not parsed .python_tag .startswith (implementation ):
105+ for priority , whl in _tag_sets (
106+ whls = whls ,
107+ implementation = implementation ,
108+ py_version = py_version ,
109+ whl_abi_tags = whl_abi_tags ,
110+ platforms = platforms ,
111+ ).items ():
112+ if priority .py == None :
86113 if logger :
87- logger .debug (lambda : "Discarding the wheel because the implementation '{}' is not compatible with target implementation '{}'" .format (
88- parsed .python_tag ,
89- implementation ,
90- ))
91- continue
92-
93- py_priority = _get_py_priority (
94- tags = parsed .python_tag ,
95- implementation = implementation ,
96- py_version = py_version ,
97- )
98- if py_priority == None :
99- if logger :
100- logger .debug (lambda : "The py_tag '{}' does not match implementation version: {} {}" .format (
101- parsed .py_tag ,
114+ logger .debug (lambda : "The python_tag in '{}' does not match implementation or version: {} {}" .format (
115+ whl .filename ,
102116 implementation ,
103117 py_version .string ,
104118 ))
105119 continue
106-
107- abi_priority = _get_priority (
108- tags = parsed .abi_tag ,
109- values = whl_abi_tags ,
110- allow_wildcard = False ,
111- )
112- if abi_priority == None :
120+ elif priority .abi == None :
113121 if logger :
114- logger .debug (lambda : "The abi '{}' does not match given list: {}" .format (
115- parsed . abi_tag ,
122+ logger .debug (lambda : "The abi_tag in '{}' does not match given list: {}" .format (
123+ whl . filename ,
116124 whl_abi_tags ,
117125 ))
118126 continue
119-
120- platform_priority = _get_priority (
121- tags = parsed .platform_tag ,
122- values = platforms ,
123- )
124- if platform_priority == None :
127+ elif priority .platform == None :
125128 if logger :
126- logger .debug (lambda : "The platform_tag '{}' does not match given list: {}" .format (
127- parsed . platform_tag ,
129+ logger .debug (lambda : "The platform_tag in '{}' does not match given list: {}" .format (
130+ whl . filename ,
128131 platforms ,
129132 ))
130133 continue
131134
132- key = (
133- # Ensure that we chose the highest compatible version
134- py_priority ,
135- platform_priority ,
136- abi_priority ,
137- )
138- candidates .setdefault (key , whl )
135+ candidates .setdefault ((
136+ priority .platform , # Prefer platform wheels
137+ priority .py , # Then prefer implementation/python version
138+ priority .abi , # Then prefer more specific ABI wheels
139+ ), whl )
139140
140141 if not candidates :
141142 return None
0 commit comments