1515"""This module is for implementing PEP508 compliant METADATA deps parsing.
1616"""
1717
18- load ("@pythons_hub//:versions.bzl" , "DEFAULT_PYTHON_VERSION" )
1918load ("//python/private:normalize_name.bzl" , "normalize_name" )
2019load (":pep508_env.bzl" , "env" )
2120load (":pep508_evaluate.bzl" , "evaluate" )
2221load (":pep508_platform.bzl" , "platform" , "platform_from_str" )
2322load (":pep508_requirement.bzl" , "requirement" )
2423
25- _ALL_OS_VALUES = [
26- "windows" ,
27- "osx" ,
28- "linux" ,
29- ]
30- _ALL_ARCH_VALUES = [
31- "aarch64" ,
32- "ppc64" ,
33- "ppc64le" ,
34- "s390x" ,
35- "x86_32" ,
36- "x86_64" ,
37- ]
38-
39- def deps (name , * , requires_dist , platforms = [], extras = [], default_python_version = None ):
24+ def deps (name , * , requires_dist , platforms = [], extras = [], excludes = [], host_python_version = None ):
4025 """Parse the RequiresDist from wheel METADATA
4126
4227 Args:
4328 name: {type}`str` the name of the wheel.
4429 requires_dist: {type}`list[str]` the list of RequiresDist lines from the
4530 METADATA file.
31+ excludes: {type}`list[str]` what packages should we exclude.
4632 extras: {type}`list[str]` the requested extras to generate targets for.
4733 platforms: {type}`list[str]` the list of target platform strings.
48- default_python_version : {type}`str` the host python version.
34+ host_python_version : {type}`str` the host python version.
4935
5036 Returns:
5137 A struct with attributes:
5238 * deps: {type}`list[str]` dependencies to include unconditionally.
5339 * deps_select: {type}`dict[str, list[str]]` dependencies to include on particular
5440 subset of target platforms.
5541 """
56- if not platforms :
57- fail ("'platforms' arg is mandatory" )
58-
59- # TODO @aignas 2025-04-16: this `default_abi` variable is only
60- # here so that we can have selects without "is_python_version" conditions.
61- # This would happen in the `multi_pip_parse` logic in WORKSPACE.
62- #
63- # In followup PRs I would like to remove this logic and mainly rely on the
64- # fact that there will be only a single ABI when internal rules pass the
65- # values in the WORKSPACE case and in that case we can get the default
66- # version from the target platform strings. What is more when we drop
67- # WORKSPACE, we can simplify the logic in `_add_req` substantially and get
68- # rid of `default_abi` all together.
69- default_python_version = default_python_version or DEFAULT_PYTHON_VERSION
42+ reqs = sorted (
43+ [requirement (r ) for r in requires_dist ],
44+ key = lambda x : "{}:{}:" .format (x .name , sorted (x .extras ), x .marker ),
45+ )
46+ deps = {}
47+ deps_select = {}
48+ name = normalize_name (name )
49+ want_extras = _resolve_extras (name , reqs , extras )
50+
51+ # drop self edges
52+ excludes = [name ] + [normalize_name (x ) for x in excludes ]
53+
7054 platforms = [
71- platform_from_str (p , python_version = default_python_version )
55+ platform_from_str (p , python_version = host_python_version )
7256 for p in platforms
7357 ]
7458
7559 abis = sorted ({p .abi : True for p in platforms if p .abi })
76- if default_python_version and len (abis ) > 1 :
77- _ , _ , minor_version = default_python_version .partition ("." )
60+ if host_python_version and len (abis ) > 1 :
61+ _ , _ , minor_version = host_python_version .partition ("." )
7862 minor_version , _ , _ = minor_version .partition ("." )
7963 default_abi = "cp3" + minor_version
8064 elif len (abis ) > 1 :
8165 fail (
82- "all python versions need to be specified explicitly with the default_python_version , got: {}, {} " .format (platforms , default_python_version ),
66+ "all python versions need to be specified explicitly, got: {}" .format (platforms ),
8367 )
8468 else :
8569 default_abi = None
8670
87- reqs = sorted (
88- [requirement (r ) for r in requires_dist ],
89- key = lambda x : "{}:{}:" .format (x .name , sorted (x .extras ), x .marker ),
90- )
91- deps = {}
92- deps_select = {}
93- name = normalize_name (name )
94- want_extras = _resolve_extras (name , reqs , extras )
71+ reqs_by_name = {}
9572
9673 for req in reqs :
97- if req .name == name :
98- # drop self edges
74+ if req .name_ in excludes :
9975 continue
10076
101- _add_req (
77+ reqs_by_name .setdefault (req .name , []).append (req )
78+
79+ for name , reqs in reqs_by_name .items ():
80+ _add_reqs (
10281 deps ,
10382 deps_select ,
104- req ,
83+ normalize_name (name ),
84+ reqs ,
10585 extras = want_extras ,
10686 platforms = platforms ,
10787 default_abi = default_abi ,
@@ -120,46 +100,20 @@ def _platform_str(self):
120100 if not self .os and not self .arch :
121101 return "//conditions:default"
122102 elif not self .arch :
123- return "@platforms//os:{}" . format ( self . os )
103+ fail ( "remove" )
124104 else :
125105 return "{}_{}" .format (self .os , self .arch )
126106
127107 minor_version = self .abi [3 :]
128108 if self .arch == None and self .os == None :
129- return str ( Label ( "//python/config_settings:is_python_3.{}" . format ( minor_version )) )
109+ fail ( "remove" )
130110
131111 return "cp3{}_{}_{}" .format (
132112 minor_version ,
133113 self .os or "anyos" ,
134114 self .arch or "anyarch" ,
135115 )
136116
137- def _platform_specializations (self , cpu_values = _ALL_ARCH_VALUES , os_values = _ALL_OS_VALUES ):
138- """Return the platform itself and all its unambiguous specializations.
139-
140- For more info about specializations see
141- https://bazel.build/docs/configurable-attributes
142- """
143- specializations = []
144- specializations .append (self )
145- if self .arch == None :
146- specializations .extend ([
147- platform (os = self .os , arch = arch , abi = self .abi )
148- for arch in cpu_values
149- ])
150- if self .os == None :
151- specializations .extend ([
152- platform (os = os , arch = self .arch , abi = self .abi )
153- for os in os_values
154- ])
155- if self .os == None and self .arch == None :
156- specializations .extend ([
157- platform (os = os , arch = arch , abi = self .abi )
158- for os in os_values
159- for arch in cpu_values
160- ])
161- return specializations
162-
163117def _add (deps , deps_select , dep , platform ):
164118 dep = normalize_name (dep )
165119
@@ -186,53 +140,7 @@ def _add(deps, deps_select, dep, platform):
186140 return
187141
188142 # Add the platform-specific branch
189- deps_select .setdefault (platform , {})
190-
191- # Add the dep to specializations of the given platform if they
192- # exist in the select statement.
193- for p in _platform_specializations (platform ):
194- if p not in deps_select :
195- continue
196-
197- deps_select [p ][dep ] = True
198-
199- if len (deps_select [platform ]) == 1 :
200- # We are adding a new item to the select and we need to ensure that
201- # existing dependencies from less specialized platforms are propagated
202- # to the newly added dependency set.
203- for p , _deps in deps_select .items ():
204- # Check if the existing platform overlaps with the given platform
205- if p == platform or platform not in _platform_specializations (p ):
206- continue
207-
208- deps_select [platform ].update (_deps )
209-
210- def _maybe_add_common_dep (deps , deps_select , platforms , dep ):
211- abis = sorted ({p .abi : True for p in platforms if p .abi })
212- if len (abis ) < 2 :
213- return
214-
215- platforms = [platform ()] + [
216- platform (abi = abi )
217- for abi in abis
218- ]
219-
220- # If the dep is targeting all target python versions, lets add it to
221- # the common dependency list to simplify the select statements.
222- for p in platforms :
223- if p not in deps_select :
224- return
225-
226- if dep not in deps_select [p ]:
227- return
228-
229- # All of the python version-specific branches have the dep, so lets add
230- # it to the common deps.
231- deps [dep ] = True
232- for p in platforms :
233- deps_select [p ].pop (dep )
234- if not deps_select [p ]:
235- deps_select .pop (p )
143+ deps_select .setdefault (platform , {})[dep ] = True
236144
237145def _resolve_extras (self_name , reqs , extras ):
238146 """Resolve extras which are due to depending on self[some_other_extra].
@@ -289,72 +197,37 @@ def _resolve_extras(self_name, reqs, extras):
289197 # Poor mans set
290198 return sorted ({x : None for x in extras })
291199
292- def _add_req (deps , deps_select , req , * , extras , platforms , default_abi = None ):
293- if not req .marker :
294- _add (deps , deps_select , req .name , None )
295- return
296-
297- match_os = len ([
298- tag
299- for tag in [
300- "os_name" ,
301- "sys_platform" ,
302- "platform_system" ,
303- ]
304- if tag in req .marker
305- ]) > 0
306- match_arch = "platform_machine" in req .marker
307- match_version = "version" in req .marker
308-
309- if not (match_os or match_arch or match_version ):
310- if [
311- True
312- for extra in extras
313- for p in platforms
314- if evaluate (
315- req .marker ,
316- env = env (
317- target_platform = p ,
318- extra = extra ,
319- ),
320- )
321- ]:
322- _add (deps , deps_select , req .name , None )
323- return
200+ def _add_reqs (deps , deps_select , dep , reqs , * , extras , platforms , default_abi = None ):
201+ for req in reqs :
202+ if not req .marker :
203+ _add (deps , deps_select , dep , None )
204+ return
324205
206+ platforms_to_add = {}
325207 for plat in platforms :
326- if not [
327- True
328- for extra in extras
329- if evaluate (
330- req .marker ,
331- env = env (
332- target_platform = plat ,
333- extra = extra ,
334- ),
335- )
336- ]:
208+ if plat in platforms_to_add :
209+ # marker evaluation is more expensive than this check
337210 continue
338211
339- if match_arch and default_abi :
340- _add (deps , deps_select , req .name , plat )
341- if plat .abi == default_abi :
342- _add (deps , deps_select , req .name , platform (os = plat .os , arch = plat .arch ))
343- elif match_arch :
344- _add (deps , deps_select , req .name , platform (os = plat .os , arch = plat .arch ))
345- elif match_os and default_abi :
346- _add (deps , deps_select , req .name , platform (os = plat .os , abi = plat .abi ))
347- if plat .abi == default_abi :
348- _add (deps , deps_select , req .name , platform (os = plat .os ))
349- elif match_os :
350- _add (deps , deps_select , req .name , platform (os = plat .os ))
351- elif match_version and default_abi :
352- _add (deps , deps_select , req .name , platform (abi = plat .abi ))
353- if plat .abi == default_abi :
354- _add (deps , deps_select , req .name , platform ())
355- elif match_version :
356- _add (deps , deps_select , req .name , None )
357- else :
358- fail ("BUG: {} support is not implemented" .format (req .marker ))
212+ added = False
213+ for extra in extras :
214+ if added :
215+ break
216+
217+ for req in reqs :
218+ if evaluate (req .marker , env = env (target_platform = plat , extra = extra )):
219+ platforms_to_add [plat ] = True
220+ added = True
221+ break
222+
223+ if len (platforms_to_add ) == len (platforms ):
224+ # the dep is in all target platforms, let's just add it to the regular
225+ # list
226+ _add (deps , deps_select , dep , None )
227+ return
359228
360- _maybe_add_common_dep (deps , deps_select , platforms , req .name )
229+ for plat in platforms_to_add :
230+ if default_abi :
231+ _add (deps , deps_select , dep , plat )
232+ if plat .abi == default_abi or not default_abi :
233+ _add (deps , deps_select , dep , platform (os = plat .os , arch = plat .arch ))
0 commit comments