Skip to content

Commit 90f6880

Browse files
authored
Merge branch 'main' into fix/call-simple-api-indexes-in-series
2 parents 086a122 + 2136215 commit 90f6880

File tree

6 files changed

+128
-110
lines changed

6 files changed

+128
-110
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ Unreleased changes template.
6060
fetching the metadata from SimpleAPI. The calls will be done in parallel to
6161
each index separately, so the extension evaluation time might slow down if
6262
not using {bzl:obj}`pip.parse.experimental_index_url_overrides`.
63+
* ({bzl:obj}`pip.parse`) Only query SimpleAPI for packages that have
64+
sha values in the `requirements.txt` file.
6365

6466
{#v0-0-0-fixed}
6567
### Fixed

python/private/pypi/extension.bzl

Lines changed: 116 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,17 @@ def _create_whl_repos(
6767
*,
6868
pip_attr,
6969
whl_overrides,
70-
simpleapi_cache,
7170
evaluate_markers = evaluate_markers,
7271
available_interpreters = INTERPRETER_LABELS,
73-
simpleapi_download = simpleapi_download):
72+
get_index_urls = None):
7473
"""create all of the whl repositories
7574
7675
Args:
7776
module_ctx: {type}`module_ctx`.
7877
pip_attr: {type}`struct` - the struct that comes from the tag class iteration.
7978
whl_overrides: {type}`dict[str, struct]` - per-wheel overrides.
80-
simpleapi_cache: {type}`dict` - an opaque dictionary used for caching the results from calling
81-
SimpleAPI evaluating all of the tag class invocations {bzl:obj}`pip.parse`.
8279
evaluate_markers: the function to use to evaluate markers.
83-
simpleapi_download: Used for testing overrides
80+
get_index_urls: A function used to get the index URLs
8481
available_interpreters: {type}`dict[str, Label]` The dictionary of available
8582
interpreters that have been registered using the `python` bzlmod extension.
8683
The keys are in the form `python_{snake_case_version}_host`. This is to be
@@ -96,12 +93,9 @@ def _create_whl_repos(
9693
aparent repository names for the hub repo and the values are the
9794
arguments that will be passed to {bzl:obj}`whl_library` repository
9895
rule.
99-
is_reproducible: {type}`bool` set to True if does not make calls to the
100-
internet to evaluate the requirements files.
10196
"""
10297
logger = repo_utils.logger(module_ctx, "pypi:create_whl_repos")
10398
python_interpreter_target = pip_attr.python_interpreter_target
104-
is_reproducible = True
10599

106100
# containers to aggregate outputs from this function
107101
whl_map = {}
@@ -158,26 +152,6 @@ def _create_whl_repos(
158152
whl_group_mapping = {}
159153
requirement_cycles = {}
160154

161-
# Create a new wheel library for each of the different whls
162-
163-
get_index_urls = None
164-
if pip_attr.experimental_index_url:
165-
get_index_urls = lambda ctx, distributions: simpleapi_download(
166-
ctx,
167-
attr = struct(
168-
index_url = pip_attr.experimental_index_url,
169-
extra_index_urls = pip_attr.experimental_extra_index_urls or [],
170-
index_url_overrides = pip_attr.experimental_index_url_overrides or {},
171-
sources = distributions,
172-
envsubst = pip_attr.envsubst,
173-
# Auth related info
174-
netrc = pip_attr.netrc,
175-
auth_patterns = pip_attr.auth_patterns,
176-
),
177-
cache = simpleapi_cache,
178-
parallel_download = pip_attr.parallel_download,
179-
)
180-
181155
requirements_by_platform = parse_requirements(
182156
module_ctx,
183157
requirements_by_platform = requirements_files_by_platform(
@@ -258,77 +232,27 @@ def _create_whl_repos(
258232
if v != default
259233
})
260234

261-
# TODO @aignas 2024-05-26: move to a separate function
262235
for requirement in requirements:
263-
dists = requirement.whls
264-
if not pip_attr.download_only and requirement.sdist:
265-
dists = dists + [requirement.sdist]
266-
267-
for distribution in dists:
268-
args = dict(whl_library_args)
269-
if pip_attr.netrc:
270-
args["netrc"] = pip_attr.netrc
271-
if pip_attr.auth_patterns:
272-
args["auth_patterns"] = pip_attr.auth_patterns
273-
274-
if not distribution.filename.endswith(".whl"):
275-
# pip is not used to download wheels and the python
276-
# `whl_library` helpers are only extracting things, however
277-
# for sdists, they will be built by `pip`, so we still
278-
# need to pass the extra args there.
279-
args["extra_pip_args"] = requirement.extra_pip_args
280-
281-
# This is no-op because pip is not used to download the wheel.
282-
args.pop("download_only", None)
283-
284-
repo_name = whl_repo_name(pip_name, distribution.filename, distribution.sha256)
285-
args["requirement"] = requirement.srcs.requirement
286-
args["urls"] = [distribution.url]
287-
args["sha256"] = distribution.sha256
288-
args["filename"] = distribution.filename
289-
args["experimental_target_platforms"] = requirement.target_platforms
290-
291-
# Pure python wheels or sdists may need to have a platform here
292-
target_platforms = None
293-
if distribution.filename.endswith("-any.whl") or not distribution.filename.endswith(".whl"):
294-
if len(requirements) > 1:
295-
target_platforms = requirement.target_platforms
236+
for repo_name, (args, config_setting) in _whl_repos(
237+
requirement = requirement,
238+
whl_library_args = whl_library_args,
239+
download_only = pip_attr.download_only,
240+
netrc = pip_attr.netrc,
241+
auth_patterns = pip_attr.auth_patterns,
242+
python_version = major_minor,
243+
multiple_requirements_for_whl = len(requirements) > 1.,
244+
).items():
245+
repo_name = "{}_{}".format(pip_name, repo_name)
246+
if repo_name in whl_libraries:
247+
fail("Attempting to creating a duplicate library {} for {}".format(
248+
repo_name,
249+
whl_name,
250+
))
296251

297252
whl_libraries[repo_name] = args
298-
299-
whl_map.setdefault(whl_name, {})[whl_config_setting(
300-
version = major_minor,
301-
filename = distribution.filename,
302-
target_platforms = target_platforms,
303-
)] = repo_name
304-
305-
if dists:
306-
is_reproducible = False
307-
continue
308-
309-
# Fallback to a pip-installed wheel
310-
args = dict(whl_library_args) # make a copy
311-
args["requirement"] = requirement.srcs.requirement_line
312-
if requirement.extra_pip_args:
313-
args["extra_pip_args"] = requirement.extra_pip_args
314-
315-
if pip_attr.download_only:
316-
args.setdefault("experimental_target_platforms", requirement.target_platforms)
317-
318-
target_platforms = requirement.target_platforms if len(requirements) > 1 else []
319-
repo_name = pypi_repo_name(
320-
pip_name,
321-
whl_name,
322-
*target_platforms
323-
)
324-
whl_libraries[repo_name] = args
325-
whl_map.setdefault(whl_name, {})[whl_config_setting(
326-
version = major_minor,
327-
target_platforms = target_platforms or None,
328-
)] = repo_name
253+
whl_map.setdefault(whl_name, {})[config_setting] = repo_name
329254

330255
return struct(
331-
is_reproducible = is_reproducible,
332256
whl_map = whl_map,
333257
exposed_packages = {
334258
whl_name: None
@@ -339,11 +263,88 @@ def _create_whl_repos(
339263
whl_libraries = whl_libraries,
340264
)
341265

342-
def parse_modules(module_ctx, _fail = fail, **kwargs):
266+
def _whl_repos(*, requirement, whl_library_args, download_only, netrc, auth_patterns, multiple_requirements_for_whl = False, python_version):
267+
ret = {}
268+
269+
dists = requirement.whls
270+
if not download_only and requirement.sdist:
271+
dists = dists + [requirement.sdist]
272+
273+
for distribution in dists:
274+
args = dict(whl_library_args)
275+
if netrc:
276+
args["netrc"] = netrc
277+
if auth_patterns:
278+
args["auth_patterns"] = auth_patterns
279+
280+
if not distribution.filename.endswith(".whl"):
281+
# pip is not used to download wheels and the python
282+
# `whl_library` helpers are only extracting things, however
283+
# for sdists, they will be built by `pip`, so we still
284+
# need to pass the extra args there.
285+
args["extra_pip_args"] = requirement.extra_pip_args
286+
287+
# This is no-op because pip is not used to download the wheel.
288+
args.pop("download_only", None)
289+
290+
args["requirement"] = requirement.srcs.requirement
291+
args["urls"] = [distribution.url]
292+
args["sha256"] = distribution.sha256
293+
args["filename"] = distribution.filename
294+
args["experimental_target_platforms"] = requirement.target_platforms
295+
296+
# Pure python wheels or sdists may need to have a platform here
297+
target_platforms = None
298+
if distribution.filename.endswith("-any.whl") or not distribution.filename.endswith(".whl"):
299+
if multiple_requirements_for_whl:
300+
target_platforms = requirement.target_platforms
301+
302+
repo_name = whl_repo_name(
303+
distribution.filename,
304+
distribution.sha256,
305+
)
306+
ret[repo_name] = (
307+
args,
308+
whl_config_setting(
309+
version = python_version,
310+
filename = distribution.filename,
311+
target_platforms = target_platforms,
312+
),
313+
)
314+
315+
if ret:
316+
return ret
317+
318+
# Fallback to a pip-installed wheel
319+
args = dict(whl_library_args) # make a copy
320+
args["requirement"] = requirement.srcs.requirement_line
321+
if requirement.extra_pip_args:
322+
args["extra_pip_args"] = requirement.extra_pip_args
323+
324+
if download_only:
325+
args.setdefault("experimental_target_platforms", requirement.target_platforms)
326+
327+
target_platforms = requirement.target_platforms if multiple_requirements_for_whl else []
328+
repo_name = pypi_repo_name(
329+
normalize_name(requirement.distribution),
330+
*target_platforms
331+
)
332+
ret[repo_name] = (
333+
args,
334+
whl_config_setting(
335+
version = python_version,
336+
target_platforms = target_platforms or None,
337+
),
338+
)
339+
340+
return ret
341+
342+
def parse_modules(module_ctx, _fail = fail, simpleapi_download = simpleapi_download, **kwargs):
343343
"""Implementation of parsing the tag classes for the extension and return a struct for registering repositories.
344344
345345
Args:
346346
module_ctx: {type}`module_ctx` module context.
347+
simpleapi_download: Used for testing overrides
347348
_fail: {type}`function` the failure function, mainly for testing.
348349
**kwargs: Extra arguments passed to the layers below.
349350
@@ -460,10 +461,29 @@ You cannot use both the additive_build_content and additive_build_content_file a
460461
else:
461462
pip_hub_map[pip_attr.hub_name].python_versions.append(pip_attr.python_version)
462463

464+
get_index_urls = None
465+
if pip_attr.experimental_index_url:
466+
is_reproducible = False
467+
get_index_urls = lambda ctx, distributions: simpleapi_download(
468+
ctx,
469+
attr = struct(
470+
index_url = pip_attr.experimental_index_url,
471+
extra_index_urls = pip_attr.experimental_extra_index_urls or [],
472+
index_url_overrides = pip_attr.experimental_index_url_overrides or {},
473+
sources = distributions,
474+
envsubst = pip_attr.envsubst,
475+
# Auth related info
476+
netrc = pip_attr.netrc,
477+
auth_patterns = pip_attr.auth_patterns,
478+
),
479+
cache = simpleapi_cache,
480+
parallel_download = pip_attr.parallel_download,
481+
)
482+
463483
out = _create_whl_repos(
464484
module_ctx,
465485
pip_attr = pip_attr,
466-
simpleapi_cache = simpleapi_cache,
486+
get_index_urls = get_index_urls,
467487
whl_overrides = whl_overrides,
468488
**kwargs
469489
)
@@ -476,7 +496,6 @@ You cannot use both the additive_build_content and additive_build_content_file a
476496
extra_aliases[hub_name].setdefault(whl_name, {}).update(aliases)
477497
exposed_packages.setdefault(hub_name, {}).update(out.exposed_packages)
478498
whl_libraries.update(out.whl_libraries)
479-
is_reproducible = is_reproducible and out.is_reproducible
480499

481500
# TODO @aignas 2024-04-05: how do we support different requirement
482501
# cycles for different abis/oses? For now we will need the users to

python/private/pypi/parse_requirements.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ def parse_requirements(
184184
req.distribution: None
185185
for reqs in requirements_by_platform.values()
186186
for req in reqs.values()
187+
if req.srcs.shas
187188
}),
188189
)
189190

python/private/pypi/whl_repo_name.bzl

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,17 @@
1818
load("//python/private:normalize_name.bzl", "normalize_name")
1919
load(":parse_whl_name.bzl", "parse_whl_name")
2020

21-
def whl_repo_name(prefix, filename, sha256):
21+
def whl_repo_name(filename, sha256):
2222
"""Return a valid whl_library repo name given a distribution filename.
2323
2424
Args:
25-
prefix: {type}`str` the prefix of the whl_library.
2625
filename: {type}`str` the filename of the distribution.
2726
sha256: {type}`str` the sha256 of the distribution.
2827
2928
Returns:
3029
a string that can be used in {obj}`whl_library`.
3130
"""
32-
parts = [prefix]
31+
parts = []
3332

3433
if not filename.endswith(".whl"):
3534
# Then the filename is basically foo-3.2.1.<ext>
@@ -51,19 +50,17 @@ def whl_repo_name(prefix, filename, sha256):
5150

5251
return "_".join(parts)
5352

54-
def pypi_repo_name(prefix, whl_name, *target_platforms):
53+
def pypi_repo_name(whl_name, *target_platforms):
5554
"""Return a valid whl_library given a requirement line.
5655
5756
Args:
58-
prefix: {type}`str` the prefix of the whl_library.
5957
whl_name: {type}`str` the whl_name to use.
6058
*target_platforms: {type}`list[str]` the target platforms to use in the name.
6159
6260
Returns:
6361
{type}`str` that can be used in {obj}`whl_library`.
6462
"""
6563
parts = [
66-
prefix,
6764
normalize_name(whl_name),
6865
]
6966
parts.extend([p.partition("_")[-1] for p in target_platforms])

tests/pypi/extension/extension_tests.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ some_pkg==0.0.1
575575
index_url = "pypi.org",
576576
index_url_overrides = {},
577577
netrc = None,
578-
sources = ["simple", "some_pkg"],
578+
sources = ["simple"],
579579
),
580580
"cache": {},
581581
"parallel_download": False,

tests/pypi/whl_repo_name/whl_repo_name_tests.bzl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,25 @@ load("//python/private/pypi:whl_repo_name.bzl", "whl_repo_name") # buildifier:
2020
_tests = []
2121

2222
def _test_simple(env):
23-
got = whl_repo_name("prefix", "foo-1.2.3-py3-none-any.whl", "deadbeef")
24-
env.expect.that_str(got).equals("prefix_foo_py3_none_any_deadbeef")
23+
got = whl_repo_name("foo-1.2.3-py3-none-any.whl", "deadbeef")
24+
env.expect.that_str(got).equals("foo_py3_none_any_deadbeef")
2525

2626
_tests.append(_test_simple)
2727

2828
def _test_sdist(env):
29-
got = whl_repo_name("prefix", "foo-1.2.3.tar.gz", "deadbeef000deadbeef")
30-
env.expect.that_str(got).equals("prefix_foo_sdist_deadbeef")
29+
got = whl_repo_name("foo-1.2.3.tar.gz", "deadbeef000deadbeef")
30+
env.expect.that_str(got).equals("foo_sdist_deadbeef")
3131

3232
_tests.append(_test_sdist)
3333

3434
def _test_platform_whl(env):
3535
got = whl_repo_name(
36-
"prefix",
3736
"foo-1.2.3-cp39.cp310-abi3-manylinux1_x86_64.manylinux_2_17_x86_64.whl",
3837
"deadbeef000deadbeef",
3938
)
4039

4140
# We only need the first segment of each
42-
env.expect.that_str(got).equals("prefix_foo_cp39_abi3_manylinux_2_5_x86_64_deadbeef")
41+
env.expect.that_str(got).equals("foo_cp39_abi3_manylinux_2_5_x86_64_deadbeef")
4342

4443
_tests.append(_test_platform_whl)
4544

0 commit comments

Comments
 (0)