@@ -27,21 +27,25 @@ load(
27
27
)
28
28
load ("@rules_python//python/pip_install:requirements_parser.bzl" , parse_requirements = "parse" )
29
29
30
- def _create_pip (module_ctx , pip_attr , whl_map ):
30
+ def _create_versioned_pip_and_whl_repos (module_ctx , pip_attr , whl_map ):
31
31
python_interpreter_target = pip_attr .python_interpreter_target
32
32
33
33
# if we do not have the python_interpreter set in the attributes
34
34
# we programtically find it.
35
+ hub_name = pip_attr .hub_name
35
36
if python_interpreter_target == None :
36
37
python_name = "python_{}" .format (pip_attr .python_version .replace ("." , "_" ))
37
38
if python_name not in INTERPRETER_LABELS .keys ():
38
- fail ("""
39
- Unable to find '{}' in the list of interpreters please update your pip.parse call with the correct python name
40
- """ .format (pip_attr .python_name ))
41
-
39
+ fail ((
40
+ "Unable to find interpreter for pip hub '{hub_name}' for " +
41
+ "python_version={version}: Make sure a corresponding " +
42
+ '`python.toolchain(python_version="{version}")` call exists'
43
+ ).format (
44
+ hub_name = hub_name ,
45
+ version = pip_attr .python_version ,
46
+ ))
42
47
python_interpreter_target = INTERPRETER_LABELS [python_name ]
43
48
44
- hub_name = pip_attr .hub_name
45
49
pip_name = hub_name + "_{}" .format (pip_attr .python_version .replace ("." , "" ))
46
50
requrements_lock = locked_requirements_label (module_ctx , pip_attr )
47
51
@@ -93,10 +97,10 @@ Unable to find '{}' in the list of interpreters please update your pip.parse cal
93
97
whl_map [hub_name ][whl_name ][pip_attr .python_version ] = pip_name + "_"
94
98
95
99
def _pip_impl (module_ctx ):
96
- """Implmentation of a class tag that creates the pip hub(s) and corresponding pip spoke, alias and whl repositories.
100
+ """Implementation of a class tag that creates the pip hub(s) and corresponding pip spoke, alias and whl repositories.
97
101
98
- This implmentation iterates through all of the " pip.parse" calls and creates
99
- different pip hubs repositories based on the "hub_name". Each of the
102
+ This implmentation iterates through all of the ` pip.parse` calls and creates
103
+ different pip hub repositories based on the "hub_name". Each of the
100
104
pip calls create spoke repos that uses a specific Python interpreter.
101
105
102
106
In a MODULES.bazel file we have:
@@ -115,13 +119,13 @@ def _pip_impl(module_ctx):
115
119
)
116
120
117
121
118
- For instance we have a hub with the name of "pip".
122
+ For instance, we have a hub with the name of "pip".
119
123
A repository named the following is created. It is actually called last when
120
124
all of the pip spokes are collected.
121
125
122
126
- @@rules_python~override~pip~pip
123
127
124
- As show in the example code above we have the following.
128
+ As shown in the example code above we have the following.
125
129
Two different pip.parse statements exist in MODULE.bazel provide the hub_name "pip".
126
130
These definitions create two different pip spoke repositories that are
127
131
related to the hub "pip".
@@ -185,22 +189,32 @@ def _pip_impl(module_ctx):
185
189
186
190
for mod in module_ctx .modules :
187
191
for pip_attr in mod .tags .parse :
188
- if pip_attr .hub_name in pip_hub_map :
192
+ hub_name = pip_attr .hub_name
193
+ if hub_name in pip_hub_map :
189
194
# We cannot have two hubs with the same name in different
190
195
# modules.
191
- if pip_hub_map [pip_attr .hub_name ].module_name != mod .name :
192
- fail ("""Unable to create pip with the hub_name '{}', same hub name
193
- in a different module found.""" .format (pip_attr .hub_name ))
194
-
195
- if pip_attr .python_version in pip_hub_map [pip_attr .hub_name ].python_versions :
196
- fail (
197
- """Unable to create pip with the hub_name '{}', same hub name
198
- using the same Python repo name '{}' found in module '{}'.""" .format (
199
- pip_attr .hub_name ,
200
- pip_attr .python_version ,
201
- mod .name ,
202
- ),
203
- )
196
+ if pip_hub_map [hub_name ].module_name != mod .name :
197
+ fail ((
198
+ "Duplicate cross-module pip hub named '{hub}': pip hub " +
199
+ "names must be unique across modules. First defined " +
200
+ "by module '{first_module}', second attempted by " +
201
+ "module '{second_module}'"
202
+ ).format (
203
+ hub = hub_name ,
204
+ first_module = pip_hub_map [hub_name ].module_name ,
205
+ second_module = mod .name ,
206
+ ))
207
+
208
+ if pip_attr .python_version in pip_hub_map [hub_name ].python_versions :
209
+ fail ((
210
+ "Duplicate pip python version '{version}' for hub " +
211
+ "'{hub}' in module '{module}': the Python versions " +
212
+ "used for a hub must be unique"
213
+ ).format (
214
+ hub = hub_name ,
215
+ module = mod .name ,
216
+ version = pip_attr .python_version ,
217
+ ))
204
218
else :
205
219
pip_hub_map [pip_attr .hub_name ].python_versions .append (pip_attr .python_version )
206
220
else :
@@ -209,17 +223,19 @@ def _pip_impl(module_ctx):
209
223
python_versions = [pip_attr .python_version ],
210
224
)
211
225
212
- _create_pip (module_ctx , pip_attr , hub_whl_map )
226
+ _create_versioned_pip_and_whl_repos (module_ctx , pip_attr , hub_whl_map )
213
227
214
228
for hub_name , whl_map in hub_whl_map .items ():
215
229
for whl_name , version_map in whl_map .items ():
216
230
if DEFAULT_PYTHON_VERSION not in version_map :
217
- fail (
218
- """
219
- Unable to find the default python version in the version map, please update your requirements files
220
- to include Python '{}'.
221
- """ .format (DEFAULT_PYTHON_VERSION ),
222
- )
231
+ fail ((
232
+ "Default python version '{version}' missing in pip " +
233
+ "hub '{hub}': update your pip.parse() calls so that " +
234
+ 'includes `python_version = "{version}"`'
235
+ ).format (
236
+ version = DEFAULT_PYTHON_VERSION ,
237
+ hub = hub_name ,
238
+ ))
223
239
224
240
# Create the alias repositories which contains different select
225
241
# statements These select statements point to the different pip
@@ -247,14 +263,34 @@ def _pip_parse_ext_attrs():
247
263
"hub_name" : attr .string (
248
264
mandatory = True ,
249
265
doc = """
250
- The unique hub name. Mulitple pip.parse calls that contain the same hub name,
251
- create spokes for specific Python versions.
266
+ The name of the repo pip dependencies will be accessible from.
267
+
268
+ This name must be unique between modules; unless your module is guaranteed to
269
+ always be the root module, it's highly recommended to include your module name
270
+ in the hub name. Repo mapping, `use_repo(..., pip="my_modules_pip_deps")`, can
271
+ be used for shorter local names within your module.
272
+
273
+ Within a module, the same `hub_name` can be specified to group different Python
274
+ versions of pip dependencies under one repository name. This allows using a
275
+ Python version-agnostic name when referring to pip dependencies; the
276
+ correct version will be automatically selected.
277
+
278
+ Typically, a module will only have a single hub of pip dependencies, but this
279
+ is not required. Each hub is a separate resolution of pip dependencies. This
280
+ means if different programs need different versions of some library, separate
281
+ hubs can be created, and each program can use its respective hub's targets.
282
+ Targets from different hubs should not be used together.
252
283
""" ,
253
284
),
254
285
"python_version" : attr .string (
255
286
mandatory = True ,
256
287
doc = """
257
- The Python version for the pip spoke.
288
+ The Python version to use for resolving the pip dependencies. If not specified,
289
+ then the default Python version (as set by the root module or rules_python)
290
+ will be used.
291
+
292
+ The version specified here must have a corresponding `python.toolchain()`
293
+ configured.
258
294
""" ,
259
295
),
260
296
}, ** pip_repository_attrs )
@@ -270,12 +306,16 @@ The Python version for the pip spoke.
270
306
271
307
pip = module_extension (
272
308
doc = """\
273
- This extension is used to create a pip hub and all of the spokes that are part of that hub.
274
- We can have multiple different hubs, but we cannot have hubs that have the same name in
275
- different modules. Each hub needs one or more spokes. A spoke contains a specific version
276
- of Python, and the requirement(s) files that are unquie to that Python version.
277
- In order to add more spokes you call this extension mulitiple times using the same hub
278
- name.
309
+ This extension is used to make dependencies from pip available.
310
+
311
+ To use, call `pip.parse()` and specify `hub_name` and your requirements file.
312
+ Dependencies will be downloaded and made available in a repo named after the
313
+ `hub_name` argument.
314
+
315
+ Each `pip.parse()` call configures a particular Python version. Multiple calls
316
+ can be made to configure different Python versions, and will be grouped by
317
+ the `hub_name` argument. This allows the same logical name, e.g. `@pip//numpy`
318
+ to automatically resolve to different, Python version-specific, libraries.
279
319
""" ,
280
320
implementation = _pip_impl ,
281
321
tag_classes = {
0 commit comments