Repository navigation
Open
Description
This ticket I am going to repurpose for implementing some sort of method to allow users override coverage library used.
Things that we could do:
- Get rid of the py script calling pip -> vendor the bzl file to load from. This is somewhat hard to maintain and when new toolchain version get added (or removed) the file may get stale. Goal state is - We have bzlmod API to set the versions and we have a
diff_testto actually do the vendoring of the .bzl file. We should leverageuv.lockfiles to actually do the configuration, they can be either user or rules_python supplied. - Make the coverage package more similar to regular pip packages. Currently it is just an
http_archive, but in reality we could have potentially something that is more similar to how it is configured. Potentials:- Create a coverage_collector rule that would not require us patching the coverage wheel.
- Change the coverage collector bootstrap to not require patching.
- Just include the py_library into the
venvand let the rest work out naturally.
- Have a some sort of hub for the coverage tool that is created by
pip.parse(hub="coverage")where weuse_repo("coverage")and then have in @rules_python an alias for the coverage tool. Thepython.toolchainthen haspython.default(coverage="@rules_python//python/private:coverage_alias"). The main idea is that this configuration could accept a regular target. Maybe we can depend onextracted_whl_srcs.
Out of scope:
- Make
pip.depsomehow provide package version, markers, etc to download a particular package. This is similar to feat(pypi): add pip.dep to declare abstract pypi dependencies #3850, but the idea is:- We (for now only
rules_python) can define a default fallback venv to theunified_hub_repoalias, so that if the user does not select anything, it just works. - We use
@pypi//coverageand it just works, if the user has a different coverage package version in their venv, it gets used instead. - The logic for such deps may be that this is for particular tools, like
twine,pip,pip-tools,coverage. The interesting thing is that those tools could be each in its own venv if we are using them in exec configuration, i.e. as a tool runner. This suggests that we may need to rething this approach of using@pypi//<dep>and instead have something like@pypi//<dep> or @pypi_tool//<dep>where we should have something similar to the Add optional dependencies to the universal_hub #3923. For now we shall allow users define a different version.
- We (for now only
TODO
Note
This list is a working plan and will be updated as the PRs land and the direction gets clearer.
PRs that can be reviewed independently:
-
refactor(toolchain): store the bundled coverage wheels as a flatuv.lock-like list (struct(url, hash = "sha256:...", marker = ...)) and select the wheel withselect_whl(refactor(toolchain): select bundled coverage wheels with select_whl #4414). -
test(coverage): check the coverage collection with the bundled coverage tool as part ofbazel testusingpy_reconfig_test(test(coverage): check the coverage collection with the bundled coverage tool #4417). -
fix(toolchain): keep thestrip_prefixwhenpython.single_version_overridedoes not override theurls; it used to reset thestrip_prefixof thefullarchives (freethreaded builds of e.g. 3.13.0) (fix(toolchain): keep the strip_prefix when single_version_override has no urls #4435).
Stacked PRs (merge in order, each contains the previous ones):
-
feat(toolchain): allow the extractedcoveragewheel (e.g.@pypi//coverage:extracted_whl_files) aspy_runtime.coverage_tool; addscoverageto the dev deps and apy_reconfig_testthat collects coverage with it (feat(toolchain): allow using a py_library such as @pypi//coverage as the coverage_tool #4433, on top of test(coverage): check the coverage collection with the bundled coverage tool #4417).@pypi//coverage(thepy_library) cannot be used, because it depends on the Python toolchain and creates a dependency cycle. -
feat(toolchain):python.single_version_override(coverage_tool = ...)andpython_register_toolchains(coverage_tool = ...)to set the coverage tool for all platforms of a version; the feat(toolchain): allow using a py_library such as @pypi//coverage as the coverage_tool #4433 test toolchain uses it (feat(toolchain): set the coverage_tool for all platforms of a version #4434, on top of feat(toolchain): allow using a py_library such as @pypi//coverage as the coverage_tool #4433). -
feat(toolchain): generate theselect_whlplatform parameters from the python version and the toolchain platform so that the platform specific wheels are used for more platforms (e.g. musllinux, riscv64, i686) instead of falling back topy3-none-any(feat(toolchain): use a uv.lock based coverage hub for the bzlmod toolchains #4415, on top of refactor(toolchain): select bundled coverage wheels with select_whl #4414). -
build(coverage): define the bundled coverage versions in apyproject.tomland update the wheels from theuv.lockfile (build(coverage): update the bundled coverage wheels from a uv.lock file #4418, on top of feat(toolchain): use a uv.lock based coverage hub for the bzlmod toolchains #4415 and test(coverage): check the coverage collection with the bundled coverage tool #4417). -
feat(coverage): use the latest coverage.py for the latest python versions and keep the old release only for the old python versions (feat(coverage): use the latest coverage.py for the latest python versions #4420, on top of build(coverage): update the bundled coverage wheels from a uv.lock file #4418).
Follow-ups:
- Drop
coverage.patchonce Bazel 7 is no longer supported: therules_pythonbootstrap importscoveragein-process, so thesys.pathpatch ofcoverage/__main__.pyonly matters if the entry point is executed as a script (e.g. by the Bazel 7 native rules). The feat(toolchain): allow using a py_library such as @pypi//coverage as the coverage_tool #4433 test shows that the unpatched PyPI wheel works. - Decide whether the bundled coverage wheels should be deprecated in favour of the user supplied ones.
Done:
- Warn when
bazel coverageis run and the selected runtime has no coverage tool, pointing to the docs (fix(coverage): warn about a missing coverage wheel only for the selected runtime #3955).
Related:
- perf(pypi): extract downloaded wheels once and reuse them #3856 extracts each downloaded wheel once and reuses it across python versions and hubs, so the coverage wheel from a hub is not extracted once per python version.
Metadata
Metadata
Assignees
Labels
No labels
Activity
test(coverage): check the coverage collection with the bundled covera…
[-]Idea - Leverage pip.dep for the coverage tooling[/-][+]feat(toolchain): allow users easily override coverage[/+]test(coverage): check the coverage collection with the bundled covera…
refactor(toolchain): select bundled coverage wheels with select_whl (b…
feat(toolchain): use the platform specific coverage wheels on more pl…
aignas commentedon Nov 9, 2026
An update on the approach that is being taken. It is split into a stack of PRs, in the order they should be merged:
py_runtime.coverage_toolaccepts apy_library, such as@pypi//coverage.rules_gobuildsnogo, thecoverage_toolattribute transitions to a configuration where a private bootstrap flag is set andcollect_code_coverageis disabled, and thepy_runtimemacro drops thecoverage_tooldependency there. This breaks the dependency cycle where thepy_libraryresolves the toolchain that depends on it.coverage, so for targets providingPyInfothe import paths are used (the newPyRuntimeInfo.coverage_imports). No patching of the wheel or searching forcoverage/__main__.pyis needed.pip.parse(target_platforms = ["*"])to use all of the configuredpip.defaultplatforms.pip.defaultplatforms for all of the platforms of the hermetic toolchains, with a test that each toolchain platform variant has a matching pip platform.python/private/coverage/pyproject.tomlwith auv.lock, which locks the latestcoveragesupporting each Python version from 3.9 onwards.rules_pythondefines arules_python_coveragepip hub from it (onepip.parseper Python version,target_platforms = ["*"]), andpython.toolchain(configure_coverage_tool = True)uses@rules_python_coverage//coverage. The docs recommend passing apy_libraryas the coverage tool and the other kinds of targets will be deprecated in the next version.WORKSPACEfiles. A bzlmod-only repository rule, which uses the same wheel selection aspip.parse, generates a checked-inworkspace_deps.bzl, and adiff_testkeeps it in sync with theuv.lock.python_register_toolchains(register_coverage_tool = True)creates the wheel repositories and a@rules_python_coveragehub from it, with a coverage tool per Python version (e.g.@rules_python_coverage//coverage:3.13), because WORKSPACE builds do not set the Python version flag by default. This replaces the hand-maintainedcoverage_deps.bzland its update script.We have abandoned the more gradual transition that #4415 originally implemented, which kept the bundled
http_archivewheels and only improved how the coverage wheel is selected for each toolchain platform (and was the first step towards selecting it from auv.lockfile). Using a regular pip hub is much better: the coverage tool is a normalpy_library, the wheel selection, platform handling and lock file updates are shared withpip.parseinstead of being maintained separately, and users can swap in their own hub.Users who want a different
coverageversion can point the toolchain at their own hub, e.g.python.single_version_platform_override(coverage_tool = "@pypi//coverage", ...)orpy_runtime(coverage_tool = "@pypi//coverage"). A dedicatedpython.default(coverage = ...)style API is not part of this stack, and the coverage tool is still not configured for the Windows toolchains.Agent-generated comment, posted automatically on behalf of @aignas.