Skip to content

Commit ad74ef7

Browse files
authored
feat!(pypi): enable bazel downloader by default (#3691)
Summary: - change: Set `https://pypi.org/simple` as the default index. - refactor: Leave the code for the legacy behaviour intact in case we need to do more work on the configuration to exclude the cases where the downloader should be used. - add: add `index_url` configuration option for setting the defaults. - add: add a small utility for parsing the arguments. - fix: downloader will not be used if there is no url associated with the source irrespective of what the index_url setting is. - fix: ensure all of the URLs are normalized when used. Fixes #260 Fixes #1357 Fixes #2241 Fixes #2951
1 parent c99d2b4 commit ad74ef7

File tree

16 files changed

+203
-140
lines changed

16 files changed

+203
-140
lines changed

.bazelrc.deleted_packages

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ common --deleted_packages=gazelle/manifest/hasher
2727
common --deleted_packages=gazelle/manifest/test
2828
common --deleted_packages=gazelle/modules_mapping
2929
common --deleted_packages=gazelle/python
30-
common --deleted_packages=gazelle/python/private
3130
common --deleted_packages=gazelle/pythonconfig
31+
common --deleted_packages=gazelle/python/private
3232
common --deleted_packages=tests/integration/compile_pip_requirements
3333
common --deleted_packages=tests/integration/compile_pip_requirements_test_from_external_repo
3434
common --deleted_packages=tests/integration/custom_commands

CHANGELOG.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ END_UNRELEASED_TEMPLATE
6767
on supported platforms (Linux/Mac with Bazel 8+, or Windows).
6868
* `--build_python_zip` on Windows is ignored. Use {obj}`py_zipapp_binary` to create
6969
zips of Python programs.
70+
* (pypi) Previously `experimental_index_url` users would not need to specify
71+
target platforms if cross-building is required. From now we will only pull
72+
wheels for the host OS to better align with how the rules work with the legacy
73+
`pip` implementation. Use {obj}`pip.parse.target_platforms` to customize the
74+
behavior.
75+
Related to [#260](https://github.com/bazel-contrib/rules_python/issues/260).
7076

7177
Other changes:
7278
* (pypi) Update dependencies used for `compile_pip_requirements`, building
@@ -75,12 +81,10 @@ Other changes:
7581
we will from now on fetch the lists of available packages on each index. The
7682
used package mappings will be written as facts to the `MODULE.bazel.lock` file
7783
on supported bazel versions and it should be done at most once. As a result,
78-
per-package {obj}`experimental_index_url_overrides` is no longer needed if the
79-
index URLs are passed to the `pip.parse` via `experimental_index_url` and
80-
`experimental_extra_index_urls`. What is more, we start implementing the flags
81-
for `--index_url` and `--extra_index_urls` more in line to how it is used in
82-
`uv` and `pip`, i.e. we default to `--index_url` if the package is not found in
83-
`--extra_index_urls`. Fixes
84+
per-package {obj}`experimental_index_url_overrides` is no longer needed . What
85+
is more, the flags for `--index_url` and `--extra-index-url` now behave in the
86+
same way as in `uv` or `pip`, i.e. we default to `--index-url` if the package
87+
is not found in `--extra-index-url`. Fixes
8488
([#3260](https://github.com/bazel-contrib/rules_python/issues/3260) and
8589
[#2632](https://github.com/bazel-contrib/rules_python/issues/2632)).
8690
* (uv) We will now use the download URL specified in the `uv`'s
@@ -130,6 +134,17 @@ Other changes:
130134
initializations and should no longer require the network access if the cache is
131135
hydrated. Implements
132136
[#2731](https://github.com/bazel-contrib/rules_python/issues/2731).
137+
* (pypi) The `--index-url` and `--extra-index-url` is now parsed from the lock
138+
file and the {obj}`pip.parse.experimental_index_url` and
139+
{obj}`pip.parse.experimental_extra_index_urls` is
140+
no longer mandatory to leverage the bazel downloader.
141+
Implements
142+
[#1357](https://github.com/bazel-contrib/rules_python/issues/1357),
143+
[#2951](https://github.com/bazel-contrib/rules_python/issues/2951).
144+
* (pypi) If cross-compilation is needed, use the {obj}`pip.parse.target_platforms`
145+
to specify exactly which platforms should be supported.
146+
Implements
147+
[#260](https://github.com/bazel-contrib/rules_python/issues/260).
133148
* (wheel) Specifying a path ending in `/` as a destination in `data_files`
134149
will now install file(s) to a folder, preserving their basename.
135150
* Various attributes and fields added to support venvs on Windows:
@@ -2315,4 +2330,4 @@ Breaking changes:
23152330
* (pip) Create all_data_requirements alias
23162331
* Expose Python C headers through the toolchain.
23172332

2318-
[0.24.0]: https://github.com/bazel-contrib/rules_python/releases/tag/0.24.0
2333+
[0.24.0]: https://github.com/bazel-contrib/rules_python/releases/tag/0.24.0

MODULE.bazel

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,8 @@ register_toolchains("@pythons_hub//:all")
6565

6666
pip = use_extension("//python/extensions:pip.bzl", "pip")
6767

68-
# NOTE @aignas 2025-07-06: we define these platforms to keep backwards compatibility with the
69-
# current `experimental_index_url` implementation. Whilst we stabilize the API this list may be
70-
# updated with a mention in the CHANGELOG.
68+
# NOTE @aignas 2025-07-06: we define these platforms to keep backwards compatibility. Whilst we
69+
# stabilize the API this list may be updated with a mention in the CHANGELOG.
7170
[
7271
pip.default(
7372
arch_name = cpu,
@@ -306,7 +305,6 @@ dev_pip = use_extension(
306305
[
307306
dev_pip.parse(
308307
download_only = True,
309-
experimental_index_url = "https://pypi.org/simple",
310308
hub_name = "dev_pip",
311309
parallel_download = False,
312310
python_version = python_version,
@@ -329,7 +327,6 @@ dev_pip = use_extension(
329327

330328
dev_pip.parse(
331329
download_only = True,
332-
experimental_index_url = "https://pypi.org/simple",
333330
hub_name = "pypiserver",
334331
python_version = "3.11",
335332
requirements_lock = "//examples/wheel:requirements_server.txt",

docs/pypi/download.md

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,8 @@ the years, people started needing support for building containers, and usually,
104104
fetching dependencies for a particular target platform that may be different from the host
105105
platform.
106106

107-
Multi-platform support for cross-building the wheels can be done in two ways:
108-
1. using {attr}`experimental_index_url` for the {bzl:obj}`pip.parse` bzlmod tag class
109-
2. using the {attr}`pip.parse.download_only` setting.
107+
Multi-platform support for cross-building the wheels can be done by
108+
using {attr}`target_platforms` for the {bzl:obj}`pip.parse` bzlmod tag class
110109

111110
:::{warning}
112111
This will not work for sdists with C extensions, but pure Python sdists may still work using the first
@@ -207,16 +206,6 @@ additional keys, which become available during dependency evaluation.
207206
(bazel-downloader)=
208207
### Bazel downloader and multi-platform wheel hub repository.
209208

210-
:::{warning}
211-
This is currently still experimental, and whilst it has been proven to work in quite a few
212-
environments, the APIs are still being finalized, and there may be changes to the APIs for this
213-
feature without much notice.
214-
215-
The issues that you can subscribe to for updates are:
216-
* {gh-issue}`260`
217-
* {gh-issue}`1357`
218-
:::
219-
220209
The {obj}`pip` extension supports pulling information from `PyPI` (or a compatible mirror), and it
221210
will ensure that the [bazel downloader][bazel_downloader] is used for downloading the wheels.
222211

@@ -228,14 +217,10 @@ This provides the following benefits:
228217
* Allow using transitions and targeting free-threaded and musl platforms more easily.
229218
* Avoids `pip` for wheel fetching and results in much faster dependency fetching.
230219

231-
To enable the feature specify {attr}`pip.parse.experimental_index_url` as shown in
232-
the {gh-path}`examples/bzlmod/MODULE.bazel` example.
233-
234-
Similar to [uv](https://docs.astral.sh/uv/configuration/indexes/), one can override the
235-
index that is used for a single package. By default, we first search in the index specified by
236-
{attr}`pip.parse.experimental_index_url`, then we iterate through the
237-
{attr}`pip.parse.experimental_extra_index_urls` unless there are overrides specified via
238-
{attr}`pip.parse.experimental_index_url_overrides`.
220+
Similar to [uv](https://docs.astral.sh/uv/configuration/indexes/), one can override the index that
221+
is used for a single package. By default, we first search in the indexes specified by
222+
`--extra-index-url`, then we fall back to the `--index-url` setting unless there are overrides
223+
specified via {attr}`pip.parse.experimental_index_url_overrides`.
239224

240225
When using this feature during the `pip` extension evaluation you will see the accessed indexes similar to below:
241226
```console

examples/bzlmod/MODULE.bazel

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -183,18 +183,6 @@ pip.default(
183183
pip.parse(
184184
# We can use `envsubst in the above
185185
envsubst = ["PIP_INDEX_URL"],
186-
# Use the bazel downloader to query the simple API for downloading the sources
187-
# Note, that we can use envsubst for this value.
188-
experimental_index_url = "${PIP_INDEX_URL:-https://pypi.org/simple}",
189-
# One can also select a particular index for a particular package.
190-
# This ensures that the setup is resistant against confusion attacks.
191-
# experimental_index_url_overrides = {
192-
# "my_package": "https://different-index-url.com",
193-
# },
194-
# Or you can specify extra indexes like with `pip`:
195-
# experimental_extra_index_urls = [
196-
# "https://different-index-url.com",
197-
# ],
198186
experimental_requirement_cycles = {
199187
"sphinx": [
200188
"sphinx",
@@ -208,6 +196,16 @@ pip.parse(
208196
extra_hub_aliases = {
209197
"wheel": ["generated_file"],
210198
},
199+
extra_pip_args = [
200+
# Use the bazel downloader to query the simple API for downloading the sources
201+
# Note, that we can use envsubst for this value.
202+
# One can also select a particular index for a particular package.
203+
# This ensures that the setup is resistant against confusion attacks.
204+
# experimental_index_url_overrides = {
205+
# "my_package": "https://different-index-url.com",
206+
# },
207+
"--index-url=${PIP_INDEX_URL:-https://pypi.org/simple}",
208+
],
211209
hub_name = "pip",
212210
python_version = "3.9",
213211
requirements_lock = "requirements_lock_3_9.txt",

python/private/pypi/BUILD.bazel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ filegroup(
5959

6060
# Keep sorted by library name and keep the files named by the main symbol they export
6161

62+
bzl_library(
63+
name = "argparse_bzl",
64+
srcs = ["argparse.bzl"],
65+
)
66+
6267
bzl_library(
6368
name = "attrs_bzl",
6469
srcs = ["attrs.bzl"],
@@ -224,6 +229,7 @@ bzl_library(
224229
name = "parse_requirements_bzl",
225230
srcs = ["parse_requirements.bzl"],
226231
deps = [
232+
":argparse_bzl",
227233
":index_sources_bzl",
228234
":parse_requirements_txt_bzl",
229235
":pypi_repo_utils_bzl",
@@ -403,6 +409,7 @@ bzl_library(
403409
name = "requirements_files_by_platform_bzl",
404410
srcs = ["requirements_files_by_platform.bzl"],
405411
deps = [
412+
":argparse_bzl",
406413
":whl_target_platforms_bzl",
407414
],
408415
)

python/private/pypi/argparse.bzl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""A small set of utilities for parsing pip args."""
2+
3+
def _get_pip_args(args, *arg_names, value = None, repeated = False):
4+
set_next = False
5+
if repeated:
6+
value = [] + (value or [])
7+
8+
for arg in (args or []):
9+
if arg in arg_names:
10+
set_next = True
11+
continue
12+
13+
val = None
14+
if set_next:
15+
set_next = False
16+
val = arg
17+
else:
18+
for arg_name in arg_names:
19+
start = "{}=".format(arg_name)
20+
21+
if arg.startswith(start):
22+
val = arg[len(start):]
23+
break
24+
25+
if val == None:
26+
continue
27+
28+
if repeated:
29+
if val not in value:
30+
value.append(val)
31+
else:
32+
value = val
33+
34+
return value
35+
36+
argparse = struct(
37+
index_url = lambda args, default: _get_pip_args(args, "-i", "--index-url", value = default),
38+
extra_index_url = lambda args, default: _get_pip_args(args, "--extra-index-url", value = default, repeated = True),
39+
platform = lambda args, default: _get_pip_args(args, "--platform", value = default, repeated = True),
40+
)

python/private/pypi/extension.bzl

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ def build_config(
115115
_configure(
116116
defaults,
117117
override = mod.is_root,
118+
index_url = tag.index_url,
118119
# extra values that we just add
119120
auth_patterns = tag.auth_patterns,
120121
netrc = tag.netrc,
@@ -125,6 +126,7 @@ def build_config(
125126

126127
return struct(
127128
auth_patterns = defaults.get("auth_patterns", {}),
129+
index_url = defaults.get("index_url", "https://pypi.org/simple").rstrip("/"),
128130
netrc = defaults.get("netrc", None),
129131
platforms = {
130132
name: _plat(**values)
@@ -449,6 +451,30 @@ Supported keys:
449451
::::{note}
450452
This is only used if the {envvar}`RULES_PYTHON_ENABLE_PIPSTAR` is enabled.
451453
::::
454+
""",
455+
),
456+
"index_url": attr.string(
457+
doc = """\
458+
The index URL to use as a default when downloading packages from PyPI. This is used if nothing is
459+
specified via `--index-url` or `--extra-index-url` parameters in the `requirements.txt` file or via
460+
the {attr}`pip.parse.extra_pip_args`.
461+
462+
This value is going to be subject to `envsubst` substitutions if necessary, look at the
463+
{attr}`pip.parse.envsubst` documentation for more information..
464+
465+
The indexes must support Simple API as described here:
466+
https://packaging.python.org/en/latest/specifications/simple-repository-api/
467+
468+
Index metadata will be used to get `sha256` values for packages even if the
469+
`sha256` values are not present in the requirements.txt lock file.
470+
471+
Defaults to `https://pypi.org/simple`.
472+
473+
:::{versionadded} 2.0.0
474+
This has been added as a replacement for
475+
{obj}`pip.parse.experimental_index_url` and
476+
{obj}`pip.parse.experimental_extra_index_urls`.
477+
:::
452478
""",
453479
),
454480
"marker": attr.string(
@@ -566,43 +592,23 @@ def _pip_parse_ext_attrs(**kwargs):
566592
attrs = dict({
567593
"experimental_extra_index_urls": attr.string_list(
568594
doc = """\
569-
The extra index URLs to use for downloading wheels using bazel downloader.
570-
Each value is going to be subject to `envsubst` substitutions if necessary.
595+
May be removed in future releases.
571596
572-
The indexes must support Simple API as described here:
573-
https://packaging.python.org/en/latest/specifications/simple-repository-api/
574-
575-
This is equivalent to `--extra-index-urls` `pip` option.
576-
577-
:::{versionchanged} 1.1.0
578-
Starting with this version we will iterate over each index specified until
579-
we find metadata for all references distributions.
597+
:::{versionchanged} 2.0.0
598+
This is deprecated, please use {obj}`pip.default.index_url` or pass the `--index-url` parameter via the
599+
lock-file or {obj}`pip.parse.extra_pip_args`.
580600
:::
581601
""",
582602
default = [],
583603
),
584604
"experimental_index_url": attr.string(
585605
default = kwargs.get("experimental_index_url", ""),
586606
doc = """\
587-
The index URL to use for downloading wheels using bazel downloader. This value is going
588-
to be subject to `envsubst` substitutions if necessary.
589-
590-
The indexes must support Simple API as described here:
591-
https://packaging.python.org/en/latest/specifications/simple-repository-api/
592-
593-
In the future this could be defaulted to `https://pypi.org` when this feature becomes
594-
stable.
595-
596-
This is equivalent to `--index-url` `pip` option.
607+
May be removed in future releases.
597608
598-
:::{versionchanged} 0.37.0
599-
If {attr}`download_only` is set, then `sdist` archives will be discarded and `pip.parse` will
600-
operate in wheel-only mode.
601-
:::
602-
603-
:::{versionchanged} 1.4.0
604-
Index metadata will be used to deduct `sha256` values for packages even if the
605-
`sha256` values are not present in the requirements.txt lock file.
609+
:::{versionchanged} 2.0.0
610+
This is deprecated, please use {obj}`pip.default.index_url` or pass the `--index-url` parameter via the
611+
lock-file or {obj}`pip.parse.extra_pip_args`.
606612
:::
607613
""",
608614
),
@@ -835,9 +841,6 @@ the BUILD files for wheels.
835841
This tag class allows for more customization of how the configuration for the hub repositories is built.
836842
837843
838-
:::{include} /_includes/experimental_api.md
839-
:::
840-
841844
:::{seealso}
842845
The [environment markers][environment_markers] specification for the explanation of the
843846
terms used in this extension.

0 commit comments

Comments
 (0)