Skip to content

Commit 2cd9502

Browse files
committed
comment: the add_dists is too coupled
decided to move this to the same file and rewire the dependency injection so that the struct stays immutable, removing the need for explanations
1 parent dec7455 commit 2cd9502

File tree

4 files changed

+145
-138
lines changed

4 files changed

+145
-138
lines changed

python/private/bzlmod/pip.bzl

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ load(
2525
load("//python/private:auth.bzl", "AUTH_ATTRS")
2626
load("//python/private:normalize_name.bzl", "normalize_name")
2727
load("//python/private:parse_requirements.bzl", "host_platform", "parse_requirements", "select_requirement")
28-
load("//python/private:parse_requirements_add_dists.bzl", "parse_requirements_add_dists")
2928
load("//python/private:parse_whl_name.bzl", "parse_whl_name")
3029
load("//python/private:pypi_index.bzl", "simpleapi_download")
3130
load("//python/private:render_pkg_aliases.bzl", "whl_alias")
@@ -163,31 +162,18 @@ def _create_whl_repos(module_ctx, pip_attr, whl_map, whl_overrides, group_map, s
163162

164163
# Create a new wheel library for each of the different whls
165164

166-
requirements_by_platform = parse_requirements(
167-
module_ctx,
168-
requirements_by_platform = pip_attr.requirements_by_platform,
169-
requirements_linux = pip_attr.requirements_linux,
170-
requirements_lock = pip_attr.requirements_lock,
171-
requirements_osx = pip_attr.requirements_darwin,
172-
requirements_windows = pip_attr.requirements_windows,
173-
extra_pip_args = pip_attr.extra_pip_args,
174-
)
175-
165+
get_index_urls = None
176166
if pip_attr.experimental_index_url:
177167
if pip_attr.download_only:
178168
fail("Currently unsupported to use `download_only` and `experimental_index_url`")
179169

180-
index_urls = simpleapi_download(
181-
module_ctx,
170+
get_index_urls = lambda ctx, distributions: simpleapi_download(
171+
ctx,
182172
attr = struct(
183173
index_url = pip_attr.experimental_index_url,
184174
extra_index_urls = pip_attr.experimental_extra_index_urls or [],
185175
index_url_overrides = pip_attr.experimental_index_url_overrides or {},
186-
sources = list({
187-
req.distribution: None
188-
for reqs in requirements_by_platform.values()
189-
for req in reqs
190-
}),
176+
sources = distributions,
191177
envsubst = pip_attr.envsubst,
192178
# Auth related info
193179
netrc = pip_attr.netrc,
@@ -196,12 +182,19 @@ def _create_whl_repos(module_ctx, pip_attr, whl_map, whl_overrides, group_map, s
196182
cache = simpleapi_cache,
197183
parallel_download = pip_attr.parallel_download,
198184
)
199-
parse_requirements_add_dists(
200-
requirements_by_platform,
201-
index_urls,
202-
python_version = major_minor,
203-
logger = logger,
204-
)
185+
186+
requirements_by_platform = parse_requirements(
187+
module_ctx,
188+
requirements_by_platform = pip_attr.requirements_by_platform,
189+
requirements_linux = pip_attr.requirements_linux,
190+
requirements_lock = pip_attr.requirements_lock,
191+
requirements_osx = pip_attr.requirements_darwin,
192+
requirements_windows = pip_attr.requirements_windows,
193+
extra_pip_args = pip_attr.extra_pip_args,
194+
get_index_urls = get_index_urls,
195+
python_version = major_minor,
196+
logger = logger,
197+
)
205198

206199
repository_platform = host_platform(module_ctx.os)
207200
for whl_name, requirements in requirements_by_platform.items():
@@ -276,11 +269,7 @@ def _create_whl_repos(module_ctx, pip_attr, whl_map, whl_overrides, group_map, s
276269
distribution = select_whl(
277270
whls = requirement.whls,
278271
want_platform = repository_platform,
279-
# NOTE @aignas 2024-06-01: we use a list for sdists because the parent
280-
# container is a struct, which is immutable and having a list
281-
# allows to store things after the fact. We ever only expect to
282-
# have a single element in the requirements.sdists list.
283-
) or (requirement.sdists[0] if requirement.sdists else None)
272+
) or requirement.sdist
284273

285274
logger.debug(lambda: "Selected: {}".format(distribution))
286275

python/private/parse_requirements.bzl

Lines changed: 105 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ behavior.
2929
load("//python/pip_install:requirements_parser.bzl", "parse")
3030
load(":normalize_name.bzl", "normalize_name")
3131
load(":pypi_index_sources.bzl", "get_simpleapi_sources")
32-
load(":whl_target_platforms.bzl", "whl_target_platforms")
32+
load(":whl_target_platforms.bzl", "select_whls", "whl_target_platforms")
3333

3434
# This includes the vendored _translate_cpu and _translate_os from
3535
# @platforms//host:extension.bzl at version 0.0.9 so that we don't
@@ -147,6 +147,9 @@ def parse_requirements(
147147
requirements_lock = None,
148148
requirements_windows = None,
149149
extra_pip_args = [],
150+
get_index_urls = None,
151+
python_version = None,
152+
logger = None,
150153
fail_fn = fail):
151154
"""Get the requirements with platforms that the requirements apply to.
152155
@@ -161,6 +164,12 @@ def parse_requirements(
161164
requirements_windows (label): The requirements file for windows OS.
162165
extra_pip_args (string list): Extra pip arguments to perform extra validations and to
163166
be joined with args fined in files.
167+
get_index_urls: Callable[[ctx, list[str]], dict], a callable to get all
168+
of the distribution URLs from a PyPI index. Accepts ctx and
169+
distribution names to query.
170+
python_version: str or None. This is needed when the get_index_urls is
171+
specified. It should be of the form "3.x.x",
172+
logger: repo_utils.logger or None, a simple struct to log diagnostic messages.
164173
fail_fn (Callable[[str], None]): A failure function used in testing failure cases.
165174
166175
Returns:
@@ -317,26 +326,47 @@ def parse_requirements(
317326
)
318327
for_req.target_platforms.append(target_platform)
319328

320-
return {
321-
whl_name: [
322-
struct(
323-
distribution = r.distribution,
324-
srcs = r.srcs,
325-
requirement_line = r.requirement_line,
326-
target_platforms = sorted(r.target_platforms),
327-
extra_pip_args = r.extra_pip_args,
328-
download = r.download,
329-
# Note, some lock file formats have URLs and dists stored in
330-
# the file, this field can be used for storing those values in
331-
# the future. This is also going to be used by the pypi_index
332-
# helper.
333-
whls = [],
334-
sdists = [],
329+
index_urls = {}
330+
if get_index_urls:
331+
if not python_version:
332+
fail_fn("'python_version' must be provided")
333+
return None
334+
335+
index_urls = get_index_urls(
336+
ctx,
337+
# Use list({}) as a way to have a set
338+
list({
339+
req.distribution: None
340+
for reqs in requirements_by_platform.values()
341+
for req in reqs.values()
342+
}),
343+
)
344+
345+
ret = {}
346+
for whl_name, reqs in requirements_by_platform.items():
347+
ret[whl_name] = []
348+
for r in sorted(reqs.values(), key = lambda r: r.requirement_line):
349+
whls, sdist = _add_dists(
350+
r,
351+
index_urls.get(whl_name),
352+
python_version = python_version,
353+
logger = logger,
335354
)
336-
for r in sorted(reqs.values(), key = lambda r: r.requirement_line)
337-
]
338-
for whl_name, reqs in requirements_by_platform.items()
339-
}
355+
356+
ret.setdefault(whl_name, []).append(
357+
struct(
358+
distribution = r.distribution,
359+
srcs = r.srcs,
360+
requirement_line = r.requirement_line,
361+
target_platforms = sorted(r.target_platforms),
362+
extra_pip_args = r.extra_pip_args,
363+
download = r.download,
364+
whls = whls,
365+
sdist = sdist,
366+
),
367+
)
368+
369+
return ret
340370

341371
def select_requirement(requirements, *, platform):
342372
"""A simple function to get a requirement for a particular platform.
@@ -383,3 +413,58 @@ def host_platform(repository_os):
383413
_translate_os(repository_os.name.lower()),
384414
_translate_cpu(repository_os.arch.lower()),
385415
)
416+
417+
def _add_dists(requirement, index_urls, python_version, logger = None):
418+
"""Populate dists based on the information from the PyPI index.
419+
420+
This function will modify the given requirements_by_platform data structure.
421+
422+
Args:
423+
requirement: The result of parse_requirements function.
424+
index_urls: The result of simpleapi_download.
425+
python_version: The version of the python interpreter.
426+
logger: A logger for printing diagnostic info.
427+
"""
428+
if not index_urls:
429+
return [], None
430+
431+
whls = []
432+
sdist = None
433+
434+
# TODO @aignas 2024-05-22: it is in theory possible to add all
435+
# requirements by version instead of by sha256. This may be useful
436+
# for some projects.
437+
for sha256 in requirement.srcs.shas:
438+
# For now if the artifact is marked as yanked we just ignore it.
439+
#
440+
# See https://packaging.python.org/en/latest/specifications/simple-repository-api/#adding-yank-support-to-the-simple-api
441+
442+
maybe_whl = index_urls.whls.get(sha256)
443+
if maybe_whl and not maybe_whl.yanked:
444+
whls.append(maybe_whl)
445+
continue
446+
447+
maybe_sdist = index_urls.sdists.get(sha256)
448+
if maybe_sdist and not maybe_sdist.yanked:
449+
sdist = maybe_sdist
450+
continue
451+
452+
if logger:
453+
logger.warn("Could not find a whl or an sdist with sha256={}".format(sha256))
454+
455+
# Filter out the wheels that are incompatible with the target_platforms.
456+
whls = select_whls(
457+
whls = whls,
458+
want_abis = [
459+
"none",
460+
"abi3",
461+
"cp" + python_version.replace(".", ""),
462+
# Older python versions have wheels for the `*m` ABI.
463+
"cp" + python_version.replace(".", "") + "m",
464+
],
465+
want_platforms = requirement.target_platforms,
466+
want_python_version = python_version,
467+
logger = logger,
468+
)
469+
470+
return whls, sdist

python/private/parse_requirements_add_dists.bzl

Lines changed: 0 additions & 79 deletions
This file was deleted.

0 commit comments

Comments
 (0)