@@ -4,55 +4,80 @@ load("//python/private:version.bzl", "version")
4
4
load (":parse_whl_name.bzl" , "parse_whl_name" )
5
5
load (":python_tag.bzl" , "PY_TAG_GENERIC" , "python_tag" )
6
6
7
- def _get_priority (* , tags , values , allow_wildcard = True ):
7
+ def _get_priority (* , tag , values , allow_wildcard = True ):
8
8
keys = []
9
9
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 )
21
20
22
21
if not keys :
23
22
return None
24
23
25
24
return max (keys )
26
25
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
36
33
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" )
39
36
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
43
40
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
+ )
51
45
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 )
54
50
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
56
81
57
82
def select_whl (* , whls , python_version , platforms , whl_abi_tags , implementation_name = "cpython" , limit = 1 , logger = None ):
58
83
"""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_
77
102
candidates = {}
78
103
implementation = python_tag (implementation_name )
79
104
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 :
86
113
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 ,
102
116
implementation ,
103
117
py_version .string ,
104
118
))
105
119
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 :
113
121
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 ,
116
124
whl_abi_tags ,
117
125
))
118
126
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 :
125
128
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 ,
128
131
platforms ,
129
132
))
130
133
continue
131
134
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 )
139
140
140
141
if not candidates :
141
142
return None
0 commit comments