@@ -27,21 +27,25 @@ load(
2727)
2828load ("@rules_python//python/pip_install:requirements_parser.bzl" , parse_requirements = "parse" )
2929
30- def _create_pip (module_ctx , pip_attr , whl_map ):
30+ def _create_versioned_pip_and_whl_repos (module_ctx , pip_attr , whl_map ):
3131 python_interpreter_target = pip_attr .python_interpreter_target
3232
3333 # if we do not have the python_interpreter set in the attributes
3434 # we programtically find it.
35+ hub_name = pip_attr .hub_name
3536 if python_interpreter_target == None :
3637 python_name = "python_{}" .format (pip_attr .python_version .replace ("." , "_" ))
3738 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+ ))
4247 python_interpreter_target = INTERPRETER_LABELS [python_name ]
4348
44- hub_name = pip_attr .hub_name
4549 pip_name = hub_name + "_{}" .format (pip_attr .python_version .replace ("." , "" ))
4650 requrements_lock = locked_requirements_label (module_ctx , pip_attr )
4751
@@ -93,10 +97,10 @@ Unable to find '{}' in the list of interpreters please update your pip.parse cal
9397 whl_map [hub_name ][whl_name ][pip_attr .python_version ] = pip_name + "_"
9498
9599def _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.
97101
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
100104 pip calls create spoke repos that uses a specific Python interpreter.
101105
102106 In a MODULES.bazel file we have:
@@ -115,13 +119,13 @@ def _pip_impl(module_ctx):
115119 )
116120
117121
118- For instance we have a hub with the name of "pip".
122+ For instance, we have a hub with the name of "pip".
119123 A repository named the following is created. It is actually called last when
120124 all of the pip spokes are collected.
121125
122126 - @@rules_python~override~pip~pip
123127
124- As show in the example code above we have the following.
128+ As shown in the example code above we have the following.
125129 Two different pip.parse statements exist in MODULE.bazel provide the hub_name "pip".
126130 These definitions create two different pip spoke repositories that are
127131 related to the hub "pip".
@@ -185,22 +189,32 @@ def _pip_impl(module_ctx):
185189
186190 for mod in module_ctx .modules :
187191 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 :
189194 # We cannot have two hubs with the same name in different
190195 # 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+ ))
204218 else :
205219 pip_hub_map [pip_attr .hub_name ].python_versions .append (pip_attr .python_version )
206220 else :
@@ -209,17 +223,19 @@ def _pip_impl(module_ctx):
209223 python_versions = [pip_attr .python_version ],
210224 )
211225
212- _create_pip (module_ctx , pip_attr , hub_whl_map )
226+ _create_versioned_pip_and_whl_repos (module_ctx , pip_attr , hub_whl_map )
213227
214228 for hub_name , whl_map in hub_whl_map .items ():
215229 for whl_name , version_map in whl_map .items ():
216230 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+ ))
223239
224240 # Create the alias repositories which contains different select
225241 # statements These select statements point to the different pip
@@ -247,14 +263,34 @@ def _pip_parse_ext_attrs():
247263 "hub_name" : attr .string (
248264 mandatory = True ,
249265 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.
252283""" ,
253284 ),
254285 "python_version" : attr .string (
255286 mandatory = True ,
256287 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.
258294""" ,
259295 ),
260296 }, ** pip_repository_attrs )
@@ -270,12 +306,16 @@ The Python version for the pip spoke.
270306
271307pip = module_extension (
272308 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.
279319""" ,
280320 implementation = _pip_impl ,
281321 tag_classes = {
0 commit comments