Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ END_UNRELEASED_TEMPLATE
default to `true`.
* (pypi) The data files of a wheel (bin, includes, etc) are now always included
as a library's data dependencies.
* (coverage) When `configure_coverage_tool = True` is set but the bundled
`coverage.py` wheel set has no entry for the requested python version and
platform, a warning is now printed instead of silently producing an empty
coverage report.

{#v0-0-0-fixed}
### Fixed
Expand Down
52 changes: 41 additions & 11 deletions python/private/coverage_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -166,30 +166,60 @@ _coverage_deps = {

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

def coverage_dep(name, python_version, platform, visibility):
"""Register a single coverage dependency based on the python version and platform.
def coverage_url_sha256(python_version, platform):
"""Look up the bundled coverage wheel URL and sha256 for a python version + platform.

Pure function exported for testing. Returns None for any combination not
covered by the bundled wheel set, including the windows branch (which is
intentionally unsupported because the upstream coverage wrapper does not
work there).

Args:
name: The name of the registered repository.
python_version: The full python version.
platform: The platform, which can be found in //python:versions.bzl PLATFORMS dict.
visibility: The visibility of the coverage tool.
platform: The platform, from //python:versions.bzl PLATFORMS.

Returns:
The label of the coverage tool if the platform is supported, otherwise - None.
A (url, sha256) tuple if the (version, platform) is in the bundled
set, otherwise None.
"""
if "windows" in platform:
# NOTE @aignas 2023-01-19: currently we do not support windows as the
# upstream coverage wrapper is written in shell. Do not log any warning
# for now as it is not actionable.
return None

abi = "cp" + version_label(python_version)
url, sha256 = _coverage_deps.get(abi, {}).get(platform, (None, ""))

if url == None:
# Some wheels are not present for some builds, so let's silently ignore those.
return None
return (url, sha256)

def coverage_dep(name, python_version, platform, visibility):
"""Register a single coverage dependency based on the python version and platform.

Args:
name: The name of the registered repository.
python_version: The full python version.
platform: The platform, which can be found in //python:versions.bzl PLATFORMS dict.
visibility: The visibility of the coverage tool.

Returns:
The label of the coverage tool if the platform is supported, otherwise - None.
"""
found = coverage_url_sha256(python_version, platform)
if found == None:
if "windows" not in platform:
# NOTE: the windows branch is intentionally silent because the
# upstream coverage wrapper is written in shell and does not
# support windows; warning there is not actionable.
# buildifier: disable=print
print((
"WARNING: rules_python's bundled coverage tool has no wheel for " +
"python_version={}, platform={}. `bazel coverage` will produce " +
"empty lcov for py_test targets in this configuration. Either " +
"pin python_version to a version in the bundled set (see " +
"python/private/coverage_deps.bzl), or configure coverage " +
"manually via py_runtime.coverage_tool. See docs/coverage.md."
).format(python_version, platform))
Comment thread
Syndic marked this conversation as resolved.
return None
url, sha256 = found

maybe(
http_archive,
Expand Down
17 changes: 17 additions & 0 deletions tests/coverage_deps/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2026 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

load(":coverage_deps_test.bzl", "coverage_deps_test_suite")

coverage_deps_test_suite(name = "coverage_deps_tests")
77 changes: 77 additions & 0 deletions tests/coverage_deps/coverage_deps_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright 2026 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"Tests for coverage_url_sha256 lookups against the bundled wheel set."

load("@rules_testing//lib:test_suite.bzl", "test_suite")
load("//python/private:coverage_deps.bzl", "coverage_url_sha256") # buildifier: disable=bzl-visibility

_tests = []

def _test_supported_version_and_platform_returns_url_and_sha(env):
result = coverage_url_sha256("3.10", "aarch64-apple-darwin")
env.expect.that_bool(result != None).equals(True)
url, sha256 = result
env.expect.that_str(url).contains("coverage-")
env.expect.that_str(url).contains("cp310")
env.expect.that_str(url).contains("macosx_11_0_arm64")
env.expect.that_int(len(sha256)).equals(64)

_tests.append(_test_supported_version_and_platform_returns_url_and_sha)

def _test_cp314_is_in_bundled_set(env):
# Regression guard: cp314 was the motivation for adding the warning.
# If a future regen accidentally drops it, this test fires.
result = coverage_url_sha256("3.14", "aarch64-apple-darwin")
env.expect.that_bool(result != None).equals(True)
url, _ = result
env.expect.that_str(url).contains("cp314")

_tests.append(_test_cp314_is_in_bundled_set)

def _test_freethreaded_variant_is_in_bundled_set(env):
# Regression guard: freethreaded variants for cp313+ are part of the
# bundled set; ensure regen does not drop them.
result = coverage_url_sha256("3.14", "aarch64-apple-darwin-freethreaded")
env.expect.that_bool(result != None).equals(True)
url, _ = result
env.expect.that_str(url).contains("cp314t")

_tests.append(_test_freethreaded_variant_is_in_bundled_set)

def _test_unsupported_version_returns_none(env):
# Python 3.7 is not in the bundled wheel set (and is far below the
# current floor). This is the path that triggers the warning in
# coverage_dep.
result = coverage_url_sha256("3.7", "aarch64-apple-darwin")
env.expect.that_bool(result == None).equals(True)

_tests.append(_test_unsupported_version_returns_none)

def _test_windows_returns_none(env):
# Windows is intentionally not supported by the bundled coverage tool;
# the lookup must return None so the caller can keep the windows
# branch silent.
result = coverage_url_sha256("3.10", "x86_64-pc-windows-msvc")
env.expect.that_bool(result == None).equals(True)

_tests.append(_test_windows_returns_none)

def coverage_deps_test_suite(name):
"""Create the test suite.

Args:
name: the name of the test suite
"""
test_suite(name = name, basic_tests = _tests)