Skip to content

Commit 1be7704

Browse files
committed
Merge branch 'main' into feat/improve-uv-mgmt
2 parents 8548c69 + 33cb431 commit 1be7704

File tree

9 files changed

+200
-54
lines changed

9 files changed

+200
-54
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,24 @@ Unreleased changes template.
6262
docs but usage of the extension may result in your setup breaking without any
6363
notice. What is more, the URLs and SHA256 values will be retrieved from the
6464
GitHub releases page metadata published by the `uv` project.
65+
* (pypi) Downgraded versions of packages: `pip` from `24.3.2` to `24.0.0` and
66+
`packaging` from `24.2` to `24.0`.
6567

6668
{#v0-0-0-fixed}
6769
### Fixed
70+
* (rules) `python_zip_file` output with `--bootstrap_impl=script` works again
71+
([#2596](https://github.com/bazelbuild/rules_python/issues/2596)).
72+
* (docs) Using `python_version` attribute for specifying python versions introduced in `v1.1.0`
6873
* (gazelle) Providing multiple input requirements files to `gazelle_python_manifest` now works correctly.
6974
* (pypi) Handle trailing slashes in pip index URLs in environment variables,
7075
fixes [#2554](https://github.com/bazelbuild/rules_python/issues/2554).
7176
* (runfiles) Runfile manifest and repository mapping files are now interpreted
7277
as UTF-8 on all platforms.
7378
* (coverage) Coverage with `--bootstrap_impl=script` is fixed
7479
([#2572](https://github.com/bazelbuild/rules_python/issues/2572)).
80+
* (pypi) Non deterministic behaviour in requirement file usage has been fixed
81+
by reverting [#2514](https://github.com/bazelbuild/rules_python/pull/2514).
82+
The related issue is [#908](https://github.com/bazelbuild/rules_python/issue/908).
7583
* (sphinxdocs) Do not crash when `tag_class` does not have a populated `doc` value.
7684
Fixes ([#2579](https://github.com/bazelbuild/rules_python/issues/2579)).
7785

docs/_includes/py_console_script_binary.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ py_console_script_binary(
1212
)
1313
```
1414

15-
Or for more advanced setups you can also specify extra dependencies and the
15+
#### Specifying extra dependencies
16+
You can also specify extra dependencies and the
1617
exact script name you want to call. It is useful for tools like `flake8`, `pylint`,
1718
`pytest`, which have plugin discovery methods and discover dependencies from the
1819
PyPI packages available in the `PYTHONPATH`.
@@ -34,17 +35,26 @@ py_console_script_binary(
3435
)
3536
```
3637

37-
A specific Python version can be forced by using the generated version-aware
38-
wrappers, e.g. to force Python 3.9:
38+
#### Using a specific Python version
39+
40+
A specific Python version can be forced by passing the desired Python version, e.g. to force Python 3.9:
3941
```starlark
40-
load("@python_versions//3.9:defs.bzl", "py_console_script_binary")
42+
load("@rules_python//python/entry_points:py_console_script_binary.bzl", "py_console_script_binary")
4143

4244
py_console_script_binary(
4345
name = "yamllint",
4446
pkg = "@pip//yamllint",
47+
python_version = "3.9"
4548
)
4649
```
4750

51+
#### Using a specific Python Version directly from a Toolchain
52+
:::{deprecated} 1.1.0
53+
The toolchain specific `py_binary` and `py_test` symbols are aliases to the regular rules.
54+
i.e. Deprecated `load("@python_versions//3.11:defs.bzl", "py_binary")` and `load("@python_versions//3.11:defs.bzl", "py_test")`
55+
56+
You should instead specify the desired python version with `python_version`; see above example.
57+
:::
4858
Alternatively, the [`py_console_script_binary.binary_rule`] arg can be passed
4959
the version-bound `py_binary` symbol, or any other `py_binary`-compatible rule
5060
of your choosing:
@@ -60,5 +70,4 @@ py_console_script_binary(
6070
```
6171

6272
[specification]: https://packaging.python.org/en/latest/specifications/entry-points/
63-
[`py_console_script_binary.binary_rule`]: #py_console_script_binary_binary_rule
64-
73+
[`py_console_script_binary.binary_rule`]: #py_console_script_binary_binary_rule

docs/toolchains.md

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ python = use_extension("@rules_python//python/extensions:python.bzl", "python")
116116
python.toolchain(python_version = "3.12")
117117
118118
# BUILD.bazel
119-
load("@python_versions//3.12:defs.bzl", "py_binary")
119+
load("@rules_python//python:py_binary.bzl", "py_binary")
120120
121-
py_binary(...)
121+
py_binary(..., python_version="3.12")
122122
```
123123

124124
### Pinning to a Python version
@@ -132,21 +132,59 @@ is most useful for two cases:
132132
typically in a mono-repo situation.
133133

134134
To configure a submodule with the version-aware rules, request the particular
135-
version you need, then use the `@python_versions` repo to use the rules that
136-
force specific versions:
135+
version you need when defining the toolchain:
137136

138137
```starlark
138+
# MODULE.bazel
139139
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
140140

141141
python.toolchain(
142142
python_version = "3.11",
143143
)
144-
use_repo(python, "python_versions")
144+
use_repo(python)
145+
```
146+
147+
Then use the `@rules_python` repo in your BUILD file to explicity pin the Python version when calling the rule:
148+
149+
```starlark
150+
# BUILD.bazel
151+
load("@rules_python//python:py_binary.bzl", "py_binary")
152+
153+
py_binary(..., python_version = "3.11")
154+
py_test(..., python_version = "3.11")
145155
```
146156

147-
Then use e.g. `load("@python_versions//3.11:defs.bzl", "py_binary")` to use
148-
the rules that force that particular version. Multiple versions can be specified
149-
and use within a single build.
157+
Multiple versions can be specified and used within a single build.
158+
159+
```starlark
160+
# MODULE.bazel
161+
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
162+
163+
python.toolchain(
164+
python_version = "3.11",
165+
is_default = True,
166+
)
167+
168+
python.toolchain(
169+
python_version = "3.12",
170+
)
171+
172+
# BUILD.bazel
173+
load("@rules_python//python:py_binary.bzl", "py_binary")
174+
load("@rules_python//python:py_test.bzl", "py_test")
175+
176+
# Defaults to 3.11
177+
py_binary(...)
178+
py_test(...)
179+
180+
# Explicitly use Python 3.11
181+
py_binary(..., python_version = "3.11")
182+
py_test(..., python_version = "3.11")
183+
184+
# Explicitly use Python 3.12
185+
py_binary(..., python_version = "3.12")
186+
py_test(..., python_version = "3.12")
187+
```
150188

151189
For more documentation, see the bzlmod examples under the {gh-path}`examples`
152190
folder. Look for the examples that contain a `MODULE.bazel` file.
@@ -159,6 +197,16 @@ The `python.toolchain()` call makes its contents available under a repo named
159197
Remember to call `use_repo()` to make repos visible to your module:
160198
`use_repo(python, "python_3_11")`
161199

200+
201+
:::{deprecated} 1.1.0
202+
The toolchain specific `py_binary` and `py_test` symbols are aliases to the regular rules.
203+
i.e. Deprecated `load("@python_versions//3.11:defs.bzl", "py_binary")` & `load("@python_versions//3.11:defs.bzl", "py_test")`
204+
205+
Usages of them should be changed to load the regular rules directly;
206+
i.e. Use `load("@rules_python//python:py_binary.bzl", "py_binary")` & `load("@rules_python//python:py_test.bzl", "py_test")` and then specify the `python_version` when using the rules corresponding to the python version you defined in your toolchain. {ref}`Library modules with version constraints`
207+
:::
208+
209+
162210
#### Toolchain usage in other rules
163211

164212
Python toolchains can be utilized in other bazel rules, such as `genrule()`, by
@@ -508,4 +556,4 @@ of available toolchains.
508556
Currently the following flags are used to influence toolchain selection:
509557
* {obj}`--@rules_python//python/config_settings:py_linux_libc` for selecting the Linux libc variant.
510558
* {obj}`--@rules_python//python/config_settings:py_freethreaded` for selecting
511-
the freethreaded experimental Python builds available from `3.13.0` onwards.
559+
the freethreaded experimental Python builds available from `3.13.0` onwards.

python/private/pypi/dependency_resolver/dependency_resolver.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import atexit
1818
import os
19-
import re
2019
import shutil
2120
import sys
2221
from pathlib import Path
@@ -118,6 +117,7 @@ def main(
118117
absolute_path_prefix = resolved_requirements_file[
119118
: -(len(requirements_file) - len(repository_prefix))
120119
]
120+
121121
# As srcs might contain references to generated files we want to
122122
# use the runfiles file first. Thus, we need to compute the relative path
123123
# from the execution root.
@@ -162,19 +162,12 @@ def main(
162162
argv.append(
163163
f"--output-file={requirements_file_relative if UPDATE else requirements_out}"
164164
)
165-
src_files = [
165+
argv.extend(
166166
(src_relative if Path(src_relative).exists() else resolved_src)
167167
for src_relative, resolved_src in zip(srcs_relative, resolved_srcs)
168-
]
169-
argv.extend(src_files)
168+
)
170169
argv.extend(extra_args)
171170

172-
# Replace in the output lock file
173-
# the lines like: # via -r /absolute/path/to/<requirements_file>
174-
# with: # via -r <requirements_file>
175-
# For Windows, we should explicitly call .as_posix() to convert \\ -> /
176-
absolute_src_prefixes = [Path(src).absolute().parent.as_posix() + "/" for src in src_files]
177-
178171
if UPDATE:
179172
print("Updating " + requirements_file_relative)
180173

@@ -192,14 +185,14 @@ def main(
192185
# and we should copy the updated requirements back to the source tree.
193186
if not absolute_output_file.samefile(requirements_file_tree):
194187
atexit.register(
195-
lambda: shutil.copy(absolute_output_file, requirements_file_tree)
188+
lambda: shutil.copy(
189+
absolute_output_file, requirements_file_tree
190+
)
196191
)
197-
cli(argv, standalone_mode=False)
192+
cli(argv, standalone_mode = False)
198193
requirements_file_relative_path = Path(requirements_file_relative)
199194
content = requirements_file_relative_path.read_text()
200195
content = content.replace(absolute_path_prefix, "")
201-
for absolute_src_prefix in absolute_src_prefixes:
202-
content = content.replace(absolute_src_prefix, "")
203196
requirements_file_relative_path.write_text(content)
204197
else:
205198
# cli will exit(0) on success
@@ -221,15 +214,6 @@ def main(
221214
golden = open(_locate(bazel_runfiles, requirements_file)).readlines()
222215
out = open(requirements_out).readlines()
223216
out = [line.replace(absolute_path_prefix, "") for line in out]
224-
225-
def replace_via_minus_r(line):
226-
if "# via -r " in line:
227-
for absolute_src_prefix in absolute_src_prefixes:
228-
line = line.replace(absolute_src_prefix, "")
229-
return line
230-
return line
231-
232-
out = [replace_via_minus_r(line) for line in out]
233217
if golden != out:
234218
import difflib
235219

python/private/pypi/deps.bzl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ _RULE_DEPS = [
5151
),
5252
(
5353
"pypi__packaging",
54-
"https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl",
55-
"09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759",
54+
"https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl",
55+
"2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
5656
),
5757
(
5858
"pypi__pep517",
@@ -61,8 +61,8 @@ _RULE_DEPS = [
6161
),
6262
(
6363
"pypi__pip",
64-
"https://files.pythonhosted.org/packages/ef/7d/500c9ad20238fcfcb4cb9243eede163594d7020ce87bd9610c9e02771876/pip-24.3.1-py3-none-any.whl",
65-
"3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed",
64+
"https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl",
65+
"ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc",
6666
),
6767
(
6868
"pypi__pip_tools",

python/private/zip_main_template.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,10 @@ def main():
286286
# The bin/ directory may not exist if it is empty.
287287
os.makedirs(os.path.dirname(python_program), exist_ok=True)
288288
try:
289-
os.symlink(_PYTHON_BINARY_ACTUAL, python_program)
289+
os.symlink(symlink_to, python_program)
290290
except OSError as e:
291291
raise Exception(
292-
f"Unable to create venv python interpreter symlink: {python_program} -> {PYTHON_BINARY_ACTUAL}"
292+
f"Unable to create venv python interpreter symlink: {python_program} -> {symlink_to}"
293293
) from e
294294

295295
# Some older Python versions on macOS (namely Python 3.7) may unintentionally

tests/bootstrap_impls/BUILD.bazel

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
load("@rules_shell//shell:sh_test.bzl", "sh_test")
2+
13
# Copyright 2023 The Bazel Authors. All rights reserved.
24
#
35
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,10 +13,40 @@
1113
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1214
# See the License for the specific language governing permissions and
1315
# limitations under the License.
14-
load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test", "sh_py_run_test")
16+
load("//tests/support:sh_py_run_test.bzl", "py_reconfig_binary", "py_reconfig_test", "sh_py_run_test")
1517
load("//tests/support:support.bzl", "SUPPORTS_BOOTSTRAP_SCRIPT")
1618
load(":venv_relative_path_tests.bzl", "relative_path_test_suite")
1719

20+
py_reconfig_binary(
21+
name = "bootstrap_script_zipapp_bin",
22+
srcs = ["bin.py"],
23+
bootstrap_impl = "script",
24+
# Force it to not be self-executable
25+
build_python_zip = "no",
26+
main = "bin.py",
27+
target_compatible_with = SUPPORTS_BOOTSTRAP_SCRIPT,
28+
)
29+
30+
filegroup(
31+
name = "bootstrap_script_zipapp_zip",
32+
testonly = 1,
33+
srcs = [":bootstrap_script_zipapp_bin"],
34+
output_group = "python_zip_file",
35+
)
36+
37+
sh_test(
38+
name = "bootstrap_script_zipapp_test",
39+
srcs = ["bootstrap_script_zipapp_test.sh"],
40+
data = [":bootstrap_script_zipapp_zip"],
41+
env = {
42+
"ZIP_RLOCATION": "$(rlocationpaths :bootstrap_script_zipapp_zip)".format(),
43+
},
44+
target_compatible_with = SUPPORTS_BOOTSTRAP_SCRIPT,
45+
deps = [
46+
"@bazel_tools//tools/bash/runfiles",
47+
],
48+
)
49+
1850
sh_py_run_test(
1951
name = "run_binary_zip_no_test",
2052
build_python_zip = "no",
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2024 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# --- begin runfiles.bash initialization v3 ---
16+
# Copy-pasted from the Bazel Bash runfiles library v3.
17+
set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
18+
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
19+
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
20+
source "$0.runfiles/$f" 2>/dev/null || \
21+
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
22+
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
23+
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
24+
# --- end runfiles.bash initialization v3 ---
25+
set +e
26+
27+
bin=$(rlocation $ZIP_RLOCATION)
28+
if [[ -z "$bin" ]]; then
29+
echo "Unable to locate test binary: $ZIP_RLOCATION"
30+
exit 1
31+
fi
32+
set -x
33+
actual=$(python3 $bin)
34+
35+
# How we detect if a zip file was executed from depends on which bootstrap
36+
# is used.
37+
# bootstrap_impl=script outputs RULES_PYTHON_ZIP_DIR=<somepath>
38+
# bootstrap_impl=system_python outputs file:.*Bazel.runfiles
39+
expected_pattern="Hello"
40+
if ! (echo "$actual" | grep "$expected_pattern" ) >/dev/null; then
41+
echo "Test case failed: $1"
42+
echo "expected output to match: $expected_pattern"
43+
echo "but got:\n$actual"
44+
exit 1
45+
fi
46+
47+
exit 0

0 commit comments

Comments
 (0)