Skip to content

Commit f8f555f

Browse files
authored
Merge branch 'main' into fix_rules_python
2 parents 3706397 + 61cfa88 commit f8f555f

10 files changed

Lines changed: 213 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ END_UNRELEASED_TEMPLATE
6565
default to `true`.
6666
* (pypi) The data files of a wheel (bin, includes, etc) are now always included
6767
as a library's data dependencies.
68+
* (coverage) When `configure_coverage_tool = True` is set but the bundled
69+
`coverage.py` wheel set has no entry for the requested python version and
70+
platform, a warning is now printed instead of silently producing an empty
71+
coverage report.
6872

6973
{#v0-0-0-fixed}
7074
### Fixed
@@ -83,7 +87,13 @@ END_UNRELEASED_TEMPLATE
8387
* (system_python) Fix AttributeError exception on Debian 10 Buster
8488
python installations which may not set `sys._base_executable`
8589
([#3774](https://github.com/bazel-contrib/rules_python/issues/3774)).
86-
90+
* (windows) Fix `py_test`/`py_binary` failure when the target name contains
91+
path separators; the bootstrap stub is now declared as a sibling of the
92+
`.exe` launcher
93+
([#3789](https://github.com/bazel-contrib/rules_python/issues/3789)).
94+
* Fix the forwarding of `target_compatible_with` from `compile_pip_requirements`
95+
towards the underlying `*.update` target.
96+
([#3787](https://github.com/bazel-contrib/rules_python/pull/3787))
8797

8898
{#v0-0-0-added}
8999
### Added
@@ -97,13 +107,21 @@ END_UNRELEASED_TEMPLATE
97107
Fixes [#3296](https://github.com/bazel-contrib/rules_python/issues/3296).
98108
* (gazelle) Support alias_kind directive.
99109
Fixes [#3183](https://github.com/bazel-contrib/rules_python/issues/3183).
100-
* (toolchains) `3.13.12`, `3.14.3` Python toolchain from [20260325] release.
101-
* (toolchains) `3.10.20`, `3.11.15`, `3.12.13`, `3.13.13` `3.14.4`, `3.15.0a8`
102-
* Python toolchain from [20260414] release.
103110
* (pypi) `package_metadata` support, fixes
104111
[#2054](https://github.com/bazel-contrib/rules_python/issues/2054).
105112
* (coverage) Add support for python 3.14 and bump `coverage.py` to 7.10.7.
106113

114+
{#v2-0-2}
115+
## [2.0.2] - 2026-05-14
116+
117+
[2.0.2]: https://github.com/bazel-contrib/rules_python/releases/tag/2.0.2
118+
119+
{#v2-0-2-added}
120+
### Added
121+
* (toolchains) `3.13.12`, `3.14.3` Python toolchain from [20260325] release.
122+
* (toolchains) `3.10.20`, `3.11.15`, `3.12.13`, `3.13.13` `3.14.4`, `3.15.0a8`
123+
* Python toolchain from [20260414] release.
124+
107125
[20260325]: https://github.com/astral-sh/python-build-standalone/releases/tag/20260325
108126
[20260414]: https://github.com/astral-sh/python-build-standalone/releases/tag/20260414
109127

@@ -233,6 +251,17 @@ Other changes:
233251
* (wheel) Add support for `add_path_prefix` argument in `py_wheel` which can be
234252
used to prepend a prefix to the files in the wheel.
235253

254+
{#v1-9-1}
255+
## [1.9.1] - 2026-05-14
256+
257+
[1.9.1]: https://github.com/bazel-contrib/rules_python/releases/tag/1.9.1
258+
259+
{#v1-9-1-added}
260+
### Added
261+
* (toolchains) `3.13.12`, `3.14.3` Python toolchain from [20260325] release.
262+
* (toolchains) `3.10.20`, `3.11.15`, `3.12.13`, `3.13.13` `3.14.4`, `3.15.0a8`
263+
* Python toolchain from [20260414] release.
264+
236265
{#v1-9-0}
237266
## [1.9.0] - 2026-02-21
238267

python/private/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ bzl_library(
175175
srcs = ["coverage_deps.bzl"],
176176
deps = [
177177
":bazel_tools_bzl",
178+
":repo_utils_bzl",
178179
":version_label_bzl",
179180
],
180181
)
@@ -293,6 +294,7 @@ bzl_library(
293294
":full_version_bzl",
294295
":internal_config_repo_bzl",
295296
":python_repository_bzl",
297+
":repo_utils_bzl",
296298
":toolchains_repo_bzl",
297299
"//python:versions_bzl",
298300
"//python/private/pypi:deps_bzl",

python/private/coverage_deps.bzl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
1919
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
20+
load("//python/private:repo_utils.bzl", "repo_utils")
2021
load("//python/private:version_label.bzl", "version_label")
2122

2223
# START: maintained by 'bazel run //tools/private/update_deps:update_coverage_deps <version>'
@@ -166,18 +167,27 @@ _coverage_deps = {
166167

167168
_coverage_patch = Label("//python/private:coverage.patch")
168169

169-
def coverage_dep(name, python_version, platform, visibility):
170+
def coverage_dep(name, python_version, platform, visibility, logger = None):
170171
"""Register a single coverage dependency based on the python version and platform.
171172
172173
Args:
173174
name: The name of the registered repository.
174175
python_version: The full python version.
175176
platform: The platform, which can be found in //python:versions.bzl PLATFORMS dict.
176177
visibility: The visibility of the coverage tool.
178+
logger: {type}`repo_utils.logger | None` Optional logger used to emit a
179+
warning when no wheel is available for the (python_version,
180+
platform) pair. If not supplied, a default logger is constructed.
177181
178182
Returns:
179183
The label of the coverage tool if the platform is supported, otherwise - None.
180184
"""
185+
if logger == None:
186+
logger = repo_utils.logger(
187+
struct(getenv = lambda _: None),
188+
name = "coverage_dep",
189+
)
190+
181191
if "windows" in platform:
182192
# NOTE @aignas 2023-01-19: currently we do not support windows as the
183193
# upstream coverage wrapper is written in shell. Do not log any warning
@@ -188,7 +198,14 @@ def coverage_dep(name, python_version, platform, visibility):
188198
url, sha256 = _coverage_deps.get(abi, {}).get(platform, (None, ""))
189199

190200
if url == None:
191-
# Some wheels are not present for some builds, so let's silently ignore those.
201+
logger.warn(lambda: (
202+
"rules_python's bundled coverage tool has no wheel for " +
203+
"python_version={}, platform={}. `bazel coverage` will produce " +
204+
"empty lcov for py_test targets in this configuration. Either " +
205+
"pin python_version to a version in the bundled set (see " +
206+
"python/private/coverage_deps.bzl), or configure coverage " +
207+
"manually via py_runtime.coverage_tool. See docs/coverage.md."
208+
).format(python_version, platform))
192209
return None
193210

194211
maybe(

python/private/py_executable.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ WARNING: Target: {}
424424

425425
# On Windows, the main executable has an "exe" extension, so
426426
# here we re-use the un-extensioned name for the bootstrap output.
427-
bootstrap_output = ctx.actions.declare_file(base_executable_name)
427+
bootstrap_output = ctx.actions.declare_file(base_executable_name, sibling = executable)
428428

429429
# The launcher looks for the non-zip executable next to
430430
# itself, so add it to the default outputs.

python/private/pypi/pip_compile.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def pip_compile(
173173
name = name + ".update",
174174
env = env,
175175
python_version = kwargs.get("python_version", None),
176+
target_compatible_with = kwargs.get("target_compatible_with", []),
176177
**attrs
177178
)
178179

python/private/python.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ def _python_impl(module_ctx):
275275
register_result = python_register_toolchains(
276276
name = toolchain_info.name,
277277
_internal_bzlmod_toolchain_call = True,
278+
_internal_module_ctx = module_ctx,
278279
**kwargs
279280
)
280281
if not register_result.impl_repos:

python/private/python_register_toolchains.bzl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ load(
2626
load(":coverage_deps.bzl", "coverage_dep")
2727
load(":full_version.bzl", "full_version")
2828
load(":python_repository.bzl", "python_repository")
29+
load(":repo_utils.bzl", "repo_utils")
2930
load(
3031
":toolchains_repo.bzl",
3132
"host_compatible_python_repo",
@@ -89,6 +90,19 @@ def python_register_toolchains(
8990
if bzlmod_toolchain_call:
9091
register_toolchains = False
9192

93+
# When invoked from the bzlmod python extension, a module_ctx is plumbed in
94+
# so the coverage_dep logger can attribute warnings to the right module and
95+
# honor module-root filtering. In the WORKSPACE/macro path no module_ctx is
96+
# available; a minimal stand-in struct gives the logger what it needs.
97+
module_ctx = kwargs.pop("_internal_module_ctx", None)
98+
if module_ctx != None:
99+
coverage_logger = repo_utils.logger(module_ctx, name = "coverage_dep")
100+
else:
101+
coverage_logger = repo_utils.logger(
102+
struct(getenv = lambda _: None),
103+
name = "coverage_dep",
104+
)
105+
92106
base_url = kwargs.pop("base_url", DEFAULT_RELEASE_BASE_URL)
93107
tool_versions = tool_versions or TOOL_VERSIONS
94108
minor_mapping = minor_mapping or MINOR_MAPPING
@@ -121,6 +135,7 @@ def python_register_toolchains(
121135
),
122136
python_version = python_version,
123137
platform = platform,
138+
logger = coverage_logger,
124139
visibility = ["@{name}_{platform}//:__subpackages__".format(
125140
name = name,
126141
platform = platform,

tests/base_rules/py_executable_base_tests.bzl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,35 @@ def _test_py_runtime_info_provided_impl(env, target):
496496

497497
_tests.append(_test_py_runtime_info_provided)
498498

499+
def _test_windows_target_with_path_separators(name, config):
500+
rt_util.helper_target(
501+
config.rule,
502+
name = name + "/nested_subject",
503+
srcs = ["main.py"],
504+
main = "main.py",
505+
)
506+
analysis_test(
507+
name = name,
508+
impl = _test_windows_target_with_path_separators_impl,
509+
target = name + "/nested_subject",
510+
config_settings = {
511+
"//command_line_option:cpu": "windows_x86_64",
512+
"//command_line_option:crosstool_top": CROSSTOOL_TOP,
513+
"//command_line_option:extra_execution_platforms": [platform_targets.WINDOWS_X86_64],
514+
"//command_line_option:extra_toolchains": [CC_TOOLCHAIN],
515+
"//command_line_option:platforms": [platform_targets.WINDOWS_X86_64],
516+
},
517+
attr_values = {},
518+
)
519+
520+
def _test_windows_target_with_path_separators_impl(env, target):
521+
target = env.expect.that_target(target)
522+
target.runfiles().contains_predicate(matching.str_endswith(
523+
target.meta.format_str("/{name}"),
524+
))
525+
526+
_tests.append(_test_windows_target_with_path_separators)
527+
499528
# =====
500529
# You were gonna add a test at the end, weren't you?
501530
# Nope. Please keep them sorted; put it in its alphabetical location.

tests/coverage_deps/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2026 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+
load(":coverage_deps_test.bzl", "coverage_deps_test_suite")
16+
17+
coverage_deps_test_suite(name = "coverage_deps_tests")
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright 2026 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+
"Tests for the warning emitted by coverage_dep when no wheel is available."
16+
17+
load("@rules_testing//lib:test_suite.bzl", "test_suite")
18+
load("//python/private:coverage_deps.bzl", "coverage_dep") # buildifier: disable=bzl-visibility
19+
load("//python/private:repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "REPO_VERBOSITY_ENV_VAR", "repo_utils") # buildifier: disable=bzl-visibility
20+
21+
_tests = []
22+
23+
def _capturing_logger():
24+
"""Build a (logger, captured_messages_list) pair.
25+
26+
The logger has its verbosity set to INFO so WARN messages are captured but
27+
nothing noisier than necessary is emitted. The printer collects the second
28+
positional argument from each printer invocation (the formatted message).
29+
"""
30+
captured = []
31+
logger = repo_utils.logger(
32+
struct(
33+
getenv = {
34+
REPO_DEBUG_ENV_VAR: None,
35+
REPO_VERBOSITY_ENV_VAR: "INFO",
36+
}.get,
37+
),
38+
name = "unit-test",
39+
printer = lambda _key, message: captured.append(message),
40+
)
41+
return logger, captured
42+
43+
def _test_unsupported_python_version_warns(env):
44+
# cp37 is not in the bundled wheel set; coverage_dep should return None
45+
# and emit a warning describing the misconfiguration.
46+
logger, captured = _capturing_logger()
47+
result = coverage_dep(
48+
name = "unused_for_test",
49+
python_version = "3.7",
50+
platform = "aarch64-apple-darwin",
51+
visibility = ["//visibility:public"],
52+
logger = logger,
53+
)
54+
env.expect.that_bool(result == None).equals(True)
55+
env.expect.that_int(len(captured)).equals(1)
56+
env.expect.that_str(captured[0]).contains("no wheel for")
57+
env.expect.that_str(captured[0]).contains("python_version=3.7")
58+
env.expect.that_str(captured[0]).contains("platform=aarch64-apple-darwin")
59+
60+
_tests.append(_test_unsupported_python_version_warns)
61+
62+
def _test_windows_platform_is_silent(env):
63+
# Windows is intentionally unsupported and not actionable; coverage_dep
64+
# must return None without logging anything.
65+
logger, captured = _capturing_logger()
66+
result = coverage_dep(
67+
name = "unused_for_test",
68+
python_version = "3.10",
69+
platform = "x86_64-pc-windows-msvc",
70+
visibility = ["//visibility:public"],
71+
logger = logger,
72+
)
73+
env.expect.that_bool(result == None).equals(True)
74+
env.expect.that_int(len(captured)).equals(0)
75+
76+
_tests.append(_test_windows_platform_is_silent)
77+
78+
# NOTE: there is intentionally no unit test for the supported-wheel path
79+
# (where coverage_dep returns a non-None label and emits no warning).
80+
# That path calls `maybe(http_archive, ...)`, which calls
81+
# `native.existing_rule()`. `native.existing_rule()` is only valid during
82+
# BUILD file, legacy macro, or rule finalizer evaluation -- not during
83+
# rule analysis, which is the phase rules_testing analysis tests run in.
84+
# Calling coverage_dep with supported args from here therefore fails with
85+
# "existing_rule() can only be used while evaluating a BUILD file, ...".
86+
# The supported-wheel path is exercised end-to-end by `bazel coverage`
87+
# against a real py_test target during ordinary use of the toolchain.
88+
89+
def coverage_deps_test_suite(name):
90+
"""Create the test suite.
91+
92+
Args:
93+
name: the name of the test suite.
94+
"""
95+
test_suite(name = name, basic_tests = _tests)

0 commit comments

Comments
 (0)