@@ -19,250 +19,27 @@ symbols should not be used and they are either undocumented here or marked as
1919for internal use only.
2020"""
2121
22- load ("//python/pip_install:requirements.bzl" , _compile_pip_requirements = "compile_pip_requirements" )
23- load ("//python/private:bzlmod_enabled.bzl" , "BZLMOD_ENABLED" )
24- load ("//python/private:full_version.bzl" , "full_version" )
2522load ("//python/private:normalize_name.bzl" , "normalize_name" )
23+ load ("//python/private/pypi:multi_pip_parse.bzl" , _multi_pip_parse = "multi_pip_parse" )
2624load ("//python/private/pypi:package_annotation.bzl" , _package_annotation = "package_annotation" )
25+ load ("//python/private/pypi:pip_compile.bzl" , "pip_compile" )
2726load ("//python/private/pypi:pip_repository.bzl" , "pip_repository" )
28- load ("//python/private/pypi:render_pkg_aliases .bzl" , "NO_MATCH_ERROR_MESSAGE_TEMPLATE " )
27+ load ("//python/private/pypi:whl_library_alias .bzl" , _whl_library_alias = "whl_library_alias " )
2928load ("//python/private/whl_filegroup:whl_filegroup.bzl" , _whl_filegroup = "whl_filegroup" )
3029
31- compile_pip_requirements = _compile_pip_requirements
30+ compile_pip_requirements = pip_compile
3231package_annotation = _package_annotation
3332pip_parse = pip_repository
3433whl_filegroup = _whl_filegroup
3534
36- def _multi_pip_parse_impl (rctx ):
37- rules_python = rctx .attr ._rules_python_workspace .workspace_name
38- load_statements = []
39- install_deps_calls = []
40- process_requirements_calls = []
41- for python_version , pypi_repository in rctx .attr .pip_parses .items ():
42- sanitized_python_version = python_version .replace ("." , "_" )
43- load_statement = """\
44- load(
45- "@{pypi_repository}//:requirements.bzl",
46- _{sanitized_python_version}_install_deps = "install_deps",
47- _{sanitized_python_version}_all_requirements = "all_requirements",
48- )""" .format (
49- pypi_repository = pypi_repository ,
50- sanitized_python_version = sanitized_python_version ,
51- )
52- load_statements .append (load_statement )
53- process_requirements_call = """\
54- _process_requirements(
55- pkg_labels = _{sanitized_python_version}_all_requirements,
56- python_version = "{python_version}",
57- repo_prefix = "{pypi_repository}_",
58- )""" .format (
59- pypi_repository = pypi_repository ,
60- python_version = python_version ,
61- sanitized_python_version = sanitized_python_version ,
62- )
63- process_requirements_calls .append (process_requirements_call )
64- install_deps_call = """ _{sanitized_python_version}_install_deps(**whl_library_kwargs)""" .format (
65- sanitized_python_version = sanitized_python_version ,
66- )
67- install_deps_calls .append (install_deps_call )
68-
69- # NOTE @aignas 2023-10-31: I am not sure it is possible to render aliases
70- # for all of the packages using the `render_pkg_aliases` function because
71- # we need to know what the list of packages for each version is and then
72- # we would be creating directories for each.
73- macro_tmpl = "@%s_{}//:{}" % rctx .attr .name
74-
75- requirements_bzl = """\
76- # Generated by python/pip.bzl
77-
78- load("@{rules_python}//python:pip.bzl", "whl_library_alias", "pip_utils")
79- {load_statements}
80-
81- _wheel_names = []
82- _version_map = dict()
83- def _process_requirements(pkg_labels, python_version, repo_prefix):
84- for pkg_label in pkg_labels:
85- wheel_name = Label(pkg_label).package
86- if not wheel_name:
87- # We are dealing with the cases where we don't have aliases.
88- workspace_name = Label(pkg_label).workspace_name
89- wheel_name = workspace_name[len(repo_prefix):]
90-
91- _wheel_names.append(wheel_name)
92- if not wheel_name in _version_map:
93- _version_map[wheel_name] = dict()
94- _version_map[wheel_name][python_version] = repo_prefix
95-
96- {process_requirements_calls}
97-
98- def requirement(name):
99- return "{macro_tmpl}".format(pip_utils.normalize_name(name), "pkg")
100-
101- def whl_requirement(name):
102- return "{macro_tmpl}".format(pip_utils.normalize_name(name), "whl")
103-
104- def data_requirement(name):
105- return "{macro_tmpl}".format(pip_utils.normalize_name(name), "data")
106-
107- def dist_info_requirement(name):
108- return "{macro_tmpl}".format(pip_utils.normalize_name(name), "dist_info")
109-
110- def install_deps(**whl_library_kwargs):
111- {install_deps_calls}
112- for wheel_name in _wheel_names:
113- whl_library_alias(
114- name = "{name}_" + wheel_name,
115- wheel_name = wheel_name,
116- default_version = "{default_version}",
117- version_map = _version_map[wheel_name],
118- )
119- """ .format (
120- name = rctx .attr .name ,
121- install_deps_calls = "\n " .join (install_deps_calls ),
122- load_statements = "\n " .join (load_statements ),
123- macro_tmpl = macro_tmpl ,
124- process_requirements_calls = "\n " .join (process_requirements_calls ),
125- rules_python = rules_python ,
126- default_version = rctx .attr .default_version ,
127- )
128- rctx .file ("requirements.bzl" , requirements_bzl )
129- rctx .file ("BUILD.bazel" , "exports_files(['requirements.bzl'])" )
130-
131- _multi_pip_parse = repository_rule (
132- _multi_pip_parse_impl ,
133- attrs = {
134- "default_version" : attr .string (),
135- "pip_parses" : attr .string_dict (),
136- "_rules_python_workspace" : attr .label (default = Label ("//:WORKSPACE" )),
137- },
138- )
139-
140- def _whl_library_alias_impl (rctx ):
141- rules_python = rctx .attr ._rules_python_workspace .workspace_name
142- if rctx .attr .default_version :
143- default_repo_prefix = rctx .attr .version_map [rctx .attr .default_version ]
144- else :
145- default_repo_prefix = None
146- version_map = rctx .attr .version_map .items ()
147- build_content = ["# Generated by python/pip.bzl" ]
148- for alias_name in ["pkg" , "whl" , "data" , "dist_info" ]:
149- build_content .append (_whl_library_render_alias_target (
150- alias_name = alias_name ,
151- default_repo_prefix = default_repo_prefix ,
152- rules_python = rules_python ,
153- version_map = version_map ,
154- wheel_name = rctx .attr .wheel_name ,
155- ))
156- rctx .file ("BUILD.bazel" , "\n " .join (build_content ))
157-
158- def _whl_library_render_alias_target (
159- alias_name ,
160- default_repo_prefix ,
161- rules_python ,
162- version_map ,
163- wheel_name ):
164- # The template below adds one @, but under bzlmod, the name
165- # is canonical, so we have to add a second @.
166- if BZLMOD_ENABLED :
167- rules_python = "@" + rules_python
168-
169- alias = ["""\
170- alias(
171- name = "{alias_name}",
172- actual = select({{""" .format (alias_name = alias_name )]
173- for [python_version , repo_prefix ] in version_map :
174- alias .append ("""\
175- "@{rules_python}//python/config_settings:is_python_{full_python_version}": "{actual}",""" .format (
176- full_python_version = full_version (python_version ),
177- actual = "@{repo_prefix}{wheel_name}//:{alias_name}" .format (
178- repo_prefix = repo_prefix ,
179- wheel_name = wheel_name ,
180- alias_name = alias_name ,
181- ),
182- rules_python = rules_python ,
183- ))
184- if default_repo_prefix :
185- default_actual = "@{repo_prefix}{wheel_name}//:{alias_name}" .format (
186- repo_prefix = default_repo_prefix ,
187- wheel_name = wheel_name ,
188- alias_name = alias_name ,
189- )
190- alias .append (' "//conditions:default": "{default_actual}",' .format (
191- default_actual = default_actual ,
192- ))
193-
194- alias .append (" }," ) # Close select expression condition dict
195- if not default_repo_prefix :
196- supported_versions = sorted ([python_version for python_version , _ in version_map ])
197- alias .append (' no_match_error="""{}""",' .format (
198- NO_MATCH_ERROR_MESSAGE_TEMPLATE .format (
199- supported_versions = ", " .join (supported_versions ),
200- rules_python = rules_python ,
201- ),
202- ))
203- alias .append (" )," ) # Close the select expression
204- alias .append (' visibility = ["//visibility:public"],' )
205- alias .append (")" ) # Close the alias() expression
206- return "\n " .join (alias )
207-
208- whl_library_alias = repository_rule (
209- _whl_library_alias_impl ,
210- attrs = {
211- "default_version" : attr .string (
212- mandatory = False ,
213- doc = "Optional Python version in major.minor format, e.g. '3.10'." +
214- "The Python version of the wheel to use when the versions " +
215- "from `version_map` don't match. This allows the default " +
216- "(version unaware) rules to match and select a wheel. If " +
217- "not specified, then the default rules won't be able to " +
218- "resolve a wheel and an error will occur." ,
219- ),
220- "version_map" : attr .string_dict (mandatory = True ),
221- "wheel_name" : attr .string (mandatory = True ),
222- "_rules_python_workspace" : attr .label (default = Label ("//:WORKSPACE" )),
223- },
224- )
225-
226- def multi_pip_parse (name , default_version , python_versions , python_interpreter_target , requirements_lock , ** kwargs ):
227- """NOT INTENDED FOR DIRECT USE!
228-
229- This is intended to be used by the multi_pip_parse implementation in the template of the
230- multi_toolchain_aliases repository rule.
231-
232- Args:
233- name: the name of the multi_pip_parse repository.
234- default_version: the default Python version.
235- python_versions: all Python toolchain versions currently registered.
236- python_interpreter_target: a dictionary which keys are Python versions and values are resolved host interpreters.
237- requirements_lock: a dictionary which keys are Python versions and values are locked requirements files.
238- **kwargs: extra arguments passed to all wrapped pip_parse.
239-
240- Returns:
241- The internal implementation of multi_pip_parse repository rule.
242- """
243- pip_parses = {}
244- for python_version in python_versions :
245- if not python_version in python_interpreter_target :
246- fail ("Missing python_interpreter_target for Python version %s in '%s'" % (python_version , name ))
247- if not python_version in requirements_lock :
248- fail ("Missing requirements_lock for Python version %s in '%s'" % (python_version , name ))
249-
250- pip_parse_name = name + "_" + python_version .replace ("." , "_" )
251- pip_parse (
252- name = pip_parse_name ,
253- python_interpreter_target = python_interpreter_target [python_version ],
254- requirements_lock = requirements_lock [python_version ],
255- ** kwargs
256- )
257- pip_parses [python_version ] = pip_parse_name
258-
259- return _multi_pip_parse (
260- name = name ,
261- default_version = default_version ,
262- pip_parses = pip_parses ,
263- )
264-
26535# Extra utilities visible to rules_python users.
26636pip_utils = struct (
26737 normalize_name = normalize_name ,
26838)
39+
40+ # The following are only exported here because they are used from
41+ # multi_toolchain_aliases repository_rule, not intended for public use.
42+ #
43+ # See ./private/toolchains_repo.bzl
44+ multi_pip_parse = _multi_pip_parse
45+ whl_library_alias = _whl_library_alias
0 commit comments