Skip to content

feat(toolchain): allow using a py_library such as @pypi//coverage as the coverage_tool - #4433

Open
aignas wants to merge 20 commits into
bazel-contrib:mainfrom
aignas:aignas.feat.coverage-tool-py-library
Open

aignas wants to merge 20 commits into
bazel-contrib:mainfrom
aignas:aignas.feat.coverage-tool-py-library

Conversation

@aignas

@aignas aignas commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Allow py_runtime.coverage_tool to be a target that provides PyInfo, such as
the coverage package from a pip.parse hub: @pypi//coverage. This allows
using the coverage version locked in the requirements instead of the bundled
one without a custom wrapper rule.

Using the py_library as the coverage tool used to create a dependency cycle:
the py_library resolves the Python toolchain, whose py_runtime depends on
the coverage_tool again. Similarly to how rules_go builds nogo, the
coverage_tool attribute now transitions to a configuration where a private
bootstrap flag is set and collect_code_coverage is disabled, and the
py_runtime macro drops the coverage_tool dependency in that configuration.
The hermetic toolchains already select the coverage tool on
collect_code_coverage, so the cycle is broken for them as well.

The bootstrap never executes the coverage tool file: it adds a directory to
sys.path and imports coverage. For targets providing PyInfo, the import
paths of the target are used instead, exposed as the new
PyRuntimeInfo.coverage_imports field and the %coverage_imports% site init
template substitution. Single file and executable coverage tools, including
the bundled one, work as before.

To test this end to end, coverage is added to the dev dependencies and a
3.13 toolchain uses @dev_pip//coverage as its coverage tool.
//tests/coverage:coverage_from_pypi_test runs the same coverage collection
checks as for the bundled coverage tool.

Work towards #3902

…ge tool

Before this change the coverage collection was only exercised by running
`bazel coverage` on a few targets in CI and the collected data was not
checked.

This adds a test that runs as part of `bazel test`. A `py_reconfig_test` is
built with `--collect_code_coverage` for a Python 3.12 toolchain that has the
bundled coverage tool configured, and `coverage_collection_test` runs it with
the same environment variables that `bazel coverage` sets and checks the line
coverage in the produced lcov file.

* Adds the `collect_code_coverage` attribute to `py_reconfig_*` rules.
* Configures the bundled coverage tool for the Python 3.12 dev toolchain.

Stacked on bazel-contrib#4415.

Work towards bazel-contrib#3902
Registering the toolchain with the `3.12` minor version made it take
precedence over the `3.12.16` toolchain that //tests/toolchains expects.
…ests

Run the instrumented program once and check the line and branch coverage
of particular lines in separate tests.
Allow py_runtime.coverage_tool to be a py_library of coverage.py, such as
@pypi//coverage, by using its coverage/__main__.py file as the entry
point. This allows users to use the coverage version that is locked in
their requirements.
@aignas

aignas commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator Author

This PR was drafted by an AI coding agent (Claude Code) working for @aignas as part of the plan in #3902. It stays a draft until @aignas has reviewed it. Review comments will be handled by @aignas, possibly with the help of the agent; any agent-written replies will be marked as such.

Agent-generated comment, posted automatically on behalf of the user.

A py_library depends on the Python toolchain, so using @pypi//coverage
as the coverage_tool of the toolchain creates a dependency cycle. Instead
find the coverage/__main__.py entry point in the files of the target, so
that @pypi//coverage:extracted_whl_files can be used.
@aignas aignas changed the title feat(toolchain): allow using a py_library as the coverage_tool feat(toolchain): allow using the extracted coverage wheel as the coverage_tool Nov 8, 2026
aignas added 12 commits November 8, 2026 12:54
It is used to test the coverage collection with the coverage tool from a
PyPI hub.
…p hub

Configure a 3.13 toolchain with the extracted coverage wheel from the
dev_pip hub as the coverage tool and run the same coverage collection
checks as for the bundled coverage tool.
Use TEST_TMPDIR directly and rely on the test working directory being the
workspace runfiles directory.
Also drop the lib.py data dependency, which is already in the runfiles via
the instrumented target.
Also test the error for a coverage_tool without coverage/__main__.py and
name the news entry after the PR.
@aignas
aignas marked this pull request as ready for review November 9, 2026 01:17
@aignas
aignas requested a review from rickeylev as a code owner November 9, 2026 01:17
…e tool

Using a `py_library`, such as `@pypi//coverage`, as the `coverage_tool`
created a dependency cycle: the `py_library` resolves the Python toolchain,
whose `py_runtime` depends on the `coverage_tool` again.

Similarly to how `rules_go` builds `nogo`, the `coverage_tool` attribute
now transitions to a configuration where a private bootstrap flag is set
and `collect_code_coverage` is disabled. The `py_runtime` macro drops the
`coverage_tool` dependency in that configuration, so the cycle is broken.
The hermetic toolchains already select on `collect_code_coverage`.
The bootstrap never executes the coverage tool file: it only adds the
grandparent directory of it to `sys.path` and imports `coverage`. Instead
of searching for `coverage/__main__.py` within the files of the target,
use the import paths of a `coverage_tool` that provides `PyInfo`, e.g.
`@pypi//coverage`.

* Adds `PyRuntimeInfo.coverage_imports`, which is mutually exclusive with
  `PyRuntimeInfo.coverage_tool`.
* Adds the `%coverage_imports%` substitution to the site init template.
@aignas
aignas marked this pull request as draft November 9, 2026 10:23
@aignas aignas changed the title feat(toolchain): allow using the extracted coverage wheel as the coverage_tool feat(toolchain): allow using a py_library such as @pypi//coverage as the coverage_tool Nov 9, 2026
@aignas
aignas marked this pull request as ready for review November 9, 2026 10:24
@aignas

aignas commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator Author

OK, this looks in a good shape. I'm glad I remembered that rules_go had some dependency cycle breaking transitions, so I asked claude to first analyse the code there and do something similar here.

Now this is hopefully compatible with what @jvolkman is doing with TreeArtifacts in #4396 and reworking the code to do whl extraction in build phase.

@rickeylev, PTAL.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant