From 1909697ce78bec9b17d348491c14c46f1289a8f2 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 7 Nov 2026 14:31:00 +0900 Subject: [PATCH 01/39] test(coverage): check the coverage collection with the bundled coverage 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 #4415. Work towards #3902 --- MODULE.bazel | 7 ++ tests/coverage/BUILD.bazel | 41 ++++++++++ tests/coverage/coverage_collection_test.py | 91 ++++++++++++++++++++++ tests/coverage/instrumented.py | 6 ++ tests/coverage/lib.py | 11 +++ tests/support/py_reconfig.bzl | 6 ++ 6 files changed, 162 insertions(+) create mode 100644 tests/coverage/BUILD.bazel create mode 100644 tests/coverage/coverage_collection_test.py create mode 100644 tests/coverage/instrumented.py create mode 100644 tests/coverage/lib.py diff --git a/MODULE.bazel b/MODULE.bazel index 985bf352f6..d8f48d8e27 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -401,6 +401,13 @@ dev_python.override( register_all_versions = True, ) +# For testing the coverage collection with the bundled coverage tool. +# See //tests/coverage:coverage_collection_test +dev_python.toolchain( + configure_coverage_tool = True, + python_version = "3.12", +) + # For testing an arbitrary runtime triggered by a custom flag. # See //tests/toolchains:custom_platform_toolchain_test dev_python.single_version_platform_override( diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel new file mode 100644 index 0000000000..57d409f1fa --- /dev/null +++ b/tests/coverage/BUILD.bazel @@ -0,0 +1,41 @@ +load("//python:py_library.bzl", "py_library") +load("//python:py_test.bzl", "py_test") +load("//tests/support:py_reconfig.bzl", "py_reconfig_test") +load("//tests/support:support.bzl", "NOT_WINDOWS", "SUPPORTS_BZLMOD") + +py_library( + name = "lib", + srcs = ["lib.py"], +) + +# The program that is built with coverage enabled. It is run by the +# `coverage_collection_test` below, which sets the same environment variables +# that `bazel coverage` would set. +py_reconfig_test( + name = "instrumented", + srcs = ["instrumented.py"], + collect_code_coverage = "true", + main = "instrumented.py", + # The bundled coverage tool is configured only for this version, see the + # `MODULE.bazel` file. + python_version = "3.12", + tags = ["manual"], + # The bundled coverage tool is not supported on Windows and it is only + # configured with bzlmod. + target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, + deps = [":lib"], +) + +py_test( + name = "coverage_collection_test", + srcs = ["coverage_collection_test.py"], + data = [ + "lib.py", + ":instrumented", + ], + env = { + "INSTRUMENTED": "$(rlocationpath :instrumented)", + }, + target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, + deps = ["//python/runfiles"], +) diff --git a/tests/coverage/coverage_collection_test.py b/tests/coverage/coverage_collection_test.py new file mode 100644 index 0000000000..0bc3511705 --- /dev/null +++ b/tests/coverage/coverage_collection_test.py @@ -0,0 +1,91 @@ +"""Check that the coverage is collected with the bundled coverage tool. + +The test runs the instrumented program with the same environment variables +that `bazel coverage` sets and checks the lcov file that it produces. +""" + +import glob +import os +import pathlib +import subprocess +import unittest + +from python.runfiles import runfiles + +_LIB = "tests/coverage/lib.py" + + +def _parse_lcov(path): + """Return a dict of the source file to a dict of line number to hit count.""" + result = {} + current = None + for line in pathlib.Path(path).read_text().splitlines(): + if line.startswith("SF:"): + current = result.setdefault(line[len("SF:") :], {}) + elif line.startswith("DA:") and current is not None: + lineno, hits = line[len("DA:") :].split(",")[:2] + current[int(lineno)] = int(hits) + return result + + +class CoverageCollectionTest(unittest.TestCase): + def test_lcov_contains_line_coverage(self): + rf = runfiles.Create() + assert rf is not None + program = rf.Rlocation(os.environ["INSTRUMENTED"]) + assert program is not None and os.path.exists(program), program + + coverage_dir = pathlib.Path(os.environ["TEST_TMPDIR"]) / "coverage" + coverage_dir.mkdir() + manifest = coverage_dir.parent / "coverage_manifest.txt" + manifest.write_text(_LIB + "\n") + + # Do not leak the coverage settings of this test (e.g. when running + # `bazel coverage`) into the instrumented program. + env = { + k: v + for k, v in os.environ.items() + if not k.startswith("COVERAGE") and k != "PYTHON_COVERAGE" + } + env.update(rf.EnvVars()) + env["COVERAGE"] = "1" + env["COVERAGE_DIR"] = str(coverage_dir) + env["COVERAGE_MANIFEST"] = str(manifest) + + workspace_dir = os.path.dirname(os.path.dirname(os.path.dirname(program))) + result = subprocess.run( + [program], + cwd=workspace_dir, + env=env, + capture_output=True, + text=True, + ) + self.assertEqual( + result.returncode, + 0, + f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}", + ) + self.assertIn("truthy", result.stdout) + + lcov_files = glob.glob(str(coverage_dir / "pylcov_*.dat")) + self.assertEqual( + len(lcov_files), + 1, + f"lcov files: {lcov_files}\nstderr:\n{result.stderr}", + ) + lcov = _parse_lcov(lcov_files[0]) + self.assertIn(_LIB, lcov, f"lcov: {lcov}") + + lib_path = rf.Rlocation("_main/" + _LIB) + assert lib_path is not None + lines = pathlib.Path(lib_path).read_text().splitlines() + hits = { + lines[lineno - 1].strip(): count for lineno, count in lcov[_LIB].items() + } + self.assertGreater(hits['return "truthy"'], 0, hits) + self.assertEqual(hits['return "falsy"'], 0, hits) + self.assertEqual(hits['return "never called"'], 0, hits) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/coverage/instrumented.py b/tests/coverage/instrumented.py new file mode 100644 index 0000000000..b0b7966c38 --- /dev/null +++ b/tests/coverage/instrumented.py @@ -0,0 +1,6 @@ +"""The program for which the coverage is collected.""" + +from tests.coverage import lib + +if __name__ == "__main__": + print(lib.covered(True)) diff --git a/tests/coverage/lib.py b/tests/coverage/lib.py new file mode 100644 index 0000000000..23af721fc0 --- /dev/null +++ b/tests/coverage/lib.py @@ -0,0 +1,11 @@ +"""A small library to check which lines are reported as covered.""" + + +def covered(value): + if value: + return "truthy" + return "falsy" + + +def not_covered(): + return "never called" diff --git a/tests/support/py_reconfig.bzl b/tests/support/py_reconfig.bzl index 9bbfdb1104..ee93b525bb 100644 --- a/tests/support/py_reconfig.bzl +++ b/tests/support/py_reconfig.bzl @@ -37,6 +37,8 @@ def _perform_transition_impl(input_settings, attr, base_impl): settings[labels.BUILD_PYTHON_ZIP] = attr.build_python_zip if attr.bootstrap_impl: settings[labels.BOOTSTRAP_IMPL] = attr.bootstrap_impl + if attr.collect_code_coverage: + settings["//command_line_option:collect_code_coverage"] = attr.collect_code_coverage == "true" if attr.extra_toolchains: settings["//command_line_option:extra_toolchains"] = attr.extra_toolchains if attr.python_src: @@ -55,6 +57,7 @@ _BUILTIN_BUILD_PYTHON_ZIP = [] if config.bazel_10_or_later else [ _RECONFIG_INPUTS = [ "//command_line_option:build_runfile_links", + "//command_line_option:collect_code_coverage", "//command_line_option:extra_toolchains", "//command_line_option:stamp", CUSTOM_RUNTIME, @@ -73,6 +76,9 @@ _RECONFIG_INHERITED_OUTPUTS = [v for v in _RECONFIG_OUTPUTS if v in _RECONFIG_IN _RECONFIG_ATTRS = { "bootstrap_impl": attrb.String(), "build_python_zip": attrb.Bool(default = config.build_python_zip_default), + "collect_code_coverage": attrb.String( + doc = "Value for the --collect_code_coverage flag, either `true` or `false`.", + ), "config_settings": attrb.LabelKeyedStringDict(), "extra_toolchains": attrb.StringList( doc = """ From e0351f63ca7c70425d9cdbe1c1a4b0ae90600481 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 7 Nov 2026 14:38:14 +0900 Subject: [PATCH 02/39] fix: register the coverage dev toolchain with a full python version Registering the toolchain with the `3.12` minor version made it take precedence over the `3.12.16` toolchain that //tests/toolchains expects. --- MODULE.bazel | 5 ++++- tests/coverage/BUILD.bazel | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index d8f48d8e27..df1326d478 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -403,9 +403,12 @@ dev_python.override( # For testing the coverage collection with the bundled coverage tool. # See //tests/coverage:coverage_collection_test +# +# NOTE: A full version is used so that the toolchain is named after it and does +# not shadow the toolchains that are checked in //tests/toolchains. dev_python.toolchain( configure_coverage_tool = True, - python_version = "3.12", + python_version = "3.12.16", ) # For testing an arbitrary runtime triggered by a custom flag. diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index 57d409f1fa..fa245addc9 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -18,7 +18,7 @@ py_reconfig_test( main = "instrumented.py", # The bundled coverage tool is configured only for this version, see the # `MODULE.bazel` file. - python_version = "3.12", + python_version = "3.12.16", tags = ["manual"], # The bundled coverage tool is not supported on Windows and it is only # configured with bzlmod. From 088bce49ae2fbb3eab95c9a84e51ae209a22a6cc Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 00:25:50 +0900 Subject: [PATCH 03/39] test(coverage): check the covered and not covered lines in separate tests Run the instrumented program once and check the line and branch coverage of particular lines in separate tests. --- tests/coverage/coverage_collection_test.py | 136 ++++++++++++++++----- tests/coverage/instrumented.py | 1 + tests/coverage/lib.py | 13 +- 3 files changed, 121 insertions(+), 29 deletions(-) diff --git a/tests/coverage/coverage_collection_test.py b/tests/coverage/coverage_collection_test.py index 0bc3511705..aaa302064e 100644 --- a/tests/coverage/coverage_collection_test.py +++ b/tests/coverage/coverage_collection_test.py @@ -1,44 +1,70 @@ """Check that the coverage is collected with the bundled coverage tool. -The test runs the instrumented program with the same environment variables -that `bazel coverage` sets and checks the lcov file that it produces. +The tests run the instrumented program with the same environment variables +that `bazel coverage` sets and check the lcov file that it produces. """ import glob import os import pathlib import subprocess +import tempfile import unittest from python.runfiles import runfiles _LIB = "tests/coverage/lib.py" +_MAIN = "tests/coverage/instrumented.py" def _parse_lcov(path): - """Return a dict of the source file to a dict of line number to hit count.""" + """Parse the line and branch coverage from an lcov file. + + Returns: + A dict of the source file to a struct-like dict with the `lines` + (line number to hit count) and `branches` (line number to the list of + the branch hit counts). + """ result = {} current = None for line in pathlib.Path(path).read_text().splitlines(): if line.startswith("SF:"): - current = result.setdefault(line[len("SF:") :], {}) - elif line.startswith("DA:") and current is not None: + current = result.setdefault( + line[len("SF:") :], + {"lines": {}, "branches": {}}, + ) + elif current is None: + continue + elif line.startswith("DA:"): lineno, hits = line[len("DA:") :].split(",")[:2] - current[int(lineno)] = int(hits) + current["lines"][int(lineno)] = int(hits) + elif line.startswith("BRDA:"): + lineno, _, _, taken = line[len("BRDA:") :].split(",") + current["branches"].setdefault(int(lineno), []).append( + 0 if taken == "-" else int(taken) + ) return result class CoverageCollectionTest(unittest.TestCase): - def test_lcov_contains_line_coverage(self): + @classmethod + def setUpClass(cls): rf = runfiles.Create() assert rf is not None program = rf.Rlocation(os.environ["INSTRUMENTED"]) assert program is not None and os.path.exists(program), program + lib_path = rf.Rlocation("_main/" + _LIB) + assert lib_path is not None + cls.lib_lines = pathlib.Path(lib_path).read_text().splitlines() - coverage_dir = pathlib.Path(os.environ["TEST_TMPDIR"]) / "coverage" + tmp = pathlib.Path( + tempfile.mkdtemp(dir=os.environ.get("TEST_TMPDIR")), + ) + coverage_dir = tmp / "coverage" coverage_dir.mkdir() - manifest = coverage_dir.parent / "coverage_manifest.txt" - manifest.write_text(_LIB + "\n") + manifest = tmp / "coverage_manifest.txt" + # Like `bazel coverage`, list all of the instrumented files. + manifest.write_text("\n".join([_LIB, _MAIN]) + "\n") # Do not leak the coverage settings of this test (e.g. when running # `bazel coverage`) into the instrumented program. @@ -53,38 +79,92 @@ def test_lcov_contains_line_coverage(self): env["COVERAGE_MANIFEST"] = str(manifest) workspace_dir = os.path.dirname(os.path.dirname(os.path.dirname(program))) - result = subprocess.run( + cls.result = subprocess.run( [program], cwd=workspace_dir, env=env, capture_output=True, text=True, ) + cls.lcov_files = glob.glob(str(coverage_dir / "pylcov_*.dat")) + cls.lcov = _parse_lcov(cls.lcov_files[0]) if len(cls.lcov_files) == 1 else {} + + def _lineno(self, content): + """Return the 1-based line number of the unique line in lib.py.""" + matches = [ + i + 1 for i, line in enumerate(self.lib_lines) if line.strip() == content + ] + self.assertEqual(len(matches), 1, f"'{content}' in {_LIB}: {matches}") + return matches[0] + + def _line_hits(self, content): + self.assertIn(_LIB, self.lcov, f"lcov: {self.lcov}") + lineno = self._lineno(content) + lines = self.lcov[_LIB]["lines"] + self.assertIn(lineno, lines, f"line {lineno} '{content}' not in lcov: {lines}") + return lines[lineno] + + def test_program_succeeds(self): self.assertEqual( - result.returncode, + self.result.returncode, 0, - f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}", + f"stdout:\n{self.result.stdout}\nstderr:\n{self.result.stderr}", ) - self.assertIn("truthy", result.stdout) + self.assertEqual(self.result.stdout.splitlines(), ["truthy", "hello coverage"]) - lcov_files = glob.glob(str(coverage_dir / "pylcov_*.dat")) + def test_single_lcov_file_is_produced(self): self.assertEqual( - len(lcov_files), + len(self.lcov_files), 1, - f"lcov files: {lcov_files}\nstderr:\n{result.stderr}", + f"lcov files: {self.lcov_files}\nstderr:\n{self.result.stderr}", ) - lcov = _parse_lcov(lcov_files[0]) - self.assertIn(_LIB, lcov, f"lcov: {lcov}") - lib_path = rf.Rlocation("_main/" + _LIB) - assert lib_path is not None - lines = pathlib.Path(lib_path).read_text().splitlines() - hits = { - lines[lineno - 1].strip(): count for lineno, count in lcov[_LIB].items() - } - self.assertGreater(hits['return "truthy"'], 0, hits) - self.assertEqual(hits['return "falsy"'], 0, hits) - self.assertEqual(hits['return "never called"'], 0, hits) + def test_instrumented_files_use_relative_paths(self): + # NOTE: coverage.py also reports the files from the source directories + # that were not imported, so only check the instrumented files. + for path in [_LIB, _MAIN]: + with self.subTest(path=path): + self.assertIn(path, self.lcov, f"lcov files: {sorted(self.lcov)}") + self.assertEqual( + [f for f in self.lcov if f != path and f.endswith("/" + path)], + [], + ) + + def test_executed_lines_are_covered(self): + for content in [ + "if value:", + 'return "truthy"', + 'return "hello " + name', + ]: + with self.subTest(line=content): + self.assertGreater(self._line_hits(content), 0) + + def test_definitions_are_covered(self): + # Module level statements are executed on import. + for content in [ + "def covered(value):", + "def not_covered():", + "class Greeter:", + "def unused(self):", + ]: + with self.subTest(line=content): + self.assertGreater(self._line_hits(content), 0) + + def test_skipped_branch_is_not_covered(self): + self.assertEqual(self._line_hits('return "falsy"'), 0) + + def test_uncalled_function_is_not_covered(self): + self.assertEqual(self._line_hits('return "never called"'), 0) + + def test_uncalled_method_is_not_covered(self): + self.assertEqual(self._line_hits('return "unused method"'), 0) + + def test_partial_branch_coverage(self): + lineno = self._lineno("if value:") + branches = self.lcov[_LIB]["branches"] + self.assertIn(lineno, branches, f"branches: {branches}") + # Only the `True` branch of the `if` was taken. + self.assertEqual(sorted(b > 0 for b in branches[lineno]), [False, True]) if __name__ == "__main__": diff --git a/tests/coverage/instrumented.py b/tests/coverage/instrumented.py index b0b7966c38..11642e8ece 100644 --- a/tests/coverage/instrumented.py +++ b/tests/coverage/instrumented.py @@ -4,3 +4,4 @@ if __name__ == "__main__": print(lib.covered(True)) + print(lib.Greeter().greet("coverage")) diff --git a/tests/coverage/lib.py b/tests/coverage/lib.py index 23af721fc0..3443fff495 100644 --- a/tests/coverage/lib.py +++ b/tests/coverage/lib.py @@ -1,4 +1,7 @@ -"""A small library to check which lines are reported as covered.""" +"""A small library to check which lines are reported as covered. + +The lines are looked up by their content in the tests, so keep them unique. +""" def covered(value): @@ -9,3 +12,11 @@ def covered(value): def not_covered(): return "never called" + + +class Greeter: + def greet(self, name): + return "hello " + name + + def unused(self): + return "unused method" From 62be72417725f93a11e2b423ee57ec50b142ecbc Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:09:28 +0900 Subject: [PATCH 04/39] feat(toolchain): allow using a py_library as the coverage_tool 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. --- docs/coverage.md | 20 ++++++++++++++++ news/coverage-tool-py-library.added.md | 4 ++++ python/private/BUILD.bazel | 1 + python/private/py_runtime_rule.bzl | 29 +++++++++++++++++++++- tests/py_runtime/py_runtime_tests.bzl | 33 ++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 news/coverage-tool-py-library.added.md diff --git a/docs/coverage.md b/docs/coverage.md index 1eeb67c808..832dc91d9e 100644 --- a/docs/coverage.md +++ b/docs/coverage.md @@ -72,3 +72,23 @@ important behaviors of the target are: * It provides a single output file OR it provides an executable output; this output is treated as the coverage entry point. * If it provides runfiles, then `runfiles.files` are included into `py_test`. +* It can also be a `py_library` of `coverage.py`, e.g. `@pypi//coverage` from + your {obj}`pip.parse` hub. Then its `coverage/__main__.py` file is used as + the coverage entry point. + +### Using `coverage` from PyPI + +To use the version of `coverage` that is locked in your requirements instead of +the one bundled with `rules_python`, point the toolchain to the `coverage` +package of your hub: + +```starlark +python.single_version_platform_override( + coverage_tool = "@pypi//coverage", + platform = "x86_64-unknown-linux-gnu", + python_version = "3.13.2", +) +``` + +Or set `py_runtime.coverage_tool = "@pypi//coverage"` when defining your own +runtime. diff --git a/news/coverage-tool-py-library.added.md b/news/coverage-tool-py-library.added.md new file mode 100644 index 0000000000..3ef4885c2a --- /dev/null +++ b/news/coverage-tool-py-library.added.md @@ -0,0 +1,4 @@ +(toolchains) {attr}`py_runtime.coverage_tool` accepts a `py_library` of +`coverage.py`, such as `@pypi//coverage`, so the locked version of `coverage` +can be used for coverage collection +([#3902](https://github.com/bazel-contrib/rules_python/issues/3902)). diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index 3fd205f5bc..b31125d9a0 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -685,6 +685,7 @@ bzl_library( deps = [ ":common_labels", ":flags", + ":py_info", ":py_internal", ":py_runtime_info", ":reexports", diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl index 4f450c4c9b..ecd1c238f7 100644 --- a/python/private/py_runtime_rule.bzl +++ b/python/private/py_runtime_rule.bzl @@ -18,6 +18,7 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load(":common_labels.bzl", "labels") load(":flags.bzl", "FreeThreadedFlag") +load(":py_info.bzl", "PyInfo") load(":py_internal.bzl", "py_internal") load(":py_runtime_info.bzl", "DEFAULT_STUB_SHEBANG", "PyRuntimeInfo") load(":reexports.bzl", "BuiltinPyRuntimeInfo") @@ -25,6 +26,23 @@ load(":version.bzl", "version") _py_builtins = py_internal +_COVERAGE_MAIN = "coverage/__main__.py" + +def _coverage_main(py_info): + """Return the `coverage/__main__.py` entry point of a `coverage` py_library. + + Args: + py_info: {type}`PyInfo` of the `coverage` library, e.g. `@pypi//coverage`. + + Returns: + {type}`File` the entry point of the coverage tool. + """ + for src in py_info.transitive_sources.to_list(): + if src.short_path == _COVERAGE_MAIN or src.short_path.endswith("/" + _COVERAGE_MAIN): + return src + + fail("coverage_tool is a py_library, but it does not contain '{}'.".format(_COVERAGE_MAIN)) + def coverage_tool_missing_message(*, coverage_enabled, coverage_tool, label): """Build the warning for a selected runtime that cannot produce coverage. @@ -132,8 +150,10 @@ def _py_runtime_impl(ctx): coverage_tool = coverage_di.files.to_list()[0] elif coverage_di.files_to_run and coverage_di.files_to_run.executable: coverage_tool = coverage_di.files_to_run.executable + elif PyInfo in ctx.attr.coverage_tool: + coverage_tool = _coverage_main(ctx.attr.coverage_tool[PyInfo]) else: - fail("coverage_tool must be an executable target or must produce exactly one file.") + fail("coverage_tool must be an executable target, a py_library or must produce exactly one file.") coverage_files = depset(transitive = [ coverage_di.files, @@ -311,6 +331,13 @@ The entry point for the tool must be loadable by a Python interpreter (e.g. a `.py` or `.pyc` file). It must accept the command line arguments of [`coverage.py`](https://coverage.readthedocs.io), at least including the `run` and `lcov` subcommands. + +If the target is a `py_library` of `coverage.py`, e.g. `@pypi//coverage`, then +its `coverage/__main__.py` file is used as the entry point. + +:::{versionchanged} VERSION_NEXT_FEATURE +A `py_library` of `coverage.py` can be used. +::: """, ), "files": attr.label_list( diff --git a/tests/py_runtime/py_runtime_tests.bzl b/tests/py_runtime/py_runtime_tests.bzl index 47cd97f142..884d92bd9d 100644 --- a/tests/py_runtime/py_runtime_tests.bzl +++ b/tests/py_runtime/py_runtime_tests.bzl @@ -17,6 +17,7 @@ load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") load("@rules_testing//lib:truth.bzl", "matching") load("@rules_testing//lib:util.bzl", rt_util = "util") +load("//python:py_library.bzl", "py_library") load("//python:py_runtime.bzl", "py_runtime") load("//python:py_runtime_info.bzl", "PyRuntimeInfo") load("//python/private:common_labels.bzl", "labels") # buildifier: disable=bzl-visibility @@ -232,6 +233,38 @@ def _test_coverage_tool_plain_files_impl(env, target): _tests.append(_test_coverage_tool_plain_files) +def _test_coverage_tool_py_library(name): + rt_util.helper_target( + py_runtime, + name = name + "_subject", + python_version = "PY3", + interpreter_path = "/bogus", + coverage_tool = name + "_coverage_lib", + ) + rt_util.helper_target( + py_library, + name = name + "_coverage_lib", + srcs = [ + "site-packages/coverage/__init__.py", + "site-packages/coverage/__main__.py", + ], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_coverage_tool_py_library_impl, + ) + +def _test_coverage_tool_py_library_impl(env, target): + info = env.expect.that_target(target).provider(PyRuntimeInfo, factory = py_runtime_info_subject) + info.coverage_tool().short_path_equals("{package}/site-packages/coverage/__main__.py") + info.coverage_files().contains_at_least([ + "{package}/site-packages/coverage/__init__.py", + "{package}/site-packages/coverage/__main__.py", + ]) + +_tests.append(_test_coverage_tool_py_library) + def _test_in_build_interpreter(name): rt_util.helper_target( py_runtime, From 18e6ef7952e85ea13e524e44f197084f68cf212c Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:42:23 +0900 Subject: [PATCH 05/39] fix: use the extracted wheel files instead of a py_library 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. --- docs/coverage.md | 22 +++++++++------ news/coverage-tool-extracted-whl.added.md | 4 +++ news/coverage-tool-py-library.added.md | 4 --- python/private/BUILD.bazel | 1 - python/private/py_runtime_rule.bzl | 34 +++++++++++++---------- tests/py_runtime/py_runtime_tests.bzl | 15 +++++----- 6 files changed, 44 insertions(+), 36 deletions(-) create mode 100644 news/coverage-tool-extracted-whl.added.md delete mode 100644 news/coverage-tool-py-library.added.md diff --git a/docs/coverage.md b/docs/coverage.md index 832dc91d9e..fd7cdbb611 100644 --- a/docs/coverage.md +++ b/docs/coverage.md @@ -72,23 +72,29 @@ important behaviors of the target are: * It provides a single output file OR it provides an executable output; this output is treated as the coverage entry point. * If it provides runfiles, then `runfiles.files` are included into `py_test`. -* It can also be a `py_library` of `coverage.py`, e.g. `@pypi//coverage` from - your {obj}`pip.parse` hub. Then its `coverage/__main__.py` file is used as - the coverage entry point. +* Otherwise, if one of its output files is `coverage/__main__.py`, then that + file is treated as the coverage entry point. This allows using the files + of an extracted `coverage` wheel. ### Using `coverage` from PyPI To use the version of `coverage` that is locked in your requirements instead of -the one bundled with `rules_python`, point the toolchain to the `coverage` -package of your hub: +the one bundled with `rules_python`, point the toolchain to the extracted files +of the `coverage` package in your {obj}`pip.parse` hub: ```starlark python.single_version_platform_override( - coverage_tool = "@pypi//coverage", + coverage_tool = "@pypi//coverage:extracted_whl_files", platform = "x86_64-unknown-linux-gnu", python_version = "3.13.2", ) ``` -Or set `py_runtime.coverage_tool = "@pypi//coverage"` when defining your own -runtime. +Or set `py_runtime.coverage_tool = "@pypi//coverage:extracted_whl_files"` when +defining your own runtime. + +:::{note} +Use the `extracted_whl_files` target and not `@pypi//coverage`: the +`py_library` depends on the Python toolchain itself, so using it as the +coverage tool of the toolchain creates a dependency cycle. +::: diff --git a/news/coverage-tool-extracted-whl.added.md b/news/coverage-tool-extracted-whl.added.md new file mode 100644 index 0000000000..88a848203c --- /dev/null +++ b/news/coverage-tool-extracted-whl.added.md @@ -0,0 +1,4 @@ +(toolchains) {attr}`py_runtime.coverage_tool` accepts the extracted files of +the `coverage` wheel, such as `@pypi//coverage:extracted_whl_files`, so the +locked version of `coverage` can be used for coverage collection +([#3902](https://github.com/bazel-contrib/rules_python/issues/3902)). diff --git a/news/coverage-tool-py-library.added.md b/news/coverage-tool-py-library.added.md deleted file mode 100644 index 3ef4885c2a..0000000000 --- a/news/coverage-tool-py-library.added.md +++ /dev/null @@ -1,4 +0,0 @@ -(toolchains) {attr}`py_runtime.coverage_tool` accepts a `py_library` of -`coverage.py`, such as `@pypi//coverage`, so the locked version of `coverage` -can be used for coverage collection -([#3902](https://github.com/bazel-contrib/rules_python/issues/3902)). diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index b31125d9a0..3fd205f5bc 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -685,7 +685,6 @@ bzl_library( deps = [ ":common_labels", ":flags", - ":py_info", ":py_internal", ":py_runtime_info", ":reexports", diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl index ecd1c238f7..e7d96b56a7 100644 --- a/python/private/py_runtime_rule.bzl +++ b/python/private/py_runtime_rule.bzl @@ -18,7 +18,6 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load(":common_labels.bzl", "labels") load(":flags.bzl", "FreeThreadedFlag") -load(":py_info.bzl", "PyInfo") load(":py_internal.bzl", "py_internal") load(":py_runtime_info.bzl", "DEFAULT_STUB_SHEBANG", "PyRuntimeInfo") load(":reexports.bzl", "BuiltinPyRuntimeInfo") @@ -28,20 +27,21 @@ _py_builtins = py_internal _COVERAGE_MAIN = "coverage/__main__.py" -def _coverage_main(py_info): - """Return the `coverage/__main__.py` entry point of a `coverage` py_library. +def _coverage_main(files): + """Return the `coverage/__main__.py` entry point from the coverage files. Args: - py_info: {type}`PyInfo` of the `coverage` library, e.g. `@pypi//coverage`. + files: {type}`depset[File]` the files of the coverage tool target, e.g. + `@pypi//coverage:extracted_whl_files`. Returns: - {type}`File` the entry point of the coverage tool. + {type}`File | None` the entry point of the coverage tool. """ - for src in py_info.transitive_sources.to_list(): - if src.short_path == _COVERAGE_MAIN or src.short_path.endswith("/" + _COVERAGE_MAIN): - return src + for f in files.to_list(): + if f.short_path == _COVERAGE_MAIN or f.short_path.endswith("/" + _COVERAGE_MAIN): + return f - fail("coverage_tool is a py_library, but it does not contain '{}'.".format(_COVERAGE_MAIN)) + return None def coverage_tool_missing_message(*, coverage_enabled, coverage_tool, label): """Build the warning for a selected runtime that cannot produce coverage. @@ -150,10 +150,10 @@ def _py_runtime_impl(ctx): coverage_tool = coverage_di.files.to_list()[0] elif coverage_di.files_to_run and coverage_di.files_to_run.executable: coverage_tool = coverage_di.files_to_run.executable - elif PyInfo in ctx.attr.coverage_tool: - coverage_tool = _coverage_main(ctx.attr.coverage_tool[PyInfo]) else: - fail("coverage_tool must be an executable target, a py_library or must produce exactly one file.") + coverage_tool = _coverage_main(coverage_di.files) + if not coverage_tool: + fail("coverage_tool must be an executable target, must produce exactly one file or must contain '{}'.".format(_COVERAGE_MAIN)) coverage_files = depset(transitive = [ coverage_di.files, @@ -332,11 +332,15 @@ The entry point for the tool must be loadable by a Python interpreter (e.g. a of [`coverage.py`](https://coverage.readthedocs.io), at least including the `run` and `lcov` subcommands. -If the target is a `py_library` of `coverage.py`, e.g. `@pypi//coverage`, then -its `coverage/__main__.py` file is used as the entry point. +If the target produces multiple files and one of them is +`coverage/__main__.py`, then it is used as the entry point. This allows using +the extracted `coverage` wheel from a {obj}`pip.parse` hub, e.g. +`@pypi//coverage:extracted_whl_files`. Note, that `@pypi//coverage` cannot be +used, because a `py_library` depends on the Python toolchain, which would +create a dependency cycle. :::{versionchanged} VERSION_NEXT_FEATURE -A `py_library` of `coverage.py` can be used. +The extracted files of the `coverage` wheel can be used. ::: """, ), diff --git a/tests/py_runtime/py_runtime_tests.bzl b/tests/py_runtime/py_runtime_tests.bzl index 884d92bd9d..d60f64fffe 100644 --- a/tests/py_runtime/py_runtime_tests.bzl +++ b/tests/py_runtime/py_runtime_tests.bzl @@ -17,7 +17,6 @@ load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") load("@rules_testing//lib:truth.bzl", "matching") load("@rules_testing//lib:util.bzl", rt_util = "util") -load("//python:py_library.bzl", "py_library") load("//python:py_runtime.bzl", "py_runtime") load("//python:py_runtime_info.bzl", "PyRuntimeInfo") load("//python/private:common_labels.bzl", "labels") # buildifier: disable=bzl-visibility @@ -233,17 +232,17 @@ def _test_coverage_tool_plain_files_impl(env, target): _tests.append(_test_coverage_tool_plain_files) -def _test_coverage_tool_py_library(name): +def _test_coverage_tool_extracted_whl_files(name): rt_util.helper_target( py_runtime, name = name + "_subject", python_version = "PY3", interpreter_path = "/bogus", - coverage_tool = name + "_coverage_lib", + coverage_tool = name + "_extracted_whl_files", ) rt_util.helper_target( - py_library, - name = name + "_coverage_lib", + native.filegroup, + name = name + "_extracted_whl_files", srcs = [ "site-packages/coverage/__init__.py", "site-packages/coverage/__main__.py", @@ -252,10 +251,10 @@ def _test_coverage_tool_py_library(name): analysis_test( name = name, target = name + "_subject", - impl = _test_coverage_tool_py_library_impl, + impl = _test_coverage_tool_extracted_whl_files_impl, ) -def _test_coverage_tool_py_library_impl(env, target): +def _test_coverage_tool_extracted_whl_files_impl(env, target): info = env.expect.that_target(target).provider(PyRuntimeInfo, factory = py_runtime_info_subject) info.coverage_tool().short_path_equals("{package}/site-packages/coverage/__main__.py") info.coverage_files().contains_at_least([ @@ -263,7 +262,7 @@ def _test_coverage_tool_py_library_impl(env, target): "{package}/site-packages/coverage/__main__.py", ]) -_tests.append(_test_coverage_tool_py_library) +_tests.append(_test_coverage_tool_extracted_whl_files) def _test_in_build_interpreter(name): rt_util.helper_target( From 08d2743d47194896d1378247535d1dd0cd60cacd Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:56:04 +0900 Subject: [PATCH 06/39] build(dev): add coverage to the dev dependencies It is used to test the coverage collection with the coverage tool from a PyPI hub. --- dev/pyproject.toml | 2 + dev/uv.lock | 264 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) diff --git a/dev/pyproject.toml b/dev/pyproject.toml index 0be689ec7b..8b1bb575c8 100644 --- a/dev/pyproject.toml +++ b/dev/pyproject.toml @@ -12,6 +12,8 @@ dependencies = [ "sphinx_rtd_theme >=2.0", # uv insists on downgrading for some reason "readthedocs-sphinx-ext", "absl-py", + # Used to test the coverage collection with coverage from a PyPI hub. + "coverage", "typing-extensions", "sphinx-reredirects", "pefile", diff --git a/dev/uv.lock b/dev/uv.lock index fe0b802252..10ef2e731a 100644 --- a/dev/uv.lock +++ b/dev/uv.lock @@ -178,6 +178,267 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "coverage" +version = "7.16.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/55/d1eaf3e73781174340a00dc1ba2aee8a65f82fadb18e2797b192b6b3925b/coverage-7.16.2.tar.gz", hash = "sha256:ca64d9f1f384f151b9511bec01126072acd2f313439f8ed015a22d8790aab6fa", size = 971999, upload-time = "2026-09-27T12:29:01.118Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/72/db17bf5f87568ab524413693385be2eeb03e652ab56c54ea05fa85515675/coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23219888477edd736b6fcaec1272d47d93b926e999641ffea7e53a1738e70b2b", size = 223670, upload-time = "2026-09-27T12:25:34.675Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e1/285727a8a74d48de256e8605413ee4cf82301228253ea58e5ab4270138b0/coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40c0f00899fe6181ae7f434ceb200e51f5ee4b8ed10e3b5f0b605f0cae15da87", size = 224191, upload-time = "2026-09-27T12:25:36.476Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/c737b73bac9bf5034f5ff45a4237b7a189d417a4e36751faf5d10c082cbe/coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4624f80732f6b427ac58f1f59c577a0994a12e8174b5af6a027b4b58795d4c3", size = 250921, upload-time = "2026-09-27T12:25:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/29/4e/e1d38d27817d91776ca543d01276a67eace3df675a673cac32b12c167d58/coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:191803c4996b499fcd78c2ad5e5f767dcc53cb4dc6de6d6a741b443a1821ef02", size = 252751, upload-time = "2026-09-27T12:25:39.291Z" }, + { url = "https://files.pythonhosted.org/packages/0e/19/14a8e44cbb2ad36ae62aaa03c5eec4a06a7f7df220ea3776af26927acbe2/coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fd670ac43b709c575aefc25bf52d8a598a3bc5017bddfd0a179152ab06a2deb", size = 254616, upload-time = "2026-09-27T12:25:40.506Z" }, + { url = "https://files.pythonhosted.org/packages/f9/72/f5bcad0d9a9b450080032344fbff7ec60c1b0e3d38019f7735dee2b69645/coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:705e5af11d34647efdc170c7840b6857c81cf74be96419a553f237e68e62cb72", size = 256541, upload-time = "2026-09-27T12:25:41.785Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9a/8c735234e8abb52bf5d063f98c780fa942e77c4c0225f9fcb9b33effc346/coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8afd9bf35cc6a1f22eb3634808fa8e0b91902459c5721ef2e4461dfe771d7f08", size = 251589, upload-time = "2026-09-27T12:25:43.269Z" }, + { url = "https://files.pythonhosted.org/packages/35/6a/1bf6d32e55642d6972aa842640e0a8850e612d19ac7d05e2c32fa59dfd34/coverage-7.16.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f43bac1856ba269b905302778d4df433d6006489a192174ad77ac528e395032", size = 252660, upload-time = "2026-09-27T12:25:45.051Z" }, + { url = "https://files.pythonhosted.org/packages/53/5b/05b1c0d0e9495cb056155a16066259adc1e935e7411a82a62779bf729975/coverage-7.16.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f8475460aa33ee28ac896ab1156d0bb3b6c639f7f8383c2677d3359eb35f8205", size = 250660, upload-time = "2026-09-27T12:25:46.51Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/5bc3db57fc9c56b4ef055725c38c34faa818a27d17d65e251371f7f0e3a0/coverage-7.16.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d6276d78f6fca7d0ac066d5da4165c5acd07829e8305c2cb900b738fb3a75a72", size = 254479, upload-time = "2026-09-27T12:25:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0f/4a5de66daef26eb919213f63b84f45d5065ab2df94cd767fc7c1174ac5e2/coverage-7.16.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:736fde09ea39646d11f8e3b76bd3425c075aa4dd45f24891970bb77c14ff20f5", size = 250940, upload-time = "2026-09-27T12:25:49.227Z" }, + { url = "https://files.pythonhosted.org/packages/57/d3/84cd6a11e707e422194739e9734d947746efa5e5b3358ff2d601f823ee45/coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c85d54e7e8a2ca932fe8399301af9b8d5907ea2a455ffaff6e7d1208db83b943", size = 251543, upload-time = "2026-09-27T12:25:50.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/bc/3d84c2e2a95f38e346ce3730d9f0e53114f4e8aace72e6e51e014afb9992/coverage-7.16.2-cp310-cp310-win32.whl", hash = "sha256:5139009b5efd2194fc168ee9362f0e191ba612ef5d29242f9269c22f9b8f80c7", size = 225732, upload-time = "2026-09-27T12:25:51.945Z" }, + { url = "https://files.pythonhosted.org/packages/07/86/31f1f3170571a345ab9d8361a7b2f8e0c5698173fbecd1f7149b6e2089ee/coverage-7.16.2-cp310-cp310-win_amd64.whl", hash = "sha256:c3305c38a2fa21a4254f2ace7dd9ef5fc569c9a558b66e7017650b3d637fb95e", size = 226357, upload-time = "2026-09-27T12:25:53.272Z" }, + { url = "https://files.pythonhosted.org/packages/58/fa/ce3baf63d85b730398d92a7162f486f3a5e4e2cc3382a02488b3943725ba/coverage-7.16.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:732d950e51f3ba4fb6209c73250f3e8924fefca42953ee04a9e65d8c02414d7d", size = 223797, upload-time = "2026-09-27T12:25:54.756Z" }, + { url = "https://files.pythonhosted.org/packages/7a/57/9ba29c2aac7f756d479f03d45762120060f0f988788001001bf36e0e6fca/coverage-7.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5dca0bb66b4c3d624ba047887bf70270030c150692d543cb501293dc38a9f4b5", size = 224303, upload-time = "2026-09-27T12:25:56.214Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/0d6d60906dca7d28cc1e3fce12a9861801c4fbb6cbf220ad78cd059c9467/coverage-7.16.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:af2a2a8c7c74de0559e0c368d94c8def9e16c58faaee33a0bf081057c4227e3b", size = 254715, upload-time = "2026-09-27T12:25:57.755Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b8/9198b865679379fb165c689c64f6e11105ef380f6bd1c7673e83f73d9f5c/coverage-7.16.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db5f8394e17f877a625b257f2ba0ce8e728a499c2c1579ad66220272cd3df510", size = 256627, upload-time = "2026-09-27T12:25:59.131Z" }, + { url = "https://files.pythonhosted.org/packages/98/79/9521462cb6072fe394701bc8974b74afd576c9c9355156c7844e1a86a42b/coverage-7.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3146d2317c75f70df2509066d979dadd941f7021cdf9b5db4bcd8568258e25", size = 258724, upload-time = "2026-09-27T12:26:00.691Z" }, + { url = "https://files.pythonhosted.org/packages/a6/76/8d7d5d633db9fe0f3182fedc731bf09f9bcf2366055735152504ad614677/coverage-7.16.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e1d0ced76318bab499693ff25f64faa343415187cb2e4d7befdfdd391a1cf6a", size = 260692, upload-time = "2026-09-27T12:26:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/4e/a7/76cb09c89ba46d74d37428bf93251fc14fb0bbe9e05cc2a5ef61773d318a/coverage-7.16.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:af98ad5ed9d6daaca956201e00bb429a7eb2b080426686f70a20353e0f9839f5", size = 254800, upload-time = "2026-09-27T12:26:03.369Z" }, + { url = "https://files.pythonhosted.org/packages/72/b6/2351c1979aaeb5b4a8091a75b90ca997ad60de36e181ddba267cf61dac97/coverage-7.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d56e4d21c56d2046447733f8b118409597db48c01efe898ee9ac24e858ec2d6", size = 256423, upload-time = "2026-09-27T12:26:04.751Z" }, + { url = "https://files.pythonhosted.org/packages/0f/f4/ad9a4f8b5cb2d494fa9452b546fe742ed2f9d3847cc14c05e36279a3e649/coverage-7.16.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1d5d0e3b660506fb84f995814e3118a21efdc0c8eb80127da1be627d90093c17", size = 254487, upload-time = "2026-09-27T12:26:06.082Z" }, + { url = "https://files.pythonhosted.org/packages/6c/1f/a520470472f3e8b01169bf42162b1470c9ba992230432f62ca36269bf3a0/coverage-7.16.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:17228fbca0f22976f797be94e975dcd237799c657d49551c7de1e0654d1202e9", size = 258259, upload-time = "2026-09-27T12:26:07.513Z" }, + { url = "https://files.pythonhosted.org/packages/09/d2/ff26d5938274745855fa61cfcba0245c88ccc10d98d2cbd96064f16cd5a7/coverage-7.16.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:bc0b0ac781d489304b741269857f1f8338b7a26b1b89c06c0344658001ec0035", size = 254216, upload-time = "2026-09-27T12:26:08.982Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1d/5d832d3b06785d9f53267e4f2724a9f60c312eee6ebed9063a461d0d3b45/coverage-7.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bf1bd822ec4e387ed245bed0d71151582cf7be9e5309bc4145eefe36083d5878", size = 255125, upload-time = "2026-09-27T12:26:10.35Z" }, + { url = "https://files.pythonhosted.org/packages/55/4d/1d33edbc2fcf7d99e384e393e712aa5a2ebbbd8409825357815982207976/coverage-7.16.2-cp311-cp311-win32.whl", hash = "sha256:7ed238d227e23cc300c3d464babdaf9f6ddc740aa1b15a77ae96136e6a7c4516", size = 225898, upload-time = "2026-09-27T12:26:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/6f/7c/676df4882118756c4f8f560c954eddb93e166d84dda8c5f0b6a829689bde/coverage-7.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:a90700f743e29aa3d75a6ff5f01953176a889c00e526194bc4d281731b88d99d", size = 226369, upload-time = "2026-09-27T12:26:13.375Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0e/a457f4a461b3c5610d845137fdd45fa465e011a64c25af440518ab1f4e41/coverage-7.16.2-cp311-cp311-win_arm64.whl", hash = "sha256:a336eec40e3520d369b8a6cdabb4f596e69a8b42927ca074aa1452fed943238a", size = 226344, upload-time = "2026-09-27T12:26:15.127Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/f8296c63c5d542f3d21aed685e56b7031a419037d155bb3382fc0940d249/coverage-7.16.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:218d742afca2b5ad5ca759e93eddedfbcc6eadf8322f080dcefc40b7bd4e2d48", size = 223969, upload-time = "2026-09-27T12:26:16.753Z" }, + { url = "https://files.pythonhosted.org/packages/90/23/6f3dcb1423a0d43216e402ea1746e4a7c7c44f38896b97dd573790f56a40/coverage-7.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9a638be322a8d76a41cdb17781c7f82aaee6a66493d8ffb7e2c09ee22423d99", size = 224328, upload-time = "2026-09-27T12:26:18.15Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7d/8f3b6dc920e3fc6732f7678785a2091db439f186afbec30dbf2214d9b1f7/coverage-7.16.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:724bd0f1e81856b35e59fc98cf7b4e544a3cb662e4e0864dca73d4326ee9d808", size = 255832, upload-time = "2026-09-27T12:26:19.799Z" }, + { url = "https://files.pythonhosted.org/packages/d1/36/6c45f15be4eca4ac1062c6a55a323286494c99726a7e58951fe85967ac08/coverage-7.16.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5375ebd99038021b35e99dc88255022912c06565d316212f4a576e4b08d30f5d", size = 258571, upload-time = "2026-09-27T12:26:21.199Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/b54cbeba3ad89082c2e441278681859e538322cc34b84b2af7ebff00080f/coverage-7.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a076277ca9f5750cc230f0f578ebd2620cec60255b25707361699fef6fb465c", size = 259680, upload-time = "2026-09-27T12:26:22.822Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a2/0dc65ec3d61930e1e4c2e371763b15eb4290896eb343a12d5d3091308116/coverage-7.16.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:58d4a54c6ea672afef66d49be922a2c69826c5ae1a42a9cd94f0c9c2bacdf800", size = 261945, upload-time = "2026-09-27T12:26:24.336Z" }, + { url = "https://files.pythonhosted.org/packages/d6/93/5fad7a61f2c14e08e98946fc31c1c7ffc1195061bf3fdc351db3be77a863/coverage-7.16.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dcbcfcc059117284c603ff8cb61a65872512882f84a8cf0339241f7f7c2f148", size = 256191, upload-time = "2026-09-27T12:26:25.89Z" }, + { url = "https://files.pythonhosted.org/packages/2d/47/74e5de9227b939ece9f64e729645ddc4296bea10dbfa98721c1333c8be2e/coverage-7.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afdf43b72ef3876c1fe66423b91466e37877c9e81e8cec70542b7e8525b9d1b7", size = 257610, upload-time = "2026-09-27T12:26:27.35Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/2cf28d40b43645d1b72388fe3ee7f7c747533a6a9557bb8c24a7ae74fe1a/coverage-7.16.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9acc7f7ec4a1b5f89bd929fde5b8a714f6fafdc6cc18725413d510aa082b47ad", size = 255757, upload-time = "2026-09-27T12:26:28.949Z" }, + { url = "https://files.pythonhosted.org/packages/d7/3d/7c149fd99fc8bbc39c80db5e688d1d39fd040be2ecb78b8335a51a55b9c0/coverage-7.16.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:80d3f7b48d43ee8fc5e8707a8adb43d743a5a1a85256c25a24f9d6d0e2238fa6", size = 259822, upload-time = "2026-09-27T12:26:30.515Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/b283fce09d5995e227bd8e513358dd7471bedc0f78abc85a925ebdb0a2f6/coverage-7.16.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:126d1af8804d7224421fe991ff65d3ce649081560df7a98b1a5ffff07f9923bd", size = 255325, upload-time = "2026-09-27T12:26:32.037Z" }, + { url = "https://files.pythonhosted.org/packages/bf/91/f3325edf0c4223fb1fe1532b8dbef2a1d2f729459a9a7d1a44d073bae534/coverage-7.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c19cd6d025c1673f22afcd22c7df8a662d779e05d8e3fa6820c22afb895b0206", size = 257191, upload-time = "2026-09-27T12:26:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/c4/89/21eb5e83ecf2eed523c4eb3d65ae513cd082c8fd1b6deb34c4cb6c332f97/coverage-7.16.2-cp312-cp312-win32.whl", hash = "sha256:152877cdc8a07264882cfcd503ba56a3ef6cba56a70e8c70f6eb8ffd7384789a", size = 226034, upload-time = "2026-09-27T12:26:35.021Z" }, + { url = "https://files.pythonhosted.org/packages/db/de/e3ad6d864c0833624b4f1f9b53f9e58e116c945e5e965c3f1e172c5e84cd/coverage-7.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:e6c52d3307824ff93b39efd99e4185d557db40bd841452abfb32e5d9151ca162", size = 226569, upload-time = "2026-09-27T12:26:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c1/bccc58ebe5489cc70628f635c1932fd371f5d7da850dbcf960f95f4c4afc/coverage-7.16.2-cp312-cp312-win_arm64.whl", hash = "sha256:a678c0b6b22086ec2427359d22e37445d4a792f5fdbbc744112c7dade65cad02", size = 226372, upload-time = "2026-09-27T12:26:38.406Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f6/8eb4f220ef24f84fb27d852d4f9bf83e0c73ec1a4a08dd9a87e3f4529739/coverage-7.16.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1a37c6e478cf687e1aa30a593d19c92c02fad9d122b51ab73f51b8dc7a0c0fc9", size = 223993, upload-time = "2026-09-27T12:26:40.164Z" }, + { url = "https://files.pythonhosted.org/packages/40/23/d4bbaf0c154e0b0c2b5264890dbf6ef098dcb50ec8f2469be9490d191660/coverage-7.16.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0993d0e90858c03943d3cb152e068a20dd4707924deec84dd2230261baae3b1b", size = 224368, upload-time = "2026-09-27T12:26:41.762Z" }, + { url = "https://files.pythonhosted.org/packages/7f/48/fc1e88fd571ec5cb38150b7f89f7696ca1bdf9920e01432febb69774cc85/coverage-7.16.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bb2fc905bbf4e6b7f40806ea79e31515abf6349594cdf0adf27c4215f0463204", size = 255358, upload-time = "2026-09-27T12:26:43.442Z" }, + { url = "https://files.pythonhosted.org/packages/1d/56/6785397d07c29c8e70fbb9a07e97d062b43c21ffc5f12385917847f09f63/coverage-7.16.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4358b9c8c0125b460407f3017c6cce8156e904b32772c5630d27112f52bdbfe5", size = 257955, upload-time = "2026-09-27T12:26:45.725Z" }, + { url = "https://files.pythonhosted.org/packages/27/3b/c8cdd07721e5f99abd81cea970d971997f99bf158c0b85f51bd284179c8b/coverage-7.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f15254427c9b33eedac4f198eaf9e356eb4f6214551afb43da6194a2c088ad7", size = 259183, upload-time = "2026-09-27T12:26:47.208Z" }, + { url = "https://files.pythonhosted.org/packages/9b/11/606b192fe43d32574ec6238549d48de588fdcc18485682a5ec0a8ac357f2/coverage-7.16.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9a75a4704ff640e46170042eec1f984385a121227c505d5a16ad8e495f452541", size = 261325, upload-time = "2026-09-27T12:26:49.084Z" }, + { url = "https://files.pythonhosted.org/packages/67/90/eea481f8b0305ceeb33f081a5f47e298391dbd1b589de0c4b3b3aa50d3f2/coverage-7.16.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:14253fc7bb15749b849795a06f5d3b6d8bc3fb8a4b5ddc341faf7a89dce205fc", size = 255531, upload-time = "2026-09-27T12:26:50.509Z" }, + { url = "https://files.pythonhosted.org/packages/6b/be/dedbf9aea1457b120c27ac10b8fc2a357f37fa2b54c3e7286d42980a0a2a/coverage-7.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:921415102a90637fcc2e3f169f61dad7699ecf690e8639fc21b813acbedc0967", size = 257329, upload-time = "2026-09-27T12:26:52.005Z" }, + { url = "https://files.pythonhosted.org/packages/fa/cb/b25c19d5bb2bd0f2e4e27fe8e2ffcae80c7a91ae181c0dc749ed60e9b1a4/coverage-7.16.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cce2bc991293f15cc4084ca116827b5900c5f34e1a54dfe83f10ab5c43162eb7", size = 255294, upload-time = "2026-09-27T12:26:53.634Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a2/892c5c5f4ad44b7b2ca009aee705191f3f268f15052244f2f9e3539b2e35/coverage-7.16.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1fa594c887365b69745f25a416806e61085dd07b94c9eae68a6e20730629b23", size = 259444, upload-time = "2026-09-27T12:26:55.243Z" }, + { url = "https://files.pythonhosted.org/packages/ed/99/a562537deba0a3e370182ae71c149be796c39d8087365f17a09188f27145/coverage-7.16.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:11e597173af1dc33d5f8a7332ada544199269a223af1ee1770ddd5e245ad0fe8", size = 255111, upload-time = "2026-09-27T12:26:56.851Z" }, + { url = "https://files.pythonhosted.org/packages/2d/20/854ec68641a9b3362ff068a32dfa41637299761617ef253791dbade6fc76/coverage-7.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e7f99698ba3a7d13988bdd984b7ebf13af4dbe2166dc8502eef90d77603b0a4", size = 256882, upload-time = "2026-09-27T12:26:58.41Z" }, + { url = "https://files.pythonhosted.org/packages/db/0d/748e4518b0ac0f9ff2687c248a6e5f8c0737306e709372632a2556f84443/coverage-7.16.2-cp313-cp313-win32.whl", hash = "sha256:f80bd9f9633eafc73d0a913ba2645c96ba58bba1befc30590f7c0fbfde59d865", size = 226045, upload-time = "2026-09-27T12:26:59.983Z" }, + { url = "https://files.pythonhosted.org/packages/31/fa/6e46edba66a183fe4d99d4bb52c173287e9b8dddabe0888d24cb8210e580/coverage-7.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:8be099e979fc42559328a21828281b4578304191ae46ed4e80a407048a82eee6", size = 226581, upload-time = "2026-09-27T12:27:01.494Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d9/9ef6845367600b336ff75d000444a0d32497d6972c833141bd39356abf68/coverage-7.16.2-cp313-cp313-win_arm64.whl", hash = "sha256:28ff850182a67d117990fa2ce5ea1032836d8c9630dae867e8bdd3bff4533b79", size = 226409, upload-time = "2026-09-27T12:27:03.116Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/577fc0803dab4155dcf808faffbdd7b159256781c0874a8586e17b81b149/coverage-7.16.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4ee546b9e4872ffa194bf07ac87bfa1202ebb824d0795dc1ef22f175545ca90a", size = 224138, upload-time = "2026-09-27T12:27:05.141Z" }, + { url = "https://files.pythonhosted.org/packages/75/9e/e3785ba3ecba2bd11efc74bfe2801ca4b78c4480b15a375648d809a59da3/coverage-7.16.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a2fac6895eb299a2e52d7bbb8fb3903502b9da8d3f5309ceb16ec40c646b58ee", size = 224445, upload-time = "2026-09-27T12:27:06.805Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d0/963ff22d3fd27117da3b8cc442f5bdc91196f783321e1a8ff0ec43476772/coverage-7.16.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:57ff3783f99d75a1e81dd56a9737eb5665e6736a5d93258ba596b6dcad8fd05b", size = 256112, upload-time = "2026-09-27T12:27:08.43Z" }, + { url = "https://files.pythonhosted.org/packages/a8/d4/a306940c81c6ae759e82fff27d20b7fdc6896e422b821f51313cce212b6c/coverage-7.16.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f37886699cb9abd29958247d718628d5bc6f39e623dff66a09e546c42a7e03", size = 258727, upload-time = "2026-09-27T12:27:09.927Z" }, + { url = "https://files.pythonhosted.org/packages/b9/a3/d3d99d93b02517087aa05bc0cf2d04d372956b849e5443e059079901429b/coverage-7.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fd7a86fdda7cb6d616d178654bd0ad6bc0f3f33c2e478aa598500a1a9e34eda", size = 259909, upload-time = "2026-09-27T12:27:11.55Z" }, + { url = "https://files.pythonhosted.org/packages/08/44/39dd599181726758dd185ae4dc0c0ab3aeabf7ca70e68e145060feeaaa16/coverage-7.16.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ac0f3b379c94acc2f7dce5f5f0b24d44fa1cc6a509717ef83dfee07450c2117c", size = 262481, upload-time = "2026-09-27T12:27:13.17Z" }, + { url = "https://files.pythonhosted.org/packages/99/e8/91ee43f6ded411460c359d7e1aebde4d6fd8f00a2e5394182d9d212eb23c/coverage-7.16.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7d0732c83746bc24123c581a85d9dd96b70ddb538c9076020aa1a041790361e9", size = 256012, upload-time = "2026-09-27T12:27:14.91Z" }, + { url = "https://files.pythonhosted.org/packages/11/8c/e9499ddc33197bd7eabcb1118ca81756fc874457b324e2b479a4804b2ad2/coverage-7.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b451c68218c150f616bc9649783ec8de76a59792c759b43aa0c9c0466a465e4", size = 257898, upload-time = "2026-09-27T12:27:16.588Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6e/c081cb5991a0afba99f9c4ad6c74a5fce9513a38ddc64e3e6680c6fed9af/coverage-7.16.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a56ac4fa5a75c7e182e8f62600cfb4aff43c5ed7356a034f3557659c3bec1d90", size = 255938, upload-time = "2026-09-27T12:27:18.19Z" }, + { url = "https://files.pythonhosted.org/packages/b2/42/1c3d819e8f9b6eb01c2fe90874d67a8882adb9507e0bbb09361ed131ea89/coverage-7.16.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4cc4f73aa3fabc36e32046d6cd2971405948d8a903636508a3d3b2f9128b3a95", size = 260368, upload-time = "2026-09-27T12:27:19.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/4f/d70eac07901fd587b6ab05e659b52afe13959992aa5113bf6cce059cc572/coverage-7.16.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:723dcdab91357159b722935b500ee8abc0a66c8c432e1e9fabf4cc7598952de8", size = 255620, upload-time = "2026-09-27T12:27:21.621Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/6d87af88317d3d9a9b18a9ca1bc1673eb516917f296e579d0d4a55cb3490/coverage-7.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5397e21a90dde0e9c6896b77ded8f0be26b66f8b22b33aed41f6043ed95d55e6", size = 257521, upload-time = "2026-09-27T12:27:23.358Z" }, + { url = "https://files.pythonhosted.org/packages/79/bb/90c2641170d2fa1a6757b3f8450ba2740197317b0ddd749e9604b914e886/coverage-7.16.2-cp314-cp314-win32.whl", hash = "sha256:848893e1d361448c113dc2f0913503522a6f7be231d0e38333d2a22d9698a011", size = 226248, upload-time = "2026-09-27T12:27:25.153Z" }, + { url = "https://files.pythonhosted.org/packages/30/08/d8d0478bb02c8eb0ae20a496fc80c40fcf4d3450bd184300d682ba2d28a6/coverage-7.16.2-cp314-cp314-win_amd64.whl", hash = "sha256:5a27b731c171e43dc8b5f32b76a5051dde2ec9b9366c87028f08a7088ebc2c7b", size = 226732, upload-time = "2026-09-27T12:27:26.907Z" }, + { url = "https://files.pythonhosted.org/packages/32/3f/0001da22155b0a8ce063ec0f7e64ecbe17b373f306e7a74435f6d6accb72/coverage-7.16.2-cp314-cp314-win_arm64.whl", hash = "sha256:1c569a9fd25505f1cd6bea90588818f90373ce90e2632e2cacf19ddbd6e14fdb", size = 226645, upload-time = "2026-09-27T12:27:28.588Z" }, + { url = "https://files.pythonhosted.org/packages/d7/85/6d8813aff9b8b8586691a9d33c43c5604f7227622574da7cdc3d91a86861/coverage-7.16.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d93db87adb6b1c1b408dce4763314b55d76a9f589e96783a84ac9e7689e48bdf", size = 224871, upload-time = "2026-09-27T12:27:30.32Z" }, + { url = "https://files.pythonhosted.org/packages/5c/70/444f3a4981ac2cda40fdcf4cc9b56a4e1a33c222abeb33e51ed3e3eb2a6b/coverage-7.16.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:aa62c85046473959c13ba9edca9dc90a77d5c1095b1ba313556314d77fe5b036", size = 225123, upload-time = "2026-09-27T12:27:32.33Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c1/980681cd7b33eb66ac835044116ef0a92e11fcc7bdd866cc89d10b1130b9/coverage-7.16.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:db76506aa5416081f3e8974ae0f7965c58ada0bb0ef7339ac86099588dbb20d3", size = 265049, upload-time = "2026-09-27T12:27:34.085Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e3/87679875c33bb2191f0f05544a1cc9adcc940fe0c35443a10f2df753dde5/coverage-7.16.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a0f2285329dac10ab08f79cb11f5692c497018e6c7c511f95e6fd63a70b8f831", size = 267717, upload-time = "2026-09-27T12:27:36.025Z" }, + { url = "https://files.pythonhosted.org/packages/76/64/5d372776d6eb523d4e93bafba2253f96984e3b18261c4cc56a50863c6d0d/coverage-7.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:382d3346d56b0eec1b793d53a4c88799c8053f516aa3a8d7c44315696954bacf", size = 270047, upload-time = "2026-09-27T12:27:37.96Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/44082ff0cbf9f97d0043f57970a71204097ec7ba606361a9fd2065393669/coverage-7.16.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:648352b94507179d82637292e7ae8802508d95f78e2f00a705a50b6c48011681", size = 271360, upload-time = "2026-09-27T12:27:39.766Z" }, + { url = "https://files.pythonhosted.org/packages/b8/17/9a215efe25b5e0ecc87c89dbe525c4a87d14d87c8c0c7316ef140a5f6f3e/coverage-7.16.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fb2bde05838fffae1a1bf75e5d411a6cac3e4e9bb97e6640fed8cd47888b33f0", size = 264736, upload-time = "2026-09-27T12:27:42.072Z" }, + { url = "https://files.pythonhosted.org/packages/a2/da/7f0a31af8e448107d4d32844bd684757f51ea907bc0c68c8fd537b2123ff/coverage-7.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6a75180829efb8ae62b4aded25be6ddca1c888d138d2d82e21d93bfbd88f41cb", size = 267600, upload-time = "2026-09-27T12:27:43.85Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/3bfecbd3366b775bacdcb3330394d356cf384b5d8f5b2146ac4b14b252b5/coverage-7.16.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:99704f73721e23859112072d522076e11c31744fc96b5652e5dd2018aa4359f7", size = 264767, upload-time = "2026-09-27T12:27:45.768Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3f/5d62163732d87e4a0c4710a0eab30f0fd6a2d480112abe2029f014fe8c9d/coverage-7.16.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:29309ccc86b7f33df7db12813c299f215bbbc470ed6292d0bedd63ffae1ebf64", size = 269026, upload-time = "2026-09-27T12:27:47.787Z" }, + { url = "https://files.pythonhosted.org/packages/49/4d/8e4579f225426535085a9be371cc75e3b026d058d679b80affbdfb4c3ef0/coverage-7.16.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:30c1b65d529e46569899fadca59e4a87c1faf2886923f1307ba61e654d4f3c20", size = 264147, upload-time = "2026-09-27T12:27:49.681Z" }, + { url = "https://files.pythonhosted.org/packages/d1/36/ef1f77e2c3f7bb03c2b13b9a2006f88700fdd75535ef158d70049f425c1c/coverage-7.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dcf4bc2aab4e16b1c4c0c2005918f23a7dd5d7821ddae82caed9e3342dc2fcce", size = 266376, upload-time = "2026-09-27T12:27:51.551Z" }, + { url = "https://files.pythonhosted.org/packages/be/79/0cb2bf4428830dec971c718c2c841a039c084415c99e67281f5a72841aab/coverage-7.16.2-cp314-cp314t-win32.whl", hash = "sha256:a9cd3de0a5bfe7b0e21ee10e1a14e3d61bf52efc88217ab1d95d6ace6970bd46", size = 226585, upload-time = "2026-09-27T12:27:53.945Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f9/da17121c16667fd84998e972200ae226a41540f6ea4795776c6d99e8976f/coverage-7.16.2-cp314-cp314t-win_amd64.whl", hash = "sha256:611a44e5229a59d7483ce830160e1a0e85f700562c7a5651c7c63fb8f4eb528c", size = 227378, upload-time = "2026-09-27T12:27:55.778Z" }, + { url = "https://files.pythonhosted.org/packages/74/89/01179c62d1b7e6e33bd5001566b02d7f778cf33d3ec1e81e94ca170c517f/coverage-7.16.2-cp314-cp314t-win_arm64.whl", hash = "sha256:22957cef43ce038641de78ba995de7568d2d6a37c6ddbf7fa0fd7d1ae2344d91", size = 227064, upload-time = "2026-09-27T12:27:57.496Z" }, + { url = "https://files.pythonhosted.org/packages/4c/57/52935003c3f627ba6e5203d7179aad32448c10899663a30336aba8e81a2c/coverage-7.16.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:414c26dfdb96aac2d570a54e03008f001e32eb2d413705365503648c6bd361d8", size = 224140, upload-time = "2026-09-27T12:27:59.343Z" }, + { url = "https://files.pythonhosted.org/packages/31/38/df472520f3e626524d7e2fc9d6da0afe7895a2f1489d36b48af8ca40bb41/coverage-7.16.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:00d3eb96e9988c45f50cccd1f1496571ac5c1f91386ac02c4d55516eeda19a24", size = 224449, upload-time = "2026-09-27T12:28:01.299Z" }, + { url = "https://files.pythonhosted.org/packages/0c/aa/3be084d5b82e63ccdad4ed751e4acbae294673573e30481d29f8b7402eec/coverage-7.16.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4dbbd1155ca46e6e0b6b89d204428c56ef6a459af21333f365d135a2820e5a09", size = 256060, upload-time = "2026-09-27T12:28:03.185Z" }, + { url = "https://files.pythonhosted.org/packages/de/29/48fca82a7ebf7ff7b2e35019cc9537e7f65e4d2aa1215cc5a8792c989251/coverage-7.16.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8fc15cc8d0d06e873c00ef18e1372d605f9aaf3de27d8c24e50782e75bc8b843", size = 259106, upload-time = "2026-09-27T12:28:05.15Z" }, + { url = "https://files.pythonhosted.org/packages/06/3d/b2d5986f2dd53fe201aa1be2e4ab204fa1aed5101e67c0dbbb419b850aee/coverage-7.16.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c6afdd69218202bc1758c9a14b86b8cf1084f37ed2ca143e567a103772b16d1", size = 260674, upload-time = "2026-09-27T12:28:06.868Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7e/b50160be3506ead12e6480d14279af7f0f17627694300a2d1fd2c42d2ff5/coverage-7.16.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aba5c63b7afdc749cc9eae943d5b868cba2b261a176378fa1c5a30bc8bc89982", size = 263139, upload-time = "2026-09-27T12:28:08.771Z" }, + { url = "https://files.pythonhosted.org/packages/14/5e/7c805ac9a32606de1399bd7e9bd375aa2f973dc61b12680d9e6403c2e891/coverage-7.16.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9174f0af24e5eff248b9dbfe76ec5275a3d19d37edbc2810543f12cf97347a34", size = 256591, upload-time = "2026-09-27T12:28:10.842Z" }, + { url = "https://files.pythonhosted.org/packages/ab/9e/76f1ed129a2daf658a3ea17122824cf2e3b91fea0460d8d3664fc5a61018/coverage-7.16.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:80e9fdb4c3d926b6ba721d4bf7435bdb869c3527ae7803290361d0ab73db13b6", size = 258708, upload-time = "2026-09-27T12:28:12.962Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b7/8d62e75f48b527619239a65294f842d4b7fd02a0839d43ae1de80184e2df/coverage-7.16.2-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:7b3bce4a0d05401d70b7d0d5ca783e686bc9d30e81dbd7d980d532609bf809e4", size = 256562, upload-time = "2026-09-27T12:28:14.934Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8d/0a15f95c3afb78e947c52644786ba4bc9de259905687dd720d5e6fae2e76/coverage-7.16.2-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:44f21e407b278efdfc1ee5e481e00518bd1d500310a30a5fbf2bcbedfef4aaf0", size = 261077, upload-time = "2026-09-27T12:28:17.215Z" }, + { url = "https://files.pythonhosted.org/packages/25/00/88389987305a47d732866c07c8a500000ab574df9505e3114ac69c8d027f/coverage-7.16.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:59c3926585e1cd1f2190f4b2ac9014de1bbeaf0d5d0587b0dc6b0aa90d17896a", size = 256034, upload-time = "2026-09-27T12:28:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/92/02/34d079d4952ad461bde037d353f9a6e037a7edc45fe0f9ee8781ff73f028/coverage-7.16.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:066429634299e14dd2d511e1e85f8f9cecc500781f6b41907c0dd6f1baea7e63", size = 258034, upload-time = "2026-09-27T12:28:21.242Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d8/3e59a62879285b464ec1b10fd824fbc1af9ce66e842cd39974f80a0becc4/coverage-7.16.2-cp315-cp315-win32.whl", hash = "sha256:893ea9cf86cb8d2546812ac93d973aaf2ee1fb45110a873b014214fd23e3725e", size = 226252, upload-time = "2026-09-27T12:28:23.102Z" }, + { url = "https://files.pythonhosted.org/packages/f4/e1/128026e1b2836e9ad6b219207ba9edf1c5e0088a7869e23088aee7fbbe7a/coverage-7.16.2-cp315-cp315-win_amd64.whl", hash = "sha256:01c6908bc613b420c26c818fe948e1b97dfd041a53c98b01c63bd8321f5c9aae", size = 226728, upload-time = "2026-09-27T12:28:25.21Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f4/c9fa8e7cf525ca7748ac52b0ee89331d13fe09808e45c679830708782e90/coverage-7.16.2-cp315-cp315-win_arm64.whl", hash = "sha256:967d72c835d7a8cf0af99ec813a2d06e3db6df706402f1fe85b31b437645f495", size = 226648, upload-time = "2026-09-27T12:28:27.136Z" }, + { url = "https://files.pythonhosted.org/packages/a2/13/e96b045447a856666f36f9c653e2a80bdaa732aaaf72412b19aa2c26a473/coverage-7.16.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:98d9c97f51b334b0adce7b964442a9af33c1a00c6ac856984cc5dc8d18f81c75", size = 224858, upload-time = "2026-09-27T12:28:29.169Z" }, + { url = "https://files.pythonhosted.org/packages/23/90/087f6ad1bd3df059632ca3407a4e6552ed1053ee35354de0a771acf35423/coverage-7.16.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:3e861f1071dcc2fec1e88bef0920f6b1eaa66a143555b4f8ab79ba2b0f30ef55", size = 225139, upload-time = "2026-09-27T12:28:31.131Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8e/285dcef0184358044e7cbcd810a1bdc9566bc620f54702d605477155df4a/coverage-7.16.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb9d92ecfe2d5b494367c67f7446f8b75b68d8d0c8cf3bc3e6997478be25d9e2", size = 265090, upload-time = "2026-09-27T12:28:33.04Z" }, + { url = "https://files.pythonhosted.org/packages/06/b2/cc83f3a6e5789a4e89059c69555bc641c2efcde568405a1c06fc702951ab/coverage-7.16.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb57acff4a74246ae513c142d4b36e18c389c3aed8661914a53f7cd0071031b2", size = 268254, upload-time = "2026-09-27T12:28:35.135Z" }, + { url = "https://files.pythonhosted.org/packages/ac/41/f548c19530f5d66ac6e3c92bbcbc49da7261de3a458b9f3e54a3efb1a0b2/coverage-7.16.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:444889f7f66b74e4455c0a97e0e166dd41177f1dca8c0239a47cff25e05ba7e1", size = 270695, upload-time = "2026-09-27T12:28:36.959Z" }, + { url = "https://files.pythonhosted.org/packages/94/61/4dc27cf82ef96434d2874110ad0cc10ea4621025705dc5049862bd3bd181/coverage-7.16.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a740ea6f083c6db7b926534d159508f80ba275ab35e722522de0d18d0f56e55f", size = 271853, upload-time = "2026-09-27T12:28:38.821Z" }, + { url = "https://files.pythonhosted.org/packages/38/29/bf8072b1b8bd5f2de8b21460a404460b1a2b97e80a9464c78ec0271f6199/coverage-7.16.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8e209591f7c41ae4a9171335cf6156afda0b21de73b02f73f5aa95b2d5fbb08d", size = 265644, upload-time = "2026-09-27T12:28:40.815Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2f/0aecb8721be5cdeb8afd9d6d9f6b463f074e4d8d37f00f4c42442522709f/coverage-7.16.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:396bb16e04ce04efbb3df91456ae4e3da918e69ecdf67fb711b0a0fdf35ccce0", size = 268530, upload-time = "2026-09-27T12:28:42.725Z" }, + { url = "https://files.pythonhosted.org/packages/ab/0b/92b4b7628268ee711249958e68fc0328779bd3d9a7ab4715379465aedb84/coverage-7.16.2-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9cdf19874e0d247f32f03609200370343c3c7aa260b191d8c2bb251d36198283", size = 265154, upload-time = "2026-09-27T12:28:44.684Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/41c95c1ab29b3dcd357cd1227181d1c98185632aca41ce670ce671b23a43/coverage-7.16.2-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:fd3d72233eb8b48acc94fa57d44e2d32ce8e7abed02882ccb6d855ccc4ed33ec", size = 269799, upload-time = "2026-09-27T12:28:46.672Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ebeb259aa5362b033a137b86d7274ff4b109d59be8cc9913889b783bf75a/coverage-7.16.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:bb4ffe96aa663cee727659db5a2afeb38c95f8677b747d447b90d6d4874ea2c5", size = 265196, upload-time = "2026-09-27T12:28:48.996Z" }, + { url = "https://files.pythonhosted.org/packages/b2/18/8437620f90d023680a072eee02f968055f3658bbfb7d386d0ea34cfb7f30/coverage-7.16.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dba2edfb054f6d4a08df9d1637c39a5aa3865bca6617c13c86be21e45658a59c", size = 267266, upload-time = "2026-09-27T12:28:51.361Z" }, + { url = "https://files.pythonhosted.org/packages/28/6c/f08e8ee4293e6434035424180bef4d45e028e8ecc006c61bf9453e74405e/coverage-7.16.2-cp315-cp315t-win32.whl", hash = "sha256:251aed777c47c77aba047096d4542889db089227655711dfc2b9c54ef0e15e35", size = 226583, upload-time = "2026-09-27T12:28:53.33Z" }, + { url = "https://files.pythonhosted.org/packages/f7/fd/3f939c2847f4a72c20cff8b1ac33da78ea91a2d38d9b43336e60db719103/coverage-7.16.2-cp315-cp315t-win_amd64.whl", hash = "sha256:2aca0bdfa9e91621d5b09d815357bf63def4fc0e9cb66da67bf2cf93f3b1a6f5", size = 227374, upload-time = "2026-09-27T12:28:55.158Z" }, + { url = "https://files.pythonhosted.org/packages/5a/35/b98cdc354c952402132e675a87f2cc3227fb68f959c84aaa491fbe15933d/coverage-7.16.2-cp315-cp315t-win_arm64.whl", hash = "sha256:b88841e654f09732804809e435b3e005a929ffd9998b872b7b213957b8759cb8", size = 227064, upload-time = "2026-09-27T12:28:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0c/7a64e1ac90541a8edf50daef0914848011fb057a5bf55284a4811e21939a/coverage-7.16.2-py3-none-any.whl", hash = "sha256:11d28e9123a9156cb405d8d27b44256c9a58fb5decc2073a8f17862057e3aa0f", size = 215754, upload-time = "2026-09-27T12:28:59.075Z" }, +] + +[[package]] +name = "coverage" +version = "7.17.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/78/4a/740c3134227763ebc30a226d7f2f29b25e35898d162dd8b2dd792be83fb2/coverage-7.17.2.tar.gz", hash = "sha256:d05a1bef52f4d7f1b484a1cf46a8929e50963f910e51396373f06cd3b6248f38", size = 996756, upload-time = "2026-11-04T19:52:34.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/9d/e3db9071ebeda48e47e5be0fd0404219954e7888f64e7c37df324a79dc3a/coverage-7.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3f49ac9c9e5fd4b450677eb44f4b14ea9ab71950b2896888c9f6a22d5cd1773", size = 227732, upload-time = "2026-11-04T19:49:22.217Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/260786d936940102052a51cb166f80f4a98e67fb6142aa050f1af48a2385/coverage-7.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d3d9d79a2b6cdcdde5a1ab1ae89953faea5cb28c0eea17a413ad997bb3cc8a1", size = 228154, upload-time = "2026-11-04T19:49:25.066Z" }, + { url = "https://files.pythonhosted.org/packages/be/80/f5176b7f6aa9e05e37e1bb5eae473c5d015485c36206934f53b2b2795076/coverage-7.17.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b71aeaa4440cbece5498acda30838685793d3bb960aff87ec97fbb546715a5f", size = 260181, upload-time = "2026-11-04T19:49:26.523Z" }, + { url = "https://files.pythonhosted.org/packages/03/9f/4669ca11ce1932cc18fce766ead327ceb41a92055608c179b137067e2805/coverage-7.17.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:01303d9e68b6e6bdc5459b54e3bd0e3539c94c25b50ac7362b7cbcacbc815fbc", size = 262404, upload-time = "2026-11-04T19:49:28.113Z" }, + { url = "https://files.pythonhosted.org/packages/ae/99/efefaa02a4b2876ffa4a389c81a8074c888442b12c64046b448718fa5590/coverage-7.17.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1947bd9d82e405c892c50b0d8dc0c1dea5df2a962129ca238c500a6bcf887509", size = 264497, upload-time = "2026-11-04T19:49:29.773Z" }, + { url = "https://files.pythonhosted.org/packages/19/2f/a565096e5dcf8c511332d397d712b056f06091ab3efc10adb5a4d22b0cc2/coverage-7.17.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2cd1a7472a972fed01c89589b1560846e32e942f8329a515c4059d4521c6579a", size = 266507, upload-time = "2026-11-04T19:49:31.507Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/87f9b02f97179f2c76fa5724e0ce00b9db2bbe16b7e21d89c8fc1f3a77d9/coverage-7.17.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b7fe217808239437b8509b869fde2e0cac48776a68c626bd6b15411b087e3de", size = 260297, upload-time = "2026-11-04T19:49:33.002Z" }, + { url = "https://files.pythonhosted.org/packages/db/12/70733b58d508bc80dc983408b06f1fd0c59fde0eb1379dd9473b3e290856/coverage-7.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:565f1f1a9632d82678525d668a10fe070a285603a9d3581505b0e1a66624ad50", size = 262196, upload-time = "2026-11-04T19:49:34.412Z" }, + { url = "https://files.pythonhosted.org/packages/33/de/0495b108a3091f2389800455b615939867a3ac19f24f0fc186f4d066b4f3/coverage-7.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:189cdffb3ebc53d6133de67952dac7cc88d1fcaff5480623fb00ef0bf1cef918", size = 260212, upload-time = "2026-11-04T19:49:38.462Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ff/39cda949e0e9bbc16620d8b843fd78929a46edda2566e54b9597bda61625/coverage-7.17.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f25209696631752468a25e5954fe119503468b3b29b6fa2c621ae766153c6f50", size = 264136, upload-time = "2026-11-04T19:49:40.266Z" }, + { url = "https://files.pythonhosted.org/packages/85/e5/0eb1bbfc80e1a03318d81fc044dc1c4fc075921f7f618298d42837dafa66/coverage-7.17.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:20faaaf3a49601afbcd6d46c17a594b0ae62631498def44e5eed94789962ea18", size = 259738, upload-time = "2026-11-04T19:49:41.79Z" }, + { url = "https://files.pythonhosted.org/packages/a7/18/098ea8ee450922eec7ab48a35b4a7fd6a49c4a97416d1e4cf66092c24dc4/coverage-7.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7c123b6bbc183a14372036769f033098026aa75fc8fba37d028b0b772356e90b", size = 260937, upload-time = "2026-11-04T19:49:43.467Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/a6ee041c4bc8b6ec7bc9f417daf89e3ee702182a48d573267ccaaed17c64/coverage-7.17.2-cp311-cp311-win32.whl", hash = "sha256:1c3146ef9d14c011517422489784a7ac344477e9cdacb05d9529e912721cd5d8", size = 229599, upload-time = "2026-11-04T19:49:45.014Z" }, + { url = "https://files.pythonhosted.org/packages/5a/df/7f077fb2e71b1d73fe8285be7c6ba43dc085f9eaa2384d4e8e55560073a3/coverage-7.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:51c6a60e63ea993113ea5cd24a401cc2faddc54aa7acfddaffa5d3b8c0161b9d", size = 230202, upload-time = "2026-11-04T19:49:46.548Z" }, + { url = "https://files.pythonhosted.org/packages/90/56/89dcd5907d0cc04b07746e62e1cb896c0f694e5af40aa0c980458825c6c5/coverage-7.17.2-cp311-cp311-win_arm64.whl", hash = "sha256:b08ee3ef53e80e5a50c68b3d9764363e373acba540e1b58daee0a9a7f23fdfb2", size = 230110, upload-time = "2026-11-04T19:49:48.005Z" }, + { url = "https://files.pythonhosted.org/packages/22/6a/a51808ac625c457326fb37b174d35e1badb1324c6def5d9668089c4b14ef/coverage-7.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:289a24443352713745f79c1b6aae4e99ced2f5ff3e86bb630c33c631d50f217c", size = 227838, upload-time = "2026-11-04T19:49:49.546Z" }, + { url = "https://files.pythonhosted.org/packages/31/24/f32ec1a5ef947294252e77c5d40ac0903ba1de4c92638edfcca8629c067f/coverage-7.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da4b0aef427eb96e422b444e5c2c3d38768fedb9141346294c38717a8b15b416", size = 228060, upload-time = "2026-11-04T19:49:51.221Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/3097367327c69e65521b9d9bf8b9d2ddecefa8c21bd08e17bc068bd3db93/coverage-7.17.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90c6c5f93606b8803bd7f8a0b2ae05df7e35da4504a1c53cfe9294a1731e1f49", size = 260359, upload-time = "2026-11-04T19:49:52.756Z" }, + { url = "https://files.pythonhosted.org/packages/73/65/774fc4ec86807b6ee66b46465bc49b34bb6d291e9bff6ddfa9708ccb4053/coverage-7.17.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36314997533266bde3d07765289674304d61bfe49165bb1b4fa9be12cb4ba0d1", size = 263687, upload-time = "2026-11-04T19:49:54.351Z" }, + { url = "https://files.pythonhosted.org/packages/d8/72/17b5df5d8211e1880169b93197ba98b21b30ccf8f116284536113b7582c5/coverage-7.17.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1682d98ded28524bc6853cc4e7c65175b3e917f38b2e2ddebb28cafd8ab4de74", size = 264220, upload-time = "2026-11-04T19:49:56.172Z" }, + { url = "https://files.pythonhosted.org/packages/de/0c/603ba7fff1aa4c8064fdf4a1fff6d9fb51c72a5828429dfe858be48e38b3/coverage-7.17.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8eab0a5cff8dbc563d1416a6e079ba3c146712e47167c022a5df5ea01bd76df9", size = 266498, upload-time = "2026-11-04T19:49:57.718Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ca/f2ae6e47e18b35a0dbddb9d8f51be34a6d66b41120f2e0f7363ccc0812f4/coverage-7.17.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:635337a96895f2ffcfe2aa7554468a988f468fb67a8007c1a65e95148c1855a9", size = 260591, upload-time = "2026-11-04T19:49:59.159Z" }, + { url = "https://files.pythonhosted.org/packages/b2/4b/b09540a46395a6a1052ad114bf366e383c64e223374f4911a85620667930/coverage-7.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79cd2d9742419b6d1aaeb1f3fb77a8e6a01c09ebe5bd1b27011752410a5e3781", size = 262107, upload-time = "2026-11-04T19:50:00.607Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c4/42783cb43481fafafffbc7be7fe6026258ffd30a575707733109c9ef0679/coverage-7.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b0209fff1ca92f1e2e344d54b5b3def74efbdb46ba807cd81976a5e9e569cac3", size = 260271, upload-time = "2026-11-04T19:50:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/37/07/192ee81dd1ec4da58ae0f03fdd0045d2b821007e4194f7704d6e495733f0/coverage-7.17.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a3984fb8c2407721f9bc05573fc9a770752efc756d55068ccbde1fe53e42233", size = 264320, upload-time = "2026-11-04T19:50:03.613Z" }, + { url = "https://files.pythonhosted.org/packages/dd/94/8d983310d98299a19ba23efbb324492f4bf8d8668d610eacd304e451f138/coverage-7.17.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:698ca1575135215be25ac9c5d14f0b5455135d29602b7ee81e511d64a0435c85", size = 259774, upload-time = "2026-11-04T19:50:05.363Z" }, + { url = "https://files.pythonhosted.org/packages/1a/85/6c923d71c76566c6c65a2af104d39d69d44ae708a700de017e6c56fedfc6/coverage-7.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:686b6f8f9e57afc3a44096b17ee8de7e11e0a12f2da819e25047af1eb3b0fc6f", size = 262258, upload-time = "2026-11-04T19:50:06.807Z" }, + { url = "https://files.pythonhosted.org/packages/54/f9/fd3c37b09891bfdfece486a6145345bf2b32332045a678afd293a7f2f05d/coverage-7.17.2-cp312-cp312-win32.whl", hash = "sha256:ee083412170118074bcc74361fcbc7104eb6d0b7c9c75eb3b84a4c324410c531", size = 229659, upload-time = "2026-11-04T19:50:08.184Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b8/4d395163c9bfae0e991abbd14dea9c9bbb4ade904e5f297cbfb7b8150216/coverage-7.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:e269dddf5ebac20dfcc5a2a459528a9d221ab828cb31dfdf19c7cfacdbcee673", size = 230240, upload-time = "2026-11-04T19:50:09.715Z" }, + { url = "https://files.pythonhosted.org/packages/a3/02/1024b8d4f60a9ecc0ee41a6000425caa63855d63e51da84c93c0b1dff894/coverage-7.17.2-cp312-cp312-win_arm64.whl", hash = "sha256:bac3c79c31f45274117ed64c65efeaed85665668aeba7d809c7c38801ada6ed4", size = 230043, upload-time = "2026-11-04T19:50:11.187Z" }, + { url = "https://files.pythonhosted.org/packages/e0/32/d750d7b10b0a9436349b3fb78491c5bb8f9b524f01ae6994d935d77d6ab8/coverage-7.17.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:882cade9b6dc43b2bc819bb444c0a13e4ed1f669a50e0de74adeec54fce486f3", size = 228978, upload-time = "2026-11-04T19:50:12.782Z" }, + { url = "https://files.pythonhosted.org/packages/63/33/468f8c63aff5deef81bfb8e44771f68ebba328c0aa46c8a6ff27dedd0fda/coverage-7.17.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:f5a875e92f10c6463027d8151459f2d92b4c13527402ed1301eaddabd79d8cf5", size = 229048, upload-time = "2026-11-04T19:50:14.255Z" }, + { url = "https://files.pythonhosted.org/packages/10/91/c8723e48c1497a3c56a3194973a416a6f0fb24560425685f960888e03b20/coverage-7.17.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e8da5e158d13556457f69245deacf6d8401a2c9c31afaddd18bea691e849b5fd", size = 227293, upload-time = "2026-11-04T19:50:15.857Z" }, + { url = "https://files.pythonhosted.org/packages/61/4f/2f57b3dc6ef31e12a99e6dcb820c4ab04d973ea1466f73c856bf920a740c/coverage-7.17.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:42133a2647982db7968db138c0f4a57f5836101d8d504600a87ef8ebfa22a4cf", size = 227599, upload-time = "2026-11-04T19:50:17.418Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fa/967e829238dda21c4bce17aef2f739187678c8e6e6fc880a8454945a7433/coverage-7.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e6c89d63d43738712ce1febddb7758ffeb56de08f1b2be68aeb9a9c7fee712c7", size = 227727, upload-time = "2026-11-04T19:50:18.924Z" }, + { url = "https://files.pythonhosted.org/packages/9d/df/803ba08e8937759dc3f18b93ece82f81b2fb446e3846e4d60a06a4e77647/coverage-7.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f94d465ee00ebe694ca866d66bbe45fa04618b1afcb377de934694f624fa7cf6", size = 227987, upload-time = "2026-11-04T19:50:20.867Z" }, + { url = "https://files.pythonhosted.org/packages/4a/12/92dcd9206e7bb817af42b8663cfeefdbb2c5a6c92cc7069d43e6892bbdeb/coverage-7.17.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7ce0acb27a823e8e5169282f538f0e182854cc8b472b88d45456f09b6fe364fd", size = 259154, upload-time = "2026-11-04T19:50:22.328Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/6b028df8050caef4ce0df177686c92dba16ab7cf004040e5518f95b57021/coverage-7.17.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70daa8e4b49541b51c0baeba0d802b0d14d3214daeebc071b3db0b0c7dc84646", size = 262065, upload-time = "2026-11-04T19:50:23.745Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/429b500937ac446e36fcce517d040b0b8bd04a4bfa087bdf864175553fea/coverage-7.17.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:95bd8dc5b356f78f10a967ec50f8902d56f24c623753ac9fce0aba9d6213d5c5", size = 262997, upload-time = "2026-11-04T19:50:25.223Z" }, + { url = "https://files.pythonhosted.org/packages/c1/91/263224a77de3ab1ee42d110b09f1ac9a02638794d3e002a859822638256a/coverage-7.17.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a5565a9dd715a5a94ffcf9a0ad4b6b1501b5441fd67d660e78742adff5d37a0a", size = 265062, upload-time = "2026-11-04T19:50:26.829Z" }, + { url = "https://files.pythonhosted.org/packages/0f/44/99b72f2961f4ba54ac14ccb016f3b212cc9a9dfda4c37e07a208efe01f9f/coverage-7.17.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:091eba4248d31b03828fc657c96d14adfaa8754f5aec7b73298b82894858db06", size = 259201, upload-time = "2026-11-04T19:50:28.386Z" }, + { url = "https://files.pythonhosted.org/packages/be/a7/e97f63a7c345077e4e398d15e3aa5068edc28cb061f997f71fd0e32703e1/coverage-7.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2742952851c7f88b1484385f68c42c6388153520efb40ff09fc852c3783aa5c", size = 261155, upload-time = "2026-11-04T19:50:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/16/0c/73e470df7db83962e2cdd6c1a7ca7dc8f8632c17a6d378d6d9124ff8bc6b/coverage-7.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1da1999e3c371edd669b48a1029c86dc0474cdc6ff152d58cb3f15cb3337af72", size = 259077, upload-time = "2026-11-04T19:50:31.437Z" }, + { url = "https://files.pythonhosted.org/packages/50/43/db8bc1b92d933b0b5f57d0e3377594c4f78ea31e84e8fbc22627fd17ecf4/coverage-7.17.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7804a6022fcd5e1d4de4dc676e985d6aea0119249e88c041e943f3cd07953527", size = 263172, upload-time = "2026-11-04T19:50:33.014Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/3e9e5cc9ccc911f8fb9595053da1ce58b9de8941b27e2f2f7058cdfc87f1/coverage-7.17.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6bdf3821fd0740b5369b9ab58b4e9d0d68a2c5c75a7ee0cd3beb624f9f15e4fa", size = 258926, upload-time = "2026-11-04T19:50:34.513Z" }, + { url = "https://files.pythonhosted.org/packages/72/9f/0d67883bf55adf50424728937cbee54d76fa137817e6bb20f97061d01816/coverage-7.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09c37817768bb71fe3e70b6ce5e6ac1a0cd7398536c2f30a4460218c7b66e93", size = 261006, upload-time = "2026-11-04T19:50:35.956Z" }, + { url = "https://files.pythonhosted.org/packages/a8/22/71965edc14736073b55a40e521791d9233d5c3194fa6887a94f4431fbbcc/coverage-7.17.2-cp313-cp313-win32.whl", hash = "sha256:c10f2a2748dd084e44811bb9b26979763af9003115d9bf93c1da69011944719f", size = 229602, upload-time = "2026-11-04T19:50:37.564Z" }, + { url = "https://files.pythonhosted.org/packages/47/0d/715cb37f51f908a6f6f33fe8c6fc95ab638b17cb014b1650daae0893c160/coverage-7.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:8dc07ed34bcb206f69d5b54fd3874f7bf39ac941c9f7aec2b53b29874fcfd77a", size = 230329, upload-time = "2026-11-04T19:50:38.988Z" }, + { url = "https://files.pythonhosted.org/packages/39/f2/a5099bdc2a530f29c818a1c7e706669451c10e5f0eb62dcff76ef3568668/coverage-7.17.2-cp313-cp313-win_arm64.whl", hash = "sha256:bb74283661a239fdaf738ac0b07db4ed9b476c7532d3e713abf0f6a072781e60", size = 230055, upload-time = "2026-11-04T19:50:40.479Z" }, + { url = "https://files.pythonhosted.org/packages/96/f9/30d6ff7a66e1519d79940fdee542ead9a63601de60be5cc9fe62c74bab58/coverage-7.17.2-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:0439f77deb5c023003533ae8909a9aa1e174c847d655327035b1e542c7c8a54e", size = 229094, upload-time = "2026-11-04T19:50:42.193Z" }, + { url = "https://files.pythonhosted.org/packages/1a/db/82c722c37dce98fe1abc953768903752bdd415dfe8cfeb7b744a7236a735/coverage-7.17.2-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e86a66f96d80c770daeed78394c1d4f6e8d03e5a3e08c5dab5275d56414f296a", size = 229213, upload-time = "2026-11-04T19:50:43.779Z" }, + { url = "https://files.pythonhosted.org/packages/7d/07/71b68d870975776cb185f11cd80e053b01739f3646488fe8b9721744f56a/coverage-7.17.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:87b30f3daa0bedda6b770b303410a6def007844772aaa143dc52675b3d95cd8a", size = 227374, upload-time = "2026-11-04T19:50:45.258Z" }, + { url = "https://files.pythonhosted.org/packages/aa/23/ed60f79576175e108c77df2bdc116f46e367aab108542ddeecaab752f0f6/coverage-7.17.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:3f219efbc3ec71c377e278dee201633e1d4ad0d2d9bdaddfd9489b2869d584c4", size = 227676, upload-time = "2026-11-04T19:50:47.095Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/60a51850d3ff659b82cc211bc98fa126aab2ac9b81bd4ed6abc3e3720a09/coverage-7.17.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:36248fb6c03341501169539bcab35eb3a59159100be1246fad3c14cb73c17685", size = 227915, upload-time = "2026-11-04T19:50:48.581Z" }, + { url = "https://files.pythonhosted.org/packages/77/80/a9539c15076fb9b95d8cbdb7f0caec90857d80bddca3eeca439f5e2e64e2/coverage-7.17.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2a6281011cf85699763bf88ab55e0774f6696f87b390f39152b14ecefadccb59", size = 228072, upload-time = "2026-11-04T19:50:50.073Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3b/0a84b6032511834105fd4f8a8b4e76927783cea03dc4796032d5c4bfec8b/coverage-7.17.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3dfefb1ec16ca1a01e5da40ba162e268ced8bd3624b9eba4e9384ebfa14af7ec", size = 259991, upload-time = "2026-11-04T19:50:51.665Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e4/a3e6b8bcc219b00ea91e36f071c2659b253e5d6e1b258c31eb4524231718/coverage-7.17.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:34e5c7a2da36804c6e6f10b368eae01ad56f5a4ac5ecce7297300a3d46fdef2a", size = 262995, upload-time = "2026-11-04T19:50:53.236Z" }, + { url = "https://files.pythonhosted.org/packages/05/54/a666b65c5bb145d8796b7902ff1c0f60600a6c2a08949e20f2fb7af15de3/coverage-7.17.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6bf531deaebf8f69ba6015bba3fb817cab6694dfb69ac2a708889dc59a3926e", size = 263980, upload-time = "2026-11-04T19:50:55.366Z" }, + { url = "https://files.pythonhosted.org/packages/a6/73/ac9d06da9e0c143b874a68b17d8dfeedc45acdf1bdcee5b22a42bece098e/coverage-7.17.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:88d4dc83b6ad09a2134cad9d8ec780b274b17ac7dfba8c2c6e92e3bc468fe85d", size = 266457, upload-time = "2026-11-04T19:50:57.04Z" }, + { url = "https://files.pythonhosted.org/packages/87/b1/8a500e16cf787383b00b5a86eb50a490b4d8ebde505f92e1aaca515afc0a/coverage-7.17.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dc17707fd463b4dfab53792e41d50a23316ac6b98539007aa2388a1d44a8535", size = 259768, upload-time = "2026-11-04T19:50:58.508Z" }, + { url = "https://files.pythonhosted.org/packages/f7/be/642762c5973b89e2b8bf66c95778bdd80e96ceea442ae8fa753732597b73/coverage-7.17.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:df0a34d445eea64f3766e9ca2ff57bcde600a89c7c0330434a7a37e8d6e2549f", size = 261831, upload-time = "2026-11-04T19:51:00.25Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b1/f7d6c0ef0a4aecf3f91f19df4ab3fa131fec8c89f554187511716584c9f5/coverage-7.17.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c5a98aff9eb681312ed403ecb7beb5f15fd25d6e0acc2554b9ca4b4d6f0b6705", size = 259871, upload-time = "2026-11-04T19:51:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/c0/3ae087d52cfa5119ad9ecbd193c40089924d1101cef7333f15a86dccb444/coverage-7.17.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7565c17092f2f4021abbe3f5007300c224f362364a388f5ddad9216dab44b611", size = 264169, upload-time = "2026-11-04T19:51:03.365Z" }, + { url = "https://files.pythonhosted.org/packages/c5/95/d324108991f892cb361ef8b2f91bd4cda9c2563e0954247409e9e6b911ab/coverage-7.17.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:555257578e20a46020c27b2af4ef99059cd906aa9b8e163baf9da9a5afb65a64", size = 259502, upload-time = "2026-11-04T19:51:04.91Z" }, + { url = "https://files.pythonhosted.org/packages/14/b5/13a62eac66cecbaaa098bb753a3693b44454a4d21d53db4f1a0953f35f0b/coverage-7.17.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:460ba2139866e758c3a8e9d9422fdc889942bcbc5ccaf710dc4fbd8707d14aa8", size = 261613, upload-time = "2026-11-04T19:51:06.414Z" }, + { url = "https://files.pythonhosted.org/packages/53/13/9ea31cfa72434449b9a98666760169d1e0ab31b3f6cdb12e68149106668e/coverage-7.17.2-cp314-cp314-win32.whl", hash = "sha256:e72cf9556f799c3fce7597c1bd7dbfc9104befbd08088deb3b9ae9fd98117c02", size = 229842, upload-time = "2026-11-04T19:51:08.61Z" }, + { url = "https://files.pythonhosted.org/packages/f6/cc/aa8f34b014979c973740f5a5ed6861ecfc6009082c70e72030ca1519e0f8/coverage-7.17.2-cp314-cp314-win_amd64.whl", hash = "sha256:205f9e329d9d5e47c320f8712326a01620689edae50531cc8934511e2c5e8f7e", size = 230474, upload-time = "2026-11-04T19:51:10.167Z" }, + { url = "https://files.pythonhosted.org/packages/49/64/16afaa19b3886dc157c3c5ebace5241b84b96a005cb28545584e66b80895/coverage-7.17.2-cp314-cp314-win_arm64.whl", hash = "sha256:a5909dd087006c70c128297312ba67d877123e871100645d9bd73ae021a4d01d", size = 230325, upload-time = "2026-11-04T19:51:11.63Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/acc865e1411c8ebc5c5d230367f3bfed5c605ed54bb41b84fc58f4e7f137/coverage-7.17.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:674bcd023a3743666d4d7a15238e57a00c968553780a36c80d3779fc410b912c", size = 228384, upload-time = "2026-11-04T19:51:13.729Z" }, + { url = "https://files.pythonhosted.org/packages/10/98/02c37c6a928ccfc84b7a43eda44d96d3b1ccbc93989c178754b7e67e7650/coverage-7.17.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ba45dc072bfbf1798e68ec81379b73473fc912174bff9f91924d77711a1d2b30", size = 228758, upload-time = "2026-11-04T19:51:15.394Z" }, + { url = "https://files.pythonhosted.org/packages/28/ab/d503b0aaebb92b171260469463915af5e16a64457b289d4cb844e8c366ac/coverage-7.17.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d8a5dc709da9b90ad5cc859a9b88c9b801e65ca2fbf7cbdbed5b9cbf457fcc9", size = 268749, upload-time = "2026-11-04T19:51:17.179Z" }, + { url = "https://files.pythonhosted.org/packages/f6/e5/6d6ba15bde22fcccd07e14305be14291d041f732771a73f787297445d3e7/coverage-7.17.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:248873a144c067f981742ff4bbfb48b57e281a8cb7a3821d4e293bdb50b47970", size = 271301, upload-time = "2026-11-04T19:51:18.893Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/bd6dcec5b8130f5bd8679087a2bc3a50a53c4ee78d5ec449610020088ff3/coverage-7.17.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0332e2b27fc5cc761d6b8a33118b958e6a46be7f00fd30e03a5cf103350c0b3", size = 273924, upload-time = "2026-11-04T19:51:20.605Z" }, + { url = "https://files.pythonhosted.org/packages/55/c1/b3f06aa1f1c51f4295fdfa6500eb7409aaa6273cf5f27bafc25217f3547d/coverage-7.17.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:27fc4384a1154f7df30d304ccddc05ef457368c065670ecbd392606279b3329a", size = 275412, upload-time = "2026-11-04T19:51:22.09Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f0/b68ab3654af3541488b26856e3043c13c782d8e6104fc8149746ecaa83f0/coverage-7.17.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:603140eeae3f9adc562f91b1b98d4d1fc491bd1f66c5082482c568d0b7891bb1", size = 268638, upload-time = "2026-11-04T19:51:23.653Z" }, + { url = "https://files.pythonhosted.org/packages/fd/16/4a9d56bd4ca4572cbd20f9843813623cef1b88b15f979ec4f381704d2f84/coverage-7.17.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:be0fec33b69c40f845c76f67fb4495b62a84da8c31a579dc6e18c5a63fa6a1f3", size = 271211, upload-time = "2026-11-04T19:51:25.678Z" }, + { url = "https://files.pythonhosted.org/packages/78/d2/1c04b38bab92d2b7d62718d170b5bea43755fefee7e29cc056448c433d92/coverage-7.17.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:960e199965e506d78f62252355397a05c8cee8072ad2d82d932745517d566539", size = 268638, upload-time = "2026-11-04T19:51:27.262Z" }, + { url = "https://files.pythonhosted.org/packages/26/c9/2c639e9aee78b0522cb3dc245de09685e34b479bcc45d4d2a277d0de1174/coverage-7.17.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d7a363c0788435ff5324d9582d37c63f215c75f974d755cfd6cfc9fbe0e64b04", size = 272935, upload-time = "2026-11-04T19:51:28.96Z" }, + { url = "https://files.pythonhosted.org/packages/79/1f/11bdaf03c1b12f88094b57420b4238d3974ebec6a7d674b645a54e24f90c/coverage-7.17.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:afd81658f17b1a50bbc1d247a61fa435be0be471246136eb946f9cbfc437a4fa", size = 268057, upload-time = "2026-11-04T19:51:30.44Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f6/43d40d1b4d4892e54c68a241cd5b92efed4d5934109e0e5e24a94759f732/coverage-7.17.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d88fcc24470d73ed4360e910f8549718b913ba56fb8775d9ef929a647835dfea", size = 269986, upload-time = "2026-11-04T19:51:31.913Z" }, + { url = "https://files.pythonhosted.org/packages/8e/33/6874185b791c71684c401fc74abd71bbdb102e8ee41c8f380cf25af41d29/coverage-7.17.2-cp314-cp314t-win32.whl", hash = "sha256:2c42c222939b8699e7bc0e453149627f211ebbbb2f74c1a04331b0cd9834e2c8", size = 230160, upload-time = "2026-11-04T19:51:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/16/e0/9057b4cf962ca6089b144f0d93a748f5e2d13876dd0f33f9d5a2c2ec49d2/coverage-7.17.2-cp314-cp314t-win_amd64.whl", hash = "sha256:b94c509e26603ba7f8cb1bf8bb63967c7be533493be5668d8442cd06ec853d76", size = 231086, upload-time = "2026-11-04T19:51:35.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4f/bd626297c942380279dff323e1d8a8937990d553119a4f887358488b1b54/coverage-7.17.2-cp314-cp314t-win_arm64.whl", hash = "sha256:eca90e028932de597e2c50f454ad8cc631281e14d20b9f801a649ab555deb580", size = 230657, upload-time = "2026-11-04T19:51:36.742Z" }, + { url = "https://files.pythonhosted.org/packages/ac/92/ff8badc05ff0c4ed26ca62fb3d155e9014ff65768b76d5f399e63d9e9a79/coverage-7.17.2-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:cbf751ae86ef0d80e719915c087b05776c8d9300141f6c3e56f536d78a62c301", size = 229092, upload-time = "2026-11-04T19:51:38.222Z" }, + { url = "https://files.pythonhosted.org/packages/5a/8b/c5a5392e5a5dceb68f3b1b8cdba925c7997699f351d811cd4a41add9c29e/coverage-7.17.2-cp315-cp315-android_24_x86_64.whl", hash = "sha256:249c4b4eaac9ced6fb85e465027ca26348111c2760108fffc356a16be4f54cd9", size = 229212, upload-time = "2026-11-04T19:51:39.738Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bc/9259a1fc49ad663861cbfb3df6a99d4c8f62a11b242a2db3358822d28b17/coverage-7.17.2-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e2fd4e664a34854cae8db58ca6132fb7b5a7efc67b14233e797194bd0d63b6b0", size = 227390, upload-time = "2026-11-04T19:51:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/20/ad/83f03da8ab846af246d4a47b651e5d90229a4c3314e31e8eecade63b378f/coverage-7.17.2-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:0712a43e39410aa567f2e85846490f8dd6c839820e7edef2142767c5c03162ba", size = 227690, upload-time = "2026-11-04T19:51:42.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/08/e110687f24245edbbe9c0986a6ccd04f1f9a6e90e329cbd4347e2b81d76e/coverage-7.17.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:298849ca0e385767096995cb57f72059b469f43f9927b8393f29d134f7bb1062", size = 227922, upload-time = "2026-11-04T19:51:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/93/24/7cfcd6adb4a0c18a10de12fd695edbf8cd121fdca0a6a63cea1bffc029fe/coverage-7.17.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:81a432793440edef268dbadd493393bd6f70fa4b87f76c58114f7f77f1ccfaea", size = 228094, upload-time = "2026-11-04T19:51:46.108Z" }, + { url = "https://files.pythonhosted.org/packages/54/7a/af167a0bb6d0564209d35c8039b290327ecf7b3e34f838f9360b56d3a03c/coverage-7.17.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d14872ccef35972c4ac290f2432f43d0a7bb230cae2b5ed26871ab86221e8b5f", size = 259940, upload-time = "2026-11-04T19:51:47.599Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c6/4f3931c3ff80a6f2608209181adb00527970a4b43b64c3aa323383c37d51/coverage-7.17.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3bd065e75c077d6de8cd3fe68699a8b47be3ec595af6b66e3629926839094609", size = 263632, upload-time = "2026-11-04T19:51:49.047Z" }, + { url = "https://files.pythonhosted.org/packages/3e/71/31a4945459008952061147ea9b85853902507d865a7272a9f64be0c4a093/coverage-7.17.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d3626b9816f7eadd0993c3703a1d8ed124b579cebe5201b446f1c0d551fa245", size = 264713, upload-time = "2026-11-04T19:51:50.569Z" }, + { url = "https://files.pythonhosted.org/packages/82/6b/a4eb258b0373034f8f19aeea4c1bc4c89942b5b228812ee9952f033dc611/coverage-7.17.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9639c2ea019c948b52067a0f74485039eb84b7a2de828ba6d178e663fb2b8bd", size = 267091, upload-time = "2026-11-04T19:51:52.09Z" }, + { url = "https://files.pythonhosted.org/packages/02/ae/9410f2f4bbaa698ff3869c7c09e0fa10d74482a1ec8040063157612f07a0/coverage-7.17.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:19d508b0611486f8e23e28abcba77795ee865ff0e0cd936349ba692c8d0d8ca4", size = 260326, upload-time = "2026-11-04T19:51:53.508Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ee/97549ec1bdf6ff6d17612dbcf4b2205daa74510273ac1efd1460e3db275c/coverage-7.17.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:cfe64d2501806a04507217852a3132a9a9076321435500bf3b11943a5c872732", size = 262595, upload-time = "2026-11-04T19:51:55.514Z" }, + { url = "https://files.pythonhosted.org/packages/36/72/76e04605522a6dd81bbf43e1b407ca611f415c04b67e18c123ee440b1d69/coverage-7.17.2-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:4274acd35aae5bbbd0908792260819b9720b171ccc099a8168f7c20ef5d6ab3a", size = 260546, upload-time = "2026-11-04T19:51:57.068Z" }, + { url = "https://files.pythonhosted.org/packages/30/8d/163e027635790ab232f3ec0261d2a26bec34c377bea2096554261444d0f5/coverage-7.17.2-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:85bccf6fe19ea3747147387971465f6a06197d6b9f6b644a97ba90ca5b09c5cd", size = 264877, upload-time = "2026-11-04T19:51:58.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/35/11dd585a06be350285d42c17aa2fccd0af18d962b6979f03dd1a258eb705/coverage-7.17.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:d5024913651813801fd43b65b6409b0d50f8bae95316c8f1512dfbd8ca96cb1f", size = 259858, upload-time = "2026-11-04T19:52:00.361Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ed/f1f3b4bf6081122bf1820b1f38352377a75a2d5f39bb6331598e165b64af/coverage-7.17.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:99b64a12fe6017c254a8027790e0eaa22eaeec201e7dfda5eaac176a6984ef27", size = 262401, upload-time = "2026-11-04T19:52:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/ab/bb/61e07a606a9f4d0f5391ac9b62b1f008a997f87860ad233f99833fd6e6bd/coverage-7.17.2-cp315-cp315-win32.whl", hash = "sha256:74d91af590514dda16165d5d7cfa94183150c6a6a80a99f61068369ae3959455", size = 229840, upload-time = "2026-11-04T19:52:04.813Z" }, + { url = "https://files.pythonhosted.org/packages/86/40/2d4980d3374a023a34c5ea7cd5d2b93c02c5588a324ca7faf8a0a8c7dbac/coverage-7.17.2-cp315-cp315-win_amd64.whl", hash = "sha256:075566a44289afb634ceaafa7d8d377bb2667a5b4a90fbee2609efdcd015c66e", size = 230461, upload-time = "2026-11-04T19:52:06.727Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c8/b307327b1256f9ed8ffc3271db041913835cfee065fbaf45105978093c08/coverage-7.17.2-cp315-cp315-win_arm64.whl", hash = "sha256:be699e76b57fede8920a0f568eff23b270d9004414b7cf6744bdda851638be6e", size = 230322, upload-time = "2026-11-04T19:52:08.191Z" }, + { url = "https://files.pythonhosted.org/packages/40/18/0912e37f23c101faebd7c5e544c6ba6bd750715f81f2a2c7ea66f83a2295/coverage-7.17.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:ffbaadf8612781358b3f949ad68dce26c5a35beaafcc48125fc231dbcce7d362", size = 228380, upload-time = "2026-11-04T19:52:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a0/c4ce9bbf82b2e9dbf2e0326004c8c37d87a275f515fc7bb22344a6162050/coverage-7.17.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d4004fce7b3a8ae5ef2af835670763f12723e9b9d74837697607b89a17f77312", size = 228776, upload-time = "2026-11-04T19:52:11.351Z" }, + { url = "https://files.pythonhosted.org/packages/3f/05/9c7429c87589534755398a58fe9837fdb2870a22d99e87623fcb261e1d4e/coverage-7.17.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a838a10afb5b60a6cc6315348b176badaf2409d821208a3e3cda1dc608129e23", size = 269129, upload-time = "2026-11-04T19:52:12.815Z" }, + { url = "https://files.pythonhosted.org/packages/e9/22/d1ad2ea7c8871d66f8453d33cdeec39986e6e1f2a104628e9bcb171f0209/coverage-7.17.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0fc17882975fe485cfc95fb2f20eed937e05e0c0d8ffa5048857cda3aa838e8a", size = 271967, upload-time = "2026-11-04T19:52:14.685Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1d/5038de4bf6549903ec3d86959c6f18aff54911616b47aa96f4d6a5660c93/coverage-7.17.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a65faa1ee98e090a5fd7c116f6d3ab5c6a85fcc46849b8494835608715836399", size = 274667, upload-time = "2026-11-04T19:52:16.122Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ea/68a26b4b5eb255d92de906fd66e8d720b1df15e705949ea9a9cef2cc7f8b/coverage-7.17.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7c17f716c837db550a202e7732cdfaa726b8515cba88be42193616913970d223", size = 276001, upload-time = "2026-11-04T19:52:17.639Z" }, + { url = "https://files.pythonhosted.org/packages/70/86/0f4238f09b22bc03a907346fabcabe5449626879cdedbc1edd362ab0e570/coverage-7.17.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:545ca42488c7f0e62324fb2621aa05737db843b75e84e65a271bbf976f496f66", size = 269687, upload-time = "2026-11-04T19:52:19.169Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/1885255425900f691c543d88394d9f0dacd2f2dc78422e660c747ca9c95c/coverage-7.17.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:8de787f533b13aeeea5d7139610eadb56f249cc739840b973603d1d18d0fb023", size = 272158, upload-time = "2026-11-04T19:52:20.714Z" }, + { url = "https://files.pythonhosted.org/packages/02/d6/0111601342d91e25d13e22a55f34600aa387c55b5027b90e78d941911821/coverage-7.17.2-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:485b2ed9e13b7e9209276843d7147e2f1cd1c816582655650a64c8f922093c58", size = 269182, upload-time = "2026-11-04T19:52:22.204Z" }, + { url = "https://files.pythonhosted.org/packages/70/48/d9bdc5346a9ad09711f55b6901ee05b57305e6b2b66e6edfd50f81f0d410/coverage-7.17.2-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:a472e23a7514478e1c8124ea056424d681e3173975650b0a06b6181658f1009a", size = 273790, upload-time = "2026-11-04T19:52:23.677Z" }, + { url = "https://files.pythonhosted.org/packages/98/75/3a4d8ce4aa59ed2e898364acf923b39471689d51876bb978c42e664cdb79/coverage-7.17.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:0a623de5c57c5c3f3aed830c73a3a5aad91dbf76d53bc30ec161be758c8ca408", size = 269277, upload-time = "2026-11-04T19:52:25.19Z" }, + { url = "https://files.pythonhosted.org/packages/4a/11/d15082e504e7852a25848ee0dcb59453579ec923c6d1f3010316e1fb6118/coverage-7.17.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:2ad189c65706306644399e48530ad1aea1dfb311bd21d988df1ace422d09f3f8", size = 270890, upload-time = "2026-11-04T19:52:26.745Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9d/6460101b0191afae9ed84c8d8136cc8cc09675f376ce08acb3118b8c27be/coverage-7.17.2-cp315-cp315t-win32.whl", hash = "sha256:6fd6f218e39a621a30dbaf9543831fa10fe713afc8a44b52fc42cfe277412609", size = 230148, upload-time = "2026-11-04T19:52:28.164Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f2/3462d9616445a8cb36ec2e027d1c0ae82f6f90f224d9851ba0b22b7425d6/coverage-7.17.2-cp315-cp315t-win_amd64.whl", hash = "sha256:ef07aae0a666ef5edcab5bc1bb590468c6f4120a8cdad50a010d042a59ab6e77", size = 231074, upload-time = "2026-11-04T19:52:29.714Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/abeca0cf853e77aba9464d4ada4603c8b6e744b667c13e1d4eefecc487af/coverage-7.17.2-cp315-cp315t-win_arm64.whl", hash = "sha256:c68f64d96857749a215657c172f7144ed026063489686b901774f271997923c2", size = 230648, upload-time = "2026-11-04T19:52:31.267Z" }, + { url = "https://files.pythonhosted.org/packages/da/91/1805cc144497d76a436f66a21fa57492bee7ed1257826fb992f03b9b2b6f/coverage-7.17.2-py3-none-any.whl", hash = "sha256:e674027ceb6bfd959f1bb035000fd20fa8b717dc6765adaaf8bca347822c8c7d", size = 219280, upload-time = "2026-11-04T19:52:32.726Z" }, +] + [[package]] name = "docutils" version = "0.21.2" @@ -664,6 +925,8 @@ version = "0.0.0" source = { virtual = "." } dependencies = [ { name = "absl-py" }, + { name = "coverage", version = "7.16.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "coverage", version = "7.17.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "macholib" }, { name = "markupsafe" }, { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -689,6 +952,7 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "absl-py" }, + { name = "coverage" }, { name = "macholib" }, { name = "markupsafe" }, { name = "myst-parser" }, From 0e78f1d818b65b9768310b55d0958b966a524ba3 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:56:05 +0900 Subject: [PATCH 07/39] test(coverage): check the coverage collection with coverage from a pip 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. --- MODULE.bazel | 20 ++++++++++++++++++++ tests/coverage/BUILD.bazel | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/MODULE.bazel b/MODULE.bazel index df1326d478..3db54ae6b1 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -411,6 +411,26 @@ dev_python.toolchain( python_version = "3.12.16", ) +# For testing the coverage collection with the coverage tool from a PyPI hub. +# See //tests/coverage:coverage_from_pypi_test +dev_python.toolchain( + python_version = "3.13.13", +) + +[ + dev_python.single_version_platform_override( + coverage_tool = "@dev_pip//coverage:extracted_whl_files", + platform = platform, + python_version = "3.13.13", + ) + for platform in [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + ] +] + # For testing an arbitrary runtime triggered by a custom flag. # See //tests/toolchains:custom_platform_toolchain_test dev_python.single_version_platform_override( diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index fa245addc9..b02535843c 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -39,3 +39,31 @@ py_test( target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, deps = ["//python/runfiles"], ) + +# The same program, but the coverage tool is the extracted `coverage` wheel from +# the `@dev_pip` hub, see the `MODULE.bazel` file. +py_reconfig_test( + name = "instrumented_pypi", + srcs = ["instrumented.py"], + collect_code_coverage = "true", + main = "instrumented.py", + python_version = "3.13.13", + tags = ["manual"], + target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, + deps = [":lib"], +) + +py_test( + name = "coverage_from_pypi_test", + srcs = ["coverage_collection_test.py"], + data = [ + "lib.py", + ":instrumented_pypi", + ], + env = { + "INSTRUMENTED": "$(rlocationpath :instrumented_pypi)", + }, + main = "coverage_collection_test.py", + target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, + deps = ["//python/runfiles"], +) From 2f48dcbcfc14bfde9787d64febdb5436f9728851 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:07:30 +0900 Subject: [PATCH 08/39] test(coverage): simplify the coverage collection test setup Use TEST_TMPDIR directly and rely on the test working directory being the workspace runfiles directory. --- tests/coverage/coverage_collection_test.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/coverage/coverage_collection_test.py b/tests/coverage/coverage_collection_test.py index aaa302064e..614688005b 100644 --- a/tests/coverage/coverage_collection_test.py +++ b/tests/coverage/coverage_collection_test.py @@ -8,7 +8,6 @@ import os import pathlib import subprocess -import tempfile import unittest from python.runfiles import runfiles @@ -57,9 +56,7 @@ def setUpClass(cls): assert lib_path is not None cls.lib_lines = pathlib.Path(lib_path).read_text().splitlines() - tmp = pathlib.Path( - tempfile.mkdtemp(dir=os.environ.get("TEST_TMPDIR")), - ) + tmp = pathlib.Path(os.environ["TEST_TMPDIR"]) coverage_dir = tmp / "coverage" coverage_dir.mkdir() manifest = tmp / "coverage_manifest.txt" @@ -78,10 +75,8 @@ def setUpClass(cls): env["COVERAGE_DIR"] = str(coverage_dir) env["COVERAGE_MANIFEST"] = str(manifest) - workspace_dir = os.path.dirname(os.path.dirname(os.path.dirname(program))) cls.result = subprocess.run( [program], - cwd=workspace_dir, env=env, capture_output=True, text=True, From 33040423327965bcdab7083a8fc84b66bb44c8f9 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:07:30 +0900 Subject: [PATCH 09/39] test(coverage): make collect_code_coverage a bool py_reconfig attr Also drop the lib.py data dependency, which is already in the runfiles via the instrumented target. --- tests/coverage/BUILD.bazel | 7 ++----- tests/support/py_reconfig.bzl | 6 +++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index fa245addc9..55e2103c16 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -14,7 +14,7 @@ py_library( py_reconfig_test( name = "instrumented", srcs = ["instrumented.py"], - collect_code_coverage = "true", + collect_code_coverage = True, main = "instrumented.py", # The bundled coverage tool is configured only for this version, see the # `MODULE.bazel` file. @@ -29,10 +29,7 @@ py_reconfig_test( py_test( name = "coverage_collection_test", srcs = ["coverage_collection_test.py"], - data = [ - "lib.py", - ":instrumented", - ], + data = [":instrumented"], env = { "INSTRUMENTED": "$(rlocationpath :instrumented)", }, diff --git a/tests/support/py_reconfig.bzl b/tests/support/py_reconfig.bzl index ee93b525bb..64a4e14ddd 100644 --- a/tests/support/py_reconfig.bzl +++ b/tests/support/py_reconfig.bzl @@ -38,7 +38,7 @@ def _perform_transition_impl(input_settings, attr, base_impl): if attr.bootstrap_impl: settings[labels.BOOTSTRAP_IMPL] = attr.bootstrap_impl if attr.collect_code_coverage: - settings["//command_line_option:collect_code_coverage"] = attr.collect_code_coverage == "true" + settings["//command_line_option:collect_code_coverage"] = True if attr.extra_toolchains: settings["//command_line_option:extra_toolchains"] = attr.extra_toolchains if attr.python_src: @@ -76,8 +76,8 @@ _RECONFIG_INHERITED_OUTPUTS = [v for v in _RECONFIG_OUTPUTS if v in _RECONFIG_IN _RECONFIG_ATTRS = { "bootstrap_impl": attrb.String(), "build_python_zip": attrb.Bool(default = config.build_python_zip_default), - "collect_code_coverage": attrb.String( - doc = "Value for the --collect_code_coverage flag, either `true` or `false`.", + "collect_code_coverage": attrb.Bool( + doc = "If true, set the --collect_code_coverage flag.", ), "config_settings": attrb.LabelKeyedStringDict(), "extra_toolchains": attrb.StringList( From 924c4a87aad464f4574b9e4dbad2bf0c4a7f4e9b Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:11:11 +0900 Subject: [PATCH 10/39] test(coverage): use the bool collect_code_coverage for instrumented_pypi --- tests/coverage/BUILD.bazel | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index f4e2a0ef5d..b9dd645a60 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -42,7 +42,7 @@ py_test( py_reconfig_test( name = "instrumented_pypi", srcs = ["instrumented.py"], - collect_code_coverage = "true", + collect_code_coverage = True, main = "instrumented.py", python_version = "3.13.13", tags = ["manual"], @@ -53,10 +53,7 @@ py_reconfig_test( py_test( name = "coverage_from_pypi_test", srcs = ["coverage_collection_test.py"], - data = [ - "lib.py", - ":instrumented_pypi", - ], + data = [":instrumented_pypi"], env = { "INSTRUMENTED": "$(rlocationpath :instrumented_pypi)", }, From e388a75feca2c007085c16bc7922ad2894077815 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:21:56 +0900 Subject: [PATCH 11/39] docs: link the coverage_tool doc to the coverage docs Also test the error for a coverage_tool without coverage/__main__.py and name the news entry after the PR. --- docs/coverage.md | 5 ++-- ...l-extracted-whl.added.md => 4433.added.md} | 0 python/private/py_runtime_rule.bzl | 7 ++--- tests/py_runtime/py_runtime_tests.bzl | 27 +++++++++++++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) rename news/{coverage-tool-extracted-whl.added.md => 4433.added.md} (100%) diff --git a/docs/coverage.md b/docs/coverage.md index fd7cdbb611..cd72e4bcc7 100644 --- a/docs/coverage.md +++ b/docs/coverage.md @@ -76,6 +76,7 @@ important behaviors of the target are: file is treated as the coverage entry point. This allows using the files of an extracted `coverage` wheel. +(coverage-from-pypi)= ### Using `coverage` from PyPI To use the version of `coverage` that is locked in your requirements instead of @@ -90,8 +91,8 @@ python.single_version_platform_override( ) ``` -Or set `py_runtime.coverage_tool = "@pypi//coverage:extracted_whl_files"` when -defining your own runtime. +Or set {attr}`py_runtime.coverage_tool` to +`@pypi//coverage:extracted_whl_files` when defining your own runtime. :::{note} Use the `extracted_whl_files` target and not `@pypi//coverage`: the diff --git a/news/coverage-tool-extracted-whl.added.md b/news/4433.added.md similarity index 100% rename from news/coverage-tool-extracted-whl.added.md rename to news/4433.added.md diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl index e7d96b56a7..f1718f6963 100644 --- a/python/private/py_runtime_rule.bzl +++ b/python/private/py_runtime_rule.bzl @@ -333,11 +333,8 @@ of [`coverage.py`](https://coverage.readthedocs.io), at least including the `run` and `lcov` subcommands. If the target produces multiple files and one of them is -`coverage/__main__.py`, then it is used as the entry point. This allows using -the extracted `coverage` wheel from a {obj}`pip.parse` hub, e.g. -`@pypi//coverage:extracted_whl_files`. Note, that `@pypi//coverage` cannot be -used, because a `py_library` depends on the Python toolchain, which would -create a dependency cycle. +`coverage/__main__.py`, then it is used as the entry point, e.g. +`@pypi//coverage:extracted_whl_files`. See {ref}`coverage-from-pypi`. :::{versionchanged} VERSION_NEXT_FEATURE The extracted files of the `coverage` wheel can be used. diff --git a/tests/py_runtime/py_runtime_tests.bzl b/tests/py_runtime/py_runtime_tests.bzl index d60f64fffe..8124f889d3 100644 --- a/tests/py_runtime/py_runtime_tests.bzl +++ b/tests/py_runtime/py_runtime_tests.bzl @@ -264,6 +264,33 @@ def _test_coverage_tool_extracted_whl_files_impl(env, target): _tests.append(_test_coverage_tool_extracted_whl_files) +def _test_coverage_tool_multiple_files_without_main(name): + rt_util.helper_target( + py_runtime, + name = name + "_subject", + python_version = "PY3", + interpreter_path = "/bogus", + coverage_tool = name + "_files", + ) + rt_util.helper_target( + native.filegroup, + name = name + "_files", + srcs = ["coverage_file1.txt", "coverage_file2.txt"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_coverage_tool_multiple_files_without_main_impl, + expect_failure = True, + ) + +def _test_coverage_tool_multiple_files_without_main_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("coverage_tool must*contain 'coverage/__main__.py'"), + ) + +_tests.append(_test_coverage_tool_multiple_files_without_main) + def _test_in_build_interpreter(name): rt_util.helper_target( py_runtime, From 35e08ea38a9565629f226b8f7aca80074cc98d1a Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 21:24:03 +0900 Subject: [PATCH 12/39] test(coverage): use pytest_test for the coverage collection test --- tests/coverage/BUILD.bazel | 4 ++-- tests/coverage/coverage_collection_test.py | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index 55e2103c16..b578e85657 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -1,5 +1,5 @@ load("//python:py_library.bzl", "py_library") -load("//python:py_test.bzl", "py_test") +load("//python:pytest_test.bzl", "pytest_test") load("//tests/support:py_reconfig.bzl", "py_reconfig_test") load("//tests/support:support.bzl", "NOT_WINDOWS", "SUPPORTS_BZLMOD") @@ -26,7 +26,7 @@ py_reconfig_test( deps = [":lib"], ) -py_test( +pytest_test( name = "coverage_collection_test", srcs = ["coverage_collection_test.py"], data = [":instrumented"], diff --git a/tests/coverage/coverage_collection_test.py b/tests/coverage/coverage_collection_test.py index 614688005b..2baad9b03e 100644 --- a/tests/coverage/coverage_collection_test.py +++ b/tests/coverage/coverage_collection_test.py @@ -160,7 +160,3 @@ def test_partial_branch_coverage(self): self.assertIn(lineno, branches, f"branches: {branches}") # Only the `True` branch of the `if` was taken. self.assertEqual(sorted(b > 0 for b in branches[lineno]), [False, True]) - - -if __name__ == "__main__": - unittest.main() From c9b66df1a6cff0d1ab6aca3fee2e66fc9d71068a Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 8 Nov 2026 21:24:49 +0900 Subject: [PATCH 13/39] test(coverage): use pytest_test for the coverage from PyPI test --- tests/coverage/BUILD.bazel | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index 06fc5f866c..b770586d5e 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -50,14 +50,13 @@ py_reconfig_test( deps = [":lib"], ) -py_test( +pytest_test( name = "coverage_from_pypi_test", srcs = ["coverage_collection_test.py"], data = [":instrumented_pypi"], env = { "INSTRUMENTED": "$(rlocationpath :instrumented_pypi)", }, - main = "coverage_collection_test.py", target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, deps = ["//python/runfiles"], ) From 197fcce4ad5a623a847b529f99f1a93b3149e99b Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 19:23:17 +0900 Subject: [PATCH 14/39] fix(toolchain): build the coverage_tool without the toolchain coverage 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`. --- MODULE.bazel | 2 +- python/private/BUILD.bazel | 21 ++++++++++++++++++++- python/private/common_labels.bzl | 1 + python/private/py_runtime_macro.bzl | 29 +++++++++++++++++++++++++++-- python/private/py_runtime_rule.bzl | 29 ++++++++++++++++++++++++++++- tests/coverage/BUILD.bazel | 4 ++-- 6 files changed, 79 insertions(+), 7 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 634a2225a8..480f30108d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -426,7 +426,7 @@ dev_python.toolchain( [ dev_python.single_version_platform_override( - coverage_tool = "@dev_pip//coverage:extracted_whl_files", + coverage_tool = "@dev_pip//coverage", platform = platform, python_version = "3.13.13", ) diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index dc0758efa5..33a911f60a 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -13,7 +13,7 @@ # limitations under the License. load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "bool_setting") load("//python:py_binary.bzl", "py_binary") load("//python:py_library.bzl", "py_library") load(":bazel_config_mode.bzl", "bazel_config_mode") @@ -164,6 +164,25 @@ alias( bazel_config_mode(name = "bazel_config_mode") +# Usually False. The `py_runtime.coverage_tool` transition sets it to True so +# that the coverage tool and its dependencies (e.g. a `py_library` from a pip hub) +# resolve to a toolchain without a coverage tool, which would otherwise be a +# dependency cycle. +bool_setting( + name = "bootstrap_coverage_tool", + build_setting_default = False, + visibility = ["//visibility:private"], +) + +config_setting( + name = "coverage_tool_active", + flag_values = { + ":bootstrap_coverage_tool": "False", + }, + # This is only because it is an implicit dependency by the toolchains. + visibility = NOT_ACTUALLY_PUBLIC, +) + # This should only be set by analysis tests to expose additional metadata to # aid testing, so a setting instead of a flag. bool_flag( diff --git a/python/private/common_labels.bzl b/python/private/common_labels.bzl index a3b9b2ac1f..0969023b0c 100644 --- a/python/private/common_labels.bzl +++ b/python/private/common_labels.bzl @@ -6,6 +6,7 @@ labels = struct( # keep sorted ADD_SRCS_TO_RUNFILES = str(Label("//python/config_settings:add_srcs_to_runfiles")), + BOOTSTRAP_COVERAGE_TOOL = str(Label("//python/private:bootstrap_coverage_tool")), BOOTSTRAP_IMPL = str(Label("//python/config_settings:bootstrap_impl")), BUILD_PYTHON_ZIP = str(Label("//python/config_settings:build_python_zip")), # NOTE: Special target; see definition for details. diff --git a/python/private/py_runtime_macro.bzl b/python/private/py_runtime_macro.bzl index 7d04388fd6..555e024b23 100644 --- a/python/private/py_runtime_macro.bzl +++ b/python/private/py_runtime_macro.bzl @@ -18,5 +18,30 @@ load(":py_runtime_rule.bzl", py_runtime_rule = "py_runtime") # NOTE: The function name is purposefully selected to match the underlying # rule name so that e.g. 'generator_function' shows as the same name so # that it is less confusing to users. -def py_runtime(**kwargs): - py_runtime_rule(**kwargs) +def py_runtime(*, name, coverage_tool = None, **kwargs): + """Wraps the py_runtime rule. + + The `coverage_tool` is dropped when building the coverage tool itself, so + that it can depend on the Python toolchain, e.g. a `py_library` from a pip + hub. Otherwise, it would be a dependency cycle. + + Args: + name: {type}`str` the name of the target. + coverage_tool: {type}`Label | select | None` the coverage tool. + **kwargs: forwarded to the rule. + """ + + # NOTE: A select can't be nested, so it is passed as is. The hermetic + # toolchains select on `collect_code_coverage`, which the `coverage_tool` + # transition disables, so that also breaks the cycle. + if coverage_tool != None and type(coverage_tool) != "select": + coverage_tool = select({ + Label("//python/private:coverage_tool_active"): coverage_tool, + "//conditions:default": None, + }) + + py_runtime_rule( + name = name, + coverage_tool = coverage_tool, + **kwargs + ) diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl index f1718f6963..d46576eedd 100644 --- a/python/private/py_runtime_rule.bzl +++ b/python/private/py_runtime_rule.bzl @@ -78,6 +78,31 @@ WARNING: Python runtime {label} has no coverage_tool. ====================================================================== """.format(label = label) +def _coverage_tool_transition_impl(_settings, _attr): + """Build the coverage tool without itself being a toolchain coverage tool. + + The coverage tool can be a target that resolves the Python toolchain itself, + e.g. a `py_library` from a pip hub. Setting the bootstrap flag makes the + {obj}`py_runtime` macro drop the `coverage_tool` dependency in that + configuration, which would otherwise be a dependency cycle. The coverage tool + itself is not instrumented, so coverage collection is disabled. + + This mirrors how `rules_go` builds `nogo`. + """ + return { + labels.BOOTSTRAP_COVERAGE_TOOL: True, + "//command_line_option:collect_code_coverage": False, + } + +_coverage_tool_transition = transition( + implementation = _coverage_tool_transition_impl, + inputs = [], + outputs = [ + labels.BOOTSTRAP_COVERAGE_TOOL, + "//command_line_option:collect_code_coverage", + ], +) + def _py_runtime_impl(ctx): interpreter_path = ctx.attr.interpreter_path or None # Convert empty string to None interpreter = ctx.attr.interpreter @@ -144,7 +169,8 @@ def _py_runtime_impl(ctx): fail("interpreter must be an executable target or must produce exactly one file.") if ctx.attr.coverage_tool: - coverage_di = ctx.attr.coverage_tool[DefaultInfo] + # A list because of the Starlark transition on the attribute. + coverage_di = ctx.attr.coverage_tool[0][DefaultInfo] if _is_singleton_depset(coverage_di.files): coverage_tool = coverage_di.files.to_list()[0] @@ -318,6 +344,7 @@ See @bazel_tools//tools/python:python_bootstrap_template.txt for more variables. ), "coverage_tool": attr.label( allow_files = False, + cfg = _coverage_tool_transition, doc = """ This is a target to use for collecting code coverage information from {rule}`py_binary` and {rule}`py_test` targets. diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index b770586d5e..6842c978d9 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -37,8 +37,8 @@ pytest_test( deps = ["//python/runfiles"], ) -# The same program, but the coverage tool is the extracted `coverage` wheel from -# the `@dev_pip` hub, see the `MODULE.bazel` file. +# The same program, but the coverage tool is the `coverage` py_library from the +# `@dev_pip` hub, see the `MODULE.bazel` file. py_reconfig_test( name = "instrumented_pypi", srcs = ["instrumented.py"], From 6f653a39facd7f3031d02cf3a531309447d4532e Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 19:23:34 +0900 Subject: [PATCH 15/39] feat(toolchain): import the coverage package using the PyInfo imports 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. --- docs/coverage.md | 28 +++++----- news/4433.added.md | 6 +-- python/private/BUILD.bazel | 1 + python/private/py_executable.bzl | 12 ++++- python/private/py_runtime_info.bzl | 30 +++++++++-- python/private/py_runtime_rule.bzl | 64 ++++++++++------------- python/private/site_init_template.py | 11 +++- tests/py_runtime/py_runtime_tests.bzl | 33 +++++++----- tests/support/py_runtime_info_subject.bzl | 12 +++++ 9 files changed, 124 insertions(+), 73 deletions(-) diff --git a/docs/coverage.md b/docs/coverage.md index cd72e4bcc7..8e620e5746 100644 --- a/docs/coverage.md +++ b/docs/coverage.md @@ -69,33 +69,35 @@ filegroup( Using `filegroup` isn't required, nor are including client libraries. The important behaviors of the target are: -* It provides a single output file OR it provides an executable output; this - output is treated as the coverage entry point. +* It provides {obj}`PyInfo` (e.g. it is a `py_library`), in which case its + import paths are used to import the `coverage` package. See + {ref}`coverage-from-pypi`. +* Otherwise, it provides a single output file OR it provides an executable + output; this output is treated as the coverage entry point. * If it provides runfiles, then `runfiles.files` are included into `py_test`. -* Otherwise, if one of its output files is `coverage/__main__.py`, then that - file is treated as the coverage entry point. This allows using the files - of an extracted `coverage` wheel. (coverage-from-pypi)= ### Using `coverage` from PyPI To use the version of `coverage` that is locked in your requirements instead of -the one bundled with `rules_python`, point the toolchain to the extracted files -of the `coverage` package in your {obj}`pip.parse` hub: +the one bundled with `rules_python`, point the toolchain to the `coverage` +package in your {obj}`pip.parse` hub: ```starlark python.single_version_platform_override( - coverage_tool = "@pypi//coverage:extracted_whl_files", + coverage_tool = "@pypi//coverage", platform = "x86_64-unknown-linux-gnu", python_version = "3.13.2", ) ``` -Or set {attr}`py_runtime.coverage_tool` to -`@pypi//coverage:extracted_whl_files` when defining your own runtime. +Or set {attr}`py_runtime.coverage_tool` to `@pypi//coverage` when defining +your own runtime. :::{note} -Use the `extracted_whl_files` target and not `@pypi//coverage`: the -`py_library` depends on the Python toolchain itself, so using it as the -coverage tool of the toolchain creates a dependency cycle. +The `py_library` itself depends on the Python toolchain. To avoid a dependency +cycle, the coverage tool is built in a configuration where the toolchain has +no coverage tool, which is similar to how `rules_go` builds `nogo`. This only +works when the runtime is defined with the {obj}`py_runtime` macro, which the +toolchains of `rules_python` use. ::: diff --git a/news/4433.added.md b/news/4433.added.md index 88a848203c..15fc854a0d 100644 --- a/news/4433.added.md +++ b/news/4433.added.md @@ -1,4 +1,4 @@ -(toolchains) {attr}`py_runtime.coverage_tool` accepts the extracted files of -the `coverage` wheel, such as `@pypi//coverage:extracted_whl_files`, so the -locked version of `coverage` can be used for coverage collection +(toolchains) {attr}`py_runtime.coverage_tool` accepts targets providing +{obj}`PyInfo`, such as `@pypi//coverage`, so the locked version of `coverage` +can be used for coverage collection ([#3902](https://github.com/bazel-contrib/rules_python/issues/3902)). diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index 33a911f60a..9295df842b 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -708,6 +708,7 @@ bzl_library( deps = [ ":common_labels", ":flags", + ":py_info", ":py_internal", ":py_runtime_info", ":reexports", diff --git a/python/private/py_executable.bzl b/python/private/py_executable.bzl index e57562b43b..269127de71 100644 --- a/python/private/py_executable.bzl +++ b/python/private/py_executable.bzl @@ -596,6 +596,7 @@ def _create_venv(ctx, output_prefix, imports, runtime_details, add_runfiles_root output = site_init, substitutions = { "%add_runfiles_root_to_sys_path%": add_runfiles_root_to_sys_path, + "%coverage_imports%": _get_coverage_imports(ctx, runtime), "%coverage_tool%": _get_coverage_tool_runfiles_path(ctx, runtime), "%import_all%": "True" if read_possibly_native_flag(ctx, "python_import_all_repositories") else "False", "%interpreter_actual_path%": interpreter_actual_path, @@ -879,6 +880,14 @@ def _venv_details( def _map_each_identity(v): return v +def _get_coverage_imports(ctx, runtime): + if (ctx.configuration.coverage_enabled and + runtime and + getattr(runtime, "coverage_imports", None)): + return ":".join(runtime.coverage_imports.to_list()) + else: + return "" + def _get_coverage_tool_runfiles_path(ctx, runtime): if (ctx.configuration.coverage_enabled and runtime and @@ -2088,7 +2097,8 @@ def _add_provider_py_runtime_info(providers, runtime_details): interpreter = py_runtime_info.interpreter, files = py_runtime_info.files, coverage_tool = py_runtime_info.coverage_tool, - coverage_files = py_runtime_info.coverage_files, + # The builtin provider only knows about coverage_tool. + coverage_files = py_runtime_info.coverage_files if py_runtime_info.coverage_tool else None, python_version = py_runtime_info.python_version, stub_shebang = py_runtime_info.stub_shebang, bootstrap_template = py_runtime_info.bootstrap_template, diff --git a/python/private/py_runtime_info.bzl b/python/private/py_runtime_info.bzl index daf01e6c7e..1931ea44a4 100644 --- a/python/private/py_runtime_info.bzl +++ b/python/private/py_runtime_info.bzl @@ -58,6 +58,7 @@ def _PyRuntimeInfo_init( interpreter_files_to_run = None, coverage_tool = None, coverage_files = None, + coverage_imports = None, pyc_tag = None, python_version, stub_shebang = None, @@ -84,11 +85,16 @@ def _PyRuntimeInfo_init( if interpreter_files_to_run.executable != interpreter: fail("'interpreter_files_to_run.executable' must match 'interpreter'") - if (coverage_tool and not coverage_files) or (not coverage_tool and coverage_files): + if coverage_tool and coverage_imports: + fail("cannot specify both 'coverage_tool' and 'coverage_imports'") + + has_coverage = bool(coverage_tool or coverage_imports) + if has_coverage != bool(coverage_files): fail( - "coverage_tool and coverage_files must both be set or neither must be set, " + - "got coverage_tool={}, coverage_files={}".format( + "coverage_files must be set if and only if coverage_tool or " + + "coverage_imports is set, got coverage_tool={}, coverage_imports={}, coverage_files={}".format( coverage_tool, + coverage_imports, coverage_files, ), ) @@ -118,6 +124,7 @@ def _PyRuntimeInfo_init( "abi_flags": abi_flags, "bootstrap_template": bootstrap_template, "coverage_files": coverage_files, + "coverage_imports": coverage_imports, "coverage_tool": coverage_tool, "files": files, "implementation_name": implementation_name, @@ -221,8 +228,18 @@ The set of substitutions depends on {obj}`--bootstrap_impl` "coverage_files": """ :type: depset[File] | None -The files required at runtime for using `coverage_tool`. Will be `None` if no -`coverage_tool` was provided. +The files required at runtime for using `coverage_tool` or `coverage_imports`. +Will be `None` if neither was provided. +""", + "coverage_imports": """ +:type: depset[str] | None + +Runfiles-root-relative import paths that make the `coverage` package importable, +e.g. the {obj}`PyInfo.imports` of a `py_library` used as the coverage tool. +Mutually exclusive with `coverage_tool`. + +:::{versionadded} VERSION_NEXT_FEATURE +::: """, "coverage_tool": """ :type: File | None @@ -322,6 +339,9 @@ The following substitutions are made during template expansion: can be a `.py` or `.pyc` file, depending on precompile settings. * `%coverage_tool%`: Runfiles-relative path to the coverage library's entry point. If coverage is not enabled or available, an empty string. +* `%coverage_imports%`: A colon-delimited string of runfiles-relative paths to + add to sys.path to import the `coverage` package. If coverage is not enabled + or the runtime has no `coverage_imports`, an empty string. * `%import_all%`: The string `True` if all repositories in the runfiles should be added to sys.path. The string `False` otherwise. * `%imports%`: A colon-delimited string of runfiles-relative paths to add to diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl index d46576eedd..80910cd045 100644 --- a/python/private/py_runtime_rule.bzl +++ b/python/private/py_runtime_rule.bzl @@ -18,6 +18,7 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load(":common_labels.bzl", "labels") load(":flags.bzl", "FreeThreadedFlag") +load(":py_info.bzl", "PyInfo") load(":py_internal.bzl", "py_internal") load(":py_runtime_info.bzl", "DEFAULT_STUB_SHEBANG", "PyRuntimeInfo") load(":reexports.bzl", "BuiltinPyRuntimeInfo") @@ -25,24 +26,6 @@ load(":version.bzl", "version") _py_builtins = py_internal -_COVERAGE_MAIN = "coverage/__main__.py" - -def _coverage_main(files): - """Return the `coverage/__main__.py` entry point from the coverage files. - - Args: - files: {type}`depset[File]` the files of the coverage tool target, e.g. - `@pypi//coverage:extracted_whl_files`. - - Returns: - {type}`File | None` the entry point of the coverage tool. - """ - for f in files.to_list(): - if f.short_path == _COVERAGE_MAIN or f.short_path.endswith("/" + _COVERAGE_MAIN): - return f - - return None - def coverage_tool_missing_message(*, coverage_enabled, coverage_tool, label): """Build the warning for a selected runtime that cannot produce coverage. @@ -168,26 +151,29 @@ def _py_runtime_impl(ctx): else: fail("interpreter must be an executable target or must produce exactly one file.") + coverage_tool = None + coverage_imports = None + coverage_files = None if ctx.attr.coverage_tool: # A list because of the Starlark transition on the attribute. - coverage_di = ctx.attr.coverage_tool[0][DefaultInfo] - - if _is_singleton_depset(coverage_di.files): + coverage_target = ctx.attr.coverage_tool[0] + coverage_di = coverage_target[DefaultInfo] + + if PyInfo in coverage_target: + # E.g. `@pypi//coverage`: the `coverage` package is imported using + # the import paths of the target. + coverage_imports = coverage_target[PyInfo].imports + elif _is_singleton_depset(coverage_di.files): coverage_tool = coverage_di.files.to_list()[0] elif coverage_di.files_to_run and coverage_di.files_to_run.executable: coverage_tool = coverage_di.files_to_run.executable else: - coverage_tool = _coverage_main(coverage_di.files) - if not coverage_tool: - fail("coverage_tool must be an executable target, must produce exactly one file or must contain '{}'.".format(_COVERAGE_MAIN)) + fail("coverage_tool must provide PyInfo, be an executable target or produce exactly one file.") coverage_files = depset(transitive = [ coverage_di.files, coverage_di.default_runfiles.files, ]) - else: - coverage_tool = None - coverage_files = None # Reported here rather than where the toolchains are registered: this rule is # analyzed once per configuration, and only for the runtime that toolchain @@ -195,7 +181,7 @@ def _py_runtime_impl(ctx): # hypothetical. See https://github.com/bazel-contrib/rules_python/issues/3950. coverage_warning = coverage_tool_missing_message( coverage_enabled = ctx.configuration.coverage_enabled, - coverage_tool = coverage_tool, + coverage_tool = coverage_tool or coverage_imports, label = ctx.label, ) if coverage_warning: @@ -236,7 +222,8 @@ def _py_runtime_impl(ctx): interpreter = interpreter, files = runtime_files if hermetic else None, coverage_tool = coverage_tool, - coverage_files = coverage_files, + # The builtin provider only knows about coverage_tool. + coverage_files = coverage_files if coverage_tool else None, python_version = python_version, stub_shebang = ctx.attr.stub_shebang, bootstrap_template = ctx.file.bootstrap_template, @@ -245,6 +232,8 @@ def _py_runtime_impl(ctx): # There are all args that BuiltinPyRuntimeInfo doesn't support py_runtime_info_kwargs.update(dict( + coverage_files = coverage_files, + coverage_imports = coverage_imports, implementation_name = ctx.attr.implementation_name, interpreter_version_info = interpreter_version_info, interpreter_files_to_run = interpreter_files_to_run, @@ -349,22 +338,23 @@ See @bazel_tools//tools/python:python_bootstrap_template.txt for more variables. This is a target to use for collecting code coverage information from {rule}`py_binary` and {rule}`py_test` targets. -If set, the target must either produce a single file or be an executable target. -The path to the single file, or the executable if the target is executable, -determines the entry point for the python coverage tool. The target and its -runfiles will be added to the runfiles when coverage is enabled. +If set, the target must either provide {obj}`PyInfo` (e.g. a `py_library` +such as `@pypi//coverage`), produce a single file or be an executable target. +If the target provides {obj}`PyInfo`, then its import paths are used to import +the `coverage` package. Otherwise, the path to the single file, or the +executable if the target is executable, determines the entry point for the +python coverage tool. The target and its runfiles will be added to the +runfiles when coverage is enabled. The entry point for the tool must be loadable by a Python interpreter (e.g. a `.py` or `.pyc` file). It must accept the command line arguments of [`coverage.py`](https://coverage.readthedocs.io), at least including the `run` and `lcov` subcommands. -If the target produces multiple files and one of them is -`coverage/__main__.py`, then it is used as the entry point, e.g. -`@pypi//coverage:extracted_whl_files`. See {ref}`coverage-from-pypi`. +See {ref}`coverage-from-pypi` for using `coverage` from a pip hub. :::{versionchanged} VERSION_NEXT_FEATURE -The extracted files of the `coverage` wheel can be used. +Targets providing {obj}`PyInfo`, such as `@pypi//coverage`, can be used. ::: """, ), diff --git a/python/private/site_init_template.py b/python/private/site_init_template.py index 81891bbd52..4436527245 100644 --- a/python/private/site_init_template.py +++ b/python/private/site_init_template.py @@ -27,6 +27,9 @@ _SELF_RUNFILES_RELATIVE_PATH = "%site_init_runfiles_path%" # Runfiles-relative path to the coverage tool entry point, if any. _COVERAGE_TOOL = "%coverage_tool%" +# Colon-delimited string of runfiles-relative import paths of the coverage +# package, if any. +_COVERAGE_IMPORTS = "%coverage_imports%" # True if the runfiles root should be added to sys.path _ADD_RUNFILES_ROOT_TO_SYS_PATH = "%add_runfiles_root_to_sys_path%" == "1" _INTERPRETER_ACTUAL_PATH = "%interpreter_actual_path%" @@ -53,6 +56,7 @@ def _print_verbose(*args, mapping=None, values=None): _print_verbose("workspace_name:", _WORKSPACE_NAME) _print_verbose("self_runfiles_path:", _SELF_RUNFILES_RELATIVE_PATH) _print_verbose("coverage_tool:", _COVERAGE_TOOL) +_print_verbose("coverage_imports:", _COVERAGE_IMPORTS) _print_verbose("interpreter_actual_path:", _INTERPRETER_ACTUAL_PATH) @@ -209,7 +213,12 @@ def _maybe_add_path(path, reason): # one executed by this one (e.g. an extension module). # NOTE: Coverage is added last to allow user dependencies to override it. coverage_setup = False - if os.environ.get("COVERAGE_DIR"): + if os.environ.get("COVERAGE_DIR") and _COVERAGE_IMPORTS: + _print_verbose_coverage(f"Using toolchain coverage_imports {_COVERAGE_IMPORTS}") + for rel_path in _COVERAGE_IMPORTS.split(":"): + _maybe_add_path(os.path.join(_RUNFILES_ROOT, rel_path), "coverage-imports") + coverage_setup = True + elif os.environ.get("COVERAGE_DIR"): cov_tool = _COVERAGE_TOOL if cov_tool: _print_verbose_coverage(f"Using toolchain coverage_tool {cov_tool}") diff --git a/tests/py_runtime/py_runtime_tests.bzl b/tests/py_runtime/py_runtime_tests.bzl index 8124f889d3..bc2a92b7e2 100644 --- a/tests/py_runtime/py_runtime_tests.bzl +++ b/tests/py_runtime/py_runtime_tests.bzl @@ -17,6 +17,7 @@ load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") load("@rules_testing//lib:truth.bzl", "matching") load("@rules_testing//lib:util.bzl", rt_util = "util") +load("//python:py_library.bzl", "py_library") load("//python:py_runtime.bzl", "py_runtime") load("//python:py_runtime_info.bzl", "PyRuntimeInfo") load("//python/private:common_labels.bzl", "labels") # buildifier: disable=bzl-visibility @@ -232,39 +233,45 @@ def _test_coverage_tool_plain_files_impl(env, target): _tests.append(_test_coverage_tool_plain_files) -def _test_coverage_tool_extracted_whl_files(name): +def _test_coverage_tool_py_library(name): rt_util.helper_target( py_runtime, name = name + "_subject", python_version = "PY3", interpreter_path = "/bogus", - coverage_tool = name + "_extracted_whl_files", + coverage_tool = name + "_coverage", ) + + # Like `@pypi//coverage`, which depends on the Python toolchain. rt_util.helper_target( - native.filegroup, - name = name + "_extracted_whl_files", + py_library, + name = name + "_coverage", srcs = [ "site-packages/coverage/__init__.py", "site-packages/coverage/__main__.py", ], + imports = ["site-packages"], ) analysis_test( name = name, target = name + "_subject", - impl = _test_coverage_tool_extracted_whl_files_impl, + impl = _test_coverage_tool_py_library_impl, ) -def _test_coverage_tool_extracted_whl_files_impl(env, target): +def _test_coverage_tool_py_library_impl(env, target): info = env.expect.that_target(target).provider(PyRuntimeInfo, factory = py_runtime_info_subject) - info.coverage_tool().short_path_equals("{package}/site-packages/coverage/__main__.py") + env.expect.that_bool(info.actual.coverage_tool == None).equals(True) + info.coverage_imports().contains_exactly_predicates([ + matching.str_endswith("tests/py_runtime/site-packages"), + ]) info.coverage_files().contains_at_least([ "{package}/site-packages/coverage/__init__.py", "{package}/site-packages/coverage/__main__.py", ]) -_tests.append(_test_coverage_tool_extracted_whl_files) +_tests.append(_test_coverage_tool_py_library) -def _test_coverage_tool_multiple_files_without_main(name): +def _test_coverage_tool_multiple_files(name): rt_util.helper_target( py_runtime, name = name + "_subject", @@ -280,16 +287,16 @@ def _test_coverage_tool_multiple_files_without_main(name): analysis_test( name = name, target = name + "_subject", - impl = _test_coverage_tool_multiple_files_without_main_impl, + impl = _test_coverage_tool_multiple_files_impl, expect_failure = True, ) -def _test_coverage_tool_multiple_files_without_main_impl(env, target): +def _test_coverage_tool_multiple_files_impl(env, target): env.expect.that_target(target).failures().contains_predicate( - matching.str_matches("coverage_tool must*contain 'coverage/__main__.py'"), + matching.str_matches("coverage_tool must provide PyInfo*produce exactly one file"), ) -_tests.append(_test_coverage_tool_multiple_files_without_main) +_tests.append(_test_coverage_tool_multiple_files) def _test_in_build_interpreter(name): rt_util.helper_target( diff --git a/tests/support/py_runtime_info_subject.bzl b/tests/support/py_runtime_info_subject.bzl index 0c6c672743..48455b0e5d 100644 --- a/tests/support/py_runtime_info_subject.bzl +++ b/tests/support/py_runtime_info_subject.bzl @@ -34,6 +34,7 @@ def py_runtime_info_subject(info, *, meta): actual = info, bootstrap_template = lambda *a, **k: _py_runtime_info_subject_bootstrap_template(self, *a, **k), coverage_files = lambda *a, **k: _py_runtime_info_subject_coverage_files(self, *a, **k), + coverage_imports = lambda *a, **k: _py_runtime_info_subject_coverage_imports(self, *a, **k), coverage_tool = lambda *a, **k: _py_runtime_info_subject_coverage_tool(self, *a, **k), files = lambda *a, **k: _py_runtime_info_subject_files(self, *a, **k), interpreter = lambda *a, **k: _py_runtime_info_subject_interpreter(self, *a, **k), @@ -69,6 +70,17 @@ def _py_runtime_info_subject_coverage_files(self): meta = self.meta.derive("coverage_files()"), ) +def _py_runtime_info_subject_coverage_imports(self): + """Returns a `CollectionSubject` for the `coverage_imports` attribute. + + Args: + self: implicitly added. + """ + return subjects.collection( + self.actual.coverage_imports.to_list(), + meta = self.meta.derive("coverage_imports()"), + ) + def _py_runtime_info_subject_coverage_tool(self): return subjects.file( self.actual.coverage_tool, From 1f8e9a8af25130c4efeba902a6ed11cb85148354 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 19:57:40 +0900 Subject: [PATCH 16/39] feat(pypi): support target_platforms = ["*"] in pip.parse Allow selecting all of the platforms configured with `pip.default` without listing them one by one. This is useful for hubs that need to support cross-building for any of the configured platforms, e.g. a hub that provides a tool for every toolchain. --- news/pip-target-platforms-all.added.md | 2 + python/private/pypi/extension.bzl | 7 ++++ python/private/pypi/hub_builder.bzl | 4 ++ tests/pypi/hub_builder/hub_builder_tests.bzl | 40 ++++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 news/pip-target-platforms-all.added.md diff --git a/news/pip-target-platforms-all.added.md b/news/pip-target-platforms-all.added.md new file mode 100644 index 0000000000..cb690ce07b --- /dev/null +++ b/news/pip-target-platforms-all.added.md @@ -0,0 +1,2 @@ +(pypi) {attr}`pip.parse.target_platforms` accepts `"*"` to evaluate the +requirements for all of the platforms configured with {obj}`pip.default`. diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index 71423e4e8b..908a40b90d 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -906,11 +906,18 @@ function in general are the same as used in the {obj}`pip.default.platform` attr If you only care for the host platform and do not have a usecase to cross-build, then you can put in a string `"{os}_{arch}"` as the value here. You could also use `"{os}_{arch}_freethreaded"` as well. +Use `"*"` to evaluate the requirements for all of the platforms configured with +{obj}`pip.default`. + :::{include} /_includes/experimental_api.md ::: :::{versionadded} 1.8.0 ::: + +:::{versionchanged} VERSION_NEXT_FEATURE +Added support for `"*"` to select all of the configured platforms. +::: """, ), "uv_lock": attr.label( diff --git a/python/private/pypi/hub_builder.bzl b/python/private/pypi/hub_builder.bzl index ef6a7247c1..df0c48794e 100644 --- a/python/private/pypi/hub_builder.bzl +++ b/python/private/pypi/hub_builder.bzl @@ -488,6 +488,10 @@ def _platforms(module_ctx, *, python_version, config, target_platforms): strict = True, ) + if "*" in target_platforms: + # All of the configured platforms. + target_platforms = [] + target_platforms = sorted({ p.format( os = repo_utils.get_platforms_os_name(module_ctx), diff --git a/tests/pypi/hub_builder/hub_builder_tests.bzl b/tests/pypi/hub_builder/hub_builder_tests.bzl index 073ccc0f58..0a5c319311 100644 --- a/tests/pypi/hub_builder/hub_builder_tests.bzl +++ b/tests/pypi/hub_builder/hub_builder_tests.bzl @@ -176,6 +176,46 @@ def _test_uv_lock_only(env): _tests.append(_test_uv_lock_only) +def _test_target_platforms_all(env): + builder = hub_builder(env) + builder.pip_parse( + _mock_mctx( + os_name = "osx", + arch_name = "aarch64", + mock_files = { + "uv.lock": """{"package":[{"name":"simple","source":{"registry":"https://pypi.org/simple"},"version":"0.0.1","wheels":[{"hash":"sha256:deadbeef","url":"https://files.pythonhosted.org/packages/simple-0.0.1-py3-none-any.whl"}]}]}""", + }, + ), + _parse( + hub_name = "pypi", + python_version = "3.15", + requirements_lock = None, + target_platforms = ["*"], + uv_lock = "uv.lock", + ), + ) + pypi = builder.build() + + pypi.exposed_packages().contains_exactly(["simple"]) + pypi.whl_map().contains_exactly({ + "simple": { + "pypi_315_simple_py3_none_any_deadbeef": [ + whl_config_setting( + target_platforms = ( + "cp315_linux_aarch64", + "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", + "cp315_osx_aarch64", + "cp315_windows_aarch64", + ), + version = "3.15", + ), + ], + }, + }) + +_tests.append(_test_target_platforms_all) + def _test_simple_multiple_requirements(env): sub_tests = { ("osx", "aarch64"): "simple==0.0.2 --hash=sha256:deadb00f", From a26b77c1225ee53fb85b15c16dc735953f3c5180 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:03:38 +0900 Subject: [PATCH 17/39] feat(toolchain): use the rules_python_coverage hub as the bzlmod coverage tool Define the `coverage` dependency in a separate `pyproject.toml` with a `uv.lock` file, which locks the latest `coverage` version for each Python version starting from 3.9. The lock file is used by the new `rules_python_coverage` pip hub, which has the wheels for all of the platforms configured with `pip.default`. With bzlmod, `python.toolchain(configure_coverage_tool = True)` now uses `@rules_python_coverage//coverage` as the coverage tool. WORKSPACE still uses the bundled coverage wheels. --- MODULE.bazel | 25 +- news/4415.changed.md | 7 + python/private/coverage/BUILD.bazel | 21 + python/private/coverage/pyproject.toml | 22 + python/private/coverage/uv.lock | 411 ++++++++++++++++++ python/private/python.bzl | 13 +- python/private/python_register_toolchains.bzl | 11 +- 7 files changed, 507 insertions(+), 3 deletions(-) create mode 100644 news/4415.changed.md create mode 100644 python/private/coverage/BUILD.bazel create mode 100644 python/private/coverage/pyproject.toml create mode 100644 python/private/coverage/uv.lock diff --git a/MODULE.bazel b/MODULE.bazel index 33e67ab9e8..8caedea391 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -97,7 +97,30 @@ pip.dep(name = "pytest") pip.dep(name = "pytest-bazel") pip.dep(name = "pytest-randomly") pip.dep(name = "pytest-shard") -use_repo(pip, "pypi", "rules_python_publish_deps") + +# The default coverage tool for the hermetic toolchains, see +# `python.toolchain.configure_coverage_tool`. The versions that do not have a +# registered toolchain are ignored. +[ + pip.parse( + download_only = True, + hub_name = "rules_python_coverage", + python_version = python_version, + target_platforms = ["*"], + uv_lock = "//python/private/coverage:uv.lock", + ) + for python_version in [ + "3.9", + "3.10", + "3.11", + "3.12", + "3.13", + "3.14", + "3.15", + ] +] + +use_repo(pip, "pypi", "rules_python_coverage", "rules_python_publish_deps") bazel_dep( name = "rules_rust", diff --git a/news/4415.changed.md b/news/4415.changed.md new file mode 100644 index 0000000000..923caeee80 --- /dev/null +++ b/news/4415.changed.md @@ -0,0 +1,7 @@ +(toolchains) With bzlmod, {attr}`python.toolchain.configure_coverage_tool` +uses the `coverage` package from the new `@rules_python_coverage` pip hub, which +locks the latest `coverage` version for each Python version starting from 3.9 +and supports all of the platforms configured with {obj}`pip.default`. Passing a +`py_library` as the {attr}`py_runtime.coverage_tool` is now the preferred way, +and using a target without `PyInfo` will be deprecated in the next version +([#3902](https://github.com/bazel-contrib/rules_python/issues/3902)). diff --git a/python/private/coverage/BUILD.bazel b/python/private/coverage/BUILD.bazel new file mode 100644 index 0000000000..f0d6dc59f5 --- /dev/null +++ b/python/private/coverage/BUILD.bazel @@ -0,0 +1,21 @@ +load("//python/private:distribution_filegroup.bzl", "distribution_filegroup") +load("//python/uv:lock.bzl", "lock") + +package(default_visibility = ["//:__subpackages__"]) + +exports_files(["uv.lock"]) + +distribution_filegroup( + name = "distribution", +) + +# Run `bazel run //python/private/coverage:requirements.update` to update the +# `uv.lock` file. +lock( + name = "requirements", + srcs = ["pyproject.toml"], + out = "uv.lock", + args = [ + "--upgrade", # always upgrade + ], +) diff --git a/python/private/coverage/pyproject.toml b/python/private/coverage/pyproject.toml new file mode 100644 index 0000000000..0bd2d84186 --- /dev/null +++ b/python/private/coverage/pyproject.toml @@ -0,0 +1,22 @@ +# The coverage tool used by default for the hermetic toolchains when +# `python.toolchain(configure_coverage_tool = True)` is used. +# +# Newer coverage versions drop support for the older python versions, so there +# is a dependency for each python version so that the latest supported version +# is locked for each of them. +# +# Run `bazel run //python/private/coverage:requirements.update` to update the +# `uv.lock` file. +[project] +name = "rules_python_coverage" +version = "0.0.0" +requires-python = ">=3.9" +dependencies = [ + "coverage; python_full_version < '3.10'", + "coverage; python_full_version >= '3.10' and python_full_version < '3.11'", + "coverage; python_full_version >= '3.11' and python_full_version < '3.12'", + "coverage; python_full_version >= '3.12' and python_full_version < '3.13'", + "coverage; python_full_version >= '3.13' and python_full_version < '3.14'", + "coverage; python_full_version >= '3.14' and python_full_version < '3.15'", + "coverage; python_full_version >= '3.15'", +] diff --git a/python/private/coverage/uv.lock b/python/private/coverage/uv.lock new file mode 100644 index 0000000000..c593457416 --- /dev/null +++ b/python/private/coverage/uv.lock @@ -0,0 +1,411 @@ +version = 1 +revision = 5 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] + +[[package]] +name = "coverage" +version = "7.10.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", size = 217987, upload-time = "2025-09-21T20:00:57.218Z" }, + { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", size = 218388, upload-time = "2025-09-21T20:01:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", size = 245148, upload-time = "2025-09-21T20:01:01.768Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", size = 246958, upload-time = "2025-09-21T20:01:03.355Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", size = 248819, upload-time = "2025-09-21T20:01:04.968Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", size = 245754, upload-time = "2025-09-21T20:01:06.321Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", size = 246860, upload-time = "2025-09-21T20:01:07.605Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", size = 244877, upload-time = "2025-09-21T20:01:08.829Z" }, + { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", size = 245108, upload-time = "2025-09-21T20:01:10.527Z" }, + { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", size = 245752, upload-time = "2025-09-21T20:01:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", size = 220497, upload-time = "2025-09-21T20:01:13.459Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", size = 221392, upload-time = "2025-09-21T20:01:14.722Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", size = 218102, upload-time = "2025-09-21T20:01:16.089Z" }, + { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", size = 218505, upload-time = "2025-09-21T20:01:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", size = 248898, upload-time = "2025-09-21T20:01:19.488Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", size = 250831, upload-time = "2025-09-21T20:01:20.817Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", size = 252937, upload-time = "2025-09-21T20:01:22.171Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", size = 249021, upload-time = "2025-09-21T20:01:23.907Z" }, + { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", size = 250626, upload-time = "2025-09-21T20:01:25.721Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", size = 248682, upload-time = "2025-09-21T20:01:27.105Z" }, + { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", size = 248402, upload-time = "2025-09-21T20:01:28.629Z" }, + { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", size = 249320, upload-time = "2025-09-21T20:01:30.004Z" }, + { url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", size = 220536, upload-time = "2025-09-21T20:01:32.184Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", size = 221425, upload-time = "2025-09-21T20:01:33.557Z" }, + { url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", size = 220103, upload-time = "2025-09-21T20:01:34.929Z" }, + { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", size = 218290, upload-time = "2025-09-21T20:01:36.455Z" }, + { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", size = 218515, upload-time = "2025-09-21T20:01:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", size = 250020, upload-time = "2025-09-21T20:01:39.617Z" }, + { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", size = 252769, upload-time = "2025-09-21T20:01:41.341Z" }, + { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", size = 253901, upload-time = "2025-09-21T20:01:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", size = 250413, upload-time = "2025-09-21T20:01:44.469Z" }, + { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", size = 251820, upload-time = "2025-09-21T20:01:45.915Z" }, + { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", size = 249941, upload-time = "2025-09-21T20:01:47.296Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", size = 249519, upload-time = "2025-09-21T20:01:48.73Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", size = 251375, upload-time = "2025-09-21T20:01:50.529Z" }, + { url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", size = 220699, upload-time = "2025-09-21T20:01:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", size = 221512, upload-time = "2025-09-21T20:01:53.481Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", size = 220147, upload-time = "2025-09-21T20:01:55.2Z" }, + { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, + { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, + { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, + { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, + { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, + { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, + { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, + { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, + { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, + { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", size = 220746, upload-time = "2025-09-21T20:02:13.919Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", size = 221541, upload-time = "2025-09-21T20:02:15.57Z" }, + { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", size = 220170, upload-time = "2025-09-21T20:02:17.395Z" }, + { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, + { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, + { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, + { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, + { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, + { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, + { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, + { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, + { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, + { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, + { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, + { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, + { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, + { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, + { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, + { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, + { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", size = 217978, upload-time = "2025-09-21T20:03:30.362Z" }, + { url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", size = 218370, upload-time = "2025-09-21T20:03:32.147Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", size = 244802, upload-time = "2025-09-21T20:03:33.919Z" }, + { url = "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40", size = 246625, upload-time = "2025-09-21T20:03:36.09Z" }, + { url = "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594", size = 248399, upload-time = "2025-09-21T20:03:38.342Z" }, + { url = "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a", size = 245142, upload-time = "2025-09-21T20:03:40.591Z" }, + { url = "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b", size = 246284, upload-time = "2025-09-21T20:03:42.355Z" }, + { url = "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3", size = 244353, upload-time = "2025-09-21T20:03:44.218Z" }, + { url = "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0", size = 244430, upload-time = "2025-09-21T20:03:46.065Z" }, + { url = "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f", size = 245311, upload-time = "2025-09-21T20:03:48.19Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431", size = 220500, upload-time = "2025-09-21T20:03:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07", size = 221408, upload-time = "2025-09-21T20:03:51.803Z" }, + { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, +] + +[[package]] +name = "coverage" +version = "7.16.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/55/d1eaf3e73781174340a00dc1ba2aee8a65f82fadb18e2797b192b6b3925b/coverage-7.16.2.tar.gz", hash = "sha256:ca64d9f1f384f151b9511bec01126072acd2f313439f8ed015a22d8790aab6fa", size = 971999, upload-time = "2026-09-27T12:29:01.118Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/72/db17bf5f87568ab524413693385be2eeb03e652ab56c54ea05fa85515675/coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23219888477edd736b6fcaec1272d47d93b926e999641ffea7e53a1738e70b2b", size = 223670, upload-time = "2026-09-27T12:25:34.675Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e1/285727a8a74d48de256e8605413ee4cf82301228253ea58e5ab4270138b0/coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40c0f00899fe6181ae7f434ceb200e51f5ee4b8ed10e3b5f0b605f0cae15da87", size = 224191, upload-time = "2026-09-27T12:25:36.476Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/c737b73bac9bf5034f5ff45a4237b7a189d417a4e36751faf5d10c082cbe/coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4624f80732f6b427ac58f1f59c577a0994a12e8174b5af6a027b4b58795d4c3", size = 250921, upload-time = "2026-09-27T12:25:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/29/4e/e1d38d27817d91776ca543d01276a67eace3df675a673cac32b12c167d58/coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:191803c4996b499fcd78c2ad5e5f767dcc53cb4dc6de6d6a741b443a1821ef02", size = 252751, upload-time = "2026-09-27T12:25:39.291Z" }, + { url = "https://files.pythonhosted.org/packages/0e/19/14a8e44cbb2ad36ae62aaa03c5eec4a06a7f7df220ea3776af26927acbe2/coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fd670ac43b709c575aefc25bf52d8a598a3bc5017bddfd0a179152ab06a2deb", size = 254616, upload-time = "2026-09-27T12:25:40.506Z" }, + { url = "https://files.pythonhosted.org/packages/f9/72/f5bcad0d9a9b450080032344fbff7ec60c1b0e3d38019f7735dee2b69645/coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:705e5af11d34647efdc170c7840b6857c81cf74be96419a553f237e68e62cb72", size = 256541, upload-time = "2026-09-27T12:25:41.785Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9a/8c735234e8abb52bf5d063f98c780fa942e77c4c0225f9fcb9b33effc346/coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8afd9bf35cc6a1f22eb3634808fa8e0b91902459c5721ef2e4461dfe771d7f08", size = 251589, upload-time = "2026-09-27T12:25:43.269Z" }, + { url = "https://files.pythonhosted.org/packages/35/6a/1bf6d32e55642d6972aa842640e0a8850e612d19ac7d05e2c32fa59dfd34/coverage-7.16.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f43bac1856ba269b905302778d4df433d6006489a192174ad77ac528e395032", size = 252660, upload-time = "2026-09-27T12:25:45.051Z" }, + { url = "https://files.pythonhosted.org/packages/53/5b/05b1c0d0e9495cb056155a16066259adc1e935e7411a82a62779bf729975/coverage-7.16.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f8475460aa33ee28ac896ab1156d0bb3b6c639f7f8383c2677d3359eb35f8205", size = 250660, upload-time = "2026-09-27T12:25:46.51Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/5bc3db57fc9c56b4ef055725c38c34faa818a27d17d65e251371f7f0e3a0/coverage-7.16.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d6276d78f6fca7d0ac066d5da4165c5acd07829e8305c2cb900b738fb3a75a72", size = 254479, upload-time = "2026-09-27T12:25:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0f/4a5de66daef26eb919213f63b84f45d5065ab2df94cd767fc7c1174ac5e2/coverage-7.16.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:736fde09ea39646d11f8e3b76bd3425c075aa4dd45f24891970bb77c14ff20f5", size = 250940, upload-time = "2026-09-27T12:25:49.227Z" }, + { url = "https://files.pythonhosted.org/packages/57/d3/84cd6a11e707e422194739e9734d947746efa5e5b3358ff2d601f823ee45/coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c85d54e7e8a2ca932fe8399301af9b8d5907ea2a455ffaff6e7d1208db83b943", size = 251543, upload-time = "2026-09-27T12:25:50.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/bc/3d84c2e2a95f38e346ce3730d9f0e53114f4e8aace72e6e51e014afb9992/coverage-7.16.2-cp310-cp310-win32.whl", hash = "sha256:5139009b5efd2194fc168ee9362f0e191ba612ef5d29242f9269c22f9b8f80c7", size = 225732, upload-time = "2026-09-27T12:25:51.945Z" }, + { url = "https://files.pythonhosted.org/packages/07/86/31f1f3170571a345ab9d8361a7b2f8e0c5698173fbecd1f7149b6e2089ee/coverage-7.16.2-cp310-cp310-win_amd64.whl", hash = "sha256:c3305c38a2fa21a4254f2ace7dd9ef5fc569c9a558b66e7017650b3d637fb95e", size = 226357, upload-time = "2026-09-27T12:25:53.272Z" }, + { url = "https://files.pythonhosted.org/packages/58/fa/ce3baf63d85b730398d92a7162f486f3a5e4e2cc3382a02488b3943725ba/coverage-7.16.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:732d950e51f3ba4fb6209c73250f3e8924fefca42953ee04a9e65d8c02414d7d", size = 223797, upload-time = "2026-09-27T12:25:54.756Z" }, + { url = "https://files.pythonhosted.org/packages/7a/57/9ba29c2aac7f756d479f03d45762120060f0f988788001001bf36e0e6fca/coverage-7.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5dca0bb66b4c3d624ba047887bf70270030c150692d543cb501293dc38a9f4b5", size = 224303, upload-time = "2026-09-27T12:25:56.214Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/0d6d60906dca7d28cc1e3fce12a9861801c4fbb6cbf220ad78cd059c9467/coverage-7.16.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:af2a2a8c7c74de0559e0c368d94c8def9e16c58faaee33a0bf081057c4227e3b", size = 254715, upload-time = "2026-09-27T12:25:57.755Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b8/9198b865679379fb165c689c64f6e11105ef380f6bd1c7673e83f73d9f5c/coverage-7.16.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db5f8394e17f877a625b257f2ba0ce8e728a499c2c1579ad66220272cd3df510", size = 256627, upload-time = "2026-09-27T12:25:59.131Z" }, + { url = "https://files.pythonhosted.org/packages/98/79/9521462cb6072fe394701bc8974b74afd576c9c9355156c7844e1a86a42b/coverage-7.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3146d2317c75f70df2509066d979dadd941f7021cdf9b5db4bcd8568258e25", size = 258724, upload-time = "2026-09-27T12:26:00.691Z" }, + { url = "https://files.pythonhosted.org/packages/a6/76/8d7d5d633db9fe0f3182fedc731bf09f9bcf2366055735152504ad614677/coverage-7.16.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e1d0ced76318bab499693ff25f64faa343415187cb2e4d7befdfdd391a1cf6a", size = 260692, upload-time = "2026-09-27T12:26:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/4e/a7/76cb09c89ba46d74d37428bf93251fc14fb0bbe9e05cc2a5ef61773d318a/coverage-7.16.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:af98ad5ed9d6daaca956201e00bb429a7eb2b080426686f70a20353e0f9839f5", size = 254800, upload-time = "2026-09-27T12:26:03.369Z" }, + { url = "https://files.pythonhosted.org/packages/72/b6/2351c1979aaeb5b4a8091a75b90ca997ad60de36e181ddba267cf61dac97/coverage-7.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d56e4d21c56d2046447733f8b118409597db48c01efe898ee9ac24e858ec2d6", size = 256423, upload-time = "2026-09-27T12:26:04.751Z" }, + { url = "https://files.pythonhosted.org/packages/0f/f4/ad9a4f8b5cb2d494fa9452b546fe742ed2f9d3847cc14c05e36279a3e649/coverage-7.16.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1d5d0e3b660506fb84f995814e3118a21efdc0c8eb80127da1be627d90093c17", size = 254487, upload-time = "2026-09-27T12:26:06.082Z" }, + { url = "https://files.pythonhosted.org/packages/6c/1f/a520470472f3e8b01169bf42162b1470c9ba992230432f62ca36269bf3a0/coverage-7.16.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:17228fbca0f22976f797be94e975dcd237799c657d49551c7de1e0654d1202e9", size = 258259, upload-time = "2026-09-27T12:26:07.513Z" }, + { url = "https://files.pythonhosted.org/packages/09/d2/ff26d5938274745855fa61cfcba0245c88ccc10d98d2cbd96064f16cd5a7/coverage-7.16.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:bc0b0ac781d489304b741269857f1f8338b7a26b1b89c06c0344658001ec0035", size = 254216, upload-time = "2026-09-27T12:26:08.982Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1d/5d832d3b06785d9f53267e4f2724a9f60c312eee6ebed9063a461d0d3b45/coverage-7.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bf1bd822ec4e387ed245bed0d71151582cf7be9e5309bc4145eefe36083d5878", size = 255125, upload-time = "2026-09-27T12:26:10.35Z" }, + { url = "https://files.pythonhosted.org/packages/55/4d/1d33edbc2fcf7d99e384e393e712aa5a2ebbbd8409825357815982207976/coverage-7.16.2-cp311-cp311-win32.whl", hash = "sha256:7ed238d227e23cc300c3d464babdaf9f6ddc740aa1b15a77ae96136e6a7c4516", size = 225898, upload-time = "2026-09-27T12:26:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/6f/7c/676df4882118756c4f8f560c954eddb93e166d84dda8c5f0b6a829689bde/coverage-7.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:a90700f743e29aa3d75a6ff5f01953176a889c00e526194bc4d281731b88d99d", size = 226369, upload-time = "2026-09-27T12:26:13.375Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0e/a457f4a461b3c5610d845137fdd45fa465e011a64c25af440518ab1f4e41/coverage-7.16.2-cp311-cp311-win_arm64.whl", hash = "sha256:a336eec40e3520d369b8a6cdabb4f596e69a8b42927ca074aa1452fed943238a", size = 226344, upload-time = "2026-09-27T12:26:15.127Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/f8296c63c5d542f3d21aed685e56b7031a419037d155bb3382fc0940d249/coverage-7.16.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:218d742afca2b5ad5ca759e93eddedfbcc6eadf8322f080dcefc40b7bd4e2d48", size = 223969, upload-time = "2026-09-27T12:26:16.753Z" }, + { url = "https://files.pythonhosted.org/packages/90/23/6f3dcb1423a0d43216e402ea1746e4a7c7c44f38896b97dd573790f56a40/coverage-7.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9a638be322a8d76a41cdb17781c7f82aaee6a66493d8ffb7e2c09ee22423d99", size = 224328, upload-time = "2026-09-27T12:26:18.15Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7d/8f3b6dc920e3fc6732f7678785a2091db439f186afbec30dbf2214d9b1f7/coverage-7.16.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:724bd0f1e81856b35e59fc98cf7b4e544a3cb662e4e0864dca73d4326ee9d808", size = 255832, upload-time = "2026-09-27T12:26:19.799Z" }, + { url = "https://files.pythonhosted.org/packages/d1/36/6c45f15be4eca4ac1062c6a55a323286494c99726a7e58951fe85967ac08/coverage-7.16.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5375ebd99038021b35e99dc88255022912c06565d316212f4a576e4b08d30f5d", size = 258571, upload-time = "2026-09-27T12:26:21.199Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/b54cbeba3ad89082c2e441278681859e538322cc34b84b2af7ebff00080f/coverage-7.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a076277ca9f5750cc230f0f578ebd2620cec60255b25707361699fef6fb465c", size = 259680, upload-time = "2026-09-27T12:26:22.822Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a2/0dc65ec3d61930e1e4c2e371763b15eb4290896eb343a12d5d3091308116/coverage-7.16.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:58d4a54c6ea672afef66d49be922a2c69826c5ae1a42a9cd94f0c9c2bacdf800", size = 261945, upload-time = "2026-09-27T12:26:24.336Z" }, + { url = "https://files.pythonhosted.org/packages/d6/93/5fad7a61f2c14e08e98946fc31c1c7ffc1195061bf3fdc351db3be77a863/coverage-7.16.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dcbcfcc059117284c603ff8cb61a65872512882f84a8cf0339241f7f7c2f148", size = 256191, upload-time = "2026-09-27T12:26:25.89Z" }, + { url = "https://files.pythonhosted.org/packages/2d/47/74e5de9227b939ece9f64e729645ddc4296bea10dbfa98721c1333c8be2e/coverage-7.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afdf43b72ef3876c1fe66423b91466e37877c9e81e8cec70542b7e8525b9d1b7", size = 257610, upload-time = "2026-09-27T12:26:27.35Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/2cf28d40b43645d1b72388fe3ee7f7c747533a6a9557bb8c24a7ae74fe1a/coverage-7.16.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9acc7f7ec4a1b5f89bd929fde5b8a714f6fafdc6cc18725413d510aa082b47ad", size = 255757, upload-time = "2026-09-27T12:26:28.949Z" }, + { url = "https://files.pythonhosted.org/packages/d7/3d/7c149fd99fc8bbc39c80db5e688d1d39fd040be2ecb78b8335a51a55b9c0/coverage-7.16.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:80d3f7b48d43ee8fc5e8707a8adb43d743a5a1a85256c25a24f9d6d0e2238fa6", size = 259822, upload-time = "2026-09-27T12:26:30.515Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/b283fce09d5995e227bd8e513358dd7471bedc0f78abc85a925ebdb0a2f6/coverage-7.16.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:126d1af8804d7224421fe991ff65d3ce649081560df7a98b1a5ffff07f9923bd", size = 255325, upload-time = "2026-09-27T12:26:32.037Z" }, + { url = "https://files.pythonhosted.org/packages/bf/91/f3325edf0c4223fb1fe1532b8dbef2a1d2f729459a9a7d1a44d073bae534/coverage-7.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c19cd6d025c1673f22afcd22c7df8a662d779e05d8e3fa6820c22afb895b0206", size = 257191, upload-time = "2026-09-27T12:26:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/c4/89/21eb5e83ecf2eed523c4eb3d65ae513cd082c8fd1b6deb34c4cb6c332f97/coverage-7.16.2-cp312-cp312-win32.whl", hash = "sha256:152877cdc8a07264882cfcd503ba56a3ef6cba56a70e8c70f6eb8ffd7384789a", size = 226034, upload-time = "2026-09-27T12:26:35.021Z" }, + { url = "https://files.pythonhosted.org/packages/db/de/e3ad6d864c0833624b4f1f9b53f9e58e116c945e5e965c3f1e172c5e84cd/coverage-7.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:e6c52d3307824ff93b39efd99e4185d557db40bd841452abfb32e5d9151ca162", size = 226569, upload-time = "2026-09-27T12:26:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c1/bccc58ebe5489cc70628f635c1932fd371f5d7da850dbcf960f95f4c4afc/coverage-7.16.2-cp312-cp312-win_arm64.whl", hash = "sha256:a678c0b6b22086ec2427359d22e37445d4a792f5fdbbc744112c7dade65cad02", size = 226372, upload-time = "2026-09-27T12:26:38.406Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f6/8eb4f220ef24f84fb27d852d4f9bf83e0c73ec1a4a08dd9a87e3f4529739/coverage-7.16.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1a37c6e478cf687e1aa30a593d19c92c02fad9d122b51ab73f51b8dc7a0c0fc9", size = 223993, upload-time = "2026-09-27T12:26:40.164Z" }, + { url = "https://files.pythonhosted.org/packages/40/23/d4bbaf0c154e0b0c2b5264890dbf6ef098dcb50ec8f2469be9490d191660/coverage-7.16.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0993d0e90858c03943d3cb152e068a20dd4707924deec84dd2230261baae3b1b", size = 224368, upload-time = "2026-09-27T12:26:41.762Z" }, + { url = "https://files.pythonhosted.org/packages/7f/48/fc1e88fd571ec5cb38150b7f89f7696ca1bdf9920e01432febb69774cc85/coverage-7.16.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bb2fc905bbf4e6b7f40806ea79e31515abf6349594cdf0adf27c4215f0463204", size = 255358, upload-time = "2026-09-27T12:26:43.442Z" }, + { url = "https://files.pythonhosted.org/packages/1d/56/6785397d07c29c8e70fbb9a07e97d062b43c21ffc5f12385917847f09f63/coverage-7.16.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4358b9c8c0125b460407f3017c6cce8156e904b32772c5630d27112f52bdbfe5", size = 257955, upload-time = "2026-09-27T12:26:45.725Z" }, + { url = "https://files.pythonhosted.org/packages/27/3b/c8cdd07721e5f99abd81cea970d971997f99bf158c0b85f51bd284179c8b/coverage-7.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f15254427c9b33eedac4f198eaf9e356eb4f6214551afb43da6194a2c088ad7", size = 259183, upload-time = "2026-09-27T12:26:47.208Z" }, + { url = "https://files.pythonhosted.org/packages/9b/11/606b192fe43d32574ec6238549d48de588fdcc18485682a5ec0a8ac357f2/coverage-7.16.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9a75a4704ff640e46170042eec1f984385a121227c505d5a16ad8e495f452541", size = 261325, upload-time = "2026-09-27T12:26:49.084Z" }, + { url = "https://files.pythonhosted.org/packages/67/90/eea481f8b0305ceeb33f081a5f47e298391dbd1b589de0c4b3b3aa50d3f2/coverage-7.16.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:14253fc7bb15749b849795a06f5d3b6d8bc3fb8a4b5ddc341faf7a89dce205fc", size = 255531, upload-time = "2026-09-27T12:26:50.509Z" }, + { url = "https://files.pythonhosted.org/packages/6b/be/dedbf9aea1457b120c27ac10b8fc2a357f37fa2b54c3e7286d42980a0a2a/coverage-7.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:921415102a90637fcc2e3f169f61dad7699ecf690e8639fc21b813acbedc0967", size = 257329, upload-time = "2026-09-27T12:26:52.005Z" }, + { url = "https://files.pythonhosted.org/packages/fa/cb/b25c19d5bb2bd0f2e4e27fe8e2ffcae80c7a91ae181c0dc749ed60e9b1a4/coverage-7.16.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cce2bc991293f15cc4084ca116827b5900c5f34e1a54dfe83f10ab5c43162eb7", size = 255294, upload-time = "2026-09-27T12:26:53.634Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a2/892c5c5f4ad44b7b2ca009aee705191f3f268f15052244f2f9e3539b2e35/coverage-7.16.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1fa594c887365b69745f25a416806e61085dd07b94c9eae68a6e20730629b23", size = 259444, upload-time = "2026-09-27T12:26:55.243Z" }, + { url = "https://files.pythonhosted.org/packages/ed/99/a562537deba0a3e370182ae71c149be796c39d8087365f17a09188f27145/coverage-7.16.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:11e597173af1dc33d5f8a7332ada544199269a223af1ee1770ddd5e245ad0fe8", size = 255111, upload-time = "2026-09-27T12:26:56.851Z" }, + { url = "https://files.pythonhosted.org/packages/2d/20/854ec68641a9b3362ff068a32dfa41637299761617ef253791dbade6fc76/coverage-7.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e7f99698ba3a7d13988bdd984b7ebf13af4dbe2166dc8502eef90d77603b0a4", size = 256882, upload-time = "2026-09-27T12:26:58.41Z" }, + { url = "https://files.pythonhosted.org/packages/db/0d/748e4518b0ac0f9ff2687c248a6e5f8c0737306e709372632a2556f84443/coverage-7.16.2-cp313-cp313-win32.whl", hash = "sha256:f80bd9f9633eafc73d0a913ba2645c96ba58bba1befc30590f7c0fbfde59d865", size = 226045, upload-time = "2026-09-27T12:26:59.983Z" }, + { url = "https://files.pythonhosted.org/packages/31/fa/6e46edba66a183fe4d99d4bb52c173287e9b8dddabe0888d24cb8210e580/coverage-7.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:8be099e979fc42559328a21828281b4578304191ae46ed4e80a407048a82eee6", size = 226581, upload-time = "2026-09-27T12:27:01.494Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d9/9ef6845367600b336ff75d000444a0d32497d6972c833141bd39356abf68/coverage-7.16.2-cp313-cp313-win_arm64.whl", hash = "sha256:28ff850182a67d117990fa2ce5ea1032836d8c9630dae867e8bdd3bff4533b79", size = 226409, upload-time = "2026-09-27T12:27:03.116Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/577fc0803dab4155dcf808faffbdd7b159256781c0874a8586e17b81b149/coverage-7.16.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4ee546b9e4872ffa194bf07ac87bfa1202ebb824d0795dc1ef22f175545ca90a", size = 224138, upload-time = "2026-09-27T12:27:05.141Z" }, + { url = "https://files.pythonhosted.org/packages/75/9e/e3785ba3ecba2bd11efc74bfe2801ca4b78c4480b15a375648d809a59da3/coverage-7.16.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a2fac6895eb299a2e52d7bbb8fb3903502b9da8d3f5309ceb16ec40c646b58ee", size = 224445, upload-time = "2026-09-27T12:27:06.805Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d0/963ff22d3fd27117da3b8cc442f5bdc91196f783321e1a8ff0ec43476772/coverage-7.16.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:57ff3783f99d75a1e81dd56a9737eb5665e6736a5d93258ba596b6dcad8fd05b", size = 256112, upload-time = "2026-09-27T12:27:08.43Z" }, + { url = "https://files.pythonhosted.org/packages/a8/d4/a306940c81c6ae759e82fff27d20b7fdc6896e422b821f51313cce212b6c/coverage-7.16.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f37886699cb9abd29958247d718628d5bc6f39e623dff66a09e546c42a7e03", size = 258727, upload-time = "2026-09-27T12:27:09.927Z" }, + { url = "https://files.pythonhosted.org/packages/b9/a3/d3d99d93b02517087aa05bc0cf2d04d372956b849e5443e059079901429b/coverage-7.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fd7a86fdda7cb6d616d178654bd0ad6bc0f3f33c2e478aa598500a1a9e34eda", size = 259909, upload-time = "2026-09-27T12:27:11.55Z" }, + { url = "https://files.pythonhosted.org/packages/08/44/39dd599181726758dd185ae4dc0c0ab3aeabf7ca70e68e145060feeaaa16/coverage-7.16.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ac0f3b379c94acc2f7dce5f5f0b24d44fa1cc6a509717ef83dfee07450c2117c", size = 262481, upload-time = "2026-09-27T12:27:13.17Z" }, + { url = "https://files.pythonhosted.org/packages/99/e8/91ee43f6ded411460c359d7e1aebde4d6fd8f00a2e5394182d9d212eb23c/coverage-7.16.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7d0732c83746bc24123c581a85d9dd96b70ddb538c9076020aa1a041790361e9", size = 256012, upload-time = "2026-09-27T12:27:14.91Z" }, + { url = "https://files.pythonhosted.org/packages/11/8c/e9499ddc33197bd7eabcb1118ca81756fc874457b324e2b479a4804b2ad2/coverage-7.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b451c68218c150f616bc9649783ec8de76a59792c759b43aa0c9c0466a465e4", size = 257898, upload-time = "2026-09-27T12:27:16.588Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6e/c081cb5991a0afba99f9c4ad6c74a5fce9513a38ddc64e3e6680c6fed9af/coverage-7.16.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a56ac4fa5a75c7e182e8f62600cfb4aff43c5ed7356a034f3557659c3bec1d90", size = 255938, upload-time = "2026-09-27T12:27:18.19Z" }, + { url = "https://files.pythonhosted.org/packages/b2/42/1c3d819e8f9b6eb01c2fe90874d67a8882adb9507e0bbb09361ed131ea89/coverage-7.16.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4cc4f73aa3fabc36e32046d6cd2971405948d8a903636508a3d3b2f9128b3a95", size = 260368, upload-time = "2026-09-27T12:27:19.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/4f/d70eac07901fd587b6ab05e659b52afe13959992aa5113bf6cce059cc572/coverage-7.16.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:723dcdab91357159b722935b500ee8abc0a66c8c432e1e9fabf4cc7598952de8", size = 255620, upload-time = "2026-09-27T12:27:21.621Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/6d87af88317d3d9a9b18a9ca1bc1673eb516917f296e579d0d4a55cb3490/coverage-7.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5397e21a90dde0e9c6896b77ded8f0be26b66f8b22b33aed41f6043ed95d55e6", size = 257521, upload-time = "2026-09-27T12:27:23.358Z" }, + { url = "https://files.pythonhosted.org/packages/79/bb/90c2641170d2fa1a6757b3f8450ba2740197317b0ddd749e9604b914e886/coverage-7.16.2-cp314-cp314-win32.whl", hash = "sha256:848893e1d361448c113dc2f0913503522a6f7be231d0e38333d2a22d9698a011", size = 226248, upload-time = "2026-09-27T12:27:25.153Z" }, + { url = "https://files.pythonhosted.org/packages/30/08/d8d0478bb02c8eb0ae20a496fc80c40fcf4d3450bd184300d682ba2d28a6/coverage-7.16.2-cp314-cp314-win_amd64.whl", hash = "sha256:5a27b731c171e43dc8b5f32b76a5051dde2ec9b9366c87028f08a7088ebc2c7b", size = 226732, upload-time = "2026-09-27T12:27:26.907Z" }, + { url = "https://files.pythonhosted.org/packages/32/3f/0001da22155b0a8ce063ec0f7e64ecbe17b373f306e7a74435f6d6accb72/coverage-7.16.2-cp314-cp314-win_arm64.whl", hash = "sha256:1c569a9fd25505f1cd6bea90588818f90373ce90e2632e2cacf19ddbd6e14fdb", size = 226645, upload-time = "2026-09-27T12:27:28.588Z" }, + { url = "https://files.pythonhosted.org/packages/d7/85/6d8813aff9b8b8586691a9d33c43c5604f7227622574da7cdc3d91a86861/coverage-7.16.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d93db87adb6b1c1b408dce4763314b55d76a9f589e96783a84ac9e7689e48bdf", size = 224871, upload-time = "2026-09-27T12:27:30.32Z" }, + { url = "https://files.pythonhosted.org/packages/5c/70/444f3a4981ac2cda40fdcf4cc9b56a4e1a33c222abeb33e51ed3e3eb2a6b/coverage-7.16.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:aa62c85046473959c13ba9edca9dc90a77d5c1095b1ba313556314d77fe5b036", size = 225123, upload-time = "2026-09-27T12:27:32.33Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c1/980681cd7b33eb66ac835044116ef0a92e11fcc7bdd866cc89d10b1130b9/coverage-7.16.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:db76506aa5416081f3e8974ae0f7965c58ada0bb0ef7339ac86099588dbb20d3", size = 265049, upload-time = "2026-09-27T12:27:34.085Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e3/87679875c33bb2191f0f05544a1cc9adcc940fe0c35443a10f2df753dde5/coverage-7.16.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a0f2285329dac10ab08f79cb11f5692c497018e6c7c511f95e6fd63a70b8f831", size = 267717, upload-time = "2026-09-27T12:27:36.025Z" }, + { url = "https://files.pythonhosted.org/packages/76/64/5d372776d6eb523d4e93bafba2253f96984e3b18261c4cc56a50863c6d0d/coverage-7.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:382d3346d56b0eec1b793d53a4c88799c8053f516aa3a8d7c44315696954bacf", size = 270047, upload-time = "2026-09-27T12:27:37.96Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/44082ff0cbf9f97d0043f57970a71204097ec7ba606361a9fd2065393669/coverage-7.16.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:648352b94507179d82637292e7ae8802508d95f78e2f00a705a50b6c48011681", size = 271360, upload-time = "2026-09-27T12:27:39.766Z" }, + { url = "https://files.pythonhosted.org/packages/b8/17/9a215efe25b5e0ecc87c89dbe525c4a87d14d87c8c0c7316ef140a5f6f3e/coverage-7.16.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fb2bde05838fffae1a1bf75e5d411a6cac3e4e9bb97e6640fed8cd47888b33f0", size = 264736, upload-time = "2026-09-27T12:27:42.072Z" }, + { url = "https://files.pythonhosted.org/packages/a2/da/7f0a31af8e448107d4d32844bd684757f51ea907bc0c68c8fd537b2123ff/coverage-7.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6a75180829efb8ae62b4aded25be6ddca1c888d138d2d82e21d93bfbd88f41cb", size = 267600, upload-time = "2026-09-27T12:27:43.85Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/3bfecbd3366b775bacdcb3330394d356cf384b5d8f5b2146ac4b14b252b5/coverage-7.16.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:99704f73721e23859112072d522076e11c31744fc96b5652e5dd2018aa4359f7", size = 264767, upload-time = "2026-09-27T12:27:45.768Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3f/5d62163732d87e4a0c4710a0eab30f0fd6a2d480112abe2029f014fe8c9d/coverage-7.16.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:29309ccc86b7f33df7db12813c299f215bbbc470ed6292d0bedd63ffae1ebf64", size = 269026, upload-time = "2026-09-27T12:27:47.787Z" }, + { url = "https://files.pythonhosted.org/packages/49/4d/8e4579f225426535085a9be371cc75e3b026d058d679b80affbdfb4c3ef0/coverage-7.16.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:30c1b65d529e46569899fadca59e4a87c1faf2886923f1307ba61e654d4f3c20", size = 264147, upload-time = "2026-09-27T12:27:49.681Z" }, + { url = "https://files.pythonhosted.org/packages/d1/36/ef1f77e2c3f7bb03c2b13b9a2006f88700fdd75535ef158d70049f425c1c/coverage-7.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dcf4bc2aab4e16b1c4c0c2005918f23a7dd5d7821ddae82caed9e3342dc2fcce", size = 266376, upload-time = "2026-09-27T12:27:51.551Z" }, + { url = "https://files.pythonhosted.org/packages/be/79/0cb2bf4428830dec971c718c2c841a039c084415c99e67281f5a72841aab/coverage-7.16.2-cp314-cp314t-win32.whl", hash = "sha256:a9cd3de0a5bfe7b0e21ee10e1a14e3d61bf52efc88217ab1d95d6ace6970bd46", size = 226585, upload-time = "2026-09-27T12:27:53.945Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f9/da17121c16667fd84998e972200ae226a41540f6ea4795776c6d99e8976f/coverage-7.16.2-cp314-cp314t-win_amd64.whl", hash = "sha256:611a44e5229a59d7483ce830160e1a0e85f700562c7a5651c7c63fb8f4eb528c", size = 227378, upload-time = "2026-09-27T12:27:55.778Z" }, + { url = "https://files.pythonhosted.org/packages/74/89/01179c62d1b7e6e33bd5001566b02d7f778cf33d3ec1e81e94ca170c517f/coverage-7.16.2-cp314-cp314t-win_arm64.whl", hash = "sha256:22957cef43ce038641de78ba995de7568d2d6a37c6ddbf7fa0fd7d1ae2344d91", size = 227064, upload-time = "2026-09-27T12:27:57.496Z" }, + { url = "https://files.pythonhosted.org/packages/4c/57/52935003c3f627ba6e5203d7179aad32448c10899663a30336aba8e81a2c/coverage-7.16.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:414c26dfdb96aac2d570a54e03008f001e32eb2d413705365503648c6bd361d8", size = 224140, upload-time = "2026-09-27T12:27:59.343Z" }, + { url = "https://files.pythonhosted.org/packages/31/38/df472520f3e626524d7e2fc9d6da0afe7895a2f1489d36b48af8ca40bb41/coverage-7.16.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:00d3eb96e9988c45f50cccd1f1496571ac5c1f91386ac02c4d55516eeda19a24", size = 224449, upload-time = "2026-09-27T12:28:01.299Z" }, + { url = "https://files.pythonhosted.org/packages/0c/aa/3be084d5b82e63ccdad4ed751e4acbae294673573e30481d29f8b7402eec/coverage-7.16.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4dbbd1155ca46e6e0b6b89d204428c56ef6a459af21333f365d135a2820e5a09", size = 256060, upload-time = "2026-09-27T12:28:03.185Z" }, + { url = "https://files.pythonhosted.org/packages/de/29/48fca82a7ebf7ff7b2e35019cc9537e7f65e4d2aa1215cc5a8792c989251/coverage-7.16.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8fc15cc8d0d06e873c00ef18e1372d605f9aaf3de27d8c24e50782e75bc8b843", size = 259106, upload-time = "2026-09-27T12:28:05.15Z" }, + { url = "https://files.pythonhosted.org/packages/06/3d/b2d5986f2dd53fe201aa1be2e4ab204fa1aed5101e67c0dbbb419b850aee/coverage-7.16.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c6afdd69218202bc1758c9a14b86b8cf1084f37ed2ca143e567a103772b16d1", size = 260674, upload-time = "2026-09-27T12:28:06.868Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7e/b50160be3506ead12e6480d14279af7f0f17627694300a2d1fd2c42d2ff5/coverage-7.16.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aba5c63b7afdc749cc9eae943d5b868cba2b261a176378fa1c5a30bc8bc89982", size = 263139, upload-time = "2026-09-27T12:28:08.771Z" }, + { url = "https://files.pythonhosted.org/packages/14/5e/7c805ac9a32606de1399bd7e9bd375aa2f973dc61b12680d9e6403c2e891/coverage-7.16.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9174f0af24e5eff248b9dbfe76ec5275a3d19d37edbc2810543f12cf97347a34", size = 256591, upload-time = "2026-09-27T12:28:10.842Z" }, + { url = "https://files.pythonhosted.org/packages/ab/9e/76f1ed129a2daf658a3ea17122824cf2e3b91fea0460d8d3664fc5a61018/coverage-7.16.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:80e9fdb4c3d926b6ba721d4bf7435bdb869c3527ae7803290361d0ab73db13b6", size = 258708, upload-time = "2026-09-27T12:28:12.962Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b7/8d62e75f48b527619239a65294f842d4b7fd02a0839d43ae1de80184e2df/coverage-7.16.2-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:7b3bce4a0d05401d70b7d0d5ca783e686bc9d30e81dbd7d980d532609bf809e4", size = 256562, upload-time = "2026-09-27T12:28:14.934Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8d/0a15f95c3afb78e947c52644786ba4bc9de259905687dd720d5e6fae2e76/coverage-7.16.2-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:44f21e407b278efdfc1ee5e481e00518bd1d500310a30a5fbf2bcbedfef4aaf0", size = 261077, upload-time = "2026-09-27T12:28:17.215Z" }, + { url = "https://files.pythonhosted.org/packages/25/00/88389987305a47d732866c07c8a500000ab574df9505e3114ac69c8d027f/coverage-7.16.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:59c3926585e1cd1f2190f4b2ac9014de1bbeaf0d5d0587b0dc6b0aa90d17896a", size = 256034, upload-time = "2026-09-27T12:28:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/92/02/34d079d4952ad461bde037d353f9a6e037a7edc45fe0f9ee8781ff73f028/coverage-7.16.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:066429634299e14dd2d511e1e85f8f9cecc500781f6b41907c0dd6f1baea7e63", size = 258034, upload-time = "2026-09-27T12:28:21.242Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d8/3e59a62879285b464ec1b10fd824fbc1af9ce66e842cd39974f80a0becc4/coverage-7.16.2-cp315-cp315-win32.whl", hash = "sha256:893ea9cf86cb8d2546812ac93d973aaf2ee1fb45110a873b014214fd23e3725e", size = 226252, upload-time = "2026-09-27T12:28:23.102Z" }, + { url = "https://files.pythonhosted.org/packages/f4/e1/128026e1b2836e9ad6b219207ba9edf1c5e0088a7869e23088aee7fbbe7a/coverage-7.16.2-cp315-cp315-win_amd64.whl", hash = "sha256:01c6908bc613b420c26c818fe948e1b97dfd041a53c98b01c63bd8321f5c9aae", size = 226728, upload-time = "2026-09-27T12:28:25.21Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f4/c9fa8e7cf525ca7748ac52b0ee89331d13fe09808e45c679830708782e90/coverage-7.16.2-cp315-cp315-win_arm64.whl", hash = "sha256:967d72c835d7a8cf0af99ec813a2d06e3db6df706402f1fe85b31b437645f495", size = 226648, upload-time = "2026-09-27T12:28:27.136Z" }, + { url = "https://files.pythonhosted.org/packages/a2/13/e96b045447a856666f36f9c653e2a80bdaa732aaaf72412b19aa2c26a473/coverage-7.16.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:98d9c97f51b334b0adce7b964442a9af33c1a00c6ac856984cc5dc8d18f81c75", size = 224858, upload-time = "2026-09-27T12:28:29.169Z" }, + { url = "https://files.pythonhosted.org/packages/23/90/087f6ad1bd3df059632ca3407a4e6552ed1053ee35354de0a771acf35423/coverage-7.16.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:3e861f1071dcc2fec1e88bef0920f6b1eaa66a143555b4f8ab79ba2b0f30ef55", size = 225139, upload-time = "2026-09-27T12:28:31.131Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8e/285dcef0184358044e7cbcd810a1bdc9566bc620f54702d605477155df4a/coverage-7.16.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb9d92ecfe2d5b494367c67f7446f8b75b68d8d0c8cf3bc3e6997478be25d9e2", size = 265090, upload-time = "2026-09-27T12:28:33.04Z" }, + { url = "https://files.pythonhosted.org/packages/06/b2/cc83f3a6e5789a4e89059c69555bc641c2efcde568405a1c06fc702951ab/coverage-7.16.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb57acff4a74246ae513c142d4b36e18c389c3aed8661914a53f7cd0071031b2", size = 268254, upload-time = "2026-09-27T12:28:35.135Z" }, + { url = "https://files.pythonhosted.org/packages/ac/41/f548c19530f5d66ac6e3c92bbcbc49da7261de3a458b9f3e54a3efb1a0b2/coverage-7.16.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:444889f7f66b74e4455c0a97e0e166dd41177f1dca8c0239a47cff25e05ba7e1", size = 270695, upload-time = "2026-09-27T12:28:36.959Z" }, + { url = "https://files.pythonhosted.org/packages/94/61/4dc27cf82ef96434d2874110ad0cc10ea4621025705dc5049862bd3bd181/coverage-7.16.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a740ea6f083c6db7b926534d159508f80ba275ab35e722522de0d18d0f56e55f", size = 271853, upload-time = "2026-09-27T12:28:38.821Z" }, + { url = "https://files.pythonhosted.org/packages/38/29/bf8072b1b8bd5f2de8b21460a404460b1a2b97e80a9464c78ec0271f6199/coverage-7.16.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8e209591f7c41ae4a9171335cf6156afda0b21de73b02f73f5aa95b2d5fbb08d", size = 265644, upload-time = "2026-09-27T12:28:40.815Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2f/0aecb8721be5cdeb8afd9d6d9f6b463f074e4d8d37f00f4c42442522709f/coverage-7.16.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:396bb16e04ce04efbb3df91456ae4e3da918e69ecdf67fb711b0a0fdf35ccce0", size = 268530, upload-time = "2026-09-27T12:28:42.725Z" }, + { url = "https://files.pythonhosted.org/packages/ab/0b/92b4b7628268ee711249958e68fc0328779bd3d9a7ab4715379465aedb84/coverage-7.16.2-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9cdf19874e0d247f32f03609200370343c3c7aa260b191d8c2bb251d36198283", size = 265154, upload-time = "2026-09-27T12:28:44.684Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/41c95c1ab29b3dcd357cd1227181d1c98185632aca41ce670ce671b23a43/coverage-7.16.2-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:fd3d72233eb8b48acc94fa57d44e2d32ce8e7abed02882ccb6d855ccc4ed33ec", size = 269799, upload-time = "2026-09-27T12:28:46.672Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ebeb259aa5362b033a137b86d7274ff4b109d59be8cc9913889b783bf75a/coverage-7.16.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:bb4ffe96aa663cee727659db5a2afeb38c95f8677b747d447b90d6d4874ea2c5", size = 265196, upload-time = "2026-09-27T12:28:48.996Z" }, + { url = "https://files.pythonhosted.org/packages/b2/18/8437620f90d023680a072eee02f968055f3658bbfb7d386d0ea34cfb7f30/coverage-7.16.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dba2edfb054f6d4a08df9d1637c39a5aa3865bca6617c13c86be21e45658a59c", size = 267266, upload-time = "2026-09-27T12:28:51.361Z" }, + { url = "https://files.pythonhosted.org/packages/28/6c/f08e8ee4293e6434035424180bef4d45e028e8ecc006c61bf9453e74405e/coverage-7.16.2-cp315-cp315t-win32.whl", hash = "sha256:251aed777c47c77aba047096d4542889db089227655711dfc2b9c54ef0e15e35", size = 226583, upload-time = "2026-09-27T12:28:53.33Z" }, + { url = "https://files.pythonhosted.org/packages/f7/fd/3f939c2847f4a72c20cff8b1ac33da78ea91a2d38d9b43336e60db719103/coverage-7.16.2-cp315-cp315t-win_amd64.whl", hash = "sha256:2aca0bdfa9e91621d5b09d815357bf63def4fc0e9cb66da67bf2cf93f3b1a6f5", size = 227374, upload-time = "2026-09-27T12:28:55.158Z" }, + { url = "https://files.pythonhosted.org/packages/5a/35/b98cdc354c952402132e675a87f2cc3227fb68f959c84aaa491fbe15933d/coverage-7.16.2-cp315-cp315t-win_arm64.whl", hash = "sha256:b88841e654f09732804809e435b3e005a929ffd9998b872b7b213957b8759cb8", size = 227064, upload-time = "2026-09-27T12:28:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0c/7a64e1ac90541a8edf50daef0914848011fb057a5bf55284a4811e21939a/coverage-7.16.2-py3-none-any.whl", hash = "sha256:11d28e9123a9156cb405d8d27b44256c9a58fb5decc2073a8f17862057e3aa0f", size = 215754, upload-time = "2026-09-27T12:28:59.075Z" }, +] + +[[package]] +name = "coverage" +version = "7.17.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/78/4a/740c3134227763ebc30a226d7f2f29b25e35898d162dd8b2dd792be83fb2/coverage-7.17.2.tar.gz", hash = "sha256:d05a1bef52f4d7f1b484a1cf46a8929e50963f910e51396373f06cd3b6248f38", size = 996756, upload-time = "2026-11-04T19:52:34.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/9d/e3db9071ebeda48e47e5be0fd0404219954e7888f64e7c37df324a79dc3a/coverage-7.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3f49ac9c9e5fd4b450677eb44f4b14ea9ab71950b2896888c9f6a22d5cd1773", size = 227732, upload-time = "2026-11-04T19:49:22.217Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/260786d936940102052a51cb166f80f4a98e67fb6142aa050f1af48a2385/coverage-7.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d3d9d79a2b6cdcdde5a1ab1ae89953faea5cb28c0eea17a413ad997bb3cc8a1", size = 228154, upload-time = "2026-11-04T19:49:25.066Z" }, + { url = "https://files.pythonhosted.org/packages/be/80/f5176b7f6aa9e05e37e1bb5eae473c5d015485c36206934f53b2b2795076/coverage-7.17.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b71aeaa4440cbece5498acda30838685793d3bb960aff87ec97fbb546715a5f", size = 260181, upload-time = "2026-11-04T19:49:26.523Z" }, + { url = "https://files.pythonhosted.org/packages/03/9f/4669ca11ce1932cc18fce766ead327ceb41a92055608c179b137067e2805/coverage-7.17.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:01303d9e68b6e6bdc5459b54e3bd0e3539c94c25b50ac7362b7cbcacbc815fbc", size = 262404, upload-time = "2026-11-04T19:49:28.113Z" }, + { url = "https://files.pythonhosted.org/packages/ae/99/efefaa02a4b2876ffa4a389c81a8074c888442b12c64046b448718fa5590/coverage-7.17.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1947bd9d82e405c892c50b0d8dc0c1dea5df2a962129ca238c500a6bcf887509", size = 264497, upload-time = "2026-11-04T19:49:29.773Z" }, + { url = "https://files.pythonhosted.org/packages/19/2f/a565096e5dcf8c511332d397d712b056f06091ab3efc10adb5a4d22b0cc2/coverage-7.17.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2cd1a7472a972fed01c89589b1560846e32e942f8329a515c4059d4521c6579a", size = 266507, upload-time = "2026-11-04T19:49:31.507Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/87f9b02f97179f2c76fa5724e0ce00b9db2bbe16b7e21d89c8fc1f3a77d9/coverage-7.17.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b7fe217808239437b8509b869fde2e0cac48776a68c626bd6b15411b087e3de", size = 260297, upload-time = "2026-11-04T19:49:33.002Z" }, + { url = "https://files.pythonhosted.org/packages/db/12/70733b58d508bc80dc983408b06f1fd0c59fde0eb1379dd9473b3e290856/coverage-7.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:565f1f1a9632d82678525d668a10fe070a285603a9d3581505b0e1a66624ad50", size = 262196, upload-time = "2026-11-04T19:49:34.412Z" }, + { url = "https://files.pythonhosted.org/packages/33/de/0495b108a3091f2389800455b615939867a3ac19f24f0fc186f4d066b4f3/coverage-7.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:189cdffb3ebc53d6133de67952dac7cc88d1fcaff5480623fb00ef0bf1cef918", size = 260212, upload-time = "2026-11-04T19:49:38.462Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ff/39cda949e0e9bbc16620d8b843fd78929a46edda2566e54b9597bda61625/coverage-7.17.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f25209696631752468a25e5954fe119503468b3b29b6fa2c621ae766153c6f50", size = 264136, upload-time = "2026-11-04T19:49:40.266Z" }, + { url = "https://files.pythonhosted.org/packages/85/e5/0eb1bbfc80e1a03318d81fc044dc1c4fc075921f7f618298d42837dafa66/coverage-7.17.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:20faaaf3a49601afbcd6d46c17a594b0ae62631498def44e5eed94789962ea18", size = 259738, upload-time = "2026-11-04T19:49:41.79Z" }, + { url = "https://files.pythonhosted.org/packages/a7/18/098ea8ee450922eec7ab48a35b4a7fd6a49c4a97416d1e4cf66092c24dc4/coverage-7.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7c123b6bbc183a14372036769f033098026aa75fc8fba37d028b0b772356e90b", size = 260937, upload-time = "2026-11-04T19:49:43.467Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/a6ee041c4bc8b6ec7bc9f417daf89e3ee702182a48d573267ccaaed17c64/coverage-7.17.2-cp311-cp311-win32.whl", hash = "sha256:1c3146ef9d14c011517422489784a7ac344477e9cdacb05d9529e912721cd5d8", size = 229599, upload-time = "2026-11-04T19:49:45.014Z" }, + { url = "https://files.pythonhosted.org/packages/5a/df/7f077fb2e71b1d73fe8285be7c6ba43dc085f9eaa2384d4e8e55560073a3/coverage-7.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:51c6a60e63ea993113ea5cd24a401cc2faddc54aa7acfddaffa5d3b8c0161b9d", size = 230202, upload-time = "2026-11-04T19:49:46.548Z" }, + { url = "https://files.pythonhosted.org/packages/90/56/89dcd5907d0cc04b07746e62e1cb896c0f694e5af40aa0c980458825c6c5/coverage-7.17.2-cp311-cp311-win_arm64.whl", hash = "sha256:b08ee3ef53e80e5a50c68b3d9764363e373acba540e1b58daee0a9a7f23fdfb2", size = 230110, upload-time = "2026-11-04T19:49:48.005Z" }, + { url = "https://files.pythonhosted.org/packages/22/6a/a51808ac625c457326fb37b174d35e1badb1324c6def5d9668089c4b14ef/coverage-7.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:289a24443352713745f79c1b6aae4e99ced2f5ff3e86bb630c33c631d50f217c", size = 227838, upload-time = "2026-11-04T19:49:49.546Z" }, + { url = "https://files.pythonhosted.org/packages/31/24/f32ec1a5ef947294252e77c5d40ac0903ba1de4c92638edfcca8629c067f/coverage-7.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da4b0aef427eb96e422b444e5c2c3d38768fedb9141346294c38717a8b15b416", size = 228060, upload-time = "2026-11-04T19:49:51.221Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/3097367327c69e65521b9d9bf8b9d2ddecefa8c21bd08e17bc068bd3db93/coverage-7.17.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90c6c5f93606b8803bd7f8a0b2ae05df7e35da4504a1c53cfe9294a1731e1f49", size = 260359, upload-time = "2026-11-04T19:49:52.756Z" }, + { url = "https://files.pythonhosted.org/packages/73/65/774fc4ec86807b6ee66b46465bc49b34bb6d291e9bff6ddfa9708ccb4053/coverage-7.17.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36314997533266bde3d07765289674304d61bfe49165bb1b4fa9be12cb4ba0d1", size = 263687, upload-time = "2026-11-04T19:49:54.351Z" }, + { url = "https://files.pythonhosted.org/packages/d8/72/17b5df5d8211e1880169b93197ba98b21b30ccf8f116284536113b7582c5/coverage-7.17.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1682d98ded28524bc6853cc4e7c65175b3e917f38b2e2ddebb28cafd8ab4de74", size = 264220, upload-time = "2026-11-04T19:49:56.172Z" }, + { url = "https://files.pythonhosted.org/packages/de/0c/603ba7fff1aa4c8064fdf4a1fff6d9fb51c72a5828429dfe858be48e38b3/coverage-7.17.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8eab0a5cff8dbc563d1416a6e079ba3c146712e47167c022a5df5ea01bd76df9", size = 266498, upload-time = "2026-11-04T19:49:57.718Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ca/f2ae6e47e18b35a0dbddb9d8f51be34a6d66b41120f2e0f7363ccc0812f4/coverage-7.17.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:635337a96895f2ffcfe2aa7554468a988f468fb67a8007c1a65e95148c1855a9", size = 260591, upload-time = "2026-11-04T19:49:59.159Z" }, + { url = "https://files.pythonhosted.org/packages/b2/4b/b09540a46395a6a1052ad114bf366e383c64e223374f4911a85620667930/coverage-7.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79cd2d9742419b6d1aaeb1f3fb77a8e6a01c09ebe5bd1b27011752410a5e3781", size = 262107, upload-time = "2026-11-04T19:50:00.607Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c4/42783cb43481fafafffbc7be7fe6026258ffd30a575707733109c9ef0679/coverage-7.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b0209fff1ca92f1e2e344d54b5b3def74efbdb46ba807cd81976a5e9e569cac3", size = 260271, upload-time = "2026-11-04T19:50:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/37/07/192ee81dd1ec4da58ae0f03fdd0045d2b821007e4194f7704d6e495733f0/coverage-7.17.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a3984fb8c2407721f9bc05573fc9a770752efc756d55068ccbde1fe53e42233", size = 264320, upload-time = "2026-11-04T19:50:03.613Z" }, + { url = "https://files.pythonhosted.org/packages/dd/94/8d983310d98299a19ba23efbb324492f4bf8d8668d610eacd304e451f138/coverage-7.17.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:698ca1575135215be25ac9c5d14f0b5455135d29602b7ee81e511d64a0435c85", size = 259774, upload-time = "2026-11-04T19:50:05.363Z" }, + { url = "https://files.pythonhosted.org/packages/1a/85/6c923d71c76566c6c65a2af104d39d69d44ae708a700de017e6c56fedfc6/coverage-7.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:686b6f8f9e57afc3a44096b17ee8de7e11e0a12f2da819e25047af1eb3b0fc6f", size = 262258, upload-time = "2026-11-04T19:50:06.807Z" }, + { url = "https://files.pythonhosted.org/packages/54/f9/fd3c37b09891bfdfece486a6145345bf2b32332045a678afd293a7f2f05d/coverage-7.17.2-cp312-cp312-win32.whl", hash = "sha256:ee083412170118074bcc74361fcbc7104eb6d0b7c9c75eb3b84a4c324410c531", size = 229659, upload-time = "2026-11-04T19:50:08.184Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b8/4d395163c9bfae0e991abbd14dea9c9bbb4ade904e5f297cbfb7b8150216/coverage-7.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:e269dddf5ebac20dfcc5a2a459528a9d221ab828cb31dfdf19c7cfacdbcee673", size = 230240, upload-time = "2026-11-04T19:50:09.715Z" }, + { url = "https://files.pythonhosted.org/packages/a3/02/1024b8d4f60a9ecc0ee41a6000425caa63855d63e51da84c93c0b1dff894/coverage-7.17.2-cp312-cp312-win_arm64.whl", hash = "sha256:bac3c79c31f45274117ed64c65efeaed85665668aeba7d809c7c38801ada6ed4", size = 230043, upload-time = "2026-11-04T19:50:11.187Z" }, + { url = "https://files.pythonhosted.org/packages/e0/32/d750d7b10b0a9436349b3fb78491c5bb8f9b524f01ae6994d935d77d6ab8/coverage-7.17.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:882cade9b6dc43b2bc819bb444c0a13e4ed1f669a50e0de74adeec54fce486f3", size = 228978, upload-time = "2026-11-04T19:50:12.782Z" }, + { url = "https://files.pythonhosted.org/packages/63/33/468f8c63aff5deef81bfb8e44771f68ebba328c0aa46c8a6ff27dedd0fda/coverage-7.17.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:f5a875e92f10c6463027d8151459f2d92b4c13527402ed1301eaddabd79d8cf5", size = 229048, upload-time = "2026-11-04T19:50:14.255Z" }, + { url = "https://files.pythonhosted.org/packages/10/91/c8723e48c1497a3c56a3194973a416a6f0fb24560425685f960888e03b20/coverage-7.17.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e8da5e158d13556457f69245deacf6d8401a2c9c31afaddd18bea691e849b5fd", size = 227293, upload-time = "2026-11-04T19:50:15.857Z" }, + { url = "https://files.pythonhosted.org/packages/61/4f/2f57b3dc6ef31e12a99e6dcb820c4ab04d973ea1466f73c856bf920a740c/coverage-7.17.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:42133a2647982db7968db138c0f4a57f5836101d8d504600a87ef8ebfa22a4cf", size = 227599, upload-time = "2026-11-04T19:50:17.418Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fa/967e829238dda21c4bce17aef2f739187678c8e6e6fc880a8454945a7433/coverage-7.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e6c89d63d43738712ce1febddb7758ffeb56de08f1b2be68aeb9a9c7fee712c7", size = 227727, upload-time = "2026-11-04T19:50:18.924Z" }, + { url = "https://files.pythonhosted.org/packages/9d/df/803ba08e8937759dc3f18b93ece82f81b2fb446e3846e4d60a06a4e77647/coverage-7.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f94d465ee00ebe694ca866d66bbe45fa04618b1afcb377de934694f624fa7cf6", size = 227987, upload-time = "2026-11-04T19:50:20.867Z" }, + { url = "https://files.pythonhosted.org/packages/4a/12/92dcd9206e7bb817af42b8663cfeefdbb2c5a6c92cc7069d43e6892bbdeb/coverage-7.17.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7ce0acb27a823e8e5169282f538f0e182854cc8b472b88d45456f09b6fe364fd", size = 259154, upload-time = "2026-11-04T19:50:22.328Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/6b028df8050caef4ce0df177686c92dba16ab7cf004040e5518f95b57021/coverage-7.17.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70daa8e4b49541b51c0baeba0d802b0d14d3214daeebc071b3db0b0c7dc84646", size = 262065, upload-time = "2026-11-04T19:50:23.745Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/429b500937ac446e36fcce517d040b0b8bd04a4bfa087bdf864175553fea/coverage-7.17.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:95bd8dc5b356f78f10a967ec50f8902d56f24c623753ac9fce0aba9d6213d5c5", size = 262997, upload-time = "2026-11-04T19:50:25.223Z" }, + { url = "https://files.pythonhosted.org/packages/c1/91/263224a77de3ab1ee42d110b09f1ac9a02638794d3e002a859822638256a/coverage-7.17.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a5565a9dd715a5a94ffcf9a0ad4b6b1501b5441fd67d660e78742adff5d37a0a", size = 265062, upload-time = "2026-11-04T19:50:26.829Z" }, + { url = "https://files.pythonhosted.org/packages/0f/44/99b72f2961f4ba54ac14ccb016f3b212cc9a9dfda4c37e07a208efe01f9f/coverage-7.17.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:091eba4248d31b03828fc657c96d14adfaa8754f5aec7b73298b82894858db06", size = 259201, upload-time = "2026-11-04T19:50:28.386Z" }, + { url = "https://files.pythonhosted.org/packages/be/a7/e97f63a7c345077e4e398d15e3aa5068edc28cb061f997f71fd0e32703e1/coverage-7.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2742952851c7f88b1484385f68c42c6388153520efb40ff09fc852c3783aa5c", size = 261155, upload-time = "2026-11-04T19:50:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/16/0c/73e470df7db83962e2cdd6c1a7ca7dc8f8632c17a6d378d6d9124ff8bc6b/coverage-7.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1da1999e3c371edd669b48a1029c86dc0474cdc6ff152d58cb3f15cb3337af72", size = 259077, upload-time = "2026-11-04T19:50:31.437Z" }, + { url = "https://files.pythonhosted.org/packages/50/43/db8bc1b92d933b0b5f57d0e3377594c4f78ea31e84e8fbc22627fd17ecf4/coverage-7.17.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7804a6022fcd5e1d4de4dc676e985d6aea0119249e88c041e943f3cd07953527", size = 263172, upload-time = "2026-11-04T19:50:33.014Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/3e9e5cc9ccc911f8fb9595053da1ce58b9de8941b27e2f2f7058cdfc87f1/coverage-7.17.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6bdf3821fd0740b5369b9ab58b4e9d0d68a2c5c75a7ee0cd3beb624f9f15e4fa", size = 258926, upload-time = "2026-11-04T19:50:34.513Z" }, + { url = "https://files.pythonhosted.org/packages/72/9f/0d67883bf55adf50424728937cbee54d76fa137817e6bb20f97061d01816/coverage-7.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09c37817768bb71fe3e70b6ce5e6ac1a0cd7398536c2f30a4460218c7b66e93", size = 261006, upload-time = "2026-11-04T19:50:35.956Z" }, + { url = "https://files.pythonhosted.org/packages/a8/22/71965edc14736073b55a40e521791d9233d5c3194fa6887a94f4431fbbcc/coverage-7.17.2-cp313-cp313-win32.whl", hash = "sha256:c10f2a2748dd084e44811bb9b26979763af9003115d9bf93c1da69011944719f", size = 229602, upload-time = "2026-11-04T19:50:37.564Z" }, + { url = "https://files.pythonhosted.org/packages/47/0d/715cb37f51f908a6f6f33fe8c6fc95ab638b17cb014b1650daae0893c160/coverage-7.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:8dc07ed34bcb206f69d5b54fd3874f7bf39ac941c9f7aec2b53b29874fcfd77a", size = 230329, upload-time = "2026-11-04T19:50:38.988Z" }, + { url = "https://files.pythonhosted.org/packages/39/f2/a5099bdc2a530f29c818a1c7e706669451c10e5f0eb62dcff76ef3568668/coverage-7.17.2-cp313-cp313-win_arm64.whl", hash = "sha256:bb74283661a239fdaf738ac0b07db4ed9b476c7532d3e713abf0f6a072781e60", size = 230055, upload-time = "2026-11-04T19:50:40.479Z" }, + { url = "https://files.pythonhosted.org/packages/96/f9/30d6ff7a66e1519d79940fdee542ead9a63601de60be5cc9fe62c74bab58/coverage-7.17.2-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:0439f77deb5c023003533ae8909a9aa1e174c847d655327035b1e542c7c8a54e", size = 229094, upload-time = "2026-11-04T19:50:42.193Z" }, + { url = "https://files.pythonhosted.org/packages/1a/db/82c722c37dce98fe1abc953768903752bdd415dfe8cfeb7b744a7236a735/coverage-7.17.2-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e86a66f96d80c770daeed78394c1d4f6e8d03e5a3e08c5dab5275d56414f296a", size = 229213, upload-time = "2026-11-04T19:50:43.779Z" }, + { url = "https://files.pythonhosted.org/packages/7d/07/71b68d870975776cb185f11cd80e053b01739f3646488fe8b9721744f56a/coverage-7.17.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:87b30f3daa0bedda6b770b303410a6def007844772aaa143dc52675b3d95cd8a", size = 227374, upload-time = "2026-11-04T19:50:45.258Z" }, + { url = "https://files.pythonhosted.org/packages/aa/23/ed60f79576175e108c77df2bdc116f46e367aab108542ddeecaab752f0f6/coverage-7.17.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:3f219efbc3ec71c377e278dee201633e1d4ad0d2d9bdaddfd9489b2869d584c4", size = 227676, upload-time = "2026-11-04T19:50:47.095Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/60a51850d3ff659b82cc211bc98fa126aab2ac9b81bd4ed6abc3e3720a09/coverage-7.17.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:36248fb6c03341501169539bcab35eb3a59159100be1246fad3c14cb73c17685", size = 227915, upload-time = "2026-11-04T19:50:48.581Z" }, + { url = "https://files.pythonhosted.org/packages/77/80/a9539c15076fb9b95d8cbdb7f0caec90857d80bddca3eeca439f5e2e64e2/coverage-7.17.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2a6281011cf85699763bf88ab55e0774f6696f87b390f39152b14ecefadccb59", size = 228072, upload-time = "2026-11-04T19:50:50.073Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3b/0a84b6032511834105fd4f8a8b4e76927783cea03dc4796032d5c4bfec8b/coverage-7.17.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3dfefb1ec16ca1a01e5da40ba162e268ced8bd3624b9eba4e9384ebfa14af7ec", size = 259991, upload-time = "2026-11-04T19:50:51.665Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e4/a3e6b8bcc219b00ea91e36f071c2659b253e5d6e1b258c31eb4524231718/coverage-7.17.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:34e5c7a2da36804c6e6f10b368eae01ad56f5a4ac5ecce7297300a3d46fdef2a", size = 262995, upload-time = "2026-11-04T19:50:53.236Z" }, + { url = "https://files.pythonhosted.org/packages/05/54/a666b65c5bb145d8796b7902ff1c0f60600a6c2a08949e20f2fb7af15de3/coverage-7.17.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6bf531deaebf8f69ba6015bba3fb817cab6694dfb69ac2a708889dc59a3926e", size = 263980, upload-time = "2026-11-04T19:50:55.366Z" }, + { url = "https://files.pythonhosted.org/packages/a6/73/ac9d06da9e0c143b874a68b17d8dfeedc45acdf1bdcee5b22a42bece098e/coverage-7.17.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:88d4dc83b6ad09a2134cad9d8ec780b274b17ac7dfba8c2c6e92e3bc468fe85d", size = 266457, upload-time = "2026-11-04T19:50:57.04Z" }, + { url = "https://files.pythonhosted.org/packages/87/b1/8a500e16cf787383b00b5a86eb50a490b4d8ebde505f92e1aaca515afc0a/coverage-7.17.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dc17707fd463b4dfab53792e41d50a23316ac6b98539007aa2388a1d44a8535", size = 259768, upload-time = "2026-11-04T19:50:58.508Z" }, + { url = "https://files.pythonhosted.org/packages/f7/be/642762c5973b89e2b8bf66c95778bdd80e96ceea442ae8fa753732597b73/coverage-7.17.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:df0a34d445eea64f3766e9ca2ff57bcde600a89c7c0330434a7a37e8d6e2549f", size = 261831, upload-time = "2026-11-04T19:51:00.25Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b1/f7d6c0ef0a4aecf3f91f19df4ab3fa131fec8c89f554187511716584c9f5/coverage-7.17.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c5a98aff9eb681312ed403ecb7beb5f15fd25d6e0acc2554b9ca4b4d6f0b6705", size = 259871, upload-time = "2026-11-04T19:51:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/c0/3ae087d52cfa5119ad9ecbd193c40089924d1101cef7333f15a86dccb444/coverage-7.17.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7565c17092f2f4021abbe3f5007300c224f362364a388f5ddad9216dab44b611", size = 264169, upload-time = "2026-11-04T19:51:03.365Z" }, + { url = "https://files.pythonhosted.org/packages/c5/95/d324108991f892cb361ef8b2f91bd4cda9c2563e0954247409e9e6b911ab/coverage-7.17.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:555257578e20a46020c27b2af4ef99059cd906aa9b8e163baf9da9a5afb65a64", size = 259502, upload-time = "2026-11-04T19:51:04.91Z" }, + { url = "https://files.pythonhosted.org/packages/14/b5/13a62eac66cecbaaa098bb753a3693b44454a4d21d53db4f1a0953f35f0b/coverage-7.17.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:460ba2139866e758c3a8e9d9422fdc889942bcbc5ccaf710dc4fbd8707d14aa8", size = 261613, upload-time = "2026-11-04T19:51:06.414Z" }, + { url = "https://files.pythonhosted.org/packages/53/13/9ea31cfa72434449b9a98666760169d1e0ab31b3f6cdb12e68149106668e/coverage-7.17.2-cp314-cp314-win32.whl", hash = "sha256:e72cf9556f799c3fce7597c1bd7dbfc9104befbd08088deb3b9ae9fd98117c02", size = 229842, upload-time = "2026-11-04T19:51:08.61Z" }, + { url = "https://files.pythonhosted.org/packages/f6/cc/aa8f34b014979c973740f5a5ed6861ecfc6009082c70e72030ca1519e0f8/coverage-7.17.2-cp314-cp314-win_amd64.whl", hash = "sha256:205f9e329d9d5e47c320f8712326a01620689edae50531cc8934511e2c5e8f7e", size = 230474, upload-time = "2026-11-04T19:51:10.167Z" }, + { url = "https://files.pythonhosted.org/packages/49/64/16afaa19b3886dc157c3c5ebace5241b84b96a005cb28545584e66b80895/coverage-7.17.2-cp314-cp314-win_arm64.whl", hash = "sha256:a5909dd087006c70c128297312ba67d877123e871100645d9bd73ae021a4d01d", size = 230325, upload-time = "2026-11-04T19:51:11.63Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/acc865e1411c8ebc5c5d230367f3bfed5c605ed54bb41b84fc58f4e7f137/coverage-7.17.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:674bcd023a3743666d4d7a15238e57a00c968553780a36c80d3779fc410b912c", size = 228384, upload-time = "2026-11-04T19:51:13.729Z" }, + { url = "https://files.pythonhosted.org/packages/10/98/02c37c6a928ccfc84b7a43eda44d96d3b1ccbc93989c178754b7e67e7650/coverage-7.17.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ba45dc072bfbf1798e68ec81379b73473fc912174bff9f91924d77711a1d2b30", size = 228758, upload-time = "2026-11-04T19:51:15.394Z" }, + { url = "https://files.pythonhosted.org/packages/28/ab/d503b0aaebb92b171260469463915af5e16a64457b289d4cb844e8c366ac/coverage-7.17.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d8a5dc709da9b90ad5cc859a9b88c9b801e65ca2fbf7cbdbed5b9cbf457fcc9", size = 268749, upload-time = "2026-11-04T19:51:17.179Z" }, + { url = "https://files.pythonhosted.org/packages/f6/e5/6d6ba15bde22fcccd07e14305be14291d041f732771a73f787297445d3e7/coverage-7.17.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:248873a144c067f981742ff4bbfb48b57e281a8cb7a3821d4e293bdb50b47970", size = 271301, upload-time = "2026-11-04T19:51:18.893Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/bd6dcec5b8130f5bd8679087a2bc3a50a53c4ee78d5ec449610020088ff3/coverage-7.17.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0332e2b27fc5cc761d6b8a33118b958e6a46be7f00fd30e03a5cf103350c0b3", size = 273924, upload-time = "2026-11-04T19:51:20.605Z" }, + { url = "https://files.pythonhosted.org/packages/55/c1/b3f06aa1f1c51f4295fdfa6500eb7409aaa6273cf5f27bafc25217f3547d/coverage-7.17.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:27fc4384a1154f7df30d304ccddc05ef457368c065670ecbd392606279b3329a", size = 275412, upload-time = "2026-11-04T19:51:22.09Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f0/b68ab3654af3541488b26856e3043c13c782d8e6104fc8149746ecaa83f0/coverage-7.17.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:603140eeae3f9adc562f91b1b98d4d1fc491bd1f66c5082482c568d0b7891bb1", size = 268638, upload-time = "2026-11-04T19:51:23.653Z" }, + { url = "https://files.pythonhosted.org/packages/fd/16/4a9d56bd4ca4572cbd20f9843813623cef1b88b15f979ec4f381704d2f84/coverage-7.17.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:be0fec33b69c40f845c76f67fb4495b62a84da8c31a579dc6e18c5a63fa6a1f3", size = 271211, upload-time = "2026-11-04T19:51:25.678Z" }, + { url = "https://files.pythonhosted.org/packages/78/d2/1c04b38bab92d2b7d62718d170b5bea43755fefee7e29cc056448c433d92/coverage-7.17.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:960e199965e506d78f62252355397a05c8cee8072ad2d82d932745517d566539", size = 268638, upload-time = "2026-11-04T19:51:27.262Z" }, + { url = "https://files.pythonhosted.org/packages/26/c9/2c639e9aee78b0522cb3dc245de09685e34b479bcc45d4d2a277d0de1174/coverage-7.17.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d7a363c0788435ff5324d9582d37c63f215c75f974d755cfd6cfc9fbe0e64b04", size = 272935, upload-time = "2026-11-04T19:51:28.96Z" }, + { url = "https://files.pythonhosted.org/packages/79/1f/11bdaf03c1b12f88094b57420b4238d3974ebec6a7d674b645a54e24f90c/coverage-7.17.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:afd81658f17b1a50bbc1d247a61fa435be0be471246136eb946f9cbfc437a4fa", size = 268057, upload-time = "2026-11-04T19:51:30.44Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f6/43d40d1b4d4892e54c68a241cd5b92efed4d5934109e0e5e24a94759f732/coverage-7.17.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d88fcc24470d73ed4360e910f8549718b913ba56fb8775d9ef929a647835dfea", size = 269986, upload-time = "2026-11-04T19:51:31.913Z" }, + { url = "https://files.pythonhosted.org/packages/8e/33/6874185b791c71684c401fc74abd71bbdb102e8ee41c8f380cf25af41d29/coverage-7.17.2-cp314-cp314t-win32.whl", hash = "sha256:2c42c222939b8699e7bc0e453149627f211ebbbb2f74c1a04331b0cd9834e2c8", size = 230160, upload-time = "2026-11-04T19:51:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/16/e0/9057b4cf962ca6089b144f0d93a748f5e2d13876dd0f33f9d5a2c2ec49d2/coverage-7.17.2-cp314-cp314t-win_amd64.whl", hash = "sha256:b94c509e26603ba7f8cb1bf8bb63967c7be533493be5668d8442cd06ec853d76", size = 231086, upload-time = "2026-11-04T19:51:35.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4f/bd626297c942380279dff323e1d8a8937990d553119a4f887358488b1b54/coverage-7.17.2-cp314-cp314t-win_arm64.whl", hash = "sha256:eca90e028932de597e2c50f454ad8cc631281e14d20b9f801a649ab555deb580", size = 230657, upload-time = "2026-11-04T19:51:36.742Z" }, + { url = "https://files.pythonhosted.org/packages/ac/92/ff8badc05ff0c4ed26ca62fb3d155e9014ff65768b76d5f399e63d9e9a79/coverage-7.17.2-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:cbf751ae86ef0d80e719915c087b05776c8d9300141f6c3e56f536d78a62c301", size = 229092, upload-time = "2026-11-04T19:51:38.222Z" }, + { url = "https://files.pythonhosted.org/packages/5a/8b/c5a5392e5a5dceb68f3b1b8cdba925c7997699f351d811cd4a41add9c29e/coverage-7.17.2-cp315-cp315-android_24_x86_64.whl", hash = "sha256:249c4b4eaac9ced6fb85e465027ca26348111c2760108fffc356a16be4f54cd9", size = 229212, upload-time = "2026-11-04T19:51:39.738Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bc/9259a1fc49ad663861cbfb3df6a99d4c8f62a11b242a2db3358822d28b17/coverage-7.17.2-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e2fd4e664a34854cae8db58ca6132fb7b5a7efc67b14233e797194bd0d63b6b0", size = 227390, upload-time = "2026-11-04T19:51:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/20/ad/83f03da8ab846af246d4a47b651e5d90229a4c3314e31e8eecade63b378f/coverage-7.17.2-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:0712a43e39410aa567f2e85846490f8dd6c839820e7edef2142767c5c03162ba", size = 227690, upload-time = "2026-11-04T19:51:42.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/08/e110687f24245edbbe9c0986a6ccd04f1f9a6e90e329cbd4347e2b81d76e/coverage-7.17.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:298849ca0e385767096995cb57f72059b469f43f9927b8393f29d134f7bb1062", size = 227922, upload-time = "2026-11-04T19:51:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/93/24/7cfcd6adb4a0c18a10de12fd695edbf8cd121fdca0a6a63cea1bffc029fe/coverage-7.17.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:81a432793440edef268dbadd493393bd6f70fa4b87f76c58114f7f77f1ccfaea", size = 228094, upload-time = "2026-11-04T19:51:46.108Z" }, + { url = "https://files.pythonhosted.org/packages/54/7a/af167a0bb6d0564209d35c8039b290327ecf7b3e34f838f9360b56d3a03c/coverage-7.17.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d14872ccef35972c4ac290f2432f43d0a7bb230cae2b5ed26871ab86221e8b5f", size = 259940, upload-time = "2026-11-04T19:51:47.599Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c6/4f3931c3ff80a6f2608209181adb00527970a4b43b64c3aa323383c37d51/coverage-7.17.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3bd065e75c077d6de8cd3fe68699a8b47be3ec595af6b66e3629926839094609", size = 263632, upload-time = "2026-11-04T19:51:49.047Z" }, + { url = "https://files.pythonhosted.org/packages/3e/71/31a4945459008952061147ea9b85853902507d865a7272a9f64be0c4a093/coverage-7.17.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d3626b9816f7eadd0993c3703a1d8ed124b579cebe5201b446f1c0d551fa245", size = 264713, upload-time = "2026-11-04T19:51:50.569Z" }, + { url = "https://files.pythonhosted.org/packages/82/6b/a4eb258b0373034f8f19aeea4c1bc4c89942b5b228812ee9952f033dc611/coverage-7.17.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9639c2ea019c948b52067a0f74485039eb84b7a2de828ba6d178e663fb2b8bd", size = 267091, upload-time = "2026-11-04T19:51:52.09Z" }, + { url = "https://files.pythonhosted.org/packages/02/ae/9410f2f4bbaa698ff3869c7c09e0fa10d74482a1ec8040063157612f07a0/coverage-7.17.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:19d508b0611486f8e23e28abcba77795ee865ff0e0cd936349ba692c8d0d8ca4", size = 260326, upload-time = "2026-11-04T19:51:53.508Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ee/97549ec1bdf6ff6d17612dbcf4b2205daa74510273ac1efd1460e3db275c/coverage-7.17.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:cfe64d2501806a04507217852a3132a9a9076321435500bf3b11943a5c872732", size = 262595, upload-time = "2026-11-04T19:51:55.514Z" }, + { url = "https://files.pythonhosted.org/packages/36/72/76e04605522a6dd81bbf43e1b407ca611f415c04b67e18c123ee440b1d69/coverage-7.17.2-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:4274acd35aae5bbbd0908792260819b9720b171ccc099a8168f7c20ef5d6ab3a", size = 260546, upload-time = "2026-11-04T19:51:57.068Z" }, + { url = "https://files.pythonhosted.org/packages/30/8d/163e027635790ab232f3ec0261d2a26bec34c377bea2096554261444d0f5/coverage-7.17.2-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:85bccf6fe19ea3747147387971465f6a06197d6b9f6b644a97ba90ca5b09c5cd", size = 264877, upload-time = "2026-11-04T19:51:58.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/35/11dd585a06be350285d42c17aa2fccd0af18d962b6979f03dd1a258eb705/coverage-7.17.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:d5024913651813801fd43b65b6409b0d50f8bae95316c8f1512dfbd8ca96cb1f", size = 259858, upload-time = "2026-11-04T19:52:00.361Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ed/f1f3b4bf6081122bf1820b1f38352377a75a2d5f39bb6331598e165b64af/coverage-7.17.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:99b64a12fe6017c254a8027790e0eaa22eaeec201e7dfda5eaac176a6984ef27", size = 262401, upload-time = "2026-11-04T19:52:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/ab/bb/61e07a606a9f4d0f5391ac9b62b1f008a997f87860ad233f99833fd6e6bd/coverage-7.17.2-cp315-cp315-win32.whl", hash = "sha256:74d91af590514dda16165d5d7cfa94183150c6a6a80a99f61068369ae3959455", size = 229840, upload-time = "2026-11-04T19:52:04.813Z" }, + { url = "https://files.pythonhosted.org/packages/86/40/2d4980d3374a023a34c5ea7cd5d2b93c02c5588a324ca7faf8a0a8c7dbac/coverage-7.17.2-cp315-cp315-win_amd64.whl", hash = "sha256:075566a44289afb634ceaafa7d8d377bb2667a5b4a90fbee2609efdcd015c66e", size = 230461, upload-time = "2026-11-04T19:52:06.727Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c8/b307327b1256f9ed8ffc3271db041913835cfee065fbaf45105978093c08/coverage-7.17.2-cp315-cp315-win_arm64.whl", hash = "sha256:be699e76b57fede8920a0f568eff23b270d9004414b7cf6744bdda851638be6e", size = 230322, upload-time = "2026-11-04T19:52:08.191Z" }, + { url = "https://files.pythonhosted.org/packages/40/18/0912e37f23c101faebd7c5e544c6ba6bd750715f81f2a2c7ea66f83a2295/coverage-7.17.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:ffbaadf8612781358b3f949ad68dce26c5a35beaafcc48125fc231dbcce7d362", size = 228380, upload-time = "2026-11-04T19:52:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a0/c4ce9bbf82b2e9dbf2e0326004c8c37d87a275f515fc7bb22344a6162050/coverage-7.17.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d4004fce7b3a8ae5ef2af835670763f12723e9b9d74837697607b89a17f77312", size = 228776, upload-time = "2026-11-04T19:52:11.351Z" }, + { url = "https://files.pythonhosted.org/packages/3f/05/9c7429c87589534755398a58fe9837fdb2870a22d99e87623fcb261e1d4e/coverage-7.17.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a838a10afb5b60a6cc6315348b176badaf2409d821208a3e3cda1dc608129e23", size = 269129, upload-time = "2026-11-04T19:52:12.815Z" }, + { url = "https://files.pythonhosted.org/packages/e9/22/d1ad2ea7c8871d66f8453d33cdeec39986e6e1f2a104628e9bcb171f0209/coverage-7.17.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0fc17882975fe485cfc95fb2f20eed937e05e0c0d8ffa5048857cda3aa838e8a", size = 271967, upload-time = "2026-11-04T19:52:14.685Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1d/5038de4bf6549903ec3d86959c6f18aff54911616b47aa96f4d6a5660c93/coverage-7.17.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a65faa1ee98e090a5fd7c116f6d3ab5c6a85fcc46849b8494835608715836399", size = 274667, upload-time = "2026-11-04T19:52:16.122Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ea/68a26b4b5eb255d92de906fd66e8d720b1df15e705949ea9a9cef2cc7f8b/coverage-7.17.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7c17f716c837db550a202e7732cdfaa726b8515cba88be42193616913970d223", size = 276001, upload-time = "2026-11-04T19:52:17.639Z" }, + { url = "https://files.pythonhosted.org/packages/70/86/0f4238f09b22bc03a907346fabcabe5449626879cdedbc1edd362ab0e570/coverage-7.17.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:545ca42488c7f0e62324fb2621aa05737db843b75e84e65a271bbf976f496f66", size = 269687, upload-time = "2026-11-04T19:52:19.169Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/1885255425900f691c543d88394d9f0dacd2f2dc78422e660c747ca9c95c/coverage-7.17.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:8de787f533b13aeeea5d7139610eadb56f249cc739840b973603d1d18d0fb023", size = 272158, upload-time = "2026-11-04T19:52:20.714Z" }, + { url = "https://files.pythonhosted.org/packages/02/d6/0111601342d91e25d13e22a55f34600aa387c55b5027b90e78d941911821/coverage-7.17.2-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:485b2ed9e13b7e9209276843d7147e2f1cd1c816582655650a64c8f922093c58", size = 269182, upload-time = "2026-11-04T19:52:22.204Z" }, + { url = "https://files.pythonhosted.org/packages/70/48/d9bdc5346a9ad09711f55b6901ee05b57305e6b2b66e6edfd50f81f0d410/coverage-7.17.2-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:a472e23a7514478e1c8124ea056424d681e3173975650b0a06b6181658f1009a", size = 273790, upload-time = "2026-11-04T19:52:23.677Z" }, + { url = "https://files.pythonhosted.org/packages/98/75/3a4d8ce4aa59ed2e898364acf923b39471689d51876bb978c42e664cdb79/coverage-7.17.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:0a623de5c57c5c3f3aed830c73a3a5aad91dbf76d53bc30ec161be758c8ca408", size = 269277, upload-time = "2026-11-04T19:52:25.19Z" }, + { url = "https://files.pythonhosted.org/packages/4a/11/d15082e504e7852a25848ee0dcb59453579ec923c6d1f3010316e1fb6118/coverage-7.17.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:2ad189c65706306644399e48530ad1aea1dfb311bd21d988df1ace422d09f3f8", size = 270890, upload-time = "2026-11-04T19:52:26.745Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9d/6460101b0191afae9ed84c8d8136cc8cc09675f376ce08acb3118b8c27be/coverage-7.17.2-cp315-cp315t-win32.whl", hash = "sha256:6fd6f218e39a621a30dbaf9543831fa10fe713afc8a44b52fc42cfe277412609", size = 230148, upload-time = "2026-11-04T19:52:28.164Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f2/3462d9616445a8cb36ec2e027d1c0ae82f6f90f224d9851ba0b22b7425d6/coverage-7.17.2-cp315-cp315t-win_amd64.whl", hash = "sha256:ef07aae0a666ef5edcab5bc1bb590468c6f4120a8cdad50a010d042a59ab6e77", size = 231074, upload-time = "2026-11-04T19:52:29.714Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/abeca0cf853e77aba9464d4ada4603c8b6e744b667c13e1d4eefecc487af/coverage-7.17.2-cp315-cp315t-win_arm64.whl", hash = "sha256:c68f64d96857749a215657c172f7144ed026063489686b901774f271997923c2", size = 230648, upload-time = "2026-11-04T19:52:31.267Z" }, + { url = "https://files.pythonhosted.org/packages/da/91/1805cc144497d76a436f66a21fa57492bee7ed1257826fb992f03b9b2b6f/coverage-7.17.2-py3-none-any.whl", hash = "sha256:e674027ceb6bfd959f1bb035000fd20fa8b717dc6765adaaf8bca347822c8c7d", size = 219280, upload-time = "2026-11-04T19:52:32.726Z" }, +] + +[[package]] +name = "rules-python-coverage" +version = "0.0.0" +source = { virtual = "." } +dependencies = [ + { name = "coverage", version = "7.10.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "coverage", version = "7.16.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "coverage", version = "7.17.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] + +[package.metadata] +requires-dist = [ + { name = "coverage", marker = "python_full_version < '3.10'" }, + { name = "coverage", marker = "python_full_version == '3.10.*'" }, + { name = "coverage", marker = "python_full_version == '3.11.*'" }, + { name = "coverage", marker = "python_full_version == '3.12.*'" }, + { name = "coverage", marker = "python_full_version == '3.13.*'" }, + { name = "coverage", marker = "python_full_version == '3.14.*'" }, + { name = "coverage", marker = "python_full_version >= '3.15'" }, +] diff --git a/python/private/python.bzl b/python/private/python.bzl index 0d392f89a0..685d93b608 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -1257,7 +1257,18 @@ Then the python interpreter will be available as `my_python_name`. attrs = { "configure_coverage_tool": attr.bool( mandatory = False, - doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.", + doc = """\ +Whether or not to configure the default coverage tool provided by `rules_python` for the compatible +toolchains. + +The coverage tool is the `coverage` package from the `@rules_python_coverage` pip hub. See +{doc}`/coverage` for more details. + +:::{versionchanged} VERSION_NEXT_FEATURE +The coverage tool is the `coverage` package from the `@rules_python_coverage` pip hub, which +supports all of the platforms configured with {obj}`pip.default`. +::: +""", ), "ignore_root_user_error": attr.bool( default = True, diff --git a/python/private/python_register_toolchains.bzl b/python/private/python_register_toolchains.bzl index 3e93c2c0a4..d82c8299bb 100644 --- a/python/private/python_register_toolchains.bzl +++ b/python/private/python_register_toolchains.bzl @@ -34,6 +34,10 @@ load( "toolchains_repo", ) +# The `coverage` package from the `rules_python_coverage` pip hub, which is +# configured in the `MODULE.bazel` file of `rules_python`. +_BZLMOD_COVERAGE_TOOL = Label("@rules_python_coverage//coverage") + def _get_platform_setting(version_info, key, *, platform, default): value = version_info.get(key, {}) if type(value) != "dict": @@ -168,7 +172,12 @@ def python_register_toolchains( default = None, ), ) - if register_coverage_tool and coverage_tool == None: + if register_coverage_tool and coverage_tool == None and bzlmod_toolchain_call: + # NOTE: The coverage tool is not supported on Windows, see + # `coverage_dep`. + if "windows" not in base_platform: + coverage_tool = str(_BZLMOD_COVERAGE_TOOL) + elif register_coverage_tool and coverage_tool == None: coverage_tool = coverage_dep( name = "{name}_{platform}_coverage".format( name = name, From c5cd90de5829e5e1b255afb95473ebc9a4348d6b Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:04:35 +0900 Subject: [PATCH 18/39] docs(coverage): prefer passing a py_library as the coverage_tool Document the `rules_python_coverage` hub and recommend passing a `py_library` as the coverage tool. Using a target without `PyInfo` will be deprecated in the next version. --- docs/coverage.md | 55 ++++++++++++++++-------------- python/private/py_runtime_rule.bzl | 8 ++++- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/docs/coverage.md b/docs/coverage.md index 8e620e5746..71050818af 100644 --- a/docs/coverage.md +++ b/docs/coverage.md @@ -8,18 +8,24 @@ when configuring toolchains. ## Enabling `rules_python` coverage support -Enabling the coverage support bundled with `rules_python` just requires setting an -argument when registering toolchains. +Enabling the coverage support provided by `rules_python` just requires setting +an argument when registering toolchains. For Bzlmod: ```starlark python.toolchain( - "@python3_14_toolchains//:all", configure_coverage_tool = True, + python_version = "3.14", ) ``` +This uses the `coverage` package from the `@rules_python_coverage` pip hub, +which `rules_python` defines from its own `uv.lock` file. The lock file has the +latest `coverage` version that supports each of the Python versions starting +from 3.9 and the wheels for all of the platforms configured with +{obj}`pip.default`, so the coverage tool works when cross-building too. + For WORKSPACE configuration: ```starlark @@ -29,7 +35,7 @@ python_register_toolchains( ``` :::{note} -This will implicitly add the version of `coverage` bundled with +This will implicitly add the version of `coverage` provided by `rules_python` to the dependencies of `py_test` rules when `bazel coverage` is run. If a target already transitively depends on a different version of `coverage`, then the behavior is undefined -- it is undefined which version comes @@ -38,12 +44,12 @@ need to manually configure coverage (see below). ::: :::{note} -The bundled `coverage` wheel set covers CPython 3.9 through 3.14 (with -freethreaded variants for 3.13+), and not every platform within that range. -When the interpreter that `bazel coverage` actually selects has no bundled -wheel, `configure_coverage_tool = True` produces no coverage tool and -`bazel coverage` emits empty lcov data; manually configure coverage (see -below) instead. +For WORKSPACE, the bundled `coverage` wheel set covers CPython 3.9 through +3.14 (with freethreaded variants for 3.13+), and not every platform within +that range. When the interpreter that `bazel coverage` actually selects has no +bundled wheel, `register_coverage_tool = True` produces no coverage tool and +`bazel coverage` emits empty lcov data; manually configure coverage (see below) +instead. `py_runtime` warns when this happens. The warning is emitted during analysis, for the runtime toolchain resolution selected and only when coverage is being @@ -54,33 +60,30 @@ registered for but a given build never uses. ## Manually configuring coverage To manually configure coverage support, you'll need to set the -`py_runtime.coverage_tool` attribute. This attribute is a target that specifies -the coverage entry point file and, optionally, client libraries that are added -to `py_test` targets. Typically, this would be a `filegroup` that looked like: +{attr}`py_runtime.coverage_tool` attribute. The preferred way is to pass a +`py_library` of the `coverage` package, e.g. `@pypi//coverage` from your +{obj}`pip.parse` hub. See {ref}`coverage-from-pypi`. -```starlark -filegroup( - name = "coverage", - srcs = ["coverage_main.py"], - data = ["coverage_lib1.py", ...] -) -``` - -Using `filegroup` isn't required, nor are including client libraries. The -important behaviors of the target are: +The important behaviors of the target are: * It provides {obj}`PyInfo` (e.g. it is a `py_library`), in which case its - import paths are used to import the `coverage` package. See - {ref}`coverage-from-pypi`. + import paths are used to import the `coverage` package. This is the + preferred way. * Otherwise, it provides a single output file OR it provides an executable output; this output is treated as the coverage entry point. * If it provides runfiles, then `runfiles.files` are included into `py_test`. +:::{deprecated} VERSION_NEXT_FEATURE +Using a target that does not provide {obj}`PyInfo`, e.g. a `filegroup` with +the coverage entry point file, will be deprecated in the next version. Use a +`py_library` instead. +::: + (coverage-from-pypi)= ### Using `coverage` from PyPI To use the version of `coverage` that is locked in your requirements instead of -the one bundled with `rules_python`, point the toolchain to the `coverage` +the one provided by `rules_python`, point the toolchain to the `coverage` package in your {obj}`pip.parse` hub: ```starlark diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl index 80910cd045..8e05ec875a 100644 --- a/python/private/py_runtime_rule.bzl +++ b/python/private/py_runtime_rule.bzl @@ -351,11 +351,17 @@ The entry point for the tool must be loadable by a Python interpreter (e.g. a of [`coverage.py`](https://coverage.readthedocs.io), at least including the `run` and `lcov` subcommands. -See {ref}`coverage-from-pypi` for using `coverage` from a pip hub. +Using a target providing {obj}`PyInfo` is the preferred way. See +{ref}`coverage-from-pypi` for using `coverage` from a pip hub. :::{versionchanged} VERSION_NEXT_FEATURE Targets providing {obj}`PyInfo`, such as `@pypi//coverage`, can be used. ::: + +:::{deprecated} VERSION_NEXT_FEATURE +Using a target that does not provide {obj}`PyInfo` will be deprecated in the +next version. +::: """, ), "files": attr.label_list( From 5149b91edef1df7d213e8e46382b3f36d9032ea2 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:11:49 +0900 Subject: [PATCH 19/39] feat(pypi): use the none-any wheel as a fallback with target_platforms = ["*"] When `target_platforms = ["*"]` is used with a `uv.lock` file, add the platform independent wheel of a package, if there is one, as a fallback for the Python version. The platforms that are not configured with `pip.default` can then still use the package, whilst the configured platforms keep using the more specific wheels. --- news/pip-target-platforms-all.added.md | 4 +- python/private/pypi/extension.bzl | 4 +- python/private/pypi/hub_builder.bzl | 24 ++++++++++ python/private/pypi/parse_requirements.bzl | 49 ++++++++++++++++++-- tests/pypi/hub_builder/hub_builder_tests.bzl | 47 +++++++++++++++++-- 5 files changed, 118 insertions(+), 10 deletions(-) diff --git a/news/pip-target-platforms-all.added.md b/news/pip-target-platforms-all.added.md index cb690ce07b..374a45f061 100644 --- a/news/pip-target-platforms-all.added.md +++ b/news/pip-target-platforms-all.added.md @@ -1,2 +1,4 @@ (pypi) {attr}`pip.parse.target_platforms` accepts `"*"` to evaluate the -requirements for all of the platforms configured with {obj}`pip.default`. +requirements for all of the platforms configured with {obj}`pip.default`. With +{attr}`pip.parse.uv_lock`, the `none-any` wheel of a package is also used for +the platforms that are not configured. diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index 908a40b90d..fa328d6556 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -907,7 +907,9 @@ If you only care for the host platform and do not have a usecase to cross-build, a string `"{os}_{arch}"` as the value here. You could also use `"{os}_{arch}_freethreaded"` as well. Use `"*"` to evaluate the requirements for all of the platforms configured with -{obj}`pip.default`. +{obj}`pip.default`. When used together with {attr}`uv_lock`, the platform +independent (`none-any`) wheel of a package, if present, is also used for the +platforms that are not configured with {obj}`pip.default`. :::{include} /_includes/experimental_api.md ::: diff --git a/python/private/pypi/hub_builder.bzl b/python/private/pypi/hub_builder.bzl index df0c48794e..1f61a5e332 100644 --- a/python/private/pypi/hub_builder.bzl +++ b/python/private/pypi/hub_builder.bzl @@ -577,6 +577,7 @@ def _create_whl_repos( extra_pip_args = pip_attr.extra_pip_args, get_index_urls = self._get_index_urls.get(python_version), toml_decode = getattr(self._config, "toml_decode", None), + any_src = "*" in pip_attr.target_platforms, logger = logger, ) @@ -631,6 +632,29 @@ def _create_whl_repos( repo = repo, ) + if getattr(whl, "any_src", None): + # Use the platform independent wheel for the platforms that are + # not configured, so it has no target platforms and the more + # specific config settings take precedence. + _add_whl_library( + self, + python_version = python_version, + whl = whl, + repo = _whl_repo( + src = whl.any_src, + index_url = whl.index_url, + whl_library_args = whl_library_args, + download_only = pip_attr.download_only, + netrc = self._config.netrc or pip_attr.netrc, + use_downloader = True, + auth_patterns = self._config.auth_patterns or pip_attr.auth_patterns, + python_version = _major_minor_version(python_version), + is_multiple_versions = whl.is_multiple_versions, + interpreter = interpreter, + enable_pipstar_extract = enable_pipstar_extract, + ), + ) + def _common_args(self, module_ctx, *, pip_attr): # Construct args separately so that the lock file can be smaller and does not include unused # attrs. diff --git a/python/private/pypi/parse_requirements.bzl b/python/private/pypi/parse_requirements.bzl index 2d8c4b4774..7913602d44 100644 --- a/python/private/pypi/parse_requirements.bzl +++ b/python/private/pypi/parse_requirements.bzl @@ -47,6 +47,7 @@ def parse_requirements( extract_url_srcs = True, uv_lock = None, toml_decode = None, + any_src = False, logger): """Get the requirements with platforms that the requirements apply to. @@ -71,6 +72,9 @@ def parse_requirements( requirements files if both are provided. toml_decode: {type}`callable | None` A function to decode TOML content (e.g. `toml.decode`). Required when `uv_lock` is provided. + any_src: {type}`bool` whether to add the `any_src` field to each + returned struct, which is the platform independent wheel of the + package or `None`. Only supported with `uv_lock`. logger: repo_utils.logger, a simple struct to log diagnostic messages. Returns: @@ -96,6 +100,7 @@ def parse_requirements( get_index_urls = get_index_urls, extract_url_srcs = extract_url_srcs, uv_lock = uv_lock, + any_src = any_src, logger = logger, ) @@ -126,11 +131,13 @@ def _parse_requirements_with_uv_lock( get_index_urls, extract_url_srcs, uv_lock, + any_src, logger): """Parse requirements using uv.lock as the primary source.""" if uv_lock: return _parse_uv_lock_json( uv_lock = uv_lock, + any_src = any_src, all_platforms = _get_all_platforms(requirements_by_platform) if requirements_by_platform else sorted(platforms.keys()), platforms = platforms, extra_pip_args = extra_pip_args, @@ -220,7 +227,7 @@ def _uv_lock_package_platforms(packages, all_platforms, platforms): queue.append(target) return result -def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, platforms = {}): +def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, platforms = {}, any_src = False): """Parse uv.lock JSON and build the same return structs as parse_requirements. Args: @@ -230,6 +237,8 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p extra_pip_args: {type}`list[str] | None` Extra pip arguments to pass through. platforms: {type}`dict[str, struct]` A dict of platform name to platform info (containing `.env` with PEP 508 marker environment). + any_src: {type}`bool` whether to add the `any_src` field with the platform + independent wheel of each package. Returns: {type}`list[struct]` The same format as {func}`parse_requirements`. @@ -247,6 +256,7 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p version = pkg["version"] norm_name = normalize_name(name) entry = uv_packages.setdefault(norm_name, { + "any_srcs": {}, "distribution": name, "resolved_srcs": [], "versions": {}, @@ -303,6 +313,35 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p requirement = _uv_lock_git_requirement(pkg["source"]["git"]), ) + # The platform independent wheel, if there is one, which can be used + # as a fallback for the platforms that are not configured. + if any_src and candidates and pkg_platforms and platforms.get(pkg_platforms[0]): + platform = platforms[pkg_platforms[0]] + any_whl = select_whl( + whls = candidates, + python_version = platform.env.get("python_full_version", "3"), + whl_platform_tags = ["any"], + whl_abi_tags = ["none"], + implementation_name = platform.env.get("implementation_name", "cpython"), + limit = 1, + logger = logger, + ) + if any_whl: + entry["any_srcs"][any_whl.filename + any_whl.digest] = struct( + distribution = name, + extra_pip_args = extra_pip_args or [], + requirement_line = "{name}{extras}=={version}".format( + name = name, + extras = extra_str, + version = version, + ), + target_platforms = [], + filename = any_whl.filename, + digest = any_whl.digest, + url = any_whl.url, + yanked = None, + ) + plat_to_src = {} for p in pkg_platforms: platform = platforms.get(p) @@ -368,7 +407,7 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p for src in info["resolved_srcs"]: for p in src.target_platforms: resolved_platforms[p] = None - item = struct( + item = dict( name = norm_name, # Only expose packages that resolved on all configured platforms. # Workspace/root members (e.g. `source = { virtual = "." }` or @@ -384,7 +423,11 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p index_url = info["index_url"], srcs = info["resolved_srcs"], ) - ret.append(item) + if any_src: + # The platform independent wheel, if there is exactly one. + any_srcs = info["any_srcs"].values() + item["any_src"] = any_srcs[0] if len(any_srcs) == 1 else None + ret.append(struct(**item)) logger.debug(lambda: "Parsed {} packages from uv.lock".format(len(ret))) return ret diff --git a/tests/pypi/hub_builder/hub_builder_tests.bzl b/tests/pypi/hub_builder/hub_builder_tests.bzl index 0a5c319311..2470cf79e1 100644 --- a/tests/pypi/hub_builder/hub_builder_tests.bzl +++ b/tests/pypi/hub_builder/hub_builder_tests.bzl @@ -183,7 +183,25 @@ def _test_target_platforms_all(env): os_name = "osx", arch_name = "aarch64", mock_files = { - "uv.lock": """{"package":[{"name":"simple","source":{"registry":"https://pypi.org/simple"},"version":"0.0.1","wheels":[{"hash":"sha256:deadbeef","url":"https://files.pythonhosted.org/packages/simple-0.0.1-py3-none-any.whl"}]}]}""", + "uv.lock": json.encode({"package": [ + { + "name": "plat", + "source": {"registry": "https://pypi.org/simple"}, + "version": "0.0.1", + "wheels": [ + {"hash": "sha256:deadb00f", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-cp315-cp315-manylinux_2_17_x86_64.whl"}, + {"hash": "sha256:deadbeef", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-py3-none-any.whl"}, + ], + }, + { + "name": "platonly", + "source": {"registry": "https://pypi.org/simple"}, + "version": "0.0.1", + "wheels": [ + {"hash": "sha256:deadbaaf", "url": "https://files.pythonhosted.org/packages/platonly-0.0.1-cp315-cp315-manylinux_2_17_x86_64.whl"}, + ], + }, + ]}), }, ), _parse( @@ -196,20 +214,39 @@ def _test_target_platforms_all(env): ) pypi = builder.build() - pypi.exposed_packages().contains_exactly(["simple"]) + # Only the packages that are available on all of the platforms are exposed. + pypi.exposed_packages().contains_exactly(["plat"]) pypi.whl_map().contains_exactly({ - "simple": { - "pypi_315_simple_py3_none_any_deadbeef": [ + "plat": { + "pypi_315_plat_cp315_cp315_manylinux_2_17_x86_64_deadb00f": [ + whl_config_setting( + target_platforms = ("cp315_linux_x86_64",), + version = "3.15", + ), + ], + "pypi_315_plat_py3_none_any_deadbeef": [ whl_config_setting( target_platforms = ( "cp315_linux_aarch64", - "cp315_linux_x86_64", "cp315_linux_x86_64_freethreaded", "cp315_osx_aarch64", "cp315_windows_aarch64", ), version = "3.15", ), + # The fallback for the platforms that are not configured. + whl_config_setting( + version = "3.15", + ), + ], + }, + # There is no platform independent wheel, so there is no fallback. + "platonly": { + "pypi_315_platonly_cp315_cp315_manylinux_2_17_x86_64_deadbaaf": [ + whl_config_setting( + target_platforms = ("cp315_linux_x86_64",), + version = "3.15", + ), ], }, }) From 059c494f5a5ccf525d51d3641caae7feda2d1767 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:16:38 +0900 Subject: [PATCH 20/39] feat(pypi): define default pip platforms for all toolchain platforms The default `pip.default` platforms only covered the `x86_64` and `aarch64` Linux, macOS and Windows platforms, so the toolchains for the other platforms could not use the wheels from a pip hub. Add the default platforms for all of the platforms that the hermetic Python toolchains support and a test that ensures that each toolchain platform variant has a matching pip platform. The glibc Linux platforms now also match on `--py_linux_libc`, so that they do not overlap with the new musl platform, which uses the `musllinux` wheels. --- news/pip-default-platforms.added.md | 5 + python/private/pypi/default_platforms.bzl | 205 ++++++++++-------- tests/pypi/default_platforms/BUILD.bazel | 3 + .../default_platforms_tests.bzl | 71 ++++++ 4 files changed, 189 insertions(+), 95 deletions(-) create mode 100644 news/pip-default-platforms.added.md create mode 100644 tests/pypi/default_platforms/BUILD.bazel create mode 100644 tests/pypi/default_platforms/default_platforms_tests.bzl diff --git a/news/pip-default-platforms.added.md b/news/pip-default-platforms.added.md new file mode 100644 index 0000000000..4a75c13588 --- /dev/null +++ b/news/pip-default-platforms.added.md @@ -0,0 +1,5 @@ +(pypi) The default {obj}`pip.default` platforms now cover all of the platforms +of the hermetic Python toolchains: `linux_armv7`, `linux_i386`, `linux_ppc`, +`linux_riscv64`, `linux_s390x`, `linux_x86_64_musl`, `osx_arm64e` and +`windows_x86_32`, together with their `_freethreaded` variants. The glibc Linux +platforms now also match on the {flag}`--py_linux_libc` flag. diff --git a/python/private/pypi/default_platforms.bzl b/python/private/pypi/default_platforms.bzl index 578e7eb31e..d3032726e1 100644 --- a/python/private/pypi/default_platforms.bzl +++ b/python/private/pypi/default_platforms.bzl @@ -1,5 +1,38 @@ """The default platforms used for selecting the wheels.""" +def _platform( + *, + name, + os_name, + arch_name, + cpu, + whl_platform_tags, + platform_version, + freethreaded, + os_constraint = None, + config_settings = [], + marker = ""): + return { + "arch_name": arch_name, + "config_settings": [ + "@platforms//cpu:{}".format(cpu), + "@platforms//os:{}".format(os_constraint or os_name), + ] + config_settings + [ + "//python/config_settings:_is_py_freethreaded_{}".format( + "yes" if freethreaded else "no", + ), + ], + "env": {"platform_version": platform_version}, + "marker": "python_version >= '3.13'" if freethreaded else marker, + "name": name, + "os_name": os_name, + "whl_abi_tags": ["cp{major}{minor}t"] if freethreaded else [ + "abi3", + "cp{major}{minor}", + ], + "whl_platform_tags": whl_platform_tags, + } + def default_platforms(): """Return the built-in default platform definitions. @@ -9,6 +42,9 @@ def default_platforms(): for build_config; root modules can override individual platforms via pip.default tags. + There is a platform for each of the platforms that the hermetic Python + toolchains support, see `//python:versions.bzl`. + Returns: A dict of platform name to platform config dicts. """ @@ -16,108 +52,87 @@ def default_platforms(): # stabilize the API this list may be updated with a mention in the CHANGELOG. platforms = {} - - # Linux platforms - for cpu in ["x86_64", "aarch64"]: - for freethreaded in ["", "_freethreaded"]: - platform_name = "linux_{}{}".format(cpu, freethreaded) - platforms[platform_name] = { - "arch_name": cpu, - "config_settings": [ - "@platforms//cpu:{}".format(cpu), - "@platforms//os:linux", - "//python/config_settings:_is_py_freethreaded_{}".format( - "yes" if freethreaded else "no", - ), - ], - "env": {"platform_version": "0"}, - "marker": "python_version >= '3.13'" if freethreaded else "", - "name": platform_name, - "os_name": "linux", - "whl_abi_tags": ["cp{major}{minor}t"] if freethreaded else [ - "abi3", - "cp{major}{minor}", + for freethreaded in ["", "_freethreaded"]: + # Linux glibc platforms. The key is the platform name suffix and the + # values are the `@platforms//cpu` constraint, the `arch_name` and the + # wheel platform tag suffix. + for name, (cpu, arch_name, tag) in { + "aarch64": ("aarch64", "aarch64", "aarch64"), + "armv7": ("armv7", "arm", "armv7l"), + "i386": ("i386", "x86_32", "i686"), + "ppc": ("ppc", "ppc", "ppc64le"), + "riscv64": ("riscv64", "riscv64", "riscv64"), + "s390x": ("s390x", "s390x", "s390x"), + "x86_64": ("x86_64", "x86_64", "x86_64"), + }.items(): + platform_name = "linux_{}{}".format(name, freethreaded) + platforms[platform_name] = _platform( + name = platform_name, + os_name = "linux", + arch_name = arch_name, + cpu = cpu, + config_settings = [ + "//python/config_settings:_is_py_linux_libc_glibc", ], - "whl_platform_tags": [ - "linux_{}".format(cpu), - "manylinux_*_{}".format(cpu), + platform_version = "0", + freethreaded = freethreaded, + whl_platform_tags = [ + "linux_{}".format(tag), + "manylinux_*_{}".format(tag), ], - } + ) + + # Linux musl platforms + platform_name = "linux_x86_64_musl{}".format(freethreaded) + platforms[platform_name] = _platform( + name = platform_name, + os_name = "linux", + arch_name = "x86_64", + cpu = "x86_64", + config_settings = [ + "//python/config_settings:_is_py_linux_libc_musl", + ], + platform_version = "0", + freethreaded = freethreaded, + whl_platform_tags = ["musllinux_*_x86_64"], + ) - # macOS platforms - for cpu, platform_tag_cpus in { - "aarch64": ["universal2", "arm64"], - "x86_64": ["universal2", "x86_64"], - }.items(): - for freethreaded in ["", "_freethreaded"]: + # macOS platforms + for cpu, platform_tag_cpus in { + "aarch64": ["universal2", "arm64"], + "arm64e": ["arm64e"], + "x86_64": ["universal2", "x86_64"], + }.items(): platform_name = "osx_{}{}".format(cpu, freethreaded) - platforms[platform_name] = { - "arch_name": cpu, - "config_settings": [ - "@platforms//cpu:{}".format(cpu), - "@platforms//os:osx", - "//python/config_settings:_is_py_freethreaded_{}".format( - "yes" if freethreaded else "no", - ), - ], - "env": {"platform_version": "14.0"}, - "marker": "python_version >= '3.13'" if freethreaded else "", - "name": platform_name, - "os_name": "osx", - "whl_abi_tags": ["cp{major}{minor}t"] if freethreaded else [ - "abi3", - "cp{major}{minor}", - ], - "whl_platform_tags": [ + platforms[platform_name] = _platform( + name = platform_name, + os_name = "osx", + arch_name = "aarch64" if cpu == "arm64e" else cpu, + cpu = cpu, + platform_version = "14.0", + freethreaded = freethreaded, + whl_platform_tags = [ "macosx_*_{}".format(suffix) for suffix in platform_tag_cpus ], - } - - # Windows x86_64 platforms - for freethreaded in ["", "_freethreaded"]: - platform_name = "windows_x86_64{}".format(freethreaded) - platforms[platform_name] = { - "arch_name": "x86_64", - "config_settings": [ - "@platforms//cpu:x86_64", - "@platforms//os:windows", - "//python/config_settings:_is_py_freethreaded_{}".format( - "yes" if freethreaded else "no", - ), - ], - "env": {"platform_version": "0"}, - "marker": "python_version >= '3.13'" if freethreaded else "", - "name": platform_name, - "os_name": "windows", - "whl_abi_tags": ["cp{major}{minor}t"] if freethreaded else [ - "abi3", - "cp{major}{minor}", - ], - "whl_platform_tags": ["win_amd64"], - } + ) - # Windows aarch64 platforms - for freethreaded in ["", "_freethreaded"]: - platform_name = "windows_aarch64{}".format(freethreaded) - platforms[platform_name] = { - "arch_name": "aarch64", - "config_settings": [ - "@platforms//cpu:aarch64", - "@platforms//os:windows", - "//python/config_settings:_is_py_freethreaded_{}".format( - "yes" if freethreaded else "no", - ), - ], - "env": {"platform_version": "0"}, - "marker": "python_version >= '3.13'" if freethreaded else "python_version >= '3.11'", - "name": platform_name, - "os_name": "windows", - "whl_abi_tags": ["cp{major}{minor}t"] if freethreaded else [ - "abi3", - "cp{major}{minor}", - ], - "whl_platform_tags": ["win_arm64"], - } + # Windows platforms + for cpu, (tag, marker) in { + "aarch64": ("win_arm64", "python_version >= '3.11'"), + "x86_32": ("win32", ""), + "x86_64": ("win_amd64", ""), + }.items(): + platform_name = "windows_{}{}".format(cpu, freethreaded) + platforms[platform_name] = _platform( + name = platform_name, + os_name = "windows", + arch_name = cpu, + cpu = cpu, + platform_version = "0", + freethreaded = freethreaded, + marker = marker, + whl_platform_tags = [tag], + ) return platforms diff --git a/tests/pypi/default_platforms/BUILD.bazel b/tests/pypi/default_platforms/BUILD.bazel new file mode 100644 index 0000000000..118f600f55 --- /dev/null +++ b/tests/pypi/default_platforms/BUILD.bazel @@ -0,0 +1,3 @@ +load(":default_platforms_tests.bzl", "default_platforms_test_suite") + +default_platforms_test_suite(name = "default_platforms_tests") diff --git a/tests/pypi/default_platforms/default_platforms_tests.bzl b/tests/pypi/default_platforms/default_platforms_tests.bzl new file mode 100644 index 0000000000..5dcbfd75d7 --- /dev/null +++ b/tests/pypi/default_platforms/default_platforms_tests.bzl @@ -0,0 +1,71 @@ +"""Tests for the default pip platforms.""" + +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("//python:versions.bzl", "PLATFORMS") +load("//python/private/pypi:default_platforms.bzl", "default_platforms") # buildifier: disable=bzl-visibility + +_tests = [] + +# The settings that select the variant of the toolchain distribution, which do +# not change the compatible wheels. +_IGNORED_SETTINGS = [ + "_is_py_pbs_distribution_", +] + +def _normalize(labels): + """Return the set of the canonical label strings. + + Args: + labels: {type}`list[str | Label]` the labels to normalize. + + Returns: + {type}`tuple[str]` the sorted canonical labels. + """ + ret = {} + for label in labels: + label = Label(label) + if any([s in label.name for s in _IGNORED_SETTINGS]): + continue + + # `@platforms//os:macos` is an alias of `@platforms//os:osx`. + if label.repo_name == "platforms" and label.package == "os" and label.name == "macos": + label = Label("@platforms//os:osx") + ret[str(label)] = None + return tuple(sorted(ret)) + +def _test_all_toolchain_platforms_have_pip_platforms(env): + pip_platforms = { + _normalize(values["config_settings"]): name + for name, values in default_platforms().items() + } + + for name, platform in PLATFORMS.items(): + want = _normalize(platform.compatible_with + platform.target_settings) + env.expect.where( + toolchain_platform = name, + ).that_str( + pip_platforms.get(want, "".format(want)), + ).not_equals( + "".format(want), + ) + +_tests.append(_test_all_toolchain_platforms_have_pip_platforms) + +def _test_pip_platforms_are_unique(env): + seen = {} + for name, values in default_platforms().items(): + key = _normalize(values["config_settings"]) + env.expect.where( + pip_platform = name, + ).that_str(seen.get(key, name)).equals(name) + seen[key] = name + +_tests.append(_test_pip_platforms_are_unique) + +def default_platforms_test_suite(name): + """Create the test suite. + + Args: + name: the name of the test suite + """ + test_suite(name = name, basic_tests = _tests) From 6073cd04754502af2dd08a9667aca46e345fc2ba Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:22:12 +0900 Subject: [PATCH 21/39] Revert "feat(pypi): use the none-any wheel as a fallback with target_platforms = ["*"]" This reverts commit 5149b91edef1df7d213e8e46382b3f36d9032ea2. --- news/pip-target-platforms-all.added.md | 4 +- python/private/pypi/extension.bzl | 4 +- python/private/pypi/hub_builder.bzl | 24 ---------- python/private/pypi/parse_requirements.bzl | 49 ++------------------ tests/pypi/hub_builder/hub_builder_tests.bzl | 47 ++----------------- 5 files changed, 10 insertions(+), 118 deletions(-) diff --git a/news/pip-target-platforms-all.added.md b/news/pip-target-platforms-all.added.md index 374a45f061..cb690ce07b 100644 --- a/news/pip-target-platforms-all.added.md +++ b/news/pip-target-platforms-all.added.md @@ -1,4 +1,2 @@ (pypi) {attr}`pip.parse.target_platforms` accepts `"*"` to evaluate the -requirements for all of the platforms configured with {obj}`pip.default`. With -{attr}`pip.parse.uv_lock`, the `none-any` wheel of a package is also used for -the platforms that are not configured. +requirements for all of the platforms configured with {obj}`pip.default`. diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index fa328d6556..908a40b90d 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -907,9 +907,7 @@ If you only care for the host platform and do not have a usecase to cross-build, a string `"{os}_{arch}"` as the value here. You could also use `"{os}_{arch}_freethreaded"` as well. Use `"*"` to evaluate the requirements for all of the platforms configured with -{obj}`pip.default`. When used together with {attr}`uv_lock`, the platform -independent (`none-any`) wheel of a package, if present, is also used for the -platforms that are not configured with {obj}`pip.default`. +{obj}`pip.default`. :::{include} /_includes/experimental_api.md ::: diff --git a/python/private/pypi/hub_builder.bzl b/python/private/pypi/hub_builder.bzl index 1f61a5e332..df0c48794e 100644 --- a/python/private/pypi/hub_builder.bzl +++ b/python/private/pypi/hub_builder.bzl @@ -577,7 +577,6 @@ def _create_whl_repos( extra_pip_args = pip_attr.extra_pip_args, get_index_urls = self._get_index_urls.get(python_version), toml_decode = getattr(self._config, "toml_decode", None), - any_src = "*" in pip_attr.target_platforms, logger = logger, ) @@ -632,29 +631,6 @@ def _create_whl_repos( repo = repo, ) - if getattr(whl, "any_src", None): - # Use the platform independent wheel for the platforms that are - # not configured, so it has no target platforms and the more - # specific config settings take precedence. - _add_whl_library( - self, - python_version = python_version, - whl = whl, - repo = _whl_repo( - src = whl.any_src, - index_url = whl.index_url, - whl_library_args = whl_library_args, - download_only = pip_attr.download_only, - netrc = self._config.netrc or pip_attr.netrc, - use_downloader = True, - auth_patterns = self._config.auth_patterns or pip_attr.auth_patterns, - python_version = _major_minor_version(python_version), - is_multiple_versions = whl.is_multiple_versions, - interpreter = interpreter, - enable_pipstar_extract = enable_pipstar_extract, - ), - ) - def _common_args(self, module_ctx, *, pip_attr): # Construct args separately so that the lock file can be smaller and does not include unused # attrs. diff --git a/python/private/pypi/parse_requirements.bzl b/python/private/pypi/parse_requirements.bzl index 7913602d44..2d8c4b4774 100644 --- a/python/private/pypi/parse_requirements.bzl +++ b/python/private/pypi/parse_requirements.bzl @@ -47,7 +47,6 @@ def parse_requirements( extract_url_srcs = True, uv_lock = None, toml_decode = None, - any_src = False, logger): """Get the requirements with platforms that the requirements apply to. @@ -72,9 +71,6 @@ def parse_requirements( requirements files if both are provided. toml_decode: {type}`callable | None` A function to decode TOML content (e.g. `toml.decode`). Required when `uv_lock` is provided. - any_src: {type}`bool` whether to add the `any_src` field to each - returned struct, which is the platform independent wheel of the - package or `None`. Only supported with `uv_lock`. logger: repo_utils.logger, a simple struct to log diagnostic messages. Returns: @@ -100,7 +96,6 @@ def parse_requirements( get_index_urls = get_index_urls, extract_url_srcs = extract_url_srcs, uv_lock = uv_lock, - any_src = any_src, logger = logger, ) @@ -131,13 +126,11 @@ def _parse_requirements_with_uv_lock( get_index_urls, extract_url_srcs, uv_lock, - any_src, logger): """Parse requirements using uv.lock as the primary source.""" if uv_lock: return _parse_uv_lock_json( uv_lock = uv_lock, - any_src = any_src, all_platforms = _get_all_platforms(requirements_by_platform) if requirements_by_platform else sorted(platforms.keys()), platforms = platforms, extra_pip_args = extra_pip_args, @@ -227,7 +220,7 @@ def _uv_lock_package_platforms(packages, all_platforms, platforms): queue.append(target) return result -def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, platforms = {}, any_src = False): +def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, platforms = {}): """Parse uv.lock JSON and build the same return structs as parse_requirements. Args: @@ -237,8 +230,6 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p extra_pip_args: {type}`list[str] | None` Extra pip arguments to pass through. platforms: {type}`dict[str, struct]` A dict of platform name to platform info (containing `.env` with PEP 508 marker environment). - any_src: {type}`bool` whether to add the `any_src` field with the platform - independent wheel of each package. Returns: {type}`list[struct]` The same format as {func}`parse_requirements`. @@ -256,7 +247,6 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p version = pkg["version"] norm_name = normalize_name(name) entry = uv_packages.setdefault(norm_name, { - "any_srcs": {}, "distribution": name, "resolved_srcs": [], "versions": {}, @@ -313,35 +303,6 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p requirement = _uv_lock_git_requirement(pkg["source"]["git"]), ) - # The platform independent wheel, if there is one, which can be used - # as a fallback for the platforms that are not configured. - if any_src and candidates and pkg_platforms and platforms.get(pkg_platforms[0]): - platform = platforms[pkg_platforms[0]] - any_whl = select_whl( - whls = candidates, - python_version = platform.env.get("python_full_version", "3"), - whl_platform_tags = ["any"], - whl_abi_tags = ["none"], - implementation_name = platform.env.get("implementation_name", "cpython"), - limit = 1, - logger = logger, - ) - if any_whl: - entry["any_srcs"][any_whl.filename + any_whl.digest] = struct( - distribution = name, - extra_pip_args = extra_pip_args or [], - requirement_line = "{name}{extras}=={version}".format( - name = name, - extras = extra_str, - version = version, - ), - target_platforms = [], - filename = any_whl.filename, - digest = any_whl.digest, - url = any_whl.url, - yanked = None, - ) - plat_to_src = {} for p in pkg_platforms: platform = platforms.get(p) @@ -407,7 +368,7 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p for src in info["resolved_srcs"]: for p in src.target_platforms: resolved_platforms[p] = None - item = dict( + item = struct( name = norm_name, # Only expose packages that resolved on all configured platforms. # Workspace/root members (e.g. `source = { virtual = "." }` or @@ -423,11 +384,7 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p index_url = info["index_url"], srcs = info["resolved_srcs"], ) - if any_src: - # The platform independent wheel, if there is exactly one. - any_srcs = info["any_srcs"].values() - item["any_src"] = any_srcs[0] if len(any_srcs) == 1 else None - ret.append(struct(**item)) + ret.append(item) logger.debug(lambda: "Parsed {} packages from uv.lock".format(len(ret))) return ret diff --git a/tests/pypi/hub_builder/hub_builder_tests.bzl b/tests/pypi/hub_builder/hub_builder_tests.bzl index 2470cf79e1..0a5c319311 100644 --- a/tests/pypi/hub_builder/hub_builder_tests.bzl +++ b/tests/pypi/hub_builder/hub_builder_tests.bzl @@ -183,25 +183,7 @@ def _test_target_platforms_all(env): os_name = "osx", arch_name = "aarch64", mock_files = { - "uv.lock": json.encode({"package": [ - { - "name": "plat", - "source": {"registry": "https://pypi.org/simple"}, - "version": "0.0.1", - "wheels": [ - {"hash": "sha256:deadb00f", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-cp315-cp315-manylinux_2_17_x86_64.whl"}, - {"hash": "sha256:deadbeef", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-py3-none-any.whl"}, - ], - }, - { - "name": "platonly", - "source": {"registry": "https://pypi.org/simple"}, - "version": "0.0.1", - "wheels": [ - {"hash": "sha256:deadbaaf", "url": "https://files.pythonhosted.org/packages/platonly-0.0.1-cp315-cp315-manylinux_2_17_x86_64.whl"}, - ], - }, - ]}), + "uv.lock": """{"package":[{"name":"simple","source":{"registry":"https://pypi.org/simple"},"version":"0.0.1","wheels":[{"hash":"sha256:deadbeef","url":"https://files.pythonhosted.org/packages/simple-0.0.1-py3-none-any.whl"}]}]}""", }, ), _parse( @@ -214,39 +196,20 @@ def _test_target_platforms_all(env): ) pypi = builder.build() - # Only the packages that are available on all of the platforms are exposed. - pypi.exposed_packages().contains_exactly(["plat"]) + pypi.exposed_packages().contains_exactly(["simple"]) pypi.whl_map().contains_exactly({ - "plat": { - "pypi_315_plat_cp315_cp315_manylinux_2_17_x86_64_deadb00f": [ - whl_config_setting( - target_platforms = ("cp315_linux_x86_64",), - version = "3.15", - ), - ], - "pypi_315_plat_py3_none_any_deadbeef": [ + "simple": { + "pypi_315_simple_py3_none_any_deadbeef": [ whl_config_setting( target_platforms = ( "cp315_linux_aarch64", + "cp315_linux_x86_64", "cp315_linux_x86_64_freethreaded", "cp315_osx_aarch64", "cp315_windows_aarch64", ), version = "3.15", ), - # The fallback for the platforms that are not configured. - whl_config_setting( - version = "3.15", - ), - ], - }, - # There is no platform independent wheel, so there is no fallback. - "platonly": { - "pypi_315_platonly_cp315_cp315_manylinux_2_17_x86_64_deadbaaf": [ - whl_config_setting( - target_platforms = ("cp315_linux_x86_64",), - version = "3.15", - ), ], }, }) From d499b587154d4e352a326a1a7268f90554afa92c Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:26:39 +0900 Subject: [PATCH 22/39] chore: rename the news entry to use the PR number --- news/{pip-target-platforms-all.added.md => 4455.added.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename news/{pip-target-platforms-all.added.md => 4455.added.md} (100%) diff --git a/news/pip-target-platforms-all.added.md b/news/4455.added.md similarity index 100% rename from news/pip-target-platforms-all.added.md rename to news/4455.added.md From ce9caee73ac914f1730ff3d9d4fd9ea276bb0873 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:26:43 +0900 Subject: [PATCH 23/39] chore: rename the news entry to use the PR number --- news/{pip-default-platforms.added.md => 4456.added.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename news/{pip-default-platforms.added.md => 4456.added.md} (100%) diff --git a/news/pip-default-platforms.added.md b/news/4456.added.md similarity index 100% rename from news/pip-default-platforms.added.md rename to news/4456.added.md From f61d79070beebc6b6483ceadba227e66d141e518 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:43:07 +0900 Subject: [PATCH 24/39] test(pypi): select platform specific wheels with target_platforms = ["*"] --- tests/pypi/hub_builder/hub_builder_tests.bzl | 74 ++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/pypi/hub_builder/hub_builder_tests.bzl b/tests/pypi/hub_builder/hub_builder_tests.bzl index 0a5c319311..c6c4f66ee9 100644 --- a/tests/pypi/hub_builder/hub_builder_tests.bzl +++ b/tests/pypi/hub_builder/hub_builder_tests.bzl @@ -216,6 +216,80 @@ def _test_target_platforms_all(env): _tests.append(_test_target_platforms_all) +def _test_target_platforms_all_platform_wheels(env): + builder = hub_builder(env) + builder.pip_parse( + _mock_mctx( + os_name = "osx", + arch_name = "aarch64", + mock_files = { + "uv.lock": json.encode({"package": [ + { + "name": "plat", + "source": {"registry": "https://pypi.org/simple"}, + "version": "0.0.1", + "wheels": [ + {"hash": "sha256:deadb001", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-cp315-cp315-manylinux_2_17_x86_64.whl"}, + {"hash": "sha256:deadb002", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-cp315-cp315t-manylinux_2_17_x86_64.whl"}, + {"hash": "sha256:deadb003", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-cp315-cp315-manylinux_2_17_aarch64.whl"}, + {"hash": "sha256:deadb004", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-cp315-cp315-macosx_11_0_arm64.whl"}, + {"hash": "sha256:deadb005", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-cp315-cp315-win_arm64.whl"}, + {"hash": "sha256:deadb006", "url": "https://files.pythonhosted.org/packages/plat-0.0.1-py3-none-any.whl"}, + ], + }, + ]}), + }, + ), + _parse( + hub_name = "pypi", + python_version = "3.15", + requirements_lock = None, + target_platforms = ["*"], + uv_lock = "uv.lock", + ), + ) + pypi = builder.build() + + pypi.exposed_packages().contains_exactly(["plat"]) + + # Each of the platforms selects its own platform specific wheel. + pypi.whl_map().contains_exactly({ + "plat": { + "pypi_315_plat_cp315_cp315_macosx_11_0_arm64_deadb004": [ + whl_config_setting( + target_platforms = ("cp315_osx_aarch64",), + version = "3.15", + ), + ], + "pypi_315_plat_cp315_cp315_manylinux_2_17_aarch64_deadb003": [ + whl_config_setting( + target_platforms = ("cp315_linux_aarch64",), + version = "3.15", + ), + ], + "pypi_315_plat_cp315_cp315_manylinux_2_17_x86_64_deadb001": [ + whl_config_setting( + target_platforms = ("cp315_linux_x86_64",), + version = "3.15", + ), + ], + "pypi_315_plat_cp315_cp315_win_arm64_deadb005": [ + whl_config_setting( + target_platforms = ("cp315_windows_aarch64",), + version = "3.15", + ), + ], + "pypi_315_plat_cp315_cp315t_manylinux_2_17_x86_64_deadb002": [ + whl_config_setting( + target_platforms = ("cp315_linux_x86_64_freethreaded",), + version = "3.15", + ), + ], + }, + }) + +_tests.append(_test_target_platforms_all_platform_wheels) + def _test_simple_multiple_requirements(env): sub_tests = { ("osx", "aarch64"): "simple==0.0.2 --hash=sha256:deadb00f", From 02d2e127663d9ecb63f9cfef49e00172e3926647 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:31:39 +0900 Subject: [PATCH 25/39] feat(toolchain): generate the coverage wheels for WORKSPACE from the uv.lock The WORKSPACE macros cannot read the coverage `uv.lock` file, so add a dev-only repository rule, which uses the same wheel selection as the `pip.parse` extension to write the wheels for each Python version and platform to a checked-in `workspace_deps.bzl` file. The file is generated with bzlmod only and a `diff_test` ensures that it is in sync with the `uv.lock` file. Run `bazel run //dev/update_deps:update_coverage_workspace_deps` to update it. This replaces the `update_coverage_deps` script, which maintained the wheel list by hand. --- MODULE.bazel | 1 + dev/update_deps/BUILD.bazel | 17 +- dev/update_deps/update_coverage_deps.py | 158 ---- .../update_coverage_workspace_deps.py | 26 + docs/devguide.md | 8 +- python/private/coverage/BUILD.bazel | 30 +- python/private/coverage/workspace_deps.bzl | 757 ++++++++++++++++++ .../private/coverage/workspace_deps_gen.bzl | 119 +++ python/private/internal_dev_deps.bzl | 7 + tests/coverage/BUILD.bazel | 11 + 10 files changed, 961 insertions(+), 173 deletions(-) delete mode 100755 dev/update_deps/update_coverage_deps.py create mode 100644 dev/update_deps/update_coverage_workspace_deps.py create mode 100755 python/private/coverage/workspace_deps.bzl create mode 100644 python/private/coverage/workspace_deps_gen.bzl diff --git a/MODULE.bazel b/MODULE.bazel index 8caedea391..ffb71b4727 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -389,6 +389,7 @@ use_repo( "patch_whl_pkg", "pkgutil_nspkg1", "pkgutil_nspkg2", + "rules_python_coverage_workspace_deps", "rules_python_runtime_env_tc_info", "somepkg_with_build_files", "whl_library_extras_direct_dep", diff --git a/dev/update_deps/BUILD.bazel b/dev/update_deps/BUILD.bazel index 9f6999650a..be3669690d 100644 --- a/dev/update_deps/BUILD.bazel +++ b/dev/update_deps/BUILD.bazel @@ -32,19 +32,16 @@ py_library( ) py_binary( - name = "update_coverage_deps", - srcs = ["update_coverage_deps.py"], - data = [ - "//python/private:coverage_deps_filegroup", - ], + name = "update_coverage_workspace_deps", + srcs = ["update_coverage_workspace_deps.py"], + data = ["@rules_python_coverage_workspace_deps//:workspace_deps.bzl"], env = { - "UPDATE_FILE": "$(rlocationpath //python/private:coverage_deps_filegroup)", + "GENERATED": "$(rlocationpath @rules_python_coverage_workspace_deps//:workspace_deps.bzl)", }, imports = ["../.."], - deps = [ - ":args", - ":update_file", - ], + # The file is generated with bzlmod. + target_compatible_with = SUPPORTS_BZLMOD, + deps = ["//python/runfiles"], ) py_binary( diff --git a/dev/update_deps/update_coverage_deps.py b/dev/update_deps/update_coverage_deps.py deleted file mode 100755 index 655a1ae6d5..0000000000 --- a/dev/update_deps/update_coverage_deps.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/python3 -B -# Copyright 2023 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. - -"""A small script to update bazel files within the repo. - -We are not running this with 'bazel run' to keep the dependencies minimal -""" - -# NOTE @aignas 2023-01-09: We should only depend on core Python 3 packages. -import argparse -import json -import os -import textwrap -from dataclasses import dataclass -from typing import Any -from urllib import request - -from dev.update_deps.args import path_from_runfiles -from dev.update_deps.update_file import update_file - - -@dataclass -class Whl: - url: str - sha256: str - marker: str - - def __repr__(self): - return "\n".join( - [ - "struct(", - f' url = "{self.url}",', - f' hash = "sha256:{self.sha256}",', - f" marker = {json.dumps(self.marker)},", - "),", - ] - ) - - -@dataclass -class Whls: - whls: list[Whl] - - def __repr__(self): - inner = textwrap.indent( - "\n".join([repr(w) for w in self.whls]), - prefix=" ", - ) - return "[\n{}\n]".format(inner) - - -def _whl(url: str, digests: dict[str, str], marker: str, **kwargs: Any) -> Whl: - return Whl( - url=url, - sha256=digests["sha256"], - marker=marker, - ) - - -@dataclass -class VersionSpec: - """A package version and the PEP 508 marker for when to use it.""" - - version: str - marker: str - - -def _parse_version_spec(value: str) -> VersionSpec: - version, _, marker = value.partition(";") - version = version.strip() - if not version: - raise argparse.ArgumentTypeError( - f"Expected '[; ]', got '{value}'" - ) - return VersionSpec(version=version, marker=marker.strip()) - - -def _parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(__doc__) - parser.add_argument( - "--name", - default="coverage", - type=str, - help="The name of the package", - ) - parser.add_argument( - "versions", - nargs="+", - type=_parse_version_spec, - help=( - "The versions of the package to download, each optionally with a " - "PEP 508 environment marker for when the version should be used, " - "e.g. \"7.10.7; python_full_version < '3.15'\". The markers should not " - "overlap." - ), - ) - parser.add_argument( - "--dry-run", - action="store_true", - help="Whether to write to files", - ) - parser.add_argument( - "--update-file", - type=path_from_runfiles, - default=os.environ.get("UPDATE_FILE"), - help="The path for the file to be updated, defaults to the value taken from UPDATE_FILE", - ) - return parser.parse_args() - - -def _get_urls(name: str, version: str) -> list[dict[str, Any]]: - api_url = f"https://pypi.org/pypi/{name}/{version}/json" - req = request.Request(api_url) - with request.urlopen(req) as response: - data = json.loads(response.read().decode("utf-8")) - return data["urls"] - - -def main(): - args = _parse_args() - - whls = [] - for spec in args.versions: - for u in _get_urls(args.name, spec.version): - if u["yanked"]: - continue - - if not u["filename"].endswith(".whl"): - continue - - whls.append(_whl(marker=spec.marker, **u)) - - whls.sort(key=lambda x: x.url.rpartition("/")[2]) - - # Update the coverage wheels, the compatible wheel is selected in Starlark - update_file( - path=args.update_file, - snippet="_coverage_wheels = {}\n".format(repr(Whls(whls))), - start_marker="# START: maintained by 'bazel run //dev/update_deps:update_coverage_deps '", - end_marker="# END: maintained by 'bazel run //dev/update_deps:update_coverage_deps '", - dry_run=args.dry_run, - ) - - -if __name__ == "__main__": - main() diff --git a/dev/update_deps/update_coverage_workspace_deps.py b/dev/update_deps/update_coverage_workspace_deps.py new file mode 100644 index 0000000000..6b1e7aaa51 --- /dev/null +++ b/dev/update_deps/update_coverage_workspace_deps.py @@ -0,0 +1,26 @@ +"""Update the generated `workspace_deps.bzl` file for the coverage tool. + +Usage: + bazel run //dev/update_deps:update_coverage_workspace_deps +""" + +import os +import pathlib +import shutil + +from python.runfiles import runfiles + + +def main() -> None: + r = runfiles.Create() + src = pathlib.Path(r.Rlocation(os.environ["GENERATED"])) + dst = ( + pathlib.Path(os.environ["BUILD_WORKSPACE_DIRECTORY"]) + / "python/private/coverage/workspace_deps.bzl" + ) + shutil.copyfile(src, dst) + print(f"Updated {dst}") + + +if __name__ == "__main__": + main() diff --git a/docs/devguide.md b/docs/devguide.md index 22839bcb42..0155937b3a 100644 --- a/docs/devguide.md +++ b/docs/devguide.md @@ -101,11 +101,11 @@ integration test. ``` bazel run //dev:all_requirements.update ``` -1. Bump the coverage dependencies using the script using: +1. Bump the coverage dependencies and update the file that the WORKSPACE + toolchains use: ``` - bazel run //dev/update_deps:update_coverage_deps - # for example: - # bazel run //dev/update_deps:update_coverage_deps 7.10.7 + bazel run //python/private/coverage:requirements.update + bazel run //dev/update_deps:update_coverage_workspace_deps ``` ## Updating tool dependencies diff --git a/python/private/coverage/BUILD.bazel b/python/private/coverage/BUILD.bazel index f0d6dc59f5..6bf9a97573 100644 --- a/python/private/coverage/BUILD.bazel +++ b/python/private/coverage/BUILD.bazel @@ -1,14 +1,37 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("//python/private:distribution_filegroup.bzl", "distribution_filegroup") load("//python/uv:lock.bzl", "lock") package(default_visibility = ["//:__subpackages__"]) -exports_files(["uv.lock"]) +exports_files([ + "uv.lock", + "workspace_deps.bzl", +]) distribution_filegroup( name = "distribution", ) +bzl_library( + name = "workspace_deps_gen", + srcs = ["workspace_deps_gen.bzl"], + deps = [ + "//python:versions", + "//python/private:repo_utils", + "//python/private:text_util", + "//python/private:version", + "//python/private/pypi:default_platforms", + "//python/private/pypi:hash", + "//python/private/pypi:parse_requirements", + "//python/private/pypi:pep508_env", + "//python/private/pypi:pep508_evaluate", + "//python/private/pypi:platform", + "//python/private/pypi:whl_repo_name", + "@toml.bzl//:toml", + ], +) + # Run `bazel run //python/private/coverage:requirements.update` to update the # `uv.lock` file. lock( @@ -19,3 +42,8 @@ lock( "--upgrade", # always upgrade ], ) + +bzl_library( + name = "workspace_deps", + srcs = ["workspace_deps.bzl"], +) diff --git a/python/private/coverage/workspace_deps.bzl b/python/private/coverage/workspace_deps.bzl new file mode 100755 index 0000000000..0759b7c2a2 --- /dev/null +++ b/python/private/coverage/workspace_deps.bzl @@ -0,0 +1,757 @@ +"""The coverage wheels for the WORKSPACE toolchains. + +@generated by `bazel run //dev/update_deps:update_coverage_workspace_deps` from +the `uv.lock` file in this directory. DO NOT EDIT. +""" + +COVERAGE_WHEELS = { + "coverage_cp310_cp310_macosx_10_9_x86_64_23219888": { + "filename": "coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl", + "integrity": "sha256-IyGYiEd+3XNrb8rsEnLUfZO5JumZZB/+p+U6FzjnCys=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/60/72/db17bf5f87568ab524413693385be2eeb03e652ab56c54ea05fa85515675/coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl", + }, + "coverage_cp310_cp310_macosx_11_0_arm64_40c0f008": { + "filename": "coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl", + "integrity": "sha256-QMDwCJn+YYGuf0NM6yAOUfXuS47RDjtfC2BfDK4V2oc=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/d1/e1/285727a8a74d48de256e8605413ee4cf82301228253ea58e5ab4270138b0/coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl", + }, + "coverage_cp310_cp310_manylinux_2_17_aarch64_9fd670ac": { + "filename": "coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-n9ZwrEO3CcV1rvwlv1LYpZijvFAXvd/QoXkVKrBqLes=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/0e/19/14a8e44cbb2ad36ae62aaa03c5eec4a06a7f7df220ea3776af26927acbe2/coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp310_cp310_manylinux_2_17_ppc64le_705e5af1": { + "filename": "coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-cF5a8R00ZH79wXDHhAtoV8gc90vpZBmlU/I35o5iy3I=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/f9/72/f5bcad0d9a9b450080032344fbff7ec60c1b0e3d38019f7735dee2b69645/coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp310_cp310_manylinux_2_31_riscv64_8afd9bf3": { + "filename": "coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-iv2b81zGofIus2NICPqOC5GQJFnFch7y5EYd/ncdfwg=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/ae/9a/8c735234e8abb52bf5d063f98c780fa942e77c4c0225f9fcb9b33effc346/coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp310_cp310_manylinux_2_5_i686_a4624f80": { + "filename": "coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-pGJPgHMva0J6xY8fWcV3oJlKEugXS1r2oCe0tYeV1MM=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/50/c7/c737b73bac9bf5034f5ff45a4237b7a189d417a4e36751faf5d10c082cbe/coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp310_cp310_manylinux_2_5_x86_64_191803c4": { + "filename": "coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-GRgDxJlrSZ/NeMKtXl92fcxTy03G3m1qdBtEOhgh7wI=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/29/4e/e1d38d27817d91776ca543d01276a67eace3df675a673cac32b12c167d58/coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp310_cp310_musllinux_1_2_x86_64_c85d54e7": { + "filename": "coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", + "integrity": "sha256-yF1U5+iiypMv6DmTAa+bjVkH6ipFX/r/bn0SCNuDuUM=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/57/d3/84cd6a11e707e422194739e9734d947746efa5e5b3358ff2d601f823ee45/coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", + }, + "coverage_cp310_cp310_win32_5139009b": { + "filename": "coverage-7.16.2-cp310-cp310-win32.whl", + "integrity": "sha256-UTkAm179IZT8Fo7pNi8OGRumEu9dKSQvkmnCL5uPgMc=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/ab/bc/3d84c2e2a95f38e346ce3730d9f0e53114f4e8aace72e6e51e014afb9992/coverage-7.16.2-cp310-cp310-win32.whl", + }, + "coverage_cp310_cp310_win_amd64_c3305c38": { + "filename": "coverage-7.16.2-cp310-cp310-win_amd64.whl", + "integrity": "sha256-wzBcOKL6IaQlTyrOfdnvX8VpyaVYtm5wF2ULPWN/uV4=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/07/86/31f1f3170571a345ab9d8361a7b2f8e0c5698173fbecd1f7149b6e2089ee/coverage-7.16.2-cp310-cp310-win_amd64.whl", + }, + "coverage_cp311_cp311_macosx_10_9_x86_64_a3f49ac9": { + "filename": "coverage-7.17.2-cp311-cp311-macosx_10_9_x86_64.whl", + "integrity": "sha256-o/Saycnl/UtFBnfrRPSxTqmrcZULKJaIjJ9qItXNF3M=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/5e/9d/e3db9071ebeda48e47e5be0fd0404219954e7888f64e7c37df324a79dc3a/coverage-7.17.2-cp311-cp311-macosx_10_9_x86_64.whl", + }, + "coverage_cp311_cp311_macosx_11_0_arm64_3d3d9d79": { + "filename": "coverage-7.17.2-cp311-cp311-macosx_11_0_arm64.whl", + "integrity": "sha256-PT2deaK2zc3eWhqxromVP66lyyjA7qF6QTrZl7s8yKE=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/49/a1/260786d936940102052a51cb166f80f4a98e67fb6142aa050f1af48a2385/coverage-7.17.2-cp311-cp311-macosx_11_0_arm64.whl", + }, + "coverage_cp311_cp311_manylinux_2_17_aarch64_1947bd9d": { + "filename": "coverage-7.17.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-GUe9nYLkBciSxQsNjcDB3qXfKpYhKcojjFAKa8+IdQk=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/ae/99/efefaa02a4b2876ffa4a389c81a8074c888442b12c64046b448718fa5590/coverage-7.17.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp311_cp311_manylinux_2_17_ppc64le_2cd1a747": { + "filename": "coverage-7.17.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-LNGnRyqXL+0ByJWJsVYIRuMulC+DKaUVxAWdRSHGV5o=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/19/2f/a565096e5dcf8c511332d397d712b056f06091ab3efc10adb5a4d22b0cc2/coverage-7.17.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp311_cp311_manylinux_2_31_riscv64_2b7fe217": { + "filename": "coverage-7.17.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-K3/iF4CCOUN7hQm4af3i4MrEh3amjGJr1rFUEbCH494=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/ab/09/87f9b02f97179f2c76fa5724e0ce00b9db2bbe16b7e21d89c8fc1f3a77d9/coverage-7.17.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp311_cp311_manylinux_2_5_i686_5b71aeaa": { + "filename": "coverage-7.17.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-W3GuqkRAy+zlSYrNowg4aFeT07uWCv+H7Jf7tUZxWl8=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/be/80/f5176b7f6aa9e05e37e1bb5eae473c5d015485c36206934f53b2b2795076/coverage-7.17.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp311_cp311_manylinux_2_5_x86_64_01303d9e": { + "filename": "coverage-7.17.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-ATA9nmi25r3FRZtU470ONTnJTCW1Csc2K3y8rLyBX7w=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/03/9f/4669ca11ce1932cc18fce766ead327ceb41a92055608c179b137067e2805/coverage-7.17.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp311_cp311_musllinux_1_2_x86_64_7c123b6b": { + "filename": "coverage-7.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", + "integrity": "sha256-fBI7a7wYOhQ3IDZ2nwMwmAJqp1/I+6N9AosLdyNW6Qs=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/a7/18/098ea8ee450922eec7ab48a35b4a7fd6a49c4a97416d1e4cf66092c24dc4/coverage-7.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", + }, + "coverage_cp311_cp311_win32_1c3146ef": { + "filename": "coverage-7.17.2-cp311-cp311-win32.whl", + "integrity": "sha256-HDFG750UwBFRdCJIl4SnrDREd+nNrLBdlSnpEnIc1dg=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/8e/1c/a6ee041c4bc8b6ec7bc9f417daf89e3ee702182a48d573267ccaaed17c64/coverage-7.17.2-cp311-cp311-win32.whl", + }, + "coverage_cp311_cp311_win_amd64_51c6a60e": { + "filename": "coverage-7.17.2-cp311-cp311-win_amd64.whl", + "integrity": "sha256-UcamDmPqmTET6lzSSkAcwvrdxUqnrP3a/6XTuMAWG50=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/5a/df/7f077fb2e71b1d73fe8285be7c6ba43dc085f9eaa2384d4e8e55560073a3/coverage-7.17.2-cp311-cp311-win_amd64.whl", + }, + "coverage_cp311_cp311_win_arm64_b08ee3ef": { + "filename": "coverage-7.17.2-cp311-cp311-win_arm64.whl", + "integrity": "sha256-sI7j71PoDlpQxos9l2Q2Pjc6y6VA4bWNruCpp/I/37I=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/90/56/89dcd5907d0cc04b07746e62e1cb896c0f694e5af40aa0c980458825c6c5/coverage-7.17.2-cp311-cp311-win_arm64.whl", + }, + "coverage_cp312_cp312_macosx_10_13_x86_64_289a2444": { + "filename": "coverage-7.17.2-cp312-cp312-macosx_10_13_x86_64.whl", + "integrity": "sha256-KJokRDNScTdF95wbaq5Omc7S9f8+hrtjDDPGMdUPIXw=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/22/6a/a51808ac625c457326fb37b174d35e1badb1324c6def5d9668089c4b14ef/coverage-7.17.2-cp312-cp312-macosx_10_13_x86_64.whl", + }, + "coverage_cp312_cp312_macosx_11_0_arm64_da4b0aef": { + "filename": "coverage-7.17.2-cp312-cp312-macosx_11_0_arm64.whl", + "integrity": "sha256-2ksK70J+uW5CK0ROXCw9OHaP7bkUE0YpTDhxeosVtBY=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/31/24/f32ec1a5ef947294252e77c5d40ac0903ba1de4c92638edfcca8629c067f/coverage-7.17.2-cp312-cp312-macosx_11_0_arm64.whl", + }, + "coverage_cp312_cp312_manylinux_2_17_aarch64_1682d98d": { + "filename": "coverage-7.17.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-FoLZje0oUkvGhTzE58ZRdbPpF/OLLi3euyjK/Yq03nQ=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/d8/72/17b5df5d8211e1880169b93197ba98b21b30ccf8f116284536113b7582c5/coverage-7.17.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp312_cp312_manylinux_2_17_ppc64le_8eab0a5c": { + "filename": "coverage-7.17.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-jqsKXP+NvFY9FBam4Hm6PBRnEuRxZ8Aipd9eoBvXbfk=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/de/0c/603ba7fff1aa4c8064fdf4a1fff6d9fb51c72a5828429dfe858be48e38b3/coverage-7.17.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp312_cp312_manylinux_2_31_riscv64_635337a9": { + "filename": "coverage-7.17.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-Y1M3qWiV8v/P4qp1VEaKmI9Gj7Z6gAfBpl6VFIwYVak=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/f4/ca/f2ae6e47e18b35a0dbddb9d8f51be34a6d66b41120f2e0f7363ccc0812f4/coverage-7.17.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp312_cp312_manylinux_2_5_i686_90c6c5f9": { + "filename": "coverage-7.17.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-kMbF+TYGuIA71/igsq4F33412kUEocU8/pKUoXMeH0k=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/01/8e/3097367327c69e65521b9d9bf8b9d2ddecefa8c21bd08e17bc068bd3db93/coverage-7.17.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp312_cp312_manylinux_2_5_x86_64_36314997": { + "filename": "coverage-7.17.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-NjFJl1MyZr3j0HdlKJZ0ME1hv+SRZbsbT6m+EstLoNE=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/73/65/774fc4ec86807b6ee66b46465bc49b34bb6d291e9bff6ddfa9708ccb4053/coverage-7.17.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp312_cp312_musllinux_1_2_x86_64_686b6f8f": { + "filename": "coverage-7.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", + "integrity": "sha256-aGtvj55Xr8OkQJaxfujefhHgoS8tqBniUEevHrOw/G8=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/1a/85/6c923d71c76566c6c65a2af104d39d69d44ae708a700de017e6c56fedfc6/coverage-7.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", + }, + "coverage_cp312_cp312_win32_ee083412": { + "filename": "coverage-7.17.2-cp312-cp312-win32.whl", + "integrity": "sha256-7gg0EhcBGAdLzHQ2H8vHEE620LfJx16zuEpMMkQQxTE=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/54/f9/fd3c37b09891bfdfece486a6145345bf2b32332045a678afd293a7f2f05d/coverage-7.17.2-cp312-cp312-win32.whl", + }, + "coverage_cp312_cp312_win_amd64_e269dddf": { + "filename": "coverage-7.17.2-cp312-cp312-win_amd64.whl", + "integrity": "sha256-4mnd3166wg38xaKkWVKKnSIauCjLMd/fGcfPrNvO5nM=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/dc/b8/4d395163c9bfae0e991abbd14dea9c9bbb4ade904e5f297cbfb7b8150216/coverage-7.17.2-cp312-cp312-win_amd64.whl", + }, + "coverage_cp312_cp312_win_arm64_bac3c79c": { + "filename": "coverage-7.17.2-cp312-cp312-win_arm64.whl", + "integrity": "sha256-usPHnDH0UnQRftZMZe/q7YVmVmiuun2AnHw4gBrabtQ=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/a3/02/1024b8d4f60a9ecc0ee41a6000425caa63855d63e51da84c93c0b1dff894/coverage-7.17.2-cp312-cp312-win_arm64.whl", + }, + "coverage_cp313_cp313_macosx_10_13_x86_64_e6c89d63": { + "filename": "coverage-7.17.2-cp313-cp313-macosx_10_13_x86_64.whl", + "integrity": "sha256-5sidY9Q3OHEs4f6923dY/+tW3gjxsr5orrmpx/7nEsc=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/9c/fa/967e829238dda21c4bce17aef2f739187678c8e6e6fc880a8454945a7433/coverage-7.17.2-cp313-cp313-macosx_10_13_x86_64.whl", + }, + "coverage_cp313_cp313_macosx_11_0_arm64_f94d465e": { + "filename": "coverage-7.17.2-cp313-cp313-macosx_11_0_arm64.whl", + "integrity": "sha256-+U1GXuAOvmlMqGbWa75F+gRhixr8s3fek0aU9iT6fPY=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/9d/df/803ba08e8937759dc3f18b93ece82f81b2fb446e3846e4d60a06a4e77647/coverage-7.17.2-cp313-cp313-macosx_11_0_arm64.whl", + }, + "coverage_cp313_cp313_manylinux_2_17_aarch64_95bd8dc5": { + "filename": "coverage-7.17.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-lb2NxbNW948QqWfsUPiQLVbyTGI3U6yfzgq6nWIT1cU=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/62/c3/429b500937ac446e36fcce517d040b0b8bd04a4bfa087bdf864175553fea/coverage-7.17.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp313_cp313_manylinux_2_17_ppc64le_a5565a9d": { + "filename": "coverage-7.17.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-pVZandcVpalP/PmgrUtrFQG1RB/WfWYOeHQq3/XTego=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/c1/91/263224a77de3ab1ee42d110b09f1ac9a02638794d3e002a859822638256a/coverage-7.17.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp313_cp313_manylinux_2_31_riscv64_091eba42": { + "filename": "coverage-7.17.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-CR66QkjTGwOCj8ZXyW0UrfqodU9a7HtzKYuCiUhY2wY=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/0f/44/99b72f2961f4ba54ac14ccb016f3b212cc9a9dfda4c37e07a208efe01f9f/coverage-7.17.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp313_cp313_manylinux_2_5_i686_7ce0acb2": { + "filename": "coverage-7.17.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-fOCssnqCPo5RaSgvU48OGChUzItHK4jUVFbwm2/jZP0=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/4a/12/92dcd9206e7bb817af42b8663cfeefdbb2c5a6c92cc7069d43e6892bbdeb/coverage-7.17.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp313_cp313_manylinux_2_5_x86_64_70daa8e4": { + "filename": "coverage-7.17.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-cNqo5LSVQbUcC666DYArDRTTIU2u68Bxs9sLDH3IRkY=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/48/2d/6b028df8050caef4ce0df177686c92dba16ab7cf004040e5518f95b57021/coverage-7.17.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp313_cp313_musllinux_1_2_x86_64_e09c3781": { + "filename": "coverage-7.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", + "integrity": "sha256-4Jw3gXdou3H+PnC2zl5qwaDNc5hTbC8wpEYCGMe2bpM=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/72/9f/0d67883bf55adf50424728937cbee54d76fa137817e6bb20f97061d01816/coverage-7.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", + }, + "coverage_cp313_cp313_win32_c10f2a27": { + "filename": "coverage-7.17.2-cp313-cp313-win32.whl", + "integrity": "sha256-wQ8qJ0jdCE5EgRu5sml5djr5ADEV2b+TwdppARlEcZ8=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/a8/22/71965edc14736073b55a40e521791d9233d5c3194fa6887a94f4431fbbcc/coverage-7.17.2-cp313-cp313-win32.whl", + }, + "coverage_cp313_cp313_win_amd64_8dc07ed3": { + "filename": "coverage-7.17.2-cp313-cp313-win_amd64.whl", + "integrity": "sha256-jcB+00vLIG9p1bVP04dPe/OayUHJ967CtTsph0/P13o=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/47/0d/715cb37f51f908a6f6f33fe8c6fc95ab638b17cb014b1650daae0893c160/coverage-7.17.2-cp313-cp313-win_amd64.whl", + }, + "coverage_cp313_cp313_win_arm64_bb742836": { + "filename": "coverage-7.17.2-cp313-cp313-win_arm64.whl", + "integrity": "sha256-u3QoNmGiOf2vc4rAsH207ZtHbHUy0+cTq/D2oHJ4HmA=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/39/f2/a5099bdc2a530f29c818a1c7e706669451c10e5f0eb62dcff76ef3568668/coverage-7.17.2-cp313-cp313-win_arm64.whl", + }, + "coverage_cp314_cp314_macosx_10_15_x86_64_36248fb6": { + "filename": "coverage-7.17.2-cp314-cp314-macosx_10_15_x86_64.whl", + "integrity": "sha256-NiSPtsAzQVARaVObyrNes6WRWRAL4SRvrTwUy3PBdoU=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/10/7e/60a51850d3ff659b82cc211bc98fa126aab2ac9b81bd4ed6abc3e3720a09/coverage-7.17.2-cp314-cp314-macosx_10_15_x86_64.whl", + }, + "coverage_cp314_cp314_macosx_11_0_arm64_2a628101": { + "filename": "coverage-7.17.2-cp314-cp314-macosx_11_0_arm64.whl", + "integrity": "sha256-KmKBARz4Vpl2O/iKtV4HdPZpb4ezkPORUrFOzvrcy1k=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/77/80/a9539c15076fb9b95d8cbdb7f0caec90857d80bddca3eeca439f5e2e64e2/coverage-7.17.2-cp314-cp314-macosx_11_0_arm64.whl", + }, + "coverage_cp314_cp314_manylinux_2_17_aarch64_e6bf531d": { + "filename": "coverage-7.17.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-5r9THerr+PabpgFbuj+4F8q2aU37aawqcIiJ3Fmjkm4=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/05/54/a666b65c5bb145d8796b7902ff1c0f60600a6c2a08949e20f2fb7af15de3/coverage-7.17.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp314_cp314_manylinux_2_17_ppc64le_88d4dc83": { + "filename": "coverage-7.17.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-iNTcg7atCaITTK2djseAsnSxesffuowsbpLjvEaP6F0=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/a6/73/ac9d06da9e0c143b874a68b17d8dfeedc45acdf1bdcee5b22a42bece098e/coverage-7.17.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp314_cp314_manylinux_2_31_riscv64_8dc17707": { + "filename": "coverage-7.17.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-jcF3B/1GO036tTeS5B1QojMWrGuYU5AHqiOIodRKhTU=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/87/b1/8a500e16cf787383b00b5a86eb50a490b4d8ebde505f92e1aaca515afc0a/coverage-7.17.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp314_cp314_manylinux_2_5_i686_3dfefb1e": { + "filename": "coverage-7.17.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-Pf77HsFsoaAeXaQLoWLiaM7YvTYkueuk6ThOv6FK9+w=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/7f/3b/0a84b6032511834105fd4f8a8b4e76927783cea03dc4796032d5c4bfec8b/coverage-7.17.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp314_cp314_manylinux_2_5_x86_64_34e5c7a2": { + "filename": "coverage-7.17.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-NOXHoto2gExubxCzaOrgGtVvWkrF7M5ylzAKPUb97yo=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/2e/e4/a3e6b8bcc219b00ea91e36f071c2659b253e5d6e1b258c31eb4524231718/coverage-7.17.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp314_cp314_musllinux_1_2_x86_64_460ba213": { + "filename": "coverage-7.17.2-cp314-cp314-musllinux_1_2_x86_64.whl", + "integrity": "sha256-RguiE5hm51jDqOnZQi/ciJlCvLxcyvcQ3E+9hwfRSqg=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/14/b5/13a62eac66cecbaaa098bb753a3693b44454a4d21d53db4f1a0953f35f0b/coverage-7.17.2-cp314-cp314-musllinux_1_2_x86_64.whl", + }, + "coverage_cp314_cp314_win32_e72cf955": { + "filename": "coverage-7.17.2-cp314-cp314-win32.whl", + "integrity": "sha256-5yz5VW95nD/OdZfBvX2/yRBL770ICI3rO5rp/ZgRfAI=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/53/13/9ea31cfa72434449b9a98666760169d1e0ab31b3f6cdb12e68149106668e/coverage-7.17.2-cp314-cp314-win32.whl", + }, + "coverage_cp314_cp314_win_amd64_205f9e32": { + "filename": "coverage-7.17.2-cp314-cp314-win_amd64.whl", + "integrity": "sha256-IF+eMp2dXkfDIPhxIyagFiBontrlBTHMiTRRHixej34=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/f6/cc/aa8f34b014979c973740f5a5ed6861ecfc6009082c70e72030ca1519e0f8/coverage-7.17.2-cp314-cp314-win_amd64.whl", + }, + "coverage_cp314_cp314_win_arm64_a5909dd0": { + "filename": "coverage-7.17.2-cp314-cp314-win_arm64.whl", + "integrity": "sha256-pZCd0IcAbHDBKClzErpn2HcSPocRAGRdm9c64CGk0B0=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/49/64/16afaa19b3886dc157c3c5ebace5241b84b96a005cb28545584e66b80895/coverage-7.17.2-cp314-cp314-win_arm64.whl", + }, + "coverage_cp314_cp314t_macosx_10_15_x86_64_674bcd02": { + "filename": "coverage-7.17.2-cp314-cp314t-macosx_10_15_x86_64.whl", + "integrity": "sha256-Z0vNAjo3Q2ZtTXoVI45XoAyWhVN4CjbIDTd5/EELkSw=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/e8/95/acc865e1411c8ebc5c5d230367f3bfed5c605ed54bb41b84fc58f4e7f137/coverage-7.17.2-cp314-cp314t-macosx_10_15_x86_64.whl", + }, + "coverage_cp314_cp314t_macosx_11_0_arm64_ba45dc07": { + "filename": "coverage-7.17.2-cp314-cp314t-macosx_11_0_arm64.whl", + "integrity": "sha256-ukXcByv78XmOaOyBN5tzRz/JEhdL/5+Rkk13cRodKzA=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/10/98/02c37c6a928ccfc84b7a43eda44d96d3b1ccbc93989c178754b7e67e7650/coverage-7.17.2-cp314-cp314t-macosx_11_0_arm64.whl", + }, + "coverage_cp314_cp314t_manylinux_2_17_aarch64_d0332e2b": { + "filename": "coverage-7.17.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-0DMuKyf8XMdh1rijMRi5WOaka+fwD9MOA6XPEDNQwLM=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/63/e7/bd6dcec5b8130f5bd8679087a2bc3a50a53c4ee78d5ec449610020088ff3/coverage-7.17.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp314_cp314t_manylinux_2_17_ppc64le_27fc4384": { + "filename": "coverage-7.17.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-J/xDhKEVT33zDTBMzdwF70VzaMBlZw7L05JgYnmzMpo=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/55/c1/b3f06aa1f1c51f4295fdfa6500eb7409aaa6273cf5f27bafc25217f3547d/coverage-7.17.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp314_cp314t_manylinux_2_31_riscv64_603140ee": { + "filename": "coverage-7.17.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-YDFA7q4/mtxWL5GxuY1NH8SRvR9mxQgkgsVo0LeJG7E=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/a7/f0/b68ab3654af3541488b26856e3043c13c782d8e6104fc8149746ecaa83f0/coverage-7.17.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp314_cp314t_manylinux_2_5_i686_0d8a5dc7": { + "filename": "coverage-7.17.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-DYpdxwnam5CtXMhZqbiMm4AeZcovv3y9vtW5y/RX/Mk=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/28/ab/d503b0aaebb92b171260469463915af5e16a64457b289d4cb844e8c366ac/coverage-7.17.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp314_cp314t_manylinux_2_5_x86_64_248873a1": { + "filename": "coverage-7.17.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-JIhzoUTAZ/mBdC/0u/tItX4oGoy3o4IdTik721C0eXA=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/f6/e5/6d6ba15bde22fcccd07e14305be14291d041f732771a73f787297445d3e7/coverage-7.17.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp314_cp314t_musllinux_1_2_x86_64_d88fcc24": { + "filename": "coverage-7.17.2-cp314-cp314t-musllinux_1_2_x86_64.whl", + "integrity": "sha256-2I/MJEcNc+1DYOkQ+FSXGLkTulb7h3XZ75KaZHg13+o=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/bb/f6/43d40d1b4d4892e54c68a241cd5b92efed4d5934109e0e5e24a94759f732/coverage-7.17.2-cp314-cp314t-musllinux_1_2_x86_64.whl", + }, + "coverage_cp314_cp314t_win32_2c42c222": { + "filename": "coverage-7.17.2-cp314-cp314t-win32.whl", + "integrity": "sha256-LELCIpObhpnnvA5FMUlifyEeu7svdMGgQzGwzZg04sg=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/8e/33/6874185b791c71684c401fc74abd71bbdb102e8ee41c8f380cf25af41d29/coverage-7.17.2-cp314-cp314t-win32.whl", + }, + "coverage_cp314_cp314t_win_amd64_b94c509e": { + "filename": "coverage-7.17.2-cp314-cp314t-win_amd64.whl", + "integrity": "sha256-uUxQniZgO6f4yxv4u2OWfHvlM0k75WaNhELNBuyFPXY=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/16/e0/9057b4cf962ca6089b144f0d93a748f5e2d13876dd0f33f9d5a2c2ec49d2/coverage-7.17.2-cp314-cp314t-win_amd64.whl", + }, + "coverage_cp314_cp314t_win_arm64_eca90e02": { + "filename": "coverage-7.17.2-cp314-cp314t-win_arm64.whl", + "integrity": "sha256-7KkOAoky3ll+LFD0VK2MxjEoHhTSC5+AGmSatVXetYA=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/5e/4f/bd626297c942380279dff323e1d8a8937990d553119a4f887358488b1b54/coverage-7.17.2-cp314-cp314t-win_arm64.whl", + }, + "coverage_cp315_cp315_macosx_10_15_x86_64_298849ca": { + "filename": "coverage-7.17.2-cp315-cp315-macosx_10_15_x86_64.whl", + "integrity": "sha256-KYhJyg44V2cJaZXLV/cgWbRp9D+ZJ7g5PynRNPe7EGI=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/f3/08/e110687f24245edbbe9c0986a6ccd04f1f9a6e90e329cbd4347e2b81d76e/coverage-7.17.2-cp315-cp315-macosx_10_15_x86_64.whl", + }, + "coverage_cp315_cp315_macosx_11_0_arm64_81a43279": { + "filename": "coverage-7.17.2-cp315-cp315-macosx_11_0_arm64.whl", + "integrity": "sha256-gaQyeTRA7e8mjbrdSTOTvW9w+kuH92xYEU9/d/HM+uo=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/93/24/7cfcd6adb4a0c18a10de12fd695edbf8cd121fdca0a6a63cea1bffc029fe/coverage-7.17.2-cp315-cp315-macosx_11_0_arm64.whl", + }, + "coverage_cp315_cp315_manylinux_2_17_aarch64_8d3626b9": { + "filename": "coverage-7.17.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-jTYmuYFvfq3QmTw3A6HY7RJLV5zr5SAbRG8cDVUfokU=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/3e/71/31a4945459008952061147ea9b85853902507d865a7272a9f64be0c4a093/coverage-7.17.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp315_cp315_manylinux_2_17_ppc64le_c9639c2e": { + "filename": "coverage-7.17.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-yWOcLqAZyUi1IGeg90SFA564S3ot6Ci6bReOZj+yuL0=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/82/6b/a4eb258b0373034f8f19aeea4c1bc4c89942b5b228812ee9952f033dc611/coverage-7.17.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp315_cp315_manylinux_2_31_riscv64_19d508b0": { + "filename": "coverage-7.17.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-GdUIsGEUhvjiPiiry6d3le6GX/DgzZNjSbppLI0NjKQ=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/02/ae/9410f2f4bbaa698ff3869c7c09e0fa10d74482a1ec8040063157612f07a0/coverage-7.17.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp315_cp315_manylinux_2_5_i686_d14872cc": { + "filename": "coverage-7.17.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-0UhyzO81lyxKwpDyQy9D0Ke7IwyuK17SaHGrhiIei18=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/54/7a/af167a0bb6d0564209d35c8039b290327ecf7b3e34f838f9360b56d3a03c/coverage-7.17.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp315_cp315_manylinux_2_5_x86_64_3bd065e7": { + "filename": "coverage-7.17.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-O9Bl51wHfW3ozT/mhpmotHvj7Fla9rZuNimSaDkJRgk=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/f5/c6/4f3931c3ff80a6f2608209181adb00527970a4b43b64c3aa323383c37d51/coverage-7.17.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp315_cp315_musllinux_1_2_x86_64_99b64a12": { + "filename": "coverage-7.17.2-cp315-cp315-musllinux_1_2_x86_64.whl", + "integrity": "sha256-mbZKEv5gF8JUqAJ3kODqoi6u7CAeff2l6qwXammE7yc=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/1a/ed/f1f3b4bf6081122bf1820b1f38352377a75a2d5f39bb6331598e165b64af/coverage-7.17.2-cp315-cp315-musllinux_1_2_x86_64.whl", + }, + "coverage_cp315_cp315_win32_74d91af5": { + "filename": "coverage-7.17.2-cp315-cp315-win32.whl", + "integrity": "sha256-dNka9ZBRTdoWFl1dfPqUGDFQxqaoCpn2EGg2muOVlFU=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/ab/bb/61e07a606a9f4d0f5391ac9b62b1f008a997f87860ad233f99833fd6e6bd/coverage-7.17.2-cp315-cp315-win32.whl", + }, + "coverage_cp315_cp315_win_amd64_075566a4": { + "filename": "coverage-7.17.2-cp315-cp315-win_amd64.whl", + "integrity": "sha256-B1VmpEKJr7Y0zqr6fY03e7JmeltKkPvuJgnv3NAVxm4=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/86/40/2d4980d3374a023a34c5ea7cd5d2b93c02c5588a324ca7faf8a0a8c7dbac/coverage-7.17.2-cp315-cp315-win_amd64.whl", + }, + "coverage_cp315_cp315_win_arm64_be699e76": { + "filename": "coverage-7.17.2-cp315-cp315-win_arm64.whl", + "integrity": "sha256-vmmedrV/7eiSCg9Wjv8jsnDZAEQUt89nRL3ahRY4vm4=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/f8/c8/b307327b1256f9ed8ffc3271db041913835cfee065fbaf45105978093c08/coverage-7.17.2-cp315-cp315-win_arm64.whl", + }, + "coverage_cp315_cp315t_macosx_10_15_x86_64_ffbaadf8": { + "filename": "coverage-7.17.2-cp315-cp315t-macosx_10_15_x86_64.whl", + "integrity": "sha256-/7qt+GEngTWLP5Sa1o3OJsWjW+qvzEgSX8Ix28zn02I=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/40/18/0912e37f23c101faebd7c5e544c6ba6bd750715f81f2a2c7ea66f83a2295/coverage-7.17.2-cp315-cp315t-macosx_10_15_x86_64.whl", + }, + "coverage_cp315_cp315t_macosx_11_0_arm64_d4004fce": { + "filename": "coverage-7.17.2-cp315-cp315t-macosx_11_0_arm64.whl", + "integrity": "sha256-1ABPzns6iuXvKvg1Zwdj8Scj6bnXSDdpdge4mhf3cxI=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/5e/a0/c4ce9bbf82b2e9dbf2e0326004c8c37d87a275f515fc7bb22344a6162050/coverage-7.17.2-cp315-cp315t-macosx_11_0_arm64.whl", + }, + "coverage_cp315_cp315t_manylinux_2_17_aarch64_a65faa1e": { + "filename": "coverage-7.17.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-pl+qHumOCQpf18EW9tOrXGqF/MRoSbhJSDVghxWDY5k=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/0d/1d/5038de4bf6549903ec3d86959c6f18aff54911616b47aa96f4d6a5660c93/coverage-7.17.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp315_cp315t_manylinux_2_17_ppc64le_7c17f716": { + "filename": "coverage-7.17.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + "integrity": "sha256-fBf3Fsg321UKIC53Ms36pya4UVy6iL5CGTYWkTlw0iM=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/8b/ea/68a26b4b5eb255d92de906fd66e8d720b1df15e705949ea9a9cef2cc7f8b/coverage-7.17.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", + }, + "coverage_cp315_cp315t_manylinux_2_31_riscv64_545ca424": { + "filename": "coverage-7.17.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-VFykJIjH8OYjJPsmIaoFc324Q7dehOZaJxu/l29Jb2Y=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/70/86/0f4238f09b22bc03a907346fabcabe5449626879cdedbc1edd362ab0e570/coverage-7.17.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp315_cp315t_manylinux_2_5_i686_a838a10a": { + "filename": "coverage-7.17.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-qDihCvtbYKbMYxU0ixdrra8kCdghIIo+PNodxggSniM=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/3f/05/9c7429c87589534755398a58fe9837fdb2870a22d99e87623fcb261e1d4e/coverage-7.17.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp315_cp315t_manylinux_2_5_x86_64_0fc17882": { + "filename": "coverage-7.17.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-D8F4gpdf5IXPyV+y8g7tk34F4MDY/6UEiFfNo6qDjoo=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/e9/22/d1ad2ea7c8871d66f8453d33cdeec39986e6e1f2a104628e9bcb171f0209/coverage-7.17.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp315_cp315t_musllinux_1_2_x86_64_2ad189c6": { + "filename": "coverage-7.17.2-cp315-cp315t-musllinux_1_2_x86_64.whl", + "integrity": "sha256-KtGJxlcGMGZEOZ5IUwrRrqHfsxG9IdmI3xrOQi0J8/g=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/4a/11/d15082e504e7852a25848ee0dcb59453579ec923c6d1f3010316e1fb6118/coverage-7.17.2-cp315-cp315t-musllinux_1_2_x86_64.whl", + }, + "coverage_cp315_cp315t_win32_6fd6f218": { + "filename": "coverage-7.17.2-cp315-cp315t-win32.whl", + "integrity": "sha256-b9byGOOaYhow26+VQ4MfoQ/nE6/IpEtS/ELP4ndBJgk=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/f1/9d/6460101b0191afae9ed84c8d8136cc8cc09675f376ce08acb3118b8c27be/coverage-7.17.2-cp315-cp315t-win32.whl", + }, + "coverage_cp315_cp315t_win_amd64_ef07aae0": { + "filename": "coverage-7.17.2-cp315-cp315t-win_amd64.whl", + "integrity": "sha256-7weq4KZm717cq1vBu1kEaMb0EgqM2tUKAQ0EKlmrbnc=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/9a/f2/3462d9616445a8cb36ec2e027d1c0ae82f6f90f224d9851ba0b22b7425d6/coverage-7.17.2-cp315-cp315t-win_amd64.whl", + }, + "coverage_cp315_cp315t_win_arm64_c68f64d9": { + "filename": "coverage-7.17.2-cp315-cp315t-win_arm64.whl", + "integrity": "sha256-xo9k2WhXdJohVlfBcvcUTtAmBjSJaGuQF3TycZl5I8I=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/4c/06/abeca0cf853e77aba9464d4ada4603c8b6e744b667c13e1d4eefecc487af/coverage-7.17.2-cp315-cp315t-win_arm64.whl", + }, + "coverage_cp39_cp39_macosx_10_9_x86_64_fff7b9c3": { + "filename": "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", + "integrity": "sha256-//e5w/GZVwIMrFRscAJTMRE9LmFTf24kQbx2V5E959M=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", + }, + "coverage_cp39_cp39_macosx_11_0_arm64_bc91b314": { + "filename": "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", + "integrity": "sha256-vJGzFM7yd0LaSG1oObZ3s/J5Pf5StRu7t89zbVwpKBw=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", + }, + "coverage_cp39_cp39_manylinux_2_17_aarch64_c7315339": { + "filename": "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + "integrity": "sha256-xzFTOerjskwtL6HtfXo4ZUy6NKE+8Z+8uUJdpG09xZQ=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", + }, + "coverage_cp39_cp39_manylinux_2_31_riscv64_912e6ebc": { + "filename": "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + "integrity": "sha256-kS5uvHpuSt/bsa7Dca0ExohUzTvzYIs1FOf/kGKTHYo=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", + }, + "coverage_cp39_cp39_manylinux_2_5_i686_567f5c15": { + "filename": "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + "integrity": "sha256-Vn9cFV7ajfHT1DnUCkWmpfAptCmwZkgjXx5+UbUis5Y=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", + }, + "coverage_cp39_cp39_manylinux_2_5_x86_64_2af88def": { + "filename": "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + "integrity": "sha256-KviN7/zIpNWXTPLVAiUbw7LbhGHwtm2ApEnDN1eqn0A=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", + }, + "coverage_cp39_cp39_musllinux_1_2_x86_64_635adb9a": { + "filename": "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", + "integrity": "sha256-Y1rbmkUHyf0u1l85aT+jHJo+46jm3GTfAz6P31KnAD8=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", + }, + "coverage_cp39_cp39_win32_5a02d5a8": { + "filename": "coverage-7.10.7-cp39-cp39-win32.whl", + "integrity": "sha256-WgLVqFDil5sKAUxBJXOVOZUXR0Oj9/pOpabpo8VhdDE=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", + }, + "coverage_cp39_cp39_win_amd64_c134869d": { + "filename": "coverage-7.10.7-cp39-cp39-win_amd64.whl", + "integrity": "sha256-wTSGnV/+NFR9FOF0yGb9j+IlSRjMCpXpkFKQO8FUPgc=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", + }, + "coverage_py3_none_any_11d28e91": { + "filename": "coverage-7.16.2-py3-none-any.whl", + "integrity": "sha256-EdKOkSOpFWy0BdjSe0QlbJpY+13swgc6jxeGIFfjqg8=", + "requirement": "coverage==7.16.2", + "url": "https://files.pythonhosted.org/packages/3f/0c/7a64e1ac90541a8edf50daef0914848011fb057a5bf55284a4811e21939a/coverage-7.16.2-py3-none-any.whl", + }, + "coverage_py3_none_any_e674027c": { + "filename": "coverage-7.17.2-py3-none-any.whl", + "integrity": "sha256-5nQCfOtr/ZWfG7A1AA/SD6i3F9xnZa2q+LyjR4IsjH0=", + "requirement": "coverage==7.17.2", + "url": "https://files.pythonhosted.org/packages/da/91/1805cc144497d76a436f66a21fa57492bee7ed1257826fb992f03b9b2b6f/coverage-7.17.2-py3-none-any.whl", + }, + "coverage_py3_none_any_f7941f6f": { + "filename": "coverage-7.10.7-py3-none-any.whl", + "integrity": "sha256-95Qfby/m3WgHoSCHN7igy88cxtewfSSZitLWNZCGgmA=", + "requirement": "coverage==7.10.7", + "url": "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", + }, +} + +COVERAGE_TOOLS = { + "3.10": { + "linux_aarch64": "coverage_cp310_cp310_manylinux_2_17_aarch64_9fd670ac", + "linux_armv7": "coverage_py3_none_any_11d28e91", + "linux_i386": "coverage_cp310_cp310_manylinux_2_5_i686_a4624f80", + "linux_ppc": "coverage_cp310_cp310_manylinux_2_17_ppc64le_705e5af1", + "linux_riscv64": "coverage_cp310_cp310_manylinux_2_31_riscv64_8afd9bf3", + "linux_s390x": "coverage_py3_none_any_11d28e91", + "linux_x86_64": "coverage_cp310_cp310_manylinux_2_5_x86_64_191803c4", + "linux_x86_64_musl": "coverage_cp310_cp310_musllinux_1_2_x86_64_c85d54e7", + "osx_aarch64": "coverage_cp310_cp310_macosx_11_0_arm64_40c0f008", + "osx_arm64e": "coverage_py3_none_any_11d28e91", + "osx_x86_64": "coverage_cp310_cp310_macosx_10_9_x86_64_23219888", + "windows_x86_32": "coverage_cp310_cp310_win32_5139009b", + "windows_x86_64": "coverage_cp310_cp310_win_amd64_c3305c38", + }, + "3.11": { + "linux_aarch64": "coverage_cp311_cp311_manylinux_2_17_aarch64_1947bd9d", + "linux_armv7": "coverage_py3_none_any_e674027c", + "linux_i386": "coverage_cp311_cp311_manylinux_2_5_i686_5b71aeaa", + "linux_ppc": "coverage_cp311_cp311_manylinux_2_17_ppc64le_2cd1a747", + "linux_riscv64": "coverage_cp311_cp311_manylinux_2_31_riscv64_2b7fe217", + "linux_s390x": "coverage_py3_none_any_e674027c", + "linux_x86_64": "coverage_cp311_cp311_manylinux_2_5_x86_64_01303d9e", + "linux_x86_64_musl": "coverage_cp311_cp311_musllinux_1_2_x86_64_7c123b6b", + "osx_aarch64": "coverage_cp311_cp311_macosx_11_0_arm64_3d3d9d79", + "osx_arm64e": "coverage_py3_none_any_e674027c", + "osx_x86_64": "coverage_cp311_cp311_macosx_10_9_x86_64_a3f49ac9", + "windows_aarch64": "coverage_cp311_cp311_win_arm64_b08ee3ef", + "windows_x86_32": "coverage_cp311_cp311_win32_1c3146ef", + "windows_x86_64": "coverage_cp311_cp311_win_amd64_51c6a60e", + }, + "3.12": { + "linux_aarch64": "coverage_cp312_cp312_manylinux_2_17_aarch64_1682d98d", + "linux_armv7": "coverage_py3_none_any_e674027c", + "linux_i386": "coverage_cp312_cp312_manylinux_2_5_i686_90c6c5f9", + "linux_ppc": "coverage_cp312_cp312_manylinux_2_17_ppc64le_8eab0a5c", + "linux_riscv64": "coverage_cp312_cp312_manylinux_2_31_riscv64_635337a9", + "linux_s390x": "coverage_py3_none_any_e674027c", + "linux_x86_64": "coverage_cp312_cp312_manylinux_2_5_x86_64_36314997", + "linux_x86_64_musl": "coverage_cp312_cp312_musllinux_1_2_x86_64_686b6f8f", + "osx_aarch64": "coverage_cp312_cp312_macosx_11_0_arm64_da4b0aef", + "osx_arm64e": "coverage_py3_none_any_e674027c", + "osx_x86_64": "coverage_cp312_cp312_macosx_10_13_x86_64_289a2444", + "windows_aarch64": "coverage_cp312_cp312_win_arm64_bac3c79c", + "windows_x86_32": "coverage_cp312_cp312_win32_ee083412", + "windows_x86_64": "coverage_cp312_cp312_win_amd64_e269dddf", + }, + "3.13": { + "linux_aarch64": "coverage_cp313_cp313_manylinux_2_17_aarch64_95bd8dc5", + "linux_aarch64_freethreaded": "coverage_py3_none_any_e674027c", + "linux_armv7": "coverage_py3_none_any_e674027c", + "linux_armv7_freethreaded": "coverage_py3_none_any_e674027c", + "linux_i386": "coverage_cp313_cp313_manylinux_2_5_i686_7ce0acb2", + "linux_i386_freethreaded": "coverage_py3_none_any_e674027c", + "linux_ppc": "coverage_cp313_cp313_manylinux_2_17_ppc64le_a5565a9d", + "linux_ppc_freethreaded": "coverage_py3_none_any_e674027c", + "linux_riscv64": "coverage_cp313_cp313_manylinux_2_31_riscv64_091eba42", + "linux_riscv64_freethreaded": "coverage_py3_none_any_e674027c", + "linux_s390x": "coverage_py3_none_any_e674027c", + "linux_s390x_freethreaded": "coverage_py3_none_any_e674027c", + "linux_x86_64": "coverage_cp313_cp313_manylinux_2_5_x86_64_70daa8e4", + "linux_x86_64_freethreaded": "coverage_py3_none_any_e674027c", + "linux_x86_64_musl": "coverage_cp313_cp313_musllinux_1_2_x86_64_e09c3781", + "linux_x86_64_musl_freethreaded": "coverage_py3_none_any_e674027c", + "osx_aarch64": "coverage_cp313_cp313_macosx_11_0_arm64_f94d465e", + "osx_aarch64_freethreaded": "coverage_py3_none_any_e674027c", + "osx_arm64e": "coverage_py3_none_any_e674027c", + "osx_arm64e_freethreaded": "coverage_py3_none_any_e674027c", + "osx_x86_64": "coverage_cp313_cp313_macosx_10_13_x86_64_e6c89d63", + "osx_x86_64_freethreaded": "coverage_py3_none_any_e674027c", + "windows_aarch64": "coverage_cp313_cp313_win_arm64_bb742836", + "windows_aarch64_freethreaded": "coverage_py3_none_any_e674027c", + "windows_x86_32": "coverage_cp313_cp313_win32_c10f2a27", + "windows_x86_32_freethreaded": "coverage_py3_none_any_e674027c", + "windows_x86_64": "coverage_cp313_cp313_win_amd64_8dc07ed3", + "windows_x86_64_freethreaded": "coverage_py3_none_any_e674027c", + }, + "3.14": { + "linux_aarch64": "coverage_cp314_cp314_manylinux_2_17_aarch64_e6bf531d", + "linux_aarch64_freethreaded": "coverage_cp314_cp314t_manylinux_2_17_aarch64_d0332e2b", + "linux_armv7": "coverage_py3_none_any_e674027c", + "linux_armv7_freethreaded": "coverage_py3_none_any_e674027c", + "linux_i386": "coverage_cp314_cp314_manylinux_2_5_i686_3dfefb1e", + "linux_i386_freethreaded": "coverage_cp314_cp314t_manylinux_2_5_i686_0d8a5dc7", + "linux_ppc": "coverage_cp314_cp314_manylinux_2_17_ppc64le_88d4dc83", + "linux_ppc_freethreaded": "coverage_cp314_cp314t_manylinux_2_17_ppc64le_27fc4384", + "linux_riscv64": "coverage_cp314_cp314_manylinux_2_31_riscv64_8dc17707", + "linux_riscv64_freethreaded": "coverage_cp314_cp314t_manylinux_2_31_riscv64_603140ee", + "linux_s390x": "coverage_py3_none_any_e674027c", + "linux_s390x_freethreaded": "coverage_py3_none_any_e674027c", + "linux_x86_64": "coverage_cp314_cp314_manylinux_2_5_x86_64_34e5c7a2", + "linux_x86_64_freethreaded": "coverage_cp314_cp314t_manylinux_2_5_x86_64_248873a1", + "linux_x86_64_musl": "coverage_cp314_cp314_musllinux_1_2_x86_64_460ba213", + "linux_x86_64_musl_freethreaded": "coverage_cp314_cp314t_musllinux_1_2_x86_64_d88fcc24", + "osx_aarch64": "coverage_cp314_cp314_macosx_11_0_arm64_2a628101", + "osx_aarch64_freethreaded": "coverage_cp314_cp314t_macosx_11_0_arm64_ba45dc07", + "osx_arm64e": "coverage_py3_none_any_e674027c", + "osx_arm64e_freethreaded": "coverage_py3_none_any_e674027c", + "osx_x86_64": "coverage_cp314_cp314_macosx_10_15_x86_64_36248fb6", + "osx_x86_64_freethreaded": "coverage_cp314_cp314t_macosx_10_15_x86_64_674bcd02", + "windows_aarch64": "coverage_cp314_cp314_win_arm64_a5909dd0", + "windows_aarch64_freethreaded": "coverage_cp314_cp314t_win_arm64_eca90e02", + "windows_x86_32": "coverage_cp314_cp314_win32_e72cf955", + "windows_x86_32_freethreaded": "coverage_cp314_cp314t_win32_2c42c222", + "windows_x86_64": "coverage_cp314_cp314_win_amd64_205f9e32", + "windows_x86_64_freethreaded": "coverage_cp314_cp314t_win_amd64_b94c509e", + }, + "3.15": { + "linux_aarch64": "coverage_cp315_cp315_manylinux_2_17_aarch64_8d3626b9", + "linux_aarch64_freethreaded": "coverage_cp315_cp315t_manylinux_2_17_aarch64_a65faa1e", + "linux_armv7": "coverage_py3_none_any_e674027c", + "linux_armv7_freethreaded": "coverage_py3_none_any_e674027c", + "linux_i386": "coverage_cp315_cp315_manylinux_2_5_i686_d14872cc", + "linux_i386_freethreaded": "coverage_cp315_cp315t_manylinux_2_5_i686_a838a10a", + "linux_ppc": "coverage_cp315_cp315_manylinux_2_17_ppc64le_c9639c2e", + "linux_ppc_freethreaded": "coverage_cp315_cp315t_manylinux_2_17_ppc64le_7c17f716", + "linux_riscv64": "coverage_cp315_cp315_manylinux_2_31_riscv64_19d508b0", + "linux_riscv64_freethreaded": "coverage_cp315_cp315t_manylinux_2_31_riscv64_545ca424", + "linux_s390x": "coverage_py3_none_any_e674027c", + "linux_s390x_freethreaded": "coverage_py3_none_any_e674027c", + "linux_x86_64": "coverage_cp315_cp315_manylinux_2_5_x86_64_3bd065e7", + "linux_x86_64_freethreaded": "coverage_cp315_cp315t_manylinux_2_5_x86_64_0fc17882", + "linux_x86_64_musl": "coverage_cp315_cp315_musllinux_1_2_x86_64_99b64a12", + "linux_x86_64_musl_freethreaded": "coverage_cp315_cp315t_musllinux_1_2_x86_64_2ad189c6", + "osx_aarch64": "coverage_cp315_cp315_macosx_11_0_arm64_81a43279", + "osx_aarch64_freethreaded": "coverage_cp315_cp315t_macosx_11_0_arm64_d4004fce", + "osx_arm64e": "coverage_py3_none_any_e674027c", + "osx_arm64e_freethreaded": "coverage_py3_none_any_e674027c", + "osx_x86_64": "coverage_cp315_cp315_macosx_10_15_x86_64_298849ca", + "osx_x86_64_freethreaded": "coverage_cp315_cp315t_macosx_10_15_x86_64_ffbaadf8", + "windows_aarch64": "coverage_cp315_cp315_win_arm64_be699e76", + "windows_aarch64_freethreaded": "coverage_cp315_cp315t_win_arm64_c68f64d9", + "windows_x86_32": "coverage_cp315_cp315_win32_74d91af5", + "windows_x86_32_freethreaded": "coverage_cp315_cp315t_win32_6fd6f218", + "windows_x86_64": "coverage_cp315_cp315_win_amd64_075566a4", + "windows_x86_64_freethreaded": "coverage_cp315_cp315t_win_amd64_ef07aae0", + }, + "3.9": { + "linux_aarch64": "coverage_cp39_cp39_manylinux_2_17_aarch64_c7315339", + "linux_armv7": "coverage_py3_none_any_f7941f6f", + "linux_i386": "coverage_cp39_cp39_manylinux_2_5_i686_567f5c15", + "linux_ppc": "coverage_py3_none_any_f7941f6f", + "linux_riscv64": "coverage_cp39_cp39_manylinux_2_31_riscv64_912e6ebc", + "linux_s390x": "coverage_py3_none_any_f7941f6f", + "linux_x86_64": "coverage_cp39_cp39_manylinux_2_5_x86_64_2af88def", + "linux_x86_64_musl": "coverage_cp39_cp39_musllinux_1_2_x86_64_635adb9a", + "osx_aarch64": "coverage_cp39_cp39_macosx_11_0_arm64_bc91b314", + "osx_arm64e": "coverage_py3_none_any_f7941f6f", + "osx_x86_64": "coverage_cp39_cp39_macosx_10_9_x86_64_fff7b9c3", + "windows_x86_32": "coverage_cp39_cp39_win32_5a02d5a8", + "windows_x86_64": "coverage_cp39_cp39_win_amd64_c134869d", + }, +} diff --git a/python/private/coverage/workspace_deps_gen.bzl b/python/private/coverage/workspace_deps_gen.bzl new file mode 100644 index 0000000000..87faf55487 --- /dev/null +++ b/python/private/coverage/workspace_deps_gen.bzl @@ -0,0 +1,119 @@ +"""A dev-only repository rule to generate the `workspace_deps.bzl` file. + +The WORKSPACE macros cannot read the `uv.lock` file, so the wheels that are +selected for each Python version and platform are written to a checked-in +`.bzl` file. This uses the same wheel selection code as the `pip.parse` bzlmod +extension. + +Run `bazel run //dev/update_deps:update_coverage_workspace_deps` to update the +checked-in file. +""" + +load("@toml.bzl", "toml") +load("//python:versions.bzl", "MINOR_MAPPING") +load("//python/private:repo_utils.bzl", "repo_utils") +load("//python/private:text_util.bzl", "render") +load("//python/private:version.bzl", "version") +load("//python/private/pypi:default_platforms.bzl", "default_platforms") +load("//python/private/pypi:hash.bzl", "hash") +load("//python/private/pypi:parse_requirements.bzl", "parse_requirements") +load("//python/private/pypi:pep508_env.bzl", "env") +load("//python/private/pypi:pep508_evaluate.bzl", "evaluate") +load("//python/private/pypi:platform.bzl", "platform") +load("//python/private/pypi:whl_repo_name.bzl", "whl_repo_name") + +_HEADER = '''\ +"""The coverage wheels for the WORKSPACE toolchains. + +@generated by `bazel run //dev/update_deps:update_coverage_workspace_deps` from +the `uv.lock` file in this directory. DO NOT EDIT. +""" +''' + +def _platforms(python_version): + """Return the platforms to select the wheels for. + + Args: + python_version: {type}`str` the full Python version. + + Returns: + {type}`dict[str, struct]` the platforms. + """ + ver = version.parse(python_version, strict = True) + ret = {} + for name, values in default_platforms().items(): + p = platform(**values) + env_ = env( + env = p.env, + os = p.os_name, + arch = p.arch_name, + python_version = python_version, + ) + if p.marker and not evaluate(p.marker, env = env_): + continue + + ret[name] = struct( + env = env_, + whl_abi_tags = [ + tag.format(major = ver.release[0], minor = ver.release[1]) + for tag in p.whl_abi_tags + ], + whl_platform_tags = p.whl_platform_tags, + ) + return ret + +def _workspace_deps_gen_impl(rctx): + logger = repo_utils.logger(rctx) + + wheels = {} + tools = {} + for minor_version, full_version in sorted( + MINOR_MAPPING.items(), + key = lambda x: version.parse(x[0]).key(), + ): + platforms = _platforms(full_version) + requirements = parse_requirements( + rctx, + platforms = platforms, + uv_lock = rctx.attr.uv_lock, + toml_decode = toml.decode, + logger = logger, + ) + for whl in requirements: + if whl.name != "coverage": + continue + + for src in whl.srcs: + name = whl_repo_name(src.filename, src.digest) + wheels[name] = { + "filename": src.filename, + "integrity": hash.integrity(src.digest), + "requirement": src.requirement_line, + "url": src.url, + } + for p in src.target_platforms: + tools.setdefault(minor_version, {})[p] = name + + rctx.file("BUILD.bazel", 'exports_files(["workspace_deps.bzl"])\n') + rctx.file("workspace_deps.bzl", "\n".join([ + _HEADER, + "COVERAGE_WHEELS = " + render.dict_dict( + {k: wheels[k] for k in sorted(wheels)}, + ), + "", + "COVERAGE_TOOLS = " + render.dict_dict( + { + minor_version: {p: tools[minor_version][p] for p in sorted(tools[minor_version])} + for minor_version in sorted(tools) + }, + ), + "", + ])) + +workspace_deps_gen = repository_rule( + implementation = _workspace_deps_gen_impl, + attrs = { + "uv_lock": attr.label(mandatory = True, allow_single_file = True), + }, + doc = "Generate the `workspace_deps.bzl` file from the coverage `uv.lock` file.", +) diff --git a/python/private/internal_dev_deps.bzl b/python/private/internal_dev_deps.bzl index 5bbee232f9..dd6195808b 100644 --- a/python/private/internal_dev_deps.bzl +++ b/python/private/internal_dev_deps.bzl @@ -14,6 +14,7 @@ """Module extension for internal dev_dependency=True setup.""" load("@bazel_ci_rules//:rbe_repo.bzl", "rbe_preconfig") +load("//python/private/coverage:workspace_deps_gen.bzl", "workspace_deps_gen") load("//python/private/pypi:whl_library.bzl", "whl_library") load("//tests/support/whl_from_dir:whl_from_dir_repo.bzl", "whl_from_dir_repo") load(":runtime_env_repo.bzl", "runtime_env_repo") @@ -30,6 +31,12 @@ def _internal_dev_deps_impl(mctx): ) runtime_env_repo(name = "rules_python_runtime_env_tc_info") + # Setup for //tests/coverage:workspace_deps_test + workspace_deps_gen( + name = "rules_python_coverage_workspace_deps", + uv_lock = "//python/private/coverage:uv.lock", + ) + # Setup for //tests/whl_with_build_files whl_from_dir_repo( name = "whl_with_build_files", diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index 6842c978d9..3fe9d0654a 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -1,3 +1,4 @@ +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") load("//python:py_library.bzl", "py_library") load("//python:pytest_test.bzl", "pytest_test") load("//tests/support:py_reconfig.bzl", "py_reconfig_test") @@ -37,6 +38,16 @@ pytest_test( deps = ["//python/runfiles"], ) +# The WORKSPACE toolchains use the wheels from the checked-in file, which is +# generated with bzlmod. +diff_test( + name = "workspace_deps_test", + failure_message = "Run `bazel run //dev/update_deps:update_coverage_workspace_deps` to update the file.", + file1 = "//python/private/coverage:workspace_deps.bzl", + file2 = "@rules_python_coverage_workspace_deps//:workspace_deps.bzl", + target_compatible_with = SUPPORTS_BZLMOD, +) + # The same program, but the coverage tool is the `coverage` py_library from the # `@dev_pip` hub, see the `MODULE.bazel` file. py_reconfig_test( From 5bbeb8f67e50cb2b68a083af07fa04593e194446 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:31:49 +0900 Subject: [PATCH 26/39] feat(toolchain): use the uv.lock based coverage hub for the WORKSPACE toolchains With WORKSPACE, `python_register_toolchains(register_coverage_tool = True)` now creates the `@rules_python_coverage` hub from the checked-in `workspace_deps.bzl` file, so the WORKSPACE toolchains use the same coverage wheels as bzlmod without any changes to the `WORKSPACE` file. The WORKSPACE builds do not set the Python version flag by default, so the hub has a coverage tool for each Python version, which only selects the wheel based on the target platform, e.g. `@rules_python_coverage//coverage:3.13`. The `coverage_deps.bzl` file is removed and the coverage collection test now also runs with WORKSPACE. --- WORKSPACE | 2 + docs/coverage.md | 19 +- news/coverage-workspace.changed.md | 5 + python/private/BUILD.bazel | 21 +- python/private/coverage/BUILD.bazel | 12 + .../private/coverage/coverage_workspace.bzl | 125 ++ python/private/coverage_deps.bzl | 1244 ----------------- python/private/py_runtime_rule.bzl | 4 +- python/private/python_register_toolchains.bzl | 26 +- tests/coverage/BUILD.bazel | 11 +- tests/coverage/coverage_collection_test.py | 6 +- tests/coverage_deps/BUILD.bazel | 17 - tests/coverage_deps/coverage_deps_test.bzl | 101 -- 13 files changed, 176 insertions(+), 1417 deletions(-) create mode 100644 news/coverage-workspace.changed.md create mode 100644 python/private/coverage/coverage_workspace.bzl delete mode 100644 python/private/coverage_deps.bzl delete mode 100644 tests/coverage_deps/BUILD.bazel delete mode 100644 tests/coverage_deps/coverage_deps_test.bzl diff --git a/WORKSPACE b/WORKSPACE index 631b94118d..9e0eb88710 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -75,6 +75,8 @@ python_register_multi_toolchains( default_version = "3.14", # Integration tests verify each version, so register all of them. python_versions = PYTHON_VERSIONS, + # See //tests/coverage:coverage_collection_test + register_coverage_tool = True, ) load("//python/uv:repositories.bzl", "uv_toolchains_manifest") diff --git a/docs/coverage.md b/docs/coverage.md index 71050818af..ab611a5823 100644 --- a/docs/coverage.md +++ b/docs/coverage.md @@ -34,6 +34,12 @@ python_register_toolchains( ) ``` +This uses the same `coverage` wheels as bzlmod, without any extra setup in the +`WORKSPACE` file. The WORKSPACE macros cannot read the `uv.lock` file, so the +selected wheels are stored in a `.bzl` file generated from it, and the +`@rules_python_coverage` repository has a coverage tool for each Python version, +e.g. `@rules_python_coverage//coverage:3.13`. + :::{note} This will implicitly add the version of `coverage` provided by `rules_python` to the dependencies of `py_test` rules when `bazel coverage` is @@ -44,14 +50,11 @@ need to manually configure coverage (see below). ::: :::{note} -For WORKSPACE, the bundled `coverage` wheel set covers CPython 3.9 through -3.14 (with freethreaded variants for 3.13+), and not every platform within -that range. When the interpreter that `bazel coverage` actually selects has no -bundled wheel, `register_coverage_tool = True` produces no coverage tool and -`bazel coverage` emits empty lcov data; manually configure coverage (see below) -instead. - -`py_runtime` warns when this happens. The warning is emitted during analysis, +The coverage tool is not configured for the Windows toolchains, so `bazel +coverage` emits empty lcov data for them; manually configure coverage (see +below) instead. + +`py_runtime` warns when the selected runtime has no coverage tool. The warning is emitted during analysis, for the runtime toolchain resolution selected and only when coverage is being collected, so it does not fire for the many platforms toolchains are registered for but a given build never uses. diff --git a/news/coverage-workspace.changed.md b/news/coverage-workspace.changed.md new file mode 100644 index 0000000000..f1bb2b7db7 --- /dev/null +++ b/news/coverage-workspace.changed.md @@ -0,0 +1,5 @@ +(toolchains) With WORKSPACE, `python_register_toolchains(register_coverage_tool = True)` +uses the same `coverage` wheels as bzlmod, which are locked in the `uv.lock` +file of `rules_python`, without any changes to the `WORKSPACE` file. The +coverage tool is now available for all of the platforms of the hermetic +toolchains. diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index 9295df842b..5ee60b45e2 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -42,12 +42,6 @@ distribution_filegroup( name = "distribution", ) -filegroup( - name = "coverage_deps_filegroup", - srcs = ["coverage_deps.bzl"], - visibility = ["//:__subpackages__"], -) - # Filegroup of bzl files that can be used by downstream rules for documentation generation filegroup( name = "bzl", @@ -369,19 +363,6 @@ bzl_library( ], ) -bzl_library( - name = "coverage_deps", - srcs = ["coverage_deps.bzl"], - deps = [ - ":repo_utils", - ":version_label", - "//python:versions", - "//python/private:bazel_tools", - "//python/private/pypi:pep508_evaluate", - "//python/private/pypi:select_whl", - ], -) - bzl_library( name = "current_py_cc_headers", srcs = ["current_py_cc_headers.bzl"], @@ -798,12 +779,12 @@ bzl_library( name = "python_register_toolchains", srcs = ["python_register_toolchains.bzl"], deps = [ - ":coverage_deps", ":full_version", ":pbs_manifest", ":python_repository", ":toolchains_repo", "//python:versions", + "//python/private/coverage:coverage_workspace", ], ) diff --git a/python/private/coverage/BUILD.bazel b/python/private/coverage/BUILD.bazel index 6bf9a97573..81b82a1475 100644 --- a/python/private/coverage/BUILD.bazel +++ b/python/private/coverage/BUILD.bazel @@ -13,6 +13,18 @@ distribution_filegroup( name = "distribution", ) +bzl_library( + name = "coverage_workspace", + srcs = ["coverage_workspace.bzl"], + deps = [ + ":workspace_deps", + "//python/private:bazel_tools", + "//python/private:text_util", + "//python/private/pypi:default_platforms", + "//python/private/pypi:whl_library", + ], +) + bzl_library( name = "workspace_deps_gen", srcs = ["workspace_deps_gen.bzl"], diff --git a/python/private/coverage/coverage_workspace.bzl b/python/private/coverage/coverage_workspace.bzl new file mode 100644 index 0000000000..6b4abbf97b --- /dev/null +++ b/python/private/coverage/coverage_workspace.bzl @@ -0,0 +1,125 @@ +"""The coverage tool for the WORKSPACE toolchains. + +The bzlmod toolchains use the `@rules_python_coverage` pip hub, which is +created from the `uv.lock` file. The WORKSPACE macros cannot read the +`uv.lock` file, so the selected wheels are stored in the generated +`workspace_deps.bzl` file and this creates an equivalent hub repository from +it. + +The WORKSPACE builds do not set the Python version flag by default, so there +is a target for each Python version in the hub, which only selects the wheel +based on the target platform. +""" + +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("//python/private:text_util.bzl", "render") +load("//python/private/pypi:default_platforms.bzl", "default_platforms") +load("//python/private/pypi:whl_library.bzl", "whl_library") +load(":workspace_deps.bzl", "COVERAGE_TOOLS", "COVERAGE_WHEELS") + +_HUB_NAME = "rules_python_coverage" + +def _wheel_repo_name(name): + return "{}_{}".format(_HUB_NAME, name) + +def _coverage_hub_repo_impl(rctx): + platforms = json.decode(rctx.attr.platforms) + tools = json.decode(rctx.attr.tools) + + used_platforms = sorted({ + p: None + for by_platform in tools.values() + for p in by_platform + }) + + rctx.file("BUILD.bazel", "\n".join([ + """load("@bazel_skylib//lib:selects.bzl", "selects")""", + "", + """package(default_visibility = ["//visibility:public"])""", + "", + """exports_files(["config.bzl"])""", + "", + ] + [ + render.call( + "selects.config_setting_group", + name = repr("is_" + p), + match_all = render.list(platforms[p]), + ) + "\n" + for p in used_platforms + ])) + + rctx.file("config.bzl", "packages = {\"coverage\": None}\n") + + rctx.file("coverage/BUILD.bazel", "\n".join([ + """package(default_visibility = ["//visibility:public"])""", + "", + ] + [ + render.alias( + name = python_version, + actual = render.select( + { + "//:is_" + p: "@{}//:pkg".format(_wheel_repo_name(name)) + for p, name in sorted(by_platform.items()) + }, + no_match_error = repr(( + "There is no coverage wheel for Python {} on the target " + + "platform. See the `python/private/coverage/workspace_deps.bzl` " + + "file in rules_python for the supported platforms." + ).format(python_version)), + ), + ) + "\n" + for python_version, by_platform in sorted(tools.items()) + ])) + +coverage_hub_repo = repository_rule( + implementation = _coverage_hub_repo_impl, + attrs = { + "platforms": attr.string( + mandatory = True, + doc = "JSON encoded dict of platform names to config setting labels.", + ), + "tools": attr.string( + mandatory = True, + doc = "JSON encoded dict of Python versions to platform names to wheel names.", + ), + }, + doc = "The hub repository for the coverage tool of the WORKSPACE toolchains.", +) + +def coverage_tool(python_version): + """Create the coverage repositories and return the coverage tool label. + + Args: + python_version: {type}`str` the full Python version of the toolchain. + + Returns: + {type}`str | None` the label of the coverage tool or `None` if there + are no coverage wheels for the Python version. + """ + major_minor = ".".join(python_version.split(".")[:2]) + if major_minor not in COVERAGE_TOOLS: + return None + + for name, whl in COVERAGE_WHEELS.items(): + maybe( + whl_library, + name = _wheel_repo_name(name), + requirement = whl["requirement"], + urls = [whl["url"]], + filename = whl["filename"], + integrity = whl["integrity"], + dep_template = "@{}//{{name}}:{{target}}".format(_HUB_NAME), + config_load = "@{}//:config.bzl".format(_HUB_NAME), + ) + + maybe( + coverage_hub_repo, + name = _HUB_NAME, + platforms = json.encode({ + name: [str(Label(setting)) for setting in values["config_settings"]] + for name, values in default_platforms().items() + }), + tools = json.encode(COVERAGE_TOOLS), + ) + + return "@{}//coverage:{}".format(_HUB_NAME, major_minor) diff --git a/python/private/coverage_deps.bzl b/python/private/coverage_deps.bzl deleted file mode 100644 index 6f826f897a..0000000000 --- a/python/private/coverage_deps.bzl +++ /dev/null @@ -1,1244 +0,0 @@ -# Copyright 2023 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. - -"""Dependencies for coverage.py used by the hermetic toolchain. -""" - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") -load("//python:versions.bzl", "FREETHREADED") -load("//python/private:repo_utils.bzl", "repo_utils") -load("//python/private:version_label.bzl", "version_label") -load("//python/private/pypi:pep508_evaluate.bzl", "evaluate") -load("//python/private/pypi:select_whl.bzl", "select_whl") - -# The wheels below were generated with: -# -# bazel run //dev/update_deps:update_coverage_deps -- \ -# "7.10.7; python_full_version < '3.15'" \ -# "7.17.0; python_full_version >= '3.15'" -# -# Newer coverage versions do not support older python versions, so each version -# has a `python_full_version` marker for when it should be used. The markers are -# evaluated against `python_version` and `python_full_version` only. -# START: maintained by 'bazel run //dev/update_deps:update_coverage_deps ' -_coverage_wheels = [ - struct( - url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", - hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", - hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", - hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", - hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", - hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", - hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", - hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", - hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", - hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", - hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", - hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", - hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", - hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", - hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", - hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", - hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", - hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", - hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", - hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", - hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", - hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", - hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", - hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", - hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", - hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", - hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", - hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", - hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", - hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", - hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", - hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", - hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", - hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", - hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", - hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", - hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", - hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", - hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", - hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", - hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", - hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", - hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", - hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", - hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", - hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", - hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", - hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", - hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", - hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", - hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", - hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", - hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", - hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", - hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", - hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", - hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", - hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", - hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", - hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", - hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", - hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", - hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", - hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", - hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", - hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", - hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", - hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", - hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", - hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", - hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", - hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", - marker = "python_full_version < '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/75/25/79a66272edd250031aeee6db87d500a0bf76390d0d5f8c2f431fe354715f/coverage-7.17.0-cp311-cp311-macosx_10_9_x86_64.whl", - hash = "sha256:99a7b390cbccc6bf029ca4da3f0f607c218bb4085e9cd6b58a90d14ca12993c3", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/52/a6/f5f6fefb2fc1305363927edad1d0a60ce6ef80d6c1a74a79e825f0f670eb/coverage-7.17.0-cp311-cp311-macosx_11_0_arm64.whl", - hash = "sha256:f72640e02a470b57b15cd655820133f6892760209283a363a60e2df932d5f0fe", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/28/61/72889bac8eb44c51f8043edb227df551dfdca1fc4819a6c6d67b3f69427b/coverage-7.17.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:2dc9233f39149b5a4f4aba17a4b6bc5cca22e52cfc4b06fdb07d014439b8b2ea", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f4/e4/03e4a5644df9b73de0d44d1b82acd18b06c42b5abc3f1433be8363fa983d/coverage-7.17.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:8e8113d3852e9377421a61de1917df27b3abb25404a4ecf69f9e64612c4345b5", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/6f/4e/949022286df7e59e8633a3a7e6635e0e6b53af7d57074cb0bf8f500373fb/coverage-7.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:c86a39539d10d666bb819c16c94d7edfc1aa2e0531b937f7c549b83d15177c6e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/27/cf/7d67f60df081006315699b7edb9402c1b47142caf1383f4e56ae8ff1a501/coverage-7.17.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - hash = "sha256:559f748446ee799803686b5784e3deb54c70d655ac386a860e06b11672504ba7", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e1/e0/9293d94afbc099990a085e88229a51c79b00828aba0f2adda32dc4cc16d1/coverage-7.17.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:34d65c9e8e257f48a654908005046fbf1bc48d9540ae3776ad08c0acd010e10c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/24/c8/bcd28efedf262c074dae76f61098739eb02b15f3f716f80cc86f9885b8a9/coverage-7.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", - hash = "sha256:5b7fa0997b8eba7378ac66f31ec7f70164490353ab79b9667d37c0d365e50177", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b8/dc/503f55e5e2200933403c90d619d9f779b5060ddd338b1bdc63cd235592cb/coverage-7.17.0-cp311-cp311-musllinux_1_2_i686.whl", - hash = "sha256:6ae0453a3390e67f7bfbe34417bbdf5b4ebfc21db23fb446a0a41115eeeca6ca", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/bb/b1/f1a57c36077cc516240636f7eff0d1427939ed795f096dd2c7769213a56c/coverage-7.17.0-cp311-cp311-musllinux_1_2_ppc64le.whl", - hash = "sha256:96151262832f3c230ebae23b2d6911ad549e1738465946c97004913ff06112ba", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a8/4f/c2e59d4a132936c7c207b9b69ab99c0f27b14c3c93266e9922aa3b562b37/coverage-7.17.0-cp311-cp311-musllinux_1_2_riscv64.whl", - hash = "sha256:cd7397de6cc782f00fe5ac1d75e9b306bcfa49c8b6b8dd6ed1f7709393ad40b3", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/05/04/50742c0ee8b6c85826ff11737c93ed957fea4c9acf4b4b99804bdac1dd84/coverage-7.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", - hash = "sha256:9d1fdf82c6fea57b5e9dada509f9ce9638c8334664e2c98c7f6f1ccf3d143ad2", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/8e/cd/83ad4bbbfcb6d045a33c14cdca313f6c9275737c67f3b7300222e3239a37/coverage-7.17.0-cp311-cp311-win32.whl", - hash = "sha256:38c86e5d74bbfd88a393fa3f91257c1225c63aa6db6d5918de3e49ab04f91c6e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ca/c8/dcc5a7effa79707eb8a61e699b141cff7c9b46036771274cfb48ea7b765f/coverage-7.17.0-cp311-cp311-win_amd64.whl", - hash = "sha256:2ed3097c6817bc1a7fc8ae0a6131db74aa1150d863cccc308237ff362d3226b2", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/d1/58/a8553e0dcd38d8136df63964638d2b588e0a4a916bff1bca996244fda693/coverage-7.17.0-cp311-cp311-win_arm64.whl", - hash = "sha256:6052e878b628abcd38fa5f2b64abebc686eed4e2267da42434017a9c6d24f241", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/0a/25/989fd3b495c4274ac0cf54cb6664326415e60183a7f8a73dd9bbb0b11e34/coverage-7.17.0-cp312-cp312-macosx_10_13_x86_64.whl", - hash = "sha256:0cfdc938ca3319d873d1f1252e60620a39aee4ebf9dbd7cc58238109ebda9c51", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/db/cb/14726426bf3241657facbf0f7df7babd8f7d3fcf8e4609b7929f9dceecbc/coverage-7.17.0-cp312-cp312-macosx_11_0_arm64.whl", - hash = "sha256:d523da3c2e4bca49417768bcdb1a443c5b359dc44b8b93a3102e118fce912c40", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/8e/97/f95ed32cd3cfbe261a3598e694cb770cefc7b2d13ff013418d6f70a44dde/coverage-7.17.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:adbf3450c0f3f2b2854e282a8758f2b3eddf787eeff24ec9c9bc2cbc38328cc1", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/91/ac/eb4c31f741509a9ef48cf6f28385e98e9de0785599282252eee252ff8af7/coverage-7.17.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:85932227e9dd38e62cd29c17fefdfecfb919eead6af1e3259522d03cf85de983", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e8/3e/d9cd006d1a3cc5deab07beb29b86cb4a10dd84d66b330820363fb8d310d6/coverage-7.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:30f5477aac9c9e4a0a641c55f8f8fddd7f3bd91176c912c50f40ffced96b4034", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c0/3a/4c872c6ae0762b09cdb8e856891bb3f584848cb60b6dda2bacb85fe2f3f0/coverage-7.17.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - hash = "sha256:0b8bb651a4406fb6a983a905309fec1783443c65e1771b41e92740a0f4a1176b", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/df/98/1d41a2373fa98b423bcd70adb91d75e38eafb417955426dbb920b10016f3/coverage-7.17.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:0edeb6594f080c8de1263cccb239cb316ef77aeb959e2e0f38f2953fcc72a49e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/92/6a/fe04b5e63e76506daa63d90bdfb00a53767fe5df18546b437d6226918d6b/coverage-7.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", - hash = "sha256:da94ec0484e84fce5c7bf8d0f54b97767a90d992653a129b35634699d516e0a5", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/4f/c3/5c5a78dc19dd4799be9c6ec022316fb25ababaeacfaf0f52f24c56a57b43/coverage-7.17.0-cp312-cp312-musllinux_1_2_i686.whl", - hash = "sha256:a25cc5e8c23a590815c6a7aa43624234d25cb23b21b484adb8a0daed59860db5", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ed/e7/4c1d8eaff4d756fefd15d58c4953a78ab656782fd0e32c5a3465dba61aa0/coverage-7.17.0-cp312-cp312-musllinux_1_2_ppc64le.whl", - hash = "sha256:db645c81cf4d6bcb392ea1894aad65d55d1588a8472c834b25ca575da3029a56", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a6/2a/0b17c3e2e7b6ff1a5d92eb58915c0d1026668928f961d14dac73cf1b6096/coverage-7.17.0-cp312-cp312-musllinux_1_2_riscv64.whl", - hash = "sha256:6a80a11b90c4a03df1502304cf24ff953255b6b5d076ef79066ecf88baaff31f", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/d3/2d/eb0cbde0535342df0d17a667163030a3afb67e5eb9e3ad94af9540041584/coverage-7.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", - hash = "sha256:e4eaf11d6b0f2147f46cfbf06fe7edcca6f095d0120da00ddc2458995ee869fc", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/2e/47/1be2a6db7eb655d0a1d899217c3f2469f9121cbbeddac35c1c8173b1482e/coverage-7.17.0-cp312-cp312-win32.whl", - hash = "sha256:912a29f338798541d795e2e25903c6fe7a99dee70a40fe4706a9a124649b523e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/11/c5/201c894c57de81875d8b8cc527ba222aa350e8cfb183eb3f6df45d06ad8e/coverage-7.17.0-cp312-cp312-win_amd64.whl", - hash = "sha256:88a1a9fb0075b83389dd9b403bb3fce976031b3c2aa0e05e3e57143cf639de69", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/0b/5d/196095339b19758cf83f507fb598bc42820ae124745feb3084662729e17a/coverage-7.17.0-cp312-cp312-win_arm64.whl", - hash = "sha256:2c680e838ff9e68d491158625ca2c1ddb17d78d791bba96a31f8b839b4990438", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/54/e6/1f01afbc9f4fa8ca5e1fa6fdf537a5c58b5765d1b4167ee284cddb9acc72/coverage-7.17.0-cp313-cp313-android_24_arm64_v8a.whl", - hash = "sha256:84538c040f4e6214f663459d89372ce4e6b2bbdd9d397ef0dca7b3ecd56b8eb2", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/92/7a/bdb7e6260c4a211389aeac9b89dc31662ebe122153ad0ecfd156897912df/coverage-7.17.0-cp313-cp313-android_24_x86_64.whl", - hash = "sha256:6abfb6a4cf1cb1b67bc8fccdde177e2d2b781d0d34449e7078156547025140e5", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/37/4e/276e7d5b3cfb124871bdb544c27b004a8b4e270e6bf8e7414149c54b5e5b/coverage-7.17.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", - hash = "sha256:ef65e96f0fc1a7c8b7644c7011d47a8228555e7888ac4d6246f715a7f3d7c398", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b3/7d/dac073db12f3c131598a6693253aded581e4d7d622a89864f11bfb0ca2f5/coverage-7.17.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", - hash = "sha256:55939ea4c097fda2edabf80fac5e49bef15597b950a9f29fc56292a895245feb", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/00/45/877b0a9248e921080ae91d115c13192d17e308f5aee86bcc7f7bc2a54ed1/coverage-7.17.0-cp313-cp313-macosx_10_13_x86_64.whl", - hash = "sha256:bfb6f868af552c0ceb2d19220b8cbcc2be096ca26163ac30e682a13041433429", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/4f/f5/2c48d47abec0e4590b302920334be5ee3854f96bbd22524feabb8d92b220/coverage-7.17.0-cp313-cp313-macosx_11_0_arm64.whl", - hash = "sha256:8d823d66afd59566030539c49c7a6acaf3083996682be2dda760c63555842094", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a8/a0/ec0442f3f372c7fd742f49b3022edd392f79e37a0048e1fa333f56d01ec3/coverage-7.17.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:50678474e714c684b82c108e63fbb8f1bcf1266d8293a49980f1d08526f093b5", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/92/e2/eb0927ba93809f76eee69fa1e47999825fc7dbbabc3e0e1a2744f2677acb/coverage-7.17.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:045c1eeb19fb4eae949850a761102fffb3a5f638b97f977912f95843e54f6669", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/9a/a2/8d4c6055efb32099b15c9cc33d007b8aabebc4832f372a3274de5ba0afc5/coverage-7.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:dfb12891a773cbc2132349c77c690a045a882fe3f98b4bda50dfabb01893f9bd", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/2c/1e/93412288688a7b03f152a734c8ef086e395108a0a6a746985d3967e220f2/coverage-7.17.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - hash = "sha256:3067608b4429629f640c1b23d2f69b05d2fd45e00ebb6ee9e8fad220a6c11722", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/3c/20/7eb8d805c4f1a212245cef20181dfbf39535357985073d49730ee9cf08d4/coverage-7.17.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:a1762e85cbbe3b5ff9c6dcbcc1b89d5c72320094292b7b9572bf882c85e428b7", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/1a/c1/1df29630b4ed17e135c76390afcc6bd273c3dc5ba2a21a76813a600fe98c/coverage-7.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", - hash = "sha256:62bb77987d3f72a0aaa77c567ee7f9806bc2e748c8f46fafbe1fdb90dd148398", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a1/79/2365d1008d28aafe468a450a24c6c63363de794b5140223d67685cd0e205/coverage-7.17.0-cp313-cp313-musllinux_1_2_i686.whl", - hash = "sha256:6cc31f27240d4e080437e3b8229d92f1d809c9a4f233607385373b73974e183e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c0/9f/f002952d295d772688dffce0d991e3822795b1e3aad158073712a5a5e86e/coverage-7.17.0-cp313-cp313-musllinux_1_2_ppc64le.whl", - hash = "sha256:61cde5222f1933d476647c5a25ed1a8678b7703098cb5a15f4a0aa39967d8c28", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/40/fe/f9e15fbb426be97cd679ee5d359151e046f103bcd76b5092c318c26ed596/coverage-7.17.0-cp313-cp313-musllinux_1_2_riscv64.whl", - hash = "sha256:11f5f7fb2d97a0fe865f5e7bad4eb0619b957637f676bc088d13844adf26ba95", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c2/4a/3c398f38694387334ff4c9f71afc8e15b4fa7db7bc0bea4d5b6032eb0df0/coverage-7.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", - hash = "sha256:6e09612b1b82c9df1d2b6c85dee88d5aad3e73d77a8ec0b16ac6a7322cbdf5d9", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/bd/a9/908a3cf1a2335aec1c788682a06c2f742a62a4f987033f080de1c5fd1997/coverage-7.17.0-cp313-cp313-win32.whl", - hash = "sha256:bd3e9eb493631ff1ca34bf5efac25b5c66724f837bf6182c4fc771c55946423d", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/13/f7/8cdd7bfd8a63b3e6b133fc395ecfc7b1cb6f561926e7e2ad050de85b83e7/coverage-7.17.0-cp313-cp313-win_amd64.whl", - hash = "sha256:f1fc9a7cea899c51d8a53e66c2f082a3d9e461ece32418d3ef0a0b90c32bfd3d", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/09/cf/0b3fc57f735cd3831ca13d0be5312a5477d5934066683c5d34018cab7541/coverage-7.17.0-cp313-cp313-win_arm64.whl", - hash = "sha256:e65a1e61a68493fbfbe44d8bec71b3231d81b9917f62d3dafe0e105072147e83", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/4d/e1/722462fe79912ebebc101497bd16c42413cde2dbae771017f1ec0acb84c2/coverage-7.17.0-cp314-cp314-android_24_arm64_v8a.whl", - hash = "sha256:dc5936491e306f2f6f095bd5fba593de11eb0375cee787ca1f245c3a333a523a", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b7/74/826b9c09f2ebfea803bccb391d935688a25309bff8e819fc446bc629c108/coverage-7.17.0-cp314-cp314-android_24_x86_64.whl", - hash = "sha256:968c5fb6e0c83cab752d79a70209648ce4d90cae815c60fa229b59e264601062", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/6d/19/c8514d7f5e0659a96ff2bc7377119dca6d420e2761c5fc69ae37618734f8/coverage-7.17.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", - hash = "sha256:12cb8a20ad904a5811e6660d829a586b216e5254b9b34b76dde8a4d65d827dc6", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/81/74/ef9433ce599f07d8633b4ce6e6d6921505c4ef3f7f9006f8eb66e2c278cf/coverage-7.17.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", - hash = "sha256:bb5ca66937c55bc85d1a5e9c5900a3e4693a8edf2280a9b3672b7b2661a84b95", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/d8/66/26e58981940e8b3944c6a9941bec5c7f50840e7009a046712e3f3bcd9e6a/coverage-7.17.0-cp314-cp314-macosx_10_15_x86_64.whl", - hash = "sha256:5cc15faaf318ee5040d835a62c75bc6f122d837274f3b5efd47bf9fbd4ac5b85", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e9/63/fe175310815cc8357b83b773c9b4c20943c269f217dd825a37c4b4bfe324/coverage-7.17.0-cp314-cp314-macosx_11_0_arm64.whl", - hash = "sha256:7d6d9d396c8b7bcaf76d774a10140b218111484e54c73a5f2a1b75655381748e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/2c/b0/ac84761f5b4bd226696b0ec4606903d2ef7208abda0aec06c14a4218def4/coverage-7.17.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:42e6f170d1ac04b7eb3363310a6c41cdfd82c1fe6bf9ffbc4a86f380bdf091c7", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e9/58/d8bdd65a661913bc12eaaa3e59742b2cb8b94f2e7f78e62184f0a58ca493/coverage-7.17.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:acdb5f3c96046efff9dc6c572a6acb2b8d0da09372d7b9a9ed7249cf7d17434f", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/7b/15/5d82e2f0a75ea149e0a6412e137e1d610538772fdfd7199737db7fe234da/coverage-7.17.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:3393209f6894713161ee82a9b0e673e58237e0f9733c6638e7a6c0c620ef0b33", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/0f/cc/e3ebfd295d1dd47c7bc84a17d73f150465a37ab388dd68fe82b495dc79a0/coverage-7.17.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - hash = "sha256:e9494d9e99389d39e6a96227749af898d8924ff591cb63944f8a98e68582bf0e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e0/9d/e2adff76d6544bbc6df7366d74967923c2de37ba889a006bfb875bf5857b/coverage-7.17.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:032d4c2db4c677eb71d43f2f8b80bf64e9346f7a3f7b6ebb13f1782f3cc6c274", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/d9/fd/ecd1e9cddf1c36b5d03c438568b1f53e63db84bb3656539d71b454484c7d/coverage-7.17.0-cp314-cp314-musllinux_1_2_aarch64.whl", - hash = "sha256:4343a9378b964eb3cb80fa90406385de91c7e982a769ca9314d848f93bc0d3f2", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/40/b6/e17a19dccdbcad494c64691f71c1dba3e55c30625bc29ffbb88ac5a0aa8b/coverage-7.17.0-cp314-cp314-musllinux_1_2_i686.whl", - hash = "sha256:b609df3d6d900464207532c5170a65c6ede8d2e0b91f151f90008a866395cd76", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/6a/0d/37dac534709a3fa43a937c8b0604839bb44b8670b83d143a4b78fec20d1e/coverage-7.17.0-cp314-cp314-musllinux_1_2_ppc64le.whl", - hash = "sha256:a01ef92caeedfa4f2ad171a3a8e6fece4565de871d8bca428151bf9929f41048", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/cb/17/7c05186925b4d64a46c271dfd944e3f0c1ad89db9095c31a6df157a75df2/coverage-7.17.0-cp314-cp314-musllinux_1_2_riscv64.whl", - hash = "sha256:a3914e2c98c34b94c26d2add3d2a61ccfd3534fc4eaa879ee290bdbb5184ba6c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/95/65/c241cdee66a0ff86c68f6d37328e735d10e3d30cd6ff66f356039eb688d2/coverage-7.17.0-cp314-cp314-musllinux_1_2_x86_64.whl", - hash = "sha256:e8a72b26dc5c9a9422e45472b1d67ba5caf273c524c8668cc04b23062663755e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/db/09/e93233fe2405574ce0a31ed201c0cba3a860e085db5d2828bf12b7dc0790/coverage-7.17.0-cp314-cp314-win32.whl", - hash = "sha256:03baf0688e14a5a44ffd1eefa8928dfc92b14565e521174e072b31b971aa1657", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/d7/77/04979eb217edb94b94afabb4bcc179f60c746f68d3fd0b12688ba9195730/coverage-7.17.0-cp314-cp314-win_amd64.whl", - hash = "sha256:3a4459b0f389dd1e51aac3dbfc4ca35b30cae7dc99caa3c0a086d1997a985709", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a0/38/155f2f2c6769f8c43eca8a5e581443faa0908828650090b97103b4a41376/coverage-7.17.0-cp314-cp314-win_arm64.whl", - hash = "sha256:a32ee5831b15ba239bef5d272ae586ca8acea4a661b5f2941f5a0d380e518b23", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/52/55/d6751572d9445485c4c5a51ec6ae11caa5c11b7ef2448cdccc884f7f54d9/coverage-7.17.0-cp314-cp314t-macosx_10_15_x86_64.whl", - hash = "sha256:9c083e253e96fdeeaf48133fe836cac86ace2db215d235700e7f7532420a200c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/2f/a8/82d37760e537bfdb9fdee285fa1e1b45bc153293d23e2331c7befedd1990/coverage-7.17.0-cp314-cp314t-macosx_11_0_arm64.whl", - hash = "sha256:b88fb4d4d7608cbcebdc7ff7dbffc2115e3b81d6c33a79147cd79d54fcb080f4", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/24/96/70ec00a9db6d7bdd3f914eba12057360769f5cb78a98456e71fe1937deb5/coverage-7.17.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:2bf3ee38a40c0570c316b9e7507c472469586906a20f289705898528c31aaea8", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/77/d8/d99be3bfbb208512f039c80b31e404138a86678fc929a17acd5b7135a6ae/coverage-7.17.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:7c570293f001ea91598b90e90cb9b98fddee833e90bbede432cbd218331ccd8e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/82/50/6cbe8b5a5060edbd21ce8109c4e900f8b7656e8b72ede6dc4d82dc0b8bfb/coverage-7.17.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:791f9ee09814dd30d9562b4f4eb037a371485ec1c830b53999d2320d20c4e764", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/bd/05/d3633add01e5518dfdacfdbd1be41cb5c581412dd8bccd663edf6ef7fec8/coverage-7.17.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - hash = "sha256:c7c5672ecf1b497de7a0b37743bef40e3b58e3207e9fe2ed6682cb0420836a88", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/57/d9/b83387780271bf9ea9f96a3aeb01c1c302c4494a927923c4fa70cd170a3f/coverage-7.17.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:337f87e62e1c5f43c8198d879f677c342d152f84d3f157add12558853cbb8eaf", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e4/be/8e80b29764ae3f4d70d44952eee3f3927eb36897490fe799e400adb7f5da/coverage-7.17.0-cp314-cp314t-musllinux_1_2_aarch64.whl", - hash = "sha256:c8a58baa8eb6154c6645eadcc19b6c089deab78c24a612a83206f2a8a5992a7c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b8/c9/e702f588fe8d83ca844f133270ad0a49bf650c8cf260551ff6f11a6b6bbf/coverage-7.17.0-cp314-cp314t-musllinux_1_2_i686.whl", - hash = "sha256:fb5745048ee20f796b0e3ab61831049e749acff188563e2af32f58ede957ac1b", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/8a/11/ee13ad14b714d75ef07c490b6e43969574a8b8b8c6d67bc741fcbacbf9c3/coverage-7.17.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", - hash = "sha256:7b5abcae74750302ac4bc77351eaee87c450e4abd3b6225eacde3858e260f8b2", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e2/bc/854e8912c11c54aaf416e74602000589c6efbbe1f251dc90202fdabddcb6/coverage-7.17.0-cp314-cp314t-musllinux_1_2_riscv64.whl", - hash = "sha256:4e153f0bd6509dbf837fd585f489b3328bfd98f2a718a0b15ae63032148b285c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a8/dd/96432830ddf63bc42654de54eee77a0cd6949cc65769bda06f353323f23a/coverage-7.17.0-cp314-cp314t-musllinux_1_2_x86_64.whl", - hash = "sha256:e42a835271922fe7bf98c7a05c78d4032f4e52bf5b07fafa1eadfdfc2f0ad22f", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ce/ee/230c6ec065a6629d41cc89323ceb54d37e2650c104f70fc108f4ee7c5051/coverage-7.17.0-cp314-cp314t-win32.whl", - hash = "sha256:5900055351ec89fdffb34b27898815106529ded29a9496464c1ebd6313fd40fa", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f0/1d/23eb6aadcb3c00764ff2961c0ff55f5dfa2543856d932b949bbc04fa68e9/coverage-7.17.0-cp314-cp314t-win_amd64.whl", - hash = "sha256:5ee9c8bd13a59e301dc2aefe7aa8384633406808de051b1982775667094eb673", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/3e/f1/7df117e8e016b7a3ee459e833b3456f3610505afefd071f8727702d98439/coverage-7.17.0-cp314-cp314t-win_arm64.whl", - hash = "sha256:0a9cb9a0fb58e6fff5442969af36094ecd6976f4907f179e33b27e15355d8f74", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/9f/a1/f9c24911755f91e6f451ca2e2c1804fbcbe9fecbc3310e341bbbbfa1d85b/coverage-7.17.0-cp315-cp315-android_24_arm64_v8a.whl", - hash = "sha256:adabc1079ba844b096799ba8638d8df434aca1613f95bb7d5791af320cf05559", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b0/a5/2b665b40c626e6bb40e5e8e23d91170b48b5f2f57186f46b07f2fac42386/coverage-7.17.0-cp315-cp315-android_24_x86_64.whl", - hash = "sha256:6b4593d2871c4c801656dd5861e759f8f5156febd6b6061b02f6a72d64618b7b", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/3d/8c/22b518d61b930b8a4d62905d046c76a03675ef19b466462011dfb16439a3/coverage-7.17.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", - hash = "sha256:c2c48a36bb970b7265b196032bfd4237c0b373f2b25ef7012d7e905b5b35c19c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/08/5b/d3b141455944e030cd4987f055f8c84a44a1a0b28d81ae93389f44051691/coverage-7.17.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", - hash = "sha256:f8bc2cd4c6aa58be1ca243fb8cddb6c3c42031fe4403991ad9cf2b3baed03d9f", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/03/24/aab2156983f7022361352640c583d08f0238e4cd7463eb921172001b4cfe/coverage-7.17.0-cp315-cp315-macosx_10_15_x86_64.whl", - hash = "sha256:cbf0b83ce927de6053279ba61342d9f8892afde1385fcba9144cd05d4023d109", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/87/db/94697b9ccf4ec94cdad07bf94e2a21b9702247cb212d2d5f18d2b9c3d0de/coverage-7.17.0-cp315-cp315-macosx_11_0_arm64.whl", - hash = "sha256:66efc0fb99cee34626fc17d3adac9c3cd816bfdad563bb1e983c3ef432f566d4", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a0/fd/db1c4ade4219831d590fff5aa5b7d9ceaf84586e8e60da6fff71b6182251/coverage-7.17.0-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:58a304efdf2253fb2ea03d45d0a04cc174a92a0cd5279c28c5bb893ce7410968", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/95/ee/84ed138990ed752abd73db4adffc1842d6152017d8e21809cc109a413fda/coverage-7.17.0-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:aed06129a9f530a948928260d7d2d81c47261355b0127e3c782251b38958463c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/64/ad/c1e4367bbd1e7d562d6bc979c82b542f2040544e95a8cd995b694caf0e1a/coverage-7.17.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:f6cdb47a9be2d3313829bed1d296086287682a3ee901ff987b3432b45705b771", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b5/3a/e8e8509b0940403eab15d86f70022790a01fce3e971a161919c544953f7a/coverage-7.17.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - hash = "sha256:804fbaf81d09b34e2f8b1159d01e211a50713d743d32fd9b2ca6cf0ae507a66f", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/1f/77/d64bbdbb734b1059ebcde2b5df567f142a9948079e51af9b0abf8c9ec17b/coverage-7.17.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:b2afebb96da9a1bbca142f8cc12320c14e5c215e0a49cded4f191f5a31506a28", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/e2/29/7cf81f08a254532c074eb52cfe6b12bb753d08f2997269c9fac8de359541/coverage-7.17.0-cp315-cp315-musllinux_1_2_aarch64.whl", - hash = "sha256:bd04fd23206be9ad3de13b0b39313094fafa66a1f6baf70f5471c004a9fac47d", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/7f/56/bacea6dee2d5ccb31ab0cfffcee87ca7efa80ff048840209c8013aeaf02e/coverage-7.17.0-cp315-cp315-musllinux_1_2_i686.whl", - hash = "sha256:5ebfbc87836529a31d25c2dec1262c38eea852ec1c46c0f690a515a7339d9c5b", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a4/97/fd8a6a63740f9ee177c53b59e2f4d0752369bb0fd154086a1e69aa1fb255/coverage-7.17.0-cp315-cp315-musllinux_1_2_ppc64le.whl", - hash = "sha256:a59c3b470ac3b2432af3598a996affbeb10236cd899a005c1c4e517fcff8b592", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c1/37/7cd3d3520d45db3788456452b65446182c08f2a7c276f40c9dd11966be02/coverage-7.17.0-cp315-cp315-musllinux_1_2_riscv64.whl", - hash = "sha256:7fef12cec4b96f59e09761cbd4fc9bdf4788062d1ae435b920241fc16c8a9549", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c1/b9/80343a55e07d854927a915cc21a52e350ae1db9ead33f0248507b960785f/coverage-7.17.0-cp315-cp315-musllinux_1_2_x86_64.whl", - hash = "sha256:a14e8d69464da9cbe1dc29e7116dbd37296d1d51a695327c478e250349085089", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f3/32/e4c816f0cb3efdc6392bb276ff88359729f9dec605bdd3c1c7a44776f759/coverage-7.17.0-cp315-cp315-win32.whl", - hash = "sha256:9a66074231799d8884d267234d49f60c90b43a945dfe069646b72f3058cd4e67", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/43/14/283120142ac281834808584249b89957de997c28573ddd65ae106b622121/coverage-7.17.0-cp315-cp315-win_amd64.whl", - hash = "sha256:79cf71e5f9a48877ccfed5baa2c72f45af4feeaa596249e23eb15b473134830e", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b9/bc/83dbc6c3bd58042afaefcb0cfd7d7a2e3076989a2f3d9bc6e6edac819784/coverage-7.17.0-cp315-cp315-win_arm64.whl", - hash = "sha256:09e563b49c81cd0a33734e926d7a4a6ef1778d74304cde2dcc3aff8b30ea1036", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/00/ed/9463d1cb1766a2570343a372c2871c7fb4d561e5051f029399f4d57ce947/coverage-7.17.0-cp315-cp315t-macosx_10_15_x86_64.whl", - hash = "sha256:a5e2e0625dfc789af0b2e10acfc106b352f314fffbd19e6392a7961c8cbe5d9a", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/ae/59/6536c7fa87be13236b4184fc395f14d18f622a5b8ef7448a6bfdf5f24ec0/coverage-7.17.0-cp315-cp315t-macosx_11_0_arm64.whl", - hash = "sha256:194562ab1eff0d85aee8a17ce4c5c0bc1ebf70a629a7145257123156ad100fd6", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/b0/bc/4ffae6038f78382c9ab6a4969cb5fc01d50b083d119851085807629abe88/coverage-7.17.0-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - hash = "sha256:85682ca89bc4601125dcdff23fa70e6274e13c9ad502fba845e6c274ab2c6569", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c0/60/cae4b708b4217d33ebf66b9c57f8910a257346b3eaf054a78d73605ddc4c/coverage-7.17.0-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - hash = "sha256:0d0fac604a962ff546beb038cba9a5a09d88e6e3f01612fb46f7bd4fe5dc8dca", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/a9/84/1225ab04e55a6a2e483e903a5eb0f8f081cb5325df833594534aa288d91c/coverage-7.17.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - hash = "sha256:72edd4f9483ae30cd1ba4f230687840ef7b58a16afb1c9e81131fd6d64655f36", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/94/ad/567f8dd9246a553aa8753732c41f0e0654e98e9a617d6282e64eae0e6aa8/coverage-7.17.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - hash = "sha256:91e487ad07c511da77b574820141a7c2da09bc2a4b4090b5be2424295d100697", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/66/5f/e0ee76895df40601e4aedffb6b8e71ea48e276a629fb4488b92cffffca14/coverage-7.17.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - hash = "sha256:1cabb31531002fef5197de5df3c84730aef01e750e25154b9813a2583435aba2", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/19/6d/d19c6e45e940e5b9ba697b7be9a8e63cef73d90bdea45b670a59df466717/coverage-7.17.0-cp315-cp315t-musllinux_1_2_aarch64.whl", - hash = "sha256:c9ac886242bbd50fb35a2242ba79555a99891e657da33f98b7ca968e3488a350", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/27/a3/b2731bb7aef2db800b330603675c3fa58a6497a05d5b5f8625f3b6285f68/coverage-7.17.0-cp315-cp315t-musllinux_1_2_i686.whl", - hash = "sha256:d45cdc495671fc99268ff3653e0da4244972e9fc0e1fc3ea9de18781ad28963a", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/50/75/d0c90a81b6b6607933109914dc8b0ef0a5f4cacf9482ca868ae773ee5d0b/coverage-7.17.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", - hash = "sha256:12961f738d717a046d4bf3f8d1727eb3ebe2ed5478ff96cd6233d4dfc39323d3", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/64/1e/f038f3a2614e9ef839a40a184713aca40b8ed79e626ab5cdc388771045df/coverage-7.17.0-cp315-cp315t-musllinux_1_2_riscv64.whl", - hash = "sha256:1ee0a982b0e1230b3edf768a7dd022bd2377303763895327ac62deb5efacee03", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c9/30/8d636e639b2d8b1934128573402a7caf66c8cfbc794fa02061d84f115c34/coverage-7.17.0-cp315-cp315t-musllinux_1_2_x86_64.whl", - hash = "sha256:460679f53893081edb94eeed011d26be37228ba649b853fd2aed7e4394fb1f35", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/73/7a/219f6b391cb0636242d06c390a8add5af91afa3e92afd0380dbedd34f487/coverage-7.17.0-cp315-cp315t-win32.whl", - hash = "sha256:600e121e412cd6bdb52ec105b95c6a04da0c9710d1534f6e64a42db667907802", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f0/3f/514d294319c5eac11494394545de209079f33993e14526c220eab29b2a96/coverage-7.17.0-cp315-cp315t-win_amd64.whl", - hash = "sha256:c3e11b81faafb1f063248735c3e2c5c4183fc1db2c7f46782170dd7760dffcbc", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/f6/82/c0d8bb0a67df838fc7bc6f4060ffc404fd1154fd18e624d83e6abc41aeef/coverage-7.17.0-cp315-cp315t-win_arm64.whl", - hash = "sha256:d472ee1c64868a917a47f02ffb4ac8c22b477f7589d57a3eb0c58b1ad4f0137c", - marker = "python_full_version >= '3.15'", - ), - struct( - url = "https://files.pythonhosted.org/packages/c9/17/48032919343afa2906112d458aceda2043afdb24a2ee8eb4c60904d8ddf1/coverage-7.17.0-py3-none-any.whl", - hash = "sha256:bac487dce0d2aff28ddf1a533a916f771416eee0e493960f611e49037b878e9d", - marker = "python_full_version >= '3.15'", - ), -] -# END: maintained by 'bazel run //dev/update_deps:update_coverage_deps ' - -# The whl platform tags that each of the supported toolchain platforms can use. -# Any toolchain platform that is not listed here can only use the `any` wheels. -_WHL_PLATFORM_TAGS = { - "aarch64-apple-darwin": ["macosx_*_arm64"], - "aarch64-unknown-linux-gnu": ["manylinux_*_aarch64"], - "x86_64-apple-darwin": ["macosx_*_x86_64"], - "x86_64-unknown-linux-gnu": ["manylinux_*_x86_64"], -} - -_coverage_patch = Label("//python/private:coverage.patch") - -def select_coverage_whl(*, python_version, platform): - """Select the coverage wheel compatible with the given toolchain. - - Args: - python_version: The full python version. - platform: The platform, which can be found in //python:versions.bzl PLATFORMS dict. - - Returns: - A struct with `filename`, `url` and `sha256` attributes of the best - matching wheel from `_coverage_wheels`. - """ - abi = "cp" + version_label(python_version) - base_platform = platform - if platform.endswith(FREETHREADED): - abi += "t" - base_platform = platform[:-len(FREETHREADED)] - - marker_env = { - "python_full_version": python_version, - "python_version": ".".join(python_version.split(".")[:2]), - } - - whls = [] - for whl in _coverage_wheels: - if whl.marker and not evaluate(whl.marker, env = marker_env): - continue - - whls.append(struct( - filename = whl.url.rpartition("/")[2], - url = whl.url, - sha256 = whl.hash.removeprefix("sha256:"), - )) - - whl = select_whl( - whls = whls, - python_version = python_version, - whl_platform_tags = ["any"] + _WHL_PLATFORM_TAGS.get(base_platform, []), - whl_abi_tags = ["none", abi], - logger = repo_utils.logger(name = "coverage_dep", verbosity_level = "WARN"), - ) - if not whl: - fail("BUG: could not find a coverage wheel for {} and {}".format(python_version, platform)) - - return whl - -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. - """ - if "windows" in platform: - # NOTE @aignas 2023-01-19: currently we do not support windows as the - # upstream coverage wrapper is written in shell. - return None - - whl = select_coverage_whl(python_version = python_version, platform = platform) - - # NOTE @aignas 2026-07-27: if the `py3-none-any` wheel is selected, then the - # same file may be extracted multiple times. The wheel is small enough to - # not matter in most cases. - - maybe( - http_archive, - name = name, - build_file_content = """ -filegroup( - name = "coverage", - srcs = ["coverage/__main__.py"], - data = glob(["coverage/*.py", "coverage/**/*.py", "coverage/*.so"]), - visibility = {visibility}, -) - """.format( - visibility = visibility, - ), - patch_args = ["-p1"], - patches = [_coverage_patch], - sha256 = whl.sha256, - type = "zip", - urls = [whl.url], - ) - - return "@{name}//:coverage".format(name = name) diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl index 8e05ec875a..6ff86f2985 100644 --- a/python/private/py_runtime_rule.bzl +++ b/python/private/py_runtime_rule.bzl @@ -53,8 +53,8 @@ WARNING: Python runtime {label} has no coverage_tool. For rules_python's hermetic toolchains, enable the bundled coverage.py: python.toolchain(configure_coverage_tool = True) # bzlmod python_register_toolchains(register_coverage_tool = True) # WORKSPACE - A bundled wheel must exist for this interpreter's version and platform; - python/private/coverage_deps.bzl lists what ships with rules_python. + A coverage wheel must exist for this interpreter's version and platform; + python/private/coverage/uv.lock lists what ships with rules_python. Otherwise, set py_runtime.coverage_tool directly. See https://rules-python.readthedocs.io/en/latest/coverage.html diff --git a/python/private/python_register_toolchains.bzl b/python/private/python_register_toolchains.bzl index d82c8299bb..d31e2da05b 100644 --- a/python/private/python_register_toolchains.bzl +++ b/python/private/python_register_toolchains.bzl @@ -23,7 +23,7 @@ load( "TOOL_VERSIONS", "get_release_info", ) -load(":coverage_deps.bzl", "coverage_dep") +load("//python/private/coverage:coverage_workspace.bzl", workspace_coverage_tool = "coverage_tool") load(":full_version.bzl", "full_version") load(":pbs_manifest.bzl", "split_pbs_distribution") load(":python_repository.bzl", "python_repository") @@ -172,24 +172,14 @@ def python_register_toolchains( default = None, ), ) - if register_coverage_tool and coverage_tool == None and bzlmod_toolchain_call: - # NOTE: The coverage tool is not supported on Windows, see - # `coverage_dep`. - if "windows" not in base_platform: + + # NOTE @aignas 2023-01-19: The coverage tool is not supported on + # Windows, because the upstream coverage wrapper is written in shell. + if register_coverage_tool and coverage_tool == None and "windows" not in base_platform: + if bzlmod_toolchain_call: coverage_tool = str(_BZLMOD_COVERAGE_TOOL) - elif register_coverage_tool and coverage_tool == None: - coverage_tool = coverage_dep( - name = "{name}_{platform}_coverage".format( - name = name, - platform = platform, - ), - python_version = python_version, - platform = base_platform, - visibility = ["@{name}_{platform}//:__subpackages__".format( - name = name, - platform = platform, - )], - ) + else: + coverage_tool = workspace_coverage_tool(python_version) impl_repo_name = "{}_{}".format(name, platform) impl_repos[impl_repo_name] = (platform, platform_info) diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index 3fe9d0654a..8a53a4f3e2 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -17,13 +17,12 @@ py_reconfig_test( srcs = ["instrumented.py"], collect_code_coverage = True, main = "instrumented.py", - # The bundled coverage tool is configured only for this version, see the - # `MODULE.bazel` file. + # The default coverage tool is configured for this version, see the + # `MODULE.bazel` and `WORKSPACE` files. python_version = "3.12.16", tags = ["manual"], - # The bundled coverage tool is not supported on Windows and it is only - # configured with bzlmod. - target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, + # The default coverage tool is not supported on Windows. + target_compatible_with = NOT_WINDOWS, deps = [":lib"], ) @@ -34,7 +33,7 @@ pytest_test( env = { "INSTRUMENTED": "$(rlocationpath :instrumented)", }, - target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, + target_compatible_with = NOT_WINDOWS, deps = ["//python/runfiles"], ) diff --git a/tests/coverage/coverage_collection_test.py b/tests/coverage/coverage_collection_test.py index d44b83bf14..72c739c83b 100644 --- a/tests/coverage/coverage_collection_test.py +++ b/tests/coverage/coverage_collection_test.py @@ -52,7 +52,11 @@ def setUpClass(cls): root = rf.root() program = root / os.environ["INSTRUMENTED"] assert program.exists(), program - cls.lib_lines = (root / "_main" / _LIB).read_text().splitlines() + # The main repository is `_main` with bzlmod and the workspace name + # with WORKSPACE. + cls.lib_lines = ( + (root / os.environ["TEST_WORKSPACE"] / _LIB).read_text().splitlines() + ) tmp = pathlib.Path(os.environ["TEST_TMPDIR"]) coverage_dir = tmp / "coverage" diff --git a/tests/coverage_deps/BUILD.bazel b/tests/coverage_deps/BUILD.bazel deleted file mode 100644 index 8ec6025902..0000000000 --- a/tests/coverage_deps/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -# 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") diff --git a/tests/coverage_deps/coverage_deps_test.bzl b/tests/coverage_deps/coverage_deps_test.bzl deleted file mode 100644 index d53ac66790..0000000000 --- a/tests/coverage_deps/coverage_deps_test.bzl +++ /dev/null @@ -1,101 +0,0 @@ -# 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_dep and the coverage wheel selection." - -load("@rules_testing//lib:test_suite.bzl", "test_suite") -load("//python/private:coverage_deps.bzl", "coverage_dep", "select_coverage_whl") # buildifier: disable=bzl-visibility - -_tests = [] - -def _test_windows_platform_returns_none(env): - # Windows is intentionally unsupported: the upstream coverage wrapper is - # written in shell. - result = coverage_dep( - name = "unused_for_test", - python_version = "3.10", - platform = "x86_64-pc-windows-msvc", - visibility = ["//visibility:public"], - ) - env.expect.that_bool(result == None).equals(True) - -_tests.append(_test_windows_platform_returns_none) - -def _test_select_platform_whl(env): - for python_version, platform, want in [ - ("3.9.18", "x86_64-unknown-linux-gnu", "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"), - ("3.10", "aarch64-unknown-linux-gnu", "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"), - ("3.12.4", "x86_64-apple-darwin", "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl"), - ("3.13.1", "aarch64-apple-darwin", "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl"), - ("3.15.0", "x86_64-apple-darwin", "coverage-7.17.0-cp315-cp315-macosx_10_15_x86_64.whl"), - ]: - got = select_coverage_whl(python_version = python_version, platform = platform) - env.expect.where( - python_version = python_version, - platform = platform, - ).that_str(got.filename).equals(want) - -_tests.append(_test_select_platform_whl) - -def _test_select_freethreaded_whl(env): - for python_version, platform, want in [ - ("3.13.1", "aarch64-apple-darwin-freethreaded", "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl"), - ("3.14.0", "x86_64-unknown-linux-gnu-freethreaded", "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"), - ("3.15.0", "aarch64-unknown-linux-gnu-freethreaded", "coverage-7.17.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"), - ]: - got = select_coverage_whl(python_version = python_version, platform = platform) - env.expect.where( - python_version = python_version, - platform = platform, - ).that_str(got.filename).equals(want) - -_tests.append(_test_select_freethreaded_whl) - -def _test_select_fallback_whl(env): - for python_version, platform, want in [ - # There are no freethreaded wheels for this version - ("3.12.4", "x86_64-unknown-linux-gnu-freethreaded", "coverage-7.10.7-py3-none-any.whl"), - # Platforms without bundled platform specific wheels - ("3.13.1", "riscv64-unknown-linux-gnu", "coverage-7.10.7-py3-none-any.whl"), - ("3.13.1", "x86_64-unknown-linux-musl", "coverage-7.10.7-py3-none-any.whl"), - ("3.15.0", "x86_64-unknown-linux-musl", "coverage-7.17.0-py3-none-any.whl"), - # Python versions without bundled platform specific wheels - ("3.16.0", "x86_64-unknown-linux-gnu", "coverage-7.17.0-py3-none-any.whl"), - ]: - got = select_coverage_whl(python_version = python_version, platform = platform) - env.expect.where( - python_version = python_version, - platform = platform, - ).that_str(got.filename).equals(want) - -_tests.append(_test_select_fallback_whl) - -# NOTE: there is intentionally no unit test for the supported-wheel path -# (where coverage_dep returns a non-None label and emits no warning). -# That path calls `maybe(http_archive, ...)`, which calls -# `native.existing_rule()`. `native.existing_rule()` is only valid during -# BUILD file, legacy macro, or rule finalizer evaluation -- not during -# rule analysis, which is the phase rules_testing analysis tests run in. -# Calling coverage_dep with supported args from here therefore fails with -# "existing_rule() can only be used while evaluating a BUILD file, ...". -# The supported-wheel path is exercised end-to-end by `bazel coverage` -# against a real py_test target during ordinary use of the toolchain. - -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) From 714a7e33fe669be1b2bf0410052937284be584fc Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:32:19 +0900 Subject: [PATCH 27/39] chore: rename the news entry to use the PR number --- news/{coverage-workspace.changed.md => 4457.changed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename news/{coverage-workspace.changed.md => 4457.changed.md} (100%) diff --git a/news/coverage-workspace.changed.md b/news/4457.changed.md similarity index 100% rename from news/coverage-workspace.changed.md rename to news/4457.changed.md From 29cdabd3d8e731c213c1f0e60389efac5eb70c8e Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:32:54 +0900 Subject: [PATCH 28/39] chore: update the vendored requirements for the new default platforms --- examples/pip_parse_vendored/requirements.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pip_parse_vendored/requirements.bzl b/examples/pip_parse_vendored/requirements.bzl index 82af35372d..95c4b985f9 100644 --- a/examples/pip_parse_vendored/requirements.bzl +++ b/examples/pip_parse_vendored/requirements.bzl @@ -34,7 +34,7 @@ all_data_requirements = [ _packages = [ ("my_project_pip_deps_vendored_certifi", "certifi==2026.10.23"), - ("my_project_pip_deps_vendored_charset_normalizer", "charset-normalizer==3.5.2 --hash=sha256:1c50fe28bbc2ced33386f298650d91218076c05420e6cbd790b913adc41659e7 --hash=sha256:4275811936e2f06feff5e598fb42a1b7ae852da8e39605211892b56b81a34efd --hash=sha256:8d86d6fc60743dc916eb79e2eb1ec4818e21e427731543af40a3021851174a13 --hash=sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685 --hash=sha256:e4e81e09c1578b8df602e3db08b0b3ea0a6947ad612f52bf8dc5ea8d47691f0c"), + ("my_project_pip_deps_vendored_charset_normalizer", "charset-normalizer==3.5.2 --hash=sha256:0521c5665880b33d603717defa76c094048900010897909952397feb3039da56 --hash=sha256:1c50fe28bbc2ced33386f298650d91218076c05420e6cbd790b913adc41659e7 --hash=sha256:4275811936e2f06feff5e598fb42a1b7ae852da8e39605211892b56b81a34efd --hash=sha256:4d48f2d08b9de5864e2c8744d4461b862fb149a18274abc8b698c45975573438 --hash=sha256:8024d00c3faf3fc0c16e07a69f4405e8eac7cc0ab15f65fe6cf43827c4cf72b4 --hash=sha256:8d86d6fc60743dc916eb79e2eb1ec4818e21e427731543af40a3021851174a13 --hash=sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685 --hash=sha256:e4e81e09c1578b8df602e3db08b0b3ea0a6947ad612f52bf8dc5ea8d47691f0c"), ("my_project_pip_deps_vendored_idna", "idna==3.20"), ("my_project_pip_deps_vendored_requests", "requests==2.34.2"), ("my_project_pip_deps_vendored_urllib3", "urllib3==2.8.0"), From 787f9380012c05923be0afc5ce63a45a8fcd9239 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:58:56 +0900 Subject: [PATCH 29/39] fix: use CreateOrRaise for the runfiles in the coverage update script --- dev/update_deps/update_coverage_workspace_deps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/update_deps/update_coverage_workspace_deps.py b/dev/update_deps/update_coverage_workspace_deps.py index 6b1e7aaa51..ccf19d61d6 100644 --- a/dev/update_deps/update_coverage_workspace_deps.py +++ b/dev/update_deps/update_coverage_workspace_deps.py @@ -12,7 +12,7 @@ def main() -> None: - r = runfiles.Create() + r = runfiles.CreateOrRaise() src = pathlib.Path(r.Rlocation(os.environ["GENERATED"])) dst = ( pathlib.Path(os.environ["BUILD_WORKSPACE_DIRECTORY"]) From 472bde067a659b4d5db6c2f7258f500b10bdc46d Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:58:57 +0900 Subject: [PATCH 30/39] fix(toolchain): do not load whl_library when registering WORKSPACE toolchains The `python/repositories.bzl` file is loaded before `py_repositories` defines the `rules_python_internal` repository, which `whl_library` depends on, so loading it created a cycle in the WORKSPACE file. Use `http_archive` with a `py_library` BUILD file for the coverage wheels instead, which only depends on `bazel_tools`. --- python/private/coverage/BUILD.bazel | 1 - .../private/coverage/coverage_workspace.bzl | 38 +++++++++++++------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/python/private/coverage/BUILD.bazel b/python/private/coverage/BUILD.bazel index 81b82a1475..e8b2464fa2 100644 --- a/python/private/coverage/BUILD.bazel +++ b/python/private/coverage/BUILD.bazel @@ -21,7 +21,6 @@ bzl_library( "//python/private:bazel_tools", "//python/private:text_util", "//python/private/pypi:default_platforms", - "//python/private/pypi:whl_library", ], ) diff --git a/python/private/coverage/coverage_workspace.bzl b/python/private/coverage/coverage_workspace.bzl index 6b4abbf97b..3dcd11f4d2 100644 --- a/python/private/coverage/coverage_workspace.bzl +++ b/python/private/coverage/coverage_workspace.bzl @@ -11,10 +11,10 @@ is a target for each Python version in the hub, which only selects the wheel based on the target platform. """ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") load("//python/private:text_util.bzl", "render") load("//python/private/pypi:default_platforms.bzl", "default_platforms") -load("//python/private/pypi:whl_library.bzl", "whl_library") load(":workspace_deps.bzl", "COVERAGE_TOOLS", "COVERAGE_WHEELS") _HUB_NAME = "rules_python_coverage" @@ -37,8 +37,6 @@ def _coverage_hub_repo_impl(rctx): "", """package(default_visibility = ["//visibility:public"])""", "", - """exports_files(["config.bzl"])""", - "", ] + [ render.call( "selects.config_setting_group", @@ -48,8 +46,6 @@ def _coverage_hub_repo_impl(rctx): for p in used_platforms ])) - rctx.file("config.bzl", "packages = {\"coverage\": None}\n") - rctx.file("coverage/BUILD.bazel", "\n".join([ """package(default_visibility = ["//visibility:public"])""", "", @@ -86,6 +82,25 @@ coverage_hub_repo = repository_rule( doc = "The hub repository for the coverage tool of the WORKSPACE toolchains.", ) +# The BUILD file for the extracted wheel. The `whl_library` is not used, +# because this is loaded before `py_repositories` defines the repositories that +# it needs. +_WHL_BUILD_FILE = """\ +load("{py_library}", "py_library") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "pkg", + srcs = glob(["coverage/**/*.py"]), + data = glob( + ["coverage/**"], + exclude = ["coverage/**/*.py"], + ), + imports = ["."], +) +""" + def coverage_tool(python_version): """Create the coverage repositories and return the coverage tool label. @@ -100,16 +115,17 @@ def coverage_tool(python_version): if major_minor not in COVERAGE_TOOLS: return None + build_file_content = _WHL_BUILD_FILE.format( + py_library = str(Label("//python:py_library.bzl")), + ) for name, whl in COVERAGE_WHEELS.items(): maybe( - whl_library, + http_archive, name = _wheel_repo_name(name), - requirement = whl["requirement"], - urls = [whl["url"]], - filename = whl["filename"], + build_file_content = build_file_content, integrity = whl["integrity"], - dep_template = "@{}//{{name}}:{{target}}".format(_HUB_NAME), - config_load = "@{}//:config.bzl".format(_HUB_NAME), + type = "zip", + urls = [whl["url"]], ) maybe( From d5ca45b9f5a7424f1ebce7fd7b670e5c696d84cf Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 22:09:45 +0900 Subject: [PATCH 31/39] fix: handle a missing runfile in the coverage update script --- dev/update_deps/update_coverage_workspace_deps.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/update_deps/update_coverage_workspace_deps.py b/dev/update_deps/update_coverage_workspace_deps.py index ccf19d61d6..41a51ca204 100644 --- a/dev/update_deps/update_coverage_workspace_deps.py +++ b/dev/update_deps/update_coverage_workspace_deps.py @@ -13,7 +13,10 @@ def main() -> None: r = runfiles.CreateOrRaise() - src = pathlib.Path(r.Rlocation(os.environ["GENERATED"])) + generated = r.Rlocation(os.environ["GENERATED"]) + if not generated: + raise FileNotFoundError(os.environ["GENERATED"]) + src = pathlib.Path(generated) dst = ( pathlib.Path(os.environ["BUILD_WORKSPACE_DIRECTORY"]) / "python/private/coverage/workspace_deps.bzl" From 84d0f4c084aa050f9bebd9d61540cf7edaf9a48c Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 22:53:03 +0900 Subject: [PATCH 32/39] feat(uv): add the vendor_uv_lock macro WORKSPACE macros cannot read files, so add a `vendor_uv_lock` macro that vendors a `uv.lock` file as a JSON string constant in a `.bzl` file. Only the fields that are needed for selecting the distributions are kept and each package and distribution is on its own line, so the diffs are small. The macro takes the name of the `lock` target that updates the `uv.lock` file and creates `.update` and `.run` targets, which run the same targets of the `lock` first and then vendor the updated `uv.lock`, so a single command updates both files. The `diff_test` only depends on the `uv.lock` file and does not need the network. --- docs/BUILD.bazel | 1 + news/4457.added.md | 6 + python/uv/BUILD.bazel | 6 + python/uv/private/BUILD.bazel | 36 ++++++ python/uv/private/vendor_uv_lock.bzl | 124 ++++++++++++++++++++ python/uv/private/vendor_uv_lock.py | 164 +++++++++++++++++++++++++++ python/uv/vendor_uv_lock.bzl | 9 ++ 7 files changed, 346 insertions(+) create mode 100644 news/4457.added.md create mode 100644 python/uv/private/vendor_uv_lock.bzl create mode 100644 python/uv/private/vendor_uv_lock.py create mode 100644 python/uv/vendor_uv_lock.bzl diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel index 3c44bd60b7..72e9858957 100644 --- a/docs/BUILD.bazel +++ b/docs/BUILD.bazel @@ -163,6 +163,7 @@ sphinx_stardocs( "//python/uv:repositories", "//python/uv:uv_toolchain", "//python/uv:uv_toolchain_info", + "//python/uv:vendor_uv_lock", "//python/zipapp:py_zipapp_binary", "//python/zipapp:py_zipapp_test", ] + ([ diff --git a/news/4457.added.md b/news/4457.added.md new file mode 100644 index 0000000000..18b71b3077 --- /dev/null +++ b/news/4457.added.md @@ -0,0 +1,6 @@ +(uv) Added the {obj}`vendor_uv_lock` macro, which vendors the needed subset of +a `uv.lock` file as a JSON string constant in a `.bzl` file, so that WORKSPACE +macros can use it. Its `.update` and `.run` targets first update the `uv.lock` +using the given {obj}`lock` target, so a single command updates both files, +and its `diff_test` checks that the vendored file is up to date without +network access. diff --git a/python/uv/BUILD.bazel b/python/uv/BUILD.bazel index e2024650bd..6a497e4f56 100644 --- a/python/uv/BUILD.bazel +++ b/python/uv/BUILD.bazel @@ -41,6 +41,12 @@ bzl_library( deps = ["//python/uv/private:lock"], ) +bzl_library( + name = "vendor_uv_lock", + srcs = ["vendor_uv_lock.bzl"], + deps = ["//python/uv/private:vendor_uv_lock"], +) + bzl_library( name = "repositories", srcs = ["repositories.bzl"], diff --git a/python/uv/private/BUILD.bazel b/python/uv/private/BUILD.bazel index e4e652ae78..4ca58b147b 100644 --- a/python/uv/private/BUILD.bazel +++ b/python/uv/private/BUILD.bazel @@ -13,6 +13,8 @@ # limitations under the License. load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("//python:py_binary.bzl", "py_binary") +load("//python:py_library.bzl", "py_library") load("//python/private:distribution_filegroup.bzl", "distribution_filegroup") package(default_visibility = ["//:__subpackages__"]) @@ -24,6 +26,40 @@ distribution_filegroup( name = "distribution", ) +# The `vendor_uv_lock` macro uses these in the packages it is called in. +exports_files( + ["vendor_uv_lock.py"], + visibility = _NOT_REALLY_PUBLIC, +) + +py_library( + name = "vendor_uv_lock_lib", + srcs = ["vendor_uv_lock.py"], + visibility = _NOT_REALLY_PUBLIC, + deps = [ + # Only used with Python < 3.11, which does not have `tomllib`. + "@pypi__tomli//:lib", + "//python/runfiles", + ], +) + +py_binary( + name = "vendor_uv_lock_tool", + srcs = ["vendor_uv_lock.py"], + main = "vendor_uv_lock.py", + visibility = _NOT_REALLY_PUBLIC, + deps = [":vendor_uv_lock_lib"], +) + +bzl_library( + name = "vendor_uv_lock", + srcs = ["vendor_uv_lock.bzl"], + deps = [ + "//python:py_binary", + "@bazel_skylib//rules:diff_test", + ], +) + filegroup( name = "lock_copier_template", srcs = ["template/lock_copier.py"], diff --git a/python/uv/private/vendor_uv_lock.bzl b/python/uv/private/vendor_uv_lock.bzl new file mode 100644 index 0000000000..1e9d95d6a3 --- /dev/null +++ b/python/uv/private/vendor_uv_lock.bzl @@ -0,0 +1,124 @@ +"""Implementation of the `vendor_uv_lock` macro.""" + +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") +load("//python:py_binary.bzl", "py_binary") + +_TOOL = Label("//python/uv/private:vendor_uv_lock.py") + +def _vendor_uv_lock_impl(ctx): + out = ctx.actions.declare_file(ctx.label.name + ".bzl") + args = ctx.actions.args() + args.add("build") + args.add("--src", ctx.file.src) + args.add("--out", out) + args.add("--constant", ctx.attr.constant) + args.add("--src_label", ctx.attr.src_label) + args.add("--update_target", ctx.attr.update_target) + ctx.actions.run( + executable = ctx.executable._tool, + arguments = [args], + inputs = [ctx.file.src], + outputs = [out], + mnemonic = "PyVendorUvLock", + progress_message = "Vendoring %{input} as a .bzl file", + ) + return [DefaultInfo(files = depset([out]))] + +_vendor_uv_lock = rule( + implementation = _vendor_uv_lock_impl, + attrs = { + "constant": attr.string(mandatory = True), + "src": attr.label(mandatory = True, allow_single_file = [".lock"]), + "src_label": attr.string(mandatory = True), + "update_target": attr.string(mandatory = True), + "_tool": attr.label( + default = "//python/uv/private:vendor_uv_lock_tool", + executable = True, + cfg = "exec", + ), + }, + doc = "Vendor a `uv.lock` file as a `.bzl` file in a build action.", +) + +def vendor_uv_lock( + *, + name, + lock, + src = "uv.lock", + out = "uv_lock.bzl", + constant = "UV_LOCK_JSON", + **kwargs): + """Vendor a `uv.lock` file as a JSON string constant in a `.bzl` file. + + WORKSPACE macros cannot read files, so this makes the `uv.lock` file + available to them, e.g. `json.decode(UV_LOCK_JSON)`. The vendored data is + a subset of the `uv.lock` file, which has only the fields that are needed + for selecting the distributions, with one package and one distribution + per line. + + This macro creates the following targets: + - `name`: the `.bzl` file vendored from `src` in a build action. + - `name.update`: runs the `.update` target of the {obj}`lock` and then + updates `out` in the source tree, so a single command updates both + files. + - `name.run`: the same, but runs the `.run` target of the {obj}`lock` + and passes the args to it. + - `name_test`: a `diff_test` that `out` is up to date. It only depends on + `src`, so it does not need the network. + + Args: + name: {type}`str` the name of the targets. + lock: {type}`str` the name of the {obj}`lock` target in the same + package that updates `src`, e.g. `requirements` for + `lock(name = "requirements")`. + src: {type}`str` the `uv.lock` file, relative to the package. + out: {type}`str` the vendored `.bzl` file, relative to the package. + constant: {type}`str` the name of the JSON string constant. + **kwargs: common kwargs passed to the rules. + + :::{versionadded} VERSION_NEXT_FEATURE + ::: + """ + pkg = native.package_name() + prefix = pkg + "/" if pkg else "" + update_target = "//{}:{}.update".format(pkg, name) + common_args = [ + "--src={}{}".format(prefix, src), + "--out={}{}".format(prefix, out), + "--constant={}".format(constant), + "--src_label=//{}:{}".format(pkg, src), + "--update_target={}".format(update_target), + ] + + _vendor_uv_lock( + name = name, + src = src, + constant = constant, + src_label = "//{}:{}".format(pkg, src), + update_target = update_target, + **kwargs + ) + + tags = ["manual"] + kwargs.pop("tags", []) + for suffix in ["update", "run"]: + runner = ":{}.{}".format(lock, suffix) + py_binary( + name = "{}.{}".format(name, suffix), + srcs = [_TOOL], + main = _TOOL, + args = ["update"] + common_args + [ + "--lock_runner=$(rlocationpath {})".format(runner), + ], + data = [runner], + deps = [Label("//python/uv/private:vendor_uv_lock_lib")], + tags = tags, + **kwargs + ) + + diff_test( + name = name + "_test", + file1 = out, + file2 = name, + failure_message = "Run `bazel run {}`".format(update_target), + **kwargs + ) diff --git a/python/uv/private/vendor_uv_lock.py b/python/uv/private/vendor_uv_lock.py new file mode 100644 index 0000000000..be2ec139d4 --- /dev/null +++ b/python/uv/private/vendor_uv_lock.py @@ -0,0 +1,164 @@ +"""Vendor a `uv.lock` file as a JSON string constant in a `.bzl` file. + +The vendored data is a subset of the `uv.lock` file, which has only the fields +that are needed for selecting the distributions. It is rendered with one +package and one distribution per line, so that the diffs are small and easy to +review. + +Usage: + vendor_uv_lock.py build --src uv.lock --out out.bzl ... + vendor_uv_lock.py update --src pkg/uv.lock --out pkg/uv_lock.bzl ... +""" + +import argparse +import json +import os +import pathlib +import subprocess +import sys + +try: + import tomllib +except ImportError: # Python < 3.11 + import tomli as tomllib # type: ignore[no-redef] + +# The keys that are kept for each package, dependency and distribution. +_PACKAGE_KEYS = ("name", "version", "source", "resolution-markers", "provides-extras") +_DEPENDENCY_KEYS = ("extra", "marker", "name", "source", "version") +_DEPENDENCY_TABLES = ("optional-dependencies", "dev-dependencies") +_DISTRIBUTION_KEYS = ("hash", "url") + +_HEADER = '''\ +"""The vendored `{src}` file. + +@generated by `bazel run {update_target}`. DO NOT EDIT. +""" + +''' + + +def _pick(d: dict, keys: tuple) -> dict: + return {k: d[k] for k in keys if k in d} + + +def trim(lock: dict) -> list: + """Return the packages of the lock file with only the needed fields.""" + packages = [] + for pkg in lock.get("package", []): + out = _pick(pkg, _PACKAGE_KEYS) + if pkg.get("dependencies"): + out["dependencies"] = [ + _pick(d, _DEPENDENCY_KEYS) for d in pkg["dependencies"] + ] + for table in _DEPENDENCY_TABLES: + if pkg.get(table): + out[table] = { + group: [_pick(d, _DEPENDENCY_KEYS) for d in deps] + for group, deps in pkg[table].items() + } + + # Only the requirements with extras are used. + requires_dist = [ + _pick(rd, ("extras", "name")) + for rd in pkg.get("metadata", {}).get("requires-dist", []) + if rd.get("extras") + ] + if requires_dist: + out["metadata"] = {"requires-dist": requires_dist} + if pkg.get("sdist"): + out["sdist"] = _pick(pkg["sdist"], _DISTRIBUTION_KEYS) + if pkg.get("wheels"): + out["wheels"] = [_pick(w, _DISTRIBUTION_KEYS) for w in pkg["wheels"]] + packages.append(out) + return packages + + +def render(packages: list, *, constant: str, src: str, update_target: str) -> str: + """Render the packages as a JSON string constant. + + Each package is on its own line and each wheel is on its own line, so that + updating a package only changes the lines of that package. + """ + lines = [] + for i, pkg in enumerate(packages): + sep = "," if i + 1 < len(packages) else "" + wheels = pkg.get("wheels", []) + head = {k: v for k, v in pkg.items() if k != "wheels"} + head_json = json.dumps(head, sort_keys=True) + if not wheels: + lines.append(head_json + sep) + continue + + lines.append(head_json[:-1] + ', "wheels": [') + for j, wheel in enumerate(wheels): + wheel_sep = "," if j + 1 < len(wheels) else "" + lines.append(" " + json.dumps(wheel, sort_keys=True) + wheel_sep) + lines.append("]}" + sep) + + body = "\n".join(lines) + return ( + _HEADER.format(src=src, update_target=update_target) + + f'{constant} = r"""{{"package": [\n{body}\n]}}\n"""\n' + ) + + +def vendor(src: pathlib.Path, out: pathlib.Path, args: argparse.Namespace) -> None: + lock = tomllib.loads(src.read_text()) + out.write_text( + render( + trim(lock), + constant=args.constant, + src=args.src_label, + update_target=args.update_target, + ) + ) + + +def _find_runfile(path: str) -> str: + from python.runfiles import runfiles + + r = runfiles.CreateOrRaise() + found = r.Rlocation(path) + if not found: + raise FileNotFoundError(path) + return found + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("mode", choices=["build", "update"]) + parser.add_argument("--src", required=True, help="The uv.lock file.") + parser.add_argument("--out", required=True, help="The .bzl file to write.") + parser.add_argument("--constant", required=True) + parser.add_argument("--src_label", required=True) + parser.add_argument("--update_target", required=True) + parser.add_argument( + "--lock_runner", + help="The runfiles path of the lock target to run before vendoring.", + ) + args, lock_args = parser.parse_known_args() + + if args.mode == "build": + vendor(pathlib.Path(args.src), pathlib.Path(args.out), args) + return 0 + + if "BUILD_WORKSPACE_DIRECTORY" not in os.environ: + raise RuntimeError("The update mode must be run via `bazel run`") + + if args.lock_runner: + # Update the uv.lock first, so that the vendored file is created from + # the updated lock file. + subprocess.run( + [_find_runfile(args.lock_runner)] + lock_args, + check=True, + ) + + workspace = pathlib.Path(os.environ["BUILD_WORKSPACE_DIRECTORY"]) + out = workspace / args.out + vendor(workspace / args.src, out, args) + print(f"OK: updated {out}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/python/uv/vendor_uv_lock.bzl b/python/uv/vendor_uv_lock.bzl new file mode 100644 index 0000000000..a3a496fc79 --- /dev/null +++ b/python/uv/vendor_uv_lock.bzl @@ -0,0 +1,9 @@ +"""Vendor a `uv.lock` file as a `.bzl` file, e.g. to use it in WORKSPACE macros. + +:::{versionadded} VERSION_NEXT_FEATURE +::: +""" + +load("//python/uv/private:vendor_uv_lock.bzl", _vendor_uv_lock = "vendor_uv_lock") + +vendor_uv_lock = _vendor_uv_lock From 9b0c9c8118fbd5fe03f394eabc44d207a71a4adb Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 22:54:32 +0900 Subject: [PATCH 33/39] refactor(toolchain): select the WORKSPACE coverage wheels from the vendored uv.lock Replace the dev-only repository rule, which generated the wheel table, with the `vendor_uv_lock` macro. The WORKSPACE toolchains now decode the vendored `uv.lock` and select the wheels with the same code as `pip.parse` in a pure `select_wheels` function, which has unit tests. The `coverage_repositories` macro gets its params from the vendored JSON constant and only selects the wheels once, as it is called for each toolchain platform. `bazel run //python/private/coverage:uv_lock_bzl.update` now updates both the `uv.lock` and the vendored file, and it is part of `bazel run //dev:all_requirements.update`. --- MODULE.bazel | 1 - dev/BUILD.bazel | 2 + dev/update_deps/BUILD.bazel | 13 - .../update_coverage_workspace_deps.py | 29 - docs/devguide.md | 7 +- python/private/coverage/BUILD.bazel | 37 +- .../private/coverage/coverage_workspace.bzl | 202 ++++- python/private/coverage/uv_lock.bzl | 356 ++++++++ python/private/coverage/workspace_deps.bzl | 757 ------------------ .../private/coverage/workspace_deps_gen.bzl | 119 --- python/private/internal_dev_deps.bzl | 7 - tests/coverage/BUILD.bazel | 14 +- tests/coverage/coverage_workspace_tests.bzl | 113 +++ 13 files changed, 665 insertions(+), 992 deletions(-) delete mode 100644 dev/update_deps/update_coverage_workspace_deps.py create mode 100644 python/private/coverage/uv_lock.bzl delete mode 100755 python/private/coverage/workspace_deps.bzl delete mode 100644 python/private/coverage/workspace_deps_gen.bzl create mode 100644 tests/coverage/coverage_workspace_tests.bzl diff --git a/MODULE.bazel b/MODULE.bazel index ffb71b4727..8caedea391 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -389,7 +389,6 @@ use_repo( "patch_whl_pkg", "pkgutil_nspkg1", "pkgutil_nspkg2", - "rules_python_coverage_workspace_deps", "rules_python_runtime_env_tc_info", "somepkg_with_build_files", "whl_library_extras_direct_dep", diff --git a/dev/BUILD.bazel b/dev/BUILD.bazel index a5e3661ead..ba5ae0bf26 100644 --- a/dev/BUILD.bazel +++ b/dev/BUILD.bazel @@ -29,6 +29,8 @@ multirun( "//examples:bzlmod_other_module_uv_lock.update", "//examples:bzlmod_uv_lock.update", "//examples/wheel:requirements_server.update", + # Updates the uv.lock and then the vendored uv_lock.bzl. + "//python/private/coverage:uv_lock_bzl.update", "//tests/multi_pypi/alpha:requirements.update", "//tests/multi_pypi/beta:requirements.update", "//tests/multiple_inputs:multiple_inputs.update", diff --git a/dev/update_deps/BUILD.bazel b/dev/update_deps/BUILD.bazel index be3669690d..4866a3df66 100644 --- a/dev/update_deps/BUILD.bazel +++ b/dev/update_deps/BUILD.bazel @@ -31,19 +31,6 @@ py_library( imports = ["../.."], ) -py_binary( - name = "update_coverage_workspace_deps", - srcs = ["update_coverage_workspace_deps.py"], - data = ["@rules_python_coverage_workspace_deps//:workspace_deps.bzl"], - env = { - "GENERATED": "$(rlocationpath @rules_python_coverage_workspace_deps//:workspace_deps.bzl)", - }, - imports = ["../.."], - # The file is generated with bzlmod. - target_compatible_with = SUPPORTS_BZLMOD, - deps = ["//python/runfiles"], -) - py_binary( name = "update_pip_deps", srcs = ["update_pip_deps.py"], diff --git a/dev/update_deps/update_coverage_workspace_deps.py b/dev/update_deps/update_coverage_workspace_deps.py deleted file mode 100644 index 41a51ca204..0000000000 --- a/dev/update_deps/update_coverage_workspace_deps.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Update the generated `workspace_deps.bzl` file for the coverage tool. - -Usage: - bazel run //dev/update_deps:update_coverage_workspace_deps -""" - -import os -import pathlib -import shutil - -from python.runfiles import runfiles - - -def main() -> None: - r = runfiles.CreateOrRaise() - generated = r.Rlocation(os.environ["GENERATED"]) - if not generated: - raise FileNotFoundError(os.environ["GENERATED"]) - src = pathlib.Path(generated) - dst = ( - pathlib.Path(os.environ["BUILD_WORKSPACE_DIRECTORY"]) - / "python/private/coverage/workspace_deps.bzl" - ) - shutil.copyfile(src, dst) - print(f"Updated {dst}") - - -if __name__ == "__main__": - main() diff --git a/docs/devguide.md b/docs/devguide.md index 0155937b3a..9d327b75de 100644 --- a/docs/devguide.md +++ b/docs/devguide.md @@ -101,11 +101,10 @@ integration test. ``` bazel run //dev:all_requirements.update ``` -1. Bump the coverage dependencies and update the file that the WORKSPACE - toolchains use: +1. Bump the coverage dependencies and the vendored `uv_lock.bzl` file that + the WORKSPACE toolchains use: ``` - bazel run //python/private/coverage:requirements.update - bazel run //dev/update_deps:update_coverage_workspace_deps + bazel run //python/private/coverage:uv_lock_bzl.update ``` ## Updating tool dependencies diff --git a/python/private/coverage/BUILD.bazel b/python/private/coverage/BUILD.bazel index e8b2464fa2..87e96dfc56 100644 --- a/python/private/coverage/BUILD.bazel +++ b/python/private/coverage/BUILD.bazel @@ -1,13 +1,14 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("//python/private:distribution_filegroup.bzl", "distribution_filegroup") load("//python/uv:lock.bzl", "lock") +load("//python/uv:vendor_uv_lock.bzl", "vendor_uv_lock") package(default_visibility = ["//:__subpackages__"]) -exports_files([ - "uv.lock", - "workspace_deps.bzl", -]) +# The `uv_lock.bzl` file is generated by `vendor_uv_lock` below. +# gazelle:exclude uv_lock.bzl + +exports_files(["uv.lock"]) distribution_filegroup( name = "distribution", @@ -17,19 +18,9 @@ bzl_library( name = "coverage_workspace", srcs = ["coverage_workspace.bzl"], deps = [ - ":workspace_deps", - "//python/private:bazel_tools", - "//python/private:text_util", - "//python/private/pypi:default_platforms", - ], -) - -bzl_library( - name = "workspace_deps_gen", - srcs = ["workspace_deps_gen.bzl"], - deps = [ + ":uv_lock", "//python:versions", - "//python/private:repo_utils", + "//python/private:bazel_tools", "//python/private:text_util", "//python/private:version", "//python/private/pypi:default_platforms", @@ -39,7 +30,6 @@ bzl_library( "//python/private/pypi:pep508_evaluate", "//python/private/pypi:platform", "//python/private/pypi:whl_repo_name", - "@toml.bzl//:toml", ], ) @@ -54,7 +44,16 @@ lock( ], ) +# The WORKSPACE toolchains read the `uv.lock` from the vendored `uv_lock.bzl`. +# Run `bazel run //python/private/coverage:uv_lock_bzl.update` to update both. +vendor_uv_lock( + name = "uv_lock_bzl", + out = "uv_lock.bzl", + lock = "requirements", +) + +# keep bzl_library( - name = "workspace_deps", - srcs = ["workspace_deps.bzl"], + name = "uv_lock", + srcs = ["uv_lock.bzl"], ) diff --git a/python/private/coverage/coverage_workspace.bzl b/python/private/coverage/coverage_workspace.bzl index 3dcd11f4d2..8afc9aebe5 100644 --- a/python/private/coverage/coverage_workspace.bzl +++ b/python/private/coverage/coverage_workspace.bzl @@ -2,9 +2,9 @@ The bzlmod toolchains use the `@rules_python_coverage` pip hub, which is created from the `uv.lock` file. The WORKSPACE macros cannot read the -`uv.lock` file, so the selected wheels are stored in the generated -`workspace_deps.bzl` file and this creates an equivalent hub repository from -it. +`uv.lock` file, so it is vendored to the `uv_lock.bzl` file by the +`vendor_uv_lock` macro and this creates an equivalent hub repository from it +with the same wheel selection as the `pip.parse` extension. The WORKSPACE builds do not set the Python version flag by default, so there is a target for each Python version in the hub, which only selects the wheel @@ -13,14 +13,33 @@ based on the target platform. load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("//python:versions.bzl", "MINOR_MAPPING") load("//python/private:text_util.bzl", "render") +load("//python/private:version.bzl", "version") load("//python/private/pypi:default_platforms.bzl", "default_platforms") -load(":workspace_deps.bzl", "COVERAGE_TOOLS", "COVERAGE_WHEELS") +load("//python/private/pypi:hash.bzl", "hash") +load("//python/private/pypi:parse_requirements.bzl", "parse_requirements") +load("//python/private/pypi:pep508_env.bzl", "env") +load("//python/private/pypi:pep508_evaluate.bzl", "evaluate") +load("//python/private/pypi:platform.bzl", "platform") +load("//python/private/pypi:whl_repo_name.bzl", "whl_repo_name") +load(":uv_lock.bzl", "UV_LOCK_JSON") _HUB_NAME = "rules_python_coverage" -def _wheel_repo_name(name): - return "{}_{}".format(_HUB_NAME, name) +def _noop(*_args, **_kwargs): + return None + +# The selection runs in WORKSPACE macros, which have no repository context to +# create a logger from. +_LOGGER = struct( + trace = _noop, + debug = _noop, + info = _noop, + warn = _noop, + fail = lambda message_cb: fail(message_cb()), + failed = lambda: False, +) def _coverage_hub_repo_impl(rctx): platforms = json.decode(rctx.attr.platforms) @@ -54,12 +73,12 @@ def _coverage_hub_repo_impl(rctx): name = python_version, actual = render.select( { - "//:is_" + p: "@{}//:pkg".format(_wheel_repo_name(name)) + "//:is_" + p: "@{}_{}//:pkg".format(rctx.attr.name, name) for p, name in sorted(by_platform.items()) }, no_match_error = repr(( "There is no coverage wheel for Python {} on the target " + - "platform. See the `python/private/coverage/workspace_deps.bzl` " + + "platform. See the `python/private/coverage/uv.lock` " + "file in rules_python for the supported platforms." ).format(python_version)), ), @@ -101,41 +120,160 @@ py_library( ) """ -def coverage_tool(python_version): - """Create the coverage repositories and return the coverage tool label. +def _platforms(python_version, platforms): + """Return the platforms to select the wheels for. + + Args: + python_version: {type}`str` the full Python version. + platforms: {type}`dict[str, dict]` the platform definitions, see + `default_platforms`. + + Returns: + {type}`dict[str, struct]` the platforms. + """ + ver = version.parse(python_version, strict = True) + ret = {} + for name, values in platforms.items(): + p = platform(**values) + env_ = env( + env = p.env, + os = p.os_name, + arch = p.arch_name, + python_version = python_version, + ) + if p.marker and not evaluate(p.marker, env = env_): + continue + + ret[name] = struct( + env = env_, + whl_abi_tags = [ + tag.format(major = ver.release[0], minor = ver.release[1]) + for tag in p.whl_abi_tags + ], + whl_platform_tags = p.whl_platform_tags, + ) + return ret + +def select_wheels(*, uv_lock, minor_mapping, platforms, package = "coverage"): + """Select the wheels of a package with the same selection as `pip.parse`. + + Args: + uv_lock: {type}`dict` the decoded `uv.lock` file. + minor_mapping: {type}`dict[str, str]` the Python versions to select + the wheels for, e.g. `{"3.13": "3.13.1"}`. + platforms: {type}`dict[str, dict]` the platform definitions, see + `default_platforms`. + package: {type}`str` the name of the package to select the wheels for. + + Returns: + {type}`struct` with `wheels`, a dict of repository name suffixes to + the wheel `filename`, `integrity` and `url`, and `tools`, a dict of + `major.minor` versions to platform names to the repository name + suffixes. + """ + wheels = {} + tools = {} + for minor_version, full_version in sorted( + minor_mapping.items(), + key = lambda x: version.parse(x[0]).key(), + ): + requirements = parse_requirements( + # The lock file is already decoded, so there is nothing to read. + struct(read = lambda _: ""), + platforms = _platforms(full_version, platforms), + uv_lock = "uv.lock", + toml_decode = lambda _: uv_lock, + logger = _LOGGER, + ) + for whl in requirements: + if whl.name != package: + continue + + for src in whl.srcs: + if not src.filename.endswith(".whl"): + continue + + name = whl_repo_name(src.filename, src.digest) + wheels[name] = { + "filename": src.filename, + "integrity": hash.integrity(src.digest), + "url": src.url, + } + for p in src.target_platforms: + tools.setdefault(minor_version, {})[p] = name + + return struct(wheels = wheels, tools = tools) + +def coverage_repositories(*, name, uv_lock, python_version, minor_mapping = MINOR_MAPPING, platforms = None): + """Create the coverage wheel repositories and the hub repository. Args: + name: {type}`str` the name of the hub repository. + uv_lock: {type}`dict` the decoded `uv.lock` file. python_version: {type}`str` the full Python version of the toolchain. + minor_mapping: {type}`dict[str, str]` the Python versions to create + the coverage tool for. + platforms: {type}`dict[str, dict] | None` the platform definitions, + defaults to `default_platforms`. Returns: {type}`str | None` the label of the coverage tool or `None` if there are no coverage wheels for the Python version. """ major_minor = ".".join(python_version.split(".")[:2]) - if major_minor not in COVERAGE_TOOLS: - return None - build_file_content = _WHL_BUILD_FILE.format( - py_library = str(Label("//python:py_library.bzl")), - ) - for name, whl in COVERAGE_WHEELS.items(): - maybe( - http_archive, - name = _wheel_repo_name(name), - build_file_content = build_file_content, - integrity = whl["integrity"], - type = "zip", - urls = [whl["url"]], + # This is called for each toolchain platform, so only select the wheels + # once. + existing = native.existing_rule(name) + if existing: + tools = json.decode(existing["tools"]) + else: + platforms = platforms or default_platforms() + selected = select_wheels( + uv_lock = uv_lock, + minor_mapping = minor_mapping, + platforms = platforms, + ) + tools = selected.tools + build_file_content = _WHL_BUILD_FILE.format( + py_library = str(Label("//python:py_library.bzl")), ) + for repo, whl in selected.wheels.items(): + maybe( + http_archive, + name = "{}_{}".format(name, repo), + build_file_content = build_file_content, + integrity = whl["integrity"], + type = "zip", + urls = [whl["url"]], + ) - maybe( - coverage_hub_repo, + coverage_hub_repo( + name = name, + platforms = json.encode({ + p: [str(Label(setting)) for setting in values["config_settings"]] + for p, values in platforms.items() + }), + tools = json.encode(tools), + ) + + if major_minor not in tools: + return None + + return "@{}//coverage:{}".format(name, major_minor) + +def coverage_tool(python_version): + """Create the coverage repositories and return the coverage tool label. + + Args: + python_version: {type}`str` the full Python version of the toolchain. + + Returns: + {type}`str | None` the label of the coverage tool or `None` if there + are no coverage wheels for the Python version. + """ + return coverage_repositories( name = _HUB_NAME, - platforms = json.encode({ - name: [str(Label(setting)) for setting in values["config_settings"]] - for name, values in default_platforms().items() - }), - tools = json.encode(COVERAGE_TOOLS), + uv_lock = json.decode(UV_LOCK_JSON), + python_version = python_version, ) - - return "@{}//coverage:{}".format(_HUB_NAME, major_minor) diff --git a/python/private/coverage/uv_lock.bzl b/python/private/coverage/uv_lock.bzl new file mode 100644 index 0000000000..c73b05ce5d --- /dev/null +++ b/python/private/coverage/uv_lock.bzl @@ -0,0 +1,356 @@ +"""The vendored `//python/private/coverage:uv.lock` file. + +@generated by `bazel run //python/private/coverage:uv_lock_bzl.update`. DO NOT EDIT. +""" + +UV_LOCK_JSON = r"""{"package": [ +{"name": "coverage", "resolution-markers": ["python_full_version < '3.10'"], "sdist": {"hash": "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", "url": "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz"}, "source": {"registry": "https://pypi.org/simple"}, "version": "7.10.7", "wheels": [ + {"hash": "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", "url": "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl"}, + {"hash": "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", "url": "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl"}, + {"hash": "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", "url": "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", "url": "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", "url": "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", "url": "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", "url": "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", "url": "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl"}, + {"hash": "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", "url": "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", "url": "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", "url": "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl"}, + {"hash": "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", "url": "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl"}, + {"hash": "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", "url": "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl"}, + {"hash": "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", "url": "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl"}, + {"hash": "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", "url": "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", "url": "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", "url": "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", "url": "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", "url": "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", "url": "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl"}, + {"hash": "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", "url": "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", "url": "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", "url": "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl"}, + {"hash": "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", "url": "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl"}, + {"hash": "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", "url": "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl"}, + {"hash": "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", "url": "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", "url": "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl"}, + {"hash": "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", "url": "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", "url": "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", "url": "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", "url": "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", "url": "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", "url": "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl"}, + {"hash": "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", "url": "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", "url": "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", "url": "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl"}, + {"hash": "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", "url": "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl"}, + {"hash": "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", "url": "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl"}, + {"hash": "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", "url": "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", "url": "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl"}, + {"hash": "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", "url": "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", "url": "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", "url": "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", "url": "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", "url": "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", "url": "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl"}, + {"hash": "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", "url": "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", "url": "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", "url": "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl"}, + {"hash": "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", "url": "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl"}, + {"hash": "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", "url": "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl"}, + {"hash": "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", "url": "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", "url": "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl"}, + {"hash": "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", "url": "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", "url": "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", "url": "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", "url": "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", "url": "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", "url": "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl"}, + {"hash": "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", "url": "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", "url": "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", "url": "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl"}, + {"hash": "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", "url": "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl"}, + {"hash": "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", "url": "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl"}, + {"hash": "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", "url": "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", "url": "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl"}, + {"hash": "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", "url": "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", "url": "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", "url": "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", "url": "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", "url": "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", "url": "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl"}, + {"hash": "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", "url": "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", "url": "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", "url": "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl"}, + {"hash": "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", "url": "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl"}, + {"hash": "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", "url": "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl"}, + {"hash": "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", "url": "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", "url": "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl"}, + {"hash": "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", "url": "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", "url": "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", "url": "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", "url": "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", "url": "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", "url": "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl"}, + {"hash": "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", "url": "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", "url": "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", "url": "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl"}, + {"hash": "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", "url": "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl"}, + {"hash": "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", "url": "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl"}, + {"hash": "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", "url": "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl"}, + {"hash": "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", "url": "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl"}, + {"hash": "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", "url": "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40", "url": "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594", "url": "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a", "url": "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b", "url": "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3", "url": "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl"}, + {"hash": "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0", "url": "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f", "url": "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431", "url": "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl"}, + {"hash": "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07", "url": "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl"}, + {"hash": "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", "url": "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl"} +]}, +{"name": "coverage", "resolution-markers": ["python_full_version == '3.10.*'"], "sdist": {"hash": "sha256:ca64d9f1f384f151b9511bec01126072acd2f313439f8ed015a22d8790aab6fa", "url": "https://files.pythonhosted.org/packages/2f/55/d1eaf3e73781174340a00dc1ba2aee8a65f82fadb18e2797b192b6b3925b/coverage-7.16.2.tar.gz"}, "source": {"registry": "https://pypi.org/simple"}, "version": "7.16.2", "wheels": [ + {"hash": "sha256:23219888477edd736b6fcaec1272d47d93b926e999641ffea7e53a1738e70b2b", "url": "https://files.pythonhosted.org/packages/60/72/db17bf5f87568ab524413693385be2eeb03e652ab56c54ea05fa85515675/coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl"}, + {"hash": "sha256:40c0f00899fe6181ae7f434ceb200e51f5ee4b8ed10e3b5f0b605f0cae15da87", "url": "https://files.pythonhosted.org/packages/d1/e1/285727a8a74d48de256e8605413ee4cf82301228253ea58e5ab4270138b0/coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl"}, + {"hash": "sha256:a4624f80732f6b427ac58f1f59c577a0994a12e8174b5af6a027b4b58795d4c3", "url": "https://files.pythonhosted.org/packages/50/c7/c737b73bac9bf5034f5ff45a4237b7a189d417a4e36751faf5d10c082cbe/coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:191803c4996b499fcd78c2ad5e5f767dcc53cb4dc6de6d6a741b443a1821ef02", "url": "https://files.pythonhosted.org/packages/29/4e/e1d38d27817d91776ca543d01276a67eace3df675a673cac32b12c167d58/coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:9fd670ac43b709c575aefc25bf52d8a598a3bc5017bddfd0a179152ab06a2deb", "url": "https://files.pythonhosted.org/packages/0e/19/14a8e44cbb2ad36ae62aaa03c5eec4a06a7f7df220ea3776af26927acbe2/coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:705e5af11d34647efdc170c7840b6857c81cf74be96419a553f237e68e62cb72", "url": "https://files.pythonhosted.org/packages/f9/72/f5bcad0d9a9b450080032344fbff7ec60c1b0e3d38019f7735dee2b69645/coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:8afd9bf35cc6a1f22eb3634808fa8e0b91902459c5721ef2e4461dfe771d7f08", "url": "https://files.pythonhosted.org/packages/ae/9a/8c735234e8abb52bf5d063f98c780fa942e77c4c0225f9fcb9b33effc346/coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:3f43bac1856ba269b905302778d4df433d6006489a192174ad77ac528e395032", "url": "https://files.pythonhosted.org/packages/35/6a/1bf6d32e55642d6972aa842640e0a8850e612d19ac7d05e2c32fa59dfd34/coverage-7.16.2-cp310-cp310-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:f8475460aa33ee28ac896ab1156d0bb3b6c639f7f8383c2677d3359eb35f8205", "url": "https://files.pythonhosted.org/packages/53/5b/05b1c0d0e9495cb056155a16066259adc1e935e7411a82a62779bf729975/coverage-7.16.2-cp310-cp310-musllinux_1_2_i686.whl"}, + {"hash": "sha256:d6276d78f6fca7d0ac066d5da4165c5acd07829e8305c2cb900b738fb3a75a72", "url": "https://files.pythonhosted.org/packages/05/33/5bc3db57fc9c56b4ef055725c38c34faa818a27d17d65e251371f7f0e3a0/coverage-7.16.2-cp310-cp310-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:736fde09ea39646d11f8e3b76bd3425c075aa4dd45f24891970bb77c14ff20f5", "url": "https://files.pythonhosted.org/packages/f6/0f/4a5de66daef26eb919213f63b84f45d5065ab2df94cd767fc7c1174ac5e2/coverage-7.16.2-cp310-cp310-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:c85d54e7e8a2ca932fe8399301af9b8d5907ea2a455ffaff6e7d1208db83b943", "url": "https://files.pythonhosted.org/packages/57/d3/84cd6a11e707e422194739e9734d947746efa5e5b3358ff2d601f823ee45/coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:5139009b5efd2194fc168ee9362f0e191ba612ef5d29242f9269c22f9b8f80c7", "url": "https://files.pythonhosted.org/packages/ab/bc/3d84c2e2a95f38e346ce3730d9f0e53114f4e8aace72e6e51e014afb9992/coverage-7.16.2-cp310-cp310-win32.whl"}, + {"hash": "sha256:c3305c38a2fa21a4254f2ace7dd9ef5fc569c9a558b66e7017650b3d637fb95e", "url": "https://files.pythonhosted.org/packages/07/86/31f1f3170571a345ab9d8361a7b2f8e0c5698173fbecd1f7149b6e2089ee/coverage-7.16.2-cp310-cp310-win_amd64.whl"}, + {"hash": "sha256:732d950e51f3ba4fb6209c73250f3e8924fefca42953ee04a9e65d8c02414d7d", "url": "https://files.pythonhosted.org/packages/58/fa/ce3baf63d85b730398d92a7162f486f3a5e4e2cc3382a02488b3943725ba/coverage-7.16.2-cp311-cp311-macosx_10_9_x86_64.whl"}, + {"hash": "sha256:5dca0bb66b4c3d624ba047887bf70270030c150692d543cb501293dc38a9f4b5", "url": "https://files.pythonhosted.org/packages/7a/57/9ba29c2aac7f756d479f03d45762120060f0f988788001001bf36e0e6fca/coverage-7.16.2-cp311-cp311-macosx_11_0_arm64.whl"}, + {"hash": "sha256:af2a2a8c7c74de0559e0c368d94c8def9e16c58faaee33a0bf081057c4227e3b", "url": "https://files.pythonhosted.org/packages/5d/7b/0d6d60906dca7d28cc1e3fce12a9861801c4fbb6cbf220ad78cd059c9467/coverage-7.16.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:db5f8394e17f877a625b257f2ba0ce8e728a499c2c1579ad66220272cd3df510", "url": "https://files.pythonhosted.org/packages/cd/b8/9198b865679379fb165c689c64f6e11105ef380f6bd1c7673e83f73d9f5c/coverage-7.16.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:5b3146d2317c75f70df2509066d979dadd941f7021cdf9b5db4bcd8568258e25", "url": "https://files.pythonhosted.org/packages/98/79/9521462cb6072fe394701bc8974b74afd576c9c9355156c7844e1a86a42b/coverage-7.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:9e1d0ced76318bab499693ff25f64faa343415187cb2e4d7befdfdd391a1cf6a", "url": "https://files.pythonhosted.org/packages/a6/76/8d7d5d633db9fe0f3182fedc731bf09f9bcf2366055735152504ad614677/coverage-7.16.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:af98ad5ed9d6daaca956201e00bb429a7eb2b080426686f70a20353e0f9839f5", "url": "https://files.pythonhosted.org/packages/4e/a7/76cb09c89ba46d74d37428bf93251fc14fb0bbe9e05cc2a5ef61773d318a/coverage-7.16.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:1d56e4d21c56d2046447733f8b118409597db48c01efe898ee9ac24e858ec2d6", "url": "https://files.pythonhosted.org/packages/72/b6/2351c1979aaeb5b4a8091a75b90ca997ad60de36e181ddba267cf61dac97/coverage-7.16.2-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:1d5d0e3b660506fb84f995814e3118a21efdc0c8eb80127da1be627d90093c17", "url": "https://files.pythonhosted.org/packages/0f/f4/ad9a4f8b5cb2d494fa9452b546fe742ed2f9d3847cc14c05e36279a3e649/coverage-7.16.2-cp311-cp311-musllinux_1_2_i686.whl"}, + {"hash": "sha256:17228fbca0f22976f797be94e975dcd237799c657d49551c7de1e0654d1202e9", "url": "https://files.pythonhosted.org/packages/6c/1f/a520470472f3e8b01169bf42162b1470c9ba992230432f62ca36269bf3a0/coverage-7.16.2-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:bc0b0ac781d489304b741269857f1f8338b7a26b1b89c06c0344658001ec0035", "url": "https://files.pythonhosted.org/packages/09/d2/ff26d5938274745855fa61cfcba0245c88ccc10d98d2cbd96064f16cd5a7/coverage-7.16.2-cp311-cp311-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:bf1bd822ec4e387ed245bed0d71151582cf7be9e5309bc4145eefe36083d5878", "url": "https://files.pythonhosted.org/packages/a4/1d/5d832d3b06785d9f53267e4f2724a9f60c312eee6ebed9063a461d0d3b45/coverage-7.16.2-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:7ed238d227e23cc300c3d464babdaf9f6ddc740aa1b15a77ae96136e6a7c4516", "url": "https://files.pythonhosted.org/packages/55/4d/1d33edbc2fcf7d99e384e393e712aa5a2ebbbd8409825357815982207976/coverage-7.16.2-cp311-cp311-win32.whl"}, + {"hash": "sha256:a90700f743e29aa3d75a6ff5f01953176a889c00e526194bc4d281731b88d99d", "url": "https://files.pythonhosted.org/packages/6f/7c/676df4882118756c4f8f560c954eddb93e166d84dda8c5f0b6a829689bde/coverage-7.16.2-cp311-cp311-win_amd64.whl"}, + {"hash": "sha256:a336eec40e3520d369b8a6cdabb4f596e69a8b42927ca074aa1452fed943238a", "url": "https://files.pythonhosted.org/packages/7a/0e/a457f4a461b3c5610d845137fdd45fa465e011a64c25af440518ab1f4e41/coverage-7.16.2-cp311-cp311-win_arm64.whl"}, + {"hash": "sha256:218d742afca2b5ad5ca759e93eddedfbcc6eadf8322f080dcefc40b7bd4e2d48", "url": "https://files.pythonhosted.org/packages/5e/2c/f8296c63c5d542f3d21aed685e56b7031a419037d155bb3382fc0940d249/coverage-7.16.2-cp312-cp312-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:a9a638be322a8d76a41cdb17781c7f82aaee6a66493d8ffb7e2c09ee22423d99", "url": "https://files.pythonhosted.org/packages/90/23/6f3dcb1423a0d43216e402ea1746e4a7c7c44f38896b97dd573790f56a40/coverage-7.16.2-cp312-cp312-macosx_11_0_arm64.whl"}, + {"hash": "sha256:724bd0f1e81856b35e59fc98cf7b4e544a3cb662e4e0864dca73d4326ee9d808", "url": "https://files.pythonhosted.org/packages/ac/7d/8f3b6dc920e3fc6732f7678785a2091db439f186afbec30dbf2214d9b1f7/coverage-7.16.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:5375ebd99038021b35e99dc88255022912c06565d316212f4a576e4b08d30f5d", "url": "https://files.pythonhosted.org/packages/d1/36/6c45f15be4eca4ac1062c6a55a323286494c99726a7e58951fe85967ac08/coverage-7.16.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:7a076277ca9f5750cc230f0f578ebd2620cec60255b25707361699fef6fb465c", "url": "https://files.pythonhosted.org/packages/34/fb/b54cbeba3ad89082c2e441278681859e538322cc34b84b2af7ebff00080f/coverage-7.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:58d4a54c6ea672afef66d49be922a2c69826c5ae1a42a9cd94f0c9c2bacdf800", "url": "https://files.pythonhosted.org/packages/6e/a2/0dc65ec3d61930e1e4c2e371763b15eb4290896eb343a12d5d3091308116/coverage-7.16.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:0dcbcfcc059117284c603ff8cb61a65872512882f84a8cf0339241f7f7c2f148", "url": "https://files.pythonhosted.org/packages/d6/93/5fad7a61f2c14e08e98946fc31c1c7ffc1195061bf3fdc351db3be77a863/coverage-7.16.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:afdf43b72ef3876c1fe66423b91466e37877c9e81e8cec70542b7e8525b9d1b7", "url": "https://files.pythonhosted.org/packages/2d/47/74e5de9227b939ece9f64e729645ddc4296bea10dbfa98721c1333c8be2e/coverage-7.16.2-cp312-cp312-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:9acc7f7ec4a1b5f89bd929fde5b8a714f6fafdc6cc18725413d510aa082b47ad", "url": "https://files.pythonhosted.org/packages/13/fe/2cf28d40b43645d1b72388fe3ee7f7c747533a6a9557bb8c24a7ae74fe1a/coverage-7.16.2-cp312-cp312-musllinux_1_2_i686.whl"}, + {"hash": "sha256:80d3f7b48d43ee8fc5e8707a8adb43d743a5a1a85256c25a24f9d6d0e2238fa6", "url": "https://files.pythonhosted.org/packages/d7/3d/7c149fd99fc8bbc39c80db5e688d1d39fd040be2ecb78b8335a51a55b9c0/coverage-7.16.2-cp312-cp312-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:126d1af8804d7224421fe991ff65d3ce649081560df7a98b1a5ffff07f9923bd", "url": "https://files.pythonhosted.org/packages/e6/3f/b283fce09d5995e227bd8e513358dd7471bedc0f78abc85a925ebdb0a2f6/coverage-7.16.2-cp312-cp312-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:c19cd6d025c1673f22afcd22c7df8a662d779e05d8e3fa6820c22afb895b0206", "url": "https://files.pythonhosted.org/packages/bf/91/f3325edf0c4223fb1fe1532b8dbef2a1d2f729459a9a7d1a44d073bae534/coverage-7.16.2-cp312-cp312-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:152877cdc8a07264882cfcd503ba56a3ef6cba56a70e8c70f6eb8ffd7384789a", "url": "https://files.pythonhosted.org/packages/c4/89/21eb5e83ecf2eed523c4eb3d65ae513cd082c8fd1b6deb34c4cb6c332f97/coverage-7.16.2-cp312-cp312-win32.whl"}, + {"hash": "sha256:e6c52d3307824ff93b39efd99e4185d557db40bd841452abfb32e5d9151ca162", "url": "https://files.pythonhosted.org/packages/db/de/e3ad6d864c0833624b4f1f9b53f9e58e116c945e5e965c3f1e172c5e84cd/coverage-7.16.2-cp312-cp312-win_amd64.whl"}, + {"hash": "sha256:a678c0b6b22086ec2427359d22e37445d4a792f5fdbbc744112c7dade65cad02", "url": "https://files.pythonhosted.org/packages/3e/c1/bccc58ebe5489cc70628f635c1932fd371f5d7da850dbcf960f95f4c4afc/coverage-7.16.2-cp312-cp312-win_arm64.whl"}, + {"hash": "sha256:1a37c6e478cf687e1aa30a593d19c92c02fad9d122b51ab73f51b8dc7a0c0fc9", "url": "https://files.pythonhosted.org/packages/f0/f6/8eb4f220ef24f84fb27d852d4f9bf83e0c73ec1a4a08dd9a87e3f4529739/coverage-7.16.2-cp313-cp313-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:0993d0e90858c03943d3cb152e068a20dd4707924deec84dd2230261baae3b1b", "url": "https://files.pythonhosted.org/packages/40/23/d4bbaf0c154e0b0c2b5264890dbf6ef098dcb50ec8f2469be9490d191660/coverage-7.16.2-cp313-cp313-macosx_11_0_arm64.whl"}, + {"hash": "sha256:bb2fc905bbf4e6b7f40806ea79e31515abf6349594cdf0adf27c4215f0463204", "url": "https://files.pythonhosted.org/packages/7f/48/fc1e88fd571ec5cb38150b7f89f7696ca1bdf9920e01432febb69774cc85/coverage-7.16.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:4358b9c8c0125b460407f3017c6cce8156e904b32772c5630d27112f52bdbfe5", "url": "https://files.pythonhosted.org/packages/1d/56/6785397d07c29c8e70fbb9a07e97d062b43c21ffc5f12385917847f09f63/coverage-7.16.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:1f15254427c9b33eedac4f198eaf9e356eb4f6214551afb43da6194a2c088ad7", "url": "https://files.pythonhosted.org/packages/27/3b/c8cdd07721e5f99abd81cea970d971997f99bf158c0b85f51bd284179c8b/coverage-7.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:9a75a4704ff640e46170042eec1f984385a121227c505d5a16ad8e495f452541", "url": "https://files.pythonhosted.org/packages/9b/11/606b192fe43d32574ec6238549d48de588fdcc18485682a5ec0a8ac357f2/coverage-7.16.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:14253fc7bb15749b849795a06f5d3b6d8bc3fb8a4b5ddc341faf7a89dce205fc", "url": "https://files.pythonhosted.org/packages/67/90/eea481f8b0305ceeb33f081a5f47e298391dbd1b589de0c4b3b3aa50d3f2/coverage-7.16.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:921415102a90637fcc2e3f169f61dad7699ecf690e8639fc21b813acbedc0967", "url": "https://files.pythonhosted.org/packages/6b/be/dedbf9aea1457b120c27ac10b8fc2a357f37fa2b54c3e7286d42980a0a2a/coverage-7.16.2-cp313-cp313-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:cce2bc991293f15cc4084ca116827b5900c5f34e1a54dfe83f10ab5c43162eb7", "url": "https://files.pythonhosted.org/packages/fa/cb/b25c19d5bb2bd0f2e4e27fe8e2ffcae80c7a91ae181c0dc749ed60e9b1a4/coverage-7.16.2-cp313-cp313-musllinux_1_2_i686.whl"}, + {"hash": "sha256:e1fa594c887365b69745f25a416806e61085dd07b94c9eae68a6e20730629b23", "url": "https://files.pythonhosted.org/packages/5f/a2/892c5c5f4ad44b7b2ca009aee705191f3f268f15052244f2f9e3539b2e35/coverage-7.16.2-cp313-cp313-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:11e597173af1dc33d5f8a7332ada544199269a223af1ee1770ddd5e245ad0fe8", "url": "https://files.pythonhosted.org/packages/ed/99/a562537deba0a3e370182ae71c149be796c39d8087365f17a09188f27145/coverage-7.16.2-cp313-cp313-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:3e7f99698ba3a7d13988bdd984b7ebf13af4dbe2166dc8502eef90d77603b0a4", "url": "https://files.pythonhosted.org/packages/2d/20/854ec68641a9b3362ff068a32dfa41637299761617ef253791dbade6fc76/coverage-7.16.2-cp313-cp313-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:f80bd9f9633eafc73d0a913ba2645c96ba58bba1befc30590f7c0fbfde59d865", "url": "https://files.pythonhosted.org/packages/db/0d/748e4518b0ac0f9ff2687c248a6e5f8c0737306e709372632a2556f84443/coverage-7.16.2-cp313-cp313-win32.whl"}, + {"hash": "sha256:8be099e979fc42559328a21828281b4578304191ae46ed4e80a407048a82eee6", "url": "https://files.pythonhosted.org/packages/31/fa/6e46edba66a183fe4d99d4bb52c173287e9b8dddabe0888d24cb8210e580/coverage-7.16.2-cp313-cp313-win_amd64.whl"}, + {"hash": "sha256:28ff850182a67d117990fa2ce5ea1032836d8c9630dae867e8bdd3bff4533b79", "url": "https://files.pythonhosted.org/packages/1b/d9/9ef6845367600b336ff75d000444a0d32497d6972c833141bd39356abf68/coverage-7.16.2-cp313-cp313-win_arm64.whl"}, + {"hash": "sha256:4ee546b9e4872ffa194bf07ac87bfa1202ebb824d0795dc1ef22f175545ca90a", "url": "https://files.pythonhosted.org/packages/59/4c/577fc0803dab4155dcf808faffbdd7b159256781c0874a8586e17b81b149/coverage-7.16.2-cp314-cp314-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:a2fac6895eb299a2e52d7bbb8fb3903502b9da8d3f5309ceb16ec40c646b58ee", "url": "https://files.pythonhosted.org/packages/75/9e/e3785ba3ecba2bd11efc74bfe2801ca4b78c4480b15a375648d809a59da3/coverage-7.16.2-cp314-cp314-macosx_11_0_arm64.whl"}, + {"hash": "sha256:57ff3783f99d75a1e81dd56a9737eb5665e6736a5d93258ba596b6dcad8fd05b", "url": "https://files.pythonhosted.org/packages/f0/d0/963ff22d3fd27117da3b8cc442f5bdc91196f783321e1a8ff0ec43476772/coverage-7.16.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:35f37886699cb9abd29958247d718628d5bc6f39e623dff66a09e546c42a7e03", "url": "https://files.pythonhosted.org/packages/a8/d4/a306940c81c6ae759e82fff27d20b7fdc6896e422b821f51313cce212b6c/coverage-7.16.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:0fd7a86fdda7cb6d616d178654bd0ad6bc0f3f33c2e478aa598500a1a9e34eda", "url": "https://files.pythonhosted.org/packages/b9/a3/d3d99d93b02517087aa05bc0cf2d04d372956b849e5443e059079901429b/coverage-7.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:ac0f3b379c94acc2f7dce5f5f0b24d44fa1cc6a509717ef83dfee07450c2117c", "url": "https://files.pythonhosted.org/packages/08/44/39dd599181726758dd185ae4dc0c0ab3aeabf7ca70e68e145060feeaaa16/coverage-7.16.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:7d0732c83746bc24123c581a85d9dd96b70ddb538c9076020aa1a041790361e9", "url": "https://files.pythonhosted.org/packages/99/e8/91ee43f6ded411460c359d7e1aebde4d6fd8f00a2e5394182d9d212eb23c/coverage-7.16.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:7b451c68218c150f616bc9649783ec8de76a59792c759b43aa0c9c0466a465e4", "url": "https://files.pythonhosted.org/packages/11/8c/e9499ddc33197bd7eabcb1118ca81756fc874457b324e2b479a4804b2ad2/coverage-7.16.2-cp314-cp314-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:a56ac4fa5a75c7e182e8f62600cfb4aff43c5ed7356a034f3557659c3bec1d90", "url": "https://files.pythonhosted.org/packages/5f/6e/c081cb5991a0afba99f9c4ad6c74a5fce9513a38ddc64e3e6680c6fed9af/coverage-7.16.2-cp314-cp314-musllinux_1_2_i686.whl"}, + {"hash": "sha256:4cc4f73aa3fabc36e32046d6cd2971405948d8a903636508a3d3b2f9128b3a95", "url": "https://files.pythonhosted.org/packages/b2/42/1c3d819e8f9b6eb01c2fe90874d67a8882adb9507e0bbb09361ed131ea89/coverage-7.16.2-cp314-cp314-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:723dcdab91357159b722935b500ee8abc0a66c8c432e1e9fabf4cc7598952de8", "url": "https://files.pythonhosted.org/packages/19/4f/d70eac07901fd587b6ab05e659b52afe13959992aa5113bf6cce059cc572/coverage-7.16.2-cp314-cp314-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:5397e21a90dde0e9c6896b77ded8f0be26b66f8b22b33aed41f6043ed95d55e6", "url": "https://files.pythonhosted.org/packages/34/5e/6d87af88317d3d9a9b18a9ca1bc1673eb516917f296e579d0d4a55cb3490/coverage-7.16.2-cp314-cp314-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:848893e1d361448c113dc2f0913503522a6f7be231d0e38333d2a22d9698a011", "url": "https://files.pythonhosted.org/packages/79/bb/90c2641170d2fa1a6757b3f8450ba2740197317b0ddd749e9604b914e886/coverage-7.16.2-cp314-cp314-win32.whl"}, + {"hash": "sha256:5a27b731c171e43dc8b5f32b76a5051dde2ec9b9366c87028f08a7088ebc2c7b", "url": "https://files.pythonhosted.org/packages/30/08/d8d0478bb02c8eb0ae20a496fc80c40fcf4d3450bd184300d682ba2d28a6/coverage-7.16.2-cp314-cp314-win_amd64.whl"}, + {"hash": "sha256:1c569a9fd25505f1cd6bea90588818f90373ce90e2632e2cacf19ddbd6e14fdb", "url": "https://files.pythonhosted.org/packages/32/3f/0001da22155b0a8ce063ec0f7e64ecbe17b373f306e7a74435f6d6accb72/coverage-7.16.2-cp314-cp314-win_arm64.whl"}, + {"hash": "sha256:d93db87adb6b1c1b408dce4763314b55d76a9f589e96783a84ac9e7689e48bdf", "url": "https://files.pythonhosted.org/packages/d7/85/6d8813aff9b8b8586691a9d33c43c5604f7227622574da7cdc3d91a86861/coverage-7.16.2-cp314-cp314t-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:aa62c85046473959c13ba9edca9dc90a77d5c1095b1ba313556314d77fe5b036", "url": "https://files.pythonhosted.org/packages/5c/70/444f3a4981ac2cda40fdcf4cc9b56a4e1a33c222abeb33e51ed3e3eb2a6b/coverage-7.16.2-cp314-cp314t-macosx_11_0_arm64.whl"}, + {"hash": "sha256:db76506aa5416081f3e8974ae0f7965c58ada0bb0ef7339ac86099588dbb20d3", "url": "https://files.pythonhosted.org/packages/d0/c1/980681cd7b33eb66ac835044116ef0a92e11fcc7bdd866cc89d10b1130b9/coverage-7.16.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:a0f2285329dac10ab08f79cb11f5692c497018e6c7c511f95e6fd63a70b8f831", "url": "https://files.pythonhosted.org/packages/b2/e3/87679875c33bb2191f0f05544a1cc9adcc940fe0c35443a10f2df753dde5/coverage-7.16.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:382d3346d56b0eec1b793d53a4c88799c8053f516aa3a8d7c44315696954bacf", "url": "https://files.pythonhosted.org/packages/76/64/5d372776d6eb523d4e93bafba2253f96984e3b18261c4cc56a50863c6d0d/coverage-7.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:648352b94507179d82637292e7ae8802508d95f78e2f00a705a50b6c48011681", "url": "https://files.pythonhosted.org/packages/be/c1/44082ff0cbf9f97d0043f57970a71204097ec7ba606361a9fd2065393669/coverage-7.16.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:fb2bde05838fffae1a1bf75e5d411a6cac3e4e9bb97e6640fed8cd47888b33f0", "url": "https://files.pythonhosted.org/packages/b8/17/9a215efe25b5e0ecc87c89dbe525c4a87d14d87c8c0c7316ef140a5f6f3e/coverage-7.16.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:6a75180829efb8ae62b4aded25be6ddca1c888d138d2d82e21d93bfbd88f41cb", "url": "https://files.pythonhosted.org/packages/a2/da/7f0a31af8e448107d4d32844bd684757f51ea907bc0c68c8fd537b2123ff/coverage-7.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:99704f73721e23859112072d522076e11c31744fc96b5652e5dd2018aa4359f7", "url": "https://files.pythonhosted.org/packages/dd/a4/3bfecbd3366b775bacdcb3330394d356cf384b5d8f5b2146ac4b14b252b5/coverage-7.16.2-cp314-cp314t-musllinux_1_2_i686.whl"}, + {"hash": "sha256:29309ccc86b7f33df7db12813c299f215bbbc470ed6292d0bedd63ffae1ebf64", "url": "https://files.pythonhosted.org/packages/b8/3f/5d62163732d87e4a0c4710a0eab30f0fd6a2d480112abe2029f014fe8c9d/coverage-7.16.2-cp314-cp314t-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:30c1b65d529e46569899fadca59e4a87c1faf2886923f1307ba61e654d4f3c20", "url": "https://files.pythonhosted.org/packages/49/4d/8e4579f225426535085a9be371cc75e3b026d058d679b80affbdfb4c3ef0/coverage-7.16.2-cp314-cp314t-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:dcf4bc2aab4e16b1c4c0c2005918f23a7dd5d7821ddae82caed9e3342dc2fcce", "url": "https://files.pythonhosted.org/packages/d1/36/ef1f77e2c3f7bb03c2b13b9a2006f88700fdd75535ef158d70049f425c1c/coverage-7.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:a9cd3de0a5bfe7b0e21ee10e1a14e3d61bf52efc88217ab1d95d6ace6970bd46", "url": "https://files.pythonhosted.org/packages/be/79/0cb2bf4428830dec971c718c2c841a039c084415c99e67281f5a72841aab/coverage-7.16.2-cp314-cp314t-win32.whl"}, + {"hash": "sha256:611a44e5229a59d7483ce830160e1a0e85f700562c7a5651c7c63fb8f4eb528c", "url": "https://files.pythonhosted.org/packages/3c/f9/da17121c16667fd84998e972200ae226a41540f6ea4795776c6d99e8976f/coverage-7.16.2-cp314-cp314t-win_amd64.whl"}, + {"hash": "sha256:22957cef43ce038641de78ba995de7568d2d6a37c6ddbf7fa0fd7d1ae2344d91", "url": "https://files.pythonhosted.org/packages/74/89/01179c62d1b7e6e33bd5001566b02d7f778cf33d3ec1e81e94ca170c517f/coverage-7.16.2-cp314-cp314t-win_arm64.whl"}, + {"hash": "sha256:414c26dfdb96aac2d570a54e03008f001e32eb2d413705365503648c6bd361d8", "url": "https://files.pythonhosted.org/packages/4c/57/52935003c3f627ba6e5203d7179aad32448c10899663a30336aba8e81a2c/coverage-7.16.2-cp315-cp315-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:00d3eb96e9988c45f50cccd1f1496571ac5c1f91386ac02c4d55516eeda19a24", "url": "https://files.pythonhosted.org/packages/31/38/df472520f3e626524d7e2fc9d6da0afe7895a2f1489d36b48af8ca40bb41/coverage-7.16.2-cp315-cp315-macosx_11_0_arm64.whl"}, + {"hash": "sha256:4dbbd1155ca46e6e0b6b89d204428c56ef6a459af21333f365d135a2820e5a09", "url": "https://files.pythonhosted.org/packages/0c/aa/3be084d5b82e63ccdad4ed751e4acbae294673573e30481d29f8b7402eec/coverage-7.16.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:8fc15cc8d0d06e873c00ef18e1372d605f9aaf3de27d8c24e50782e75bc8b843", "url": "https://files.pythonhosted.org/packages/de/29/48fca82a7ebf7ff7b2e35019cc9537e7f65e4d2aa1215cc5a8792c989251/coverage-7.16.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:9c6afdd69218202bc1758c9a14b86b8cf1084f37ed2ca143e567a103772b16d1", "url": "https://files.pythonhosted.org/packages/06/3d/b2d5986f2dd53fe201aa1be2e4ab204fa1aed5101e67c0dbbb419b850aee/coverage-7.16.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:aba5c63b7afdc749cc9eae943d5b868cba2b261a176378fa1c5a30bc8bc89982", "url": "https://files.pythonhosted.org/packages/ce/7e/b50160be3506ead12e6480d14279af7f0f17627694300a2d1fd2c42d2ff5/coverage-7.16.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:9174f0af24e5eff248b9dbfe76ec5275a3d19d37edbc2810543f12cf97347a34", "url": "https://files.pythonhosted.org/packages/14/5e/7c805ac9a32606de1399bd7e9bd375aa2f973dc61b12680d9e6403c2e891/coverage-7.16.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:80e9fdb4c3d926b6ba721d4bf7435bdb869c3527ae7803290361d0ab73db13b6", "url": "https://files.pythonhosted.org/packages/ab/9e/76f1ed129a2daf658a3ea17122824cf2e3b91fea0460d8d3664fc5a61018/coverage-7.16.2-cp315-cp315-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:7b3bce4a0d05401d70b7d0d5ca783e686bc9d30e81dbd7d980d532609bf809e4", "url": "https://files.pythonhosted.org/packages/5a/b7/8d62e75f48b527619239a65294f842d4b7fd02a0839d43ae1de80184e2df/coverage-7.16.2-cp315-cp315-musllinux_1_2_i686.whl"}, + {"hash": "sha256:44f21e407b278efdfc1ee5e481e00518bd1d500310a30a5fbf2bcbedfef4aaf0", "url": "https://files.pythonhosted.org/packages/b8/8d/0a15f95c3afb78e947c52644786ba4bc9de259905687dd720d5e6fae2e76/coverage-7.16.2-cp315-cp315-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:59c3926585e1cd1f2190f4b2ac9014de1bbeaf0d5d0587b0dc6b0aa90d17896a", "url": "https://files.pythonhosted.org/packages/25/00/88389987305a47d732866c07c8a500000ab574df9505e3114ac69c8d027f/coverage-7.16.2-cp315-cp315-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:066429634299e14dd2d511e1e85f8f9cecc500781f6b41907c0dd6f1baea7e63", "url": "https://files.pythonhosted.org/packages/92/02/34d079d4952ad461bde037d353f9a6e037a7edc45fe0f9ee8781ff73f028/coverage-7.16.2-cp315-cp315-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:893ea9cf86cb8d2546812ac93d973aaf2ee1fb45110a873b014214fd23e3725e", "url": "https://files.pythonhosted.org/packages/f6/d8/3e59a62879285b464ec1b10fd824fbc1af9ce66e842cd39974f80a0becc4/coverage-7.16.2-cp315-cp315-win32.whl"}, + {"hash": "sha256:01c6908bc613b420c26c818fe948e1b97dfd041a53c98b01c63bd8321f5c9aae", "url": "https://files.pythonhosted.org/packages/f4/e1/128026e1b2836e9ad6b219207ba9edf1c5e0088a7869e23088aee7fbbe7a/coverage-7.16.2-cp315-cp315-win_amd64.whl"}, + {"hash": "sha256:967d72c835d7a8cf0af99ec813a2d06e3db6df706402f1fe85b31b437645f495", "url": "https://files.pythonhosted.org/packages/a8/f4/c9fa8e7cf525ca7748ac52b0ee89331d13fe09808e45c679830708782e90/coverage-7.16.2-cp315-cp315-win_arm64.whl"}, + {"hash": "sha256:98d9c97f51b334b0adce7b964442a9af33c1a00c6ac856984cc5dc8d18f81c75", "url": "https://files.pythonhosted.org/packages/a2/13/e96b045447a856666f36f9c653e2a80bdaa732aaaf72412b19aa2c26a473/coverage-7.16.2-cp315-cp315t-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:3e861f1071dcc2fec1e88bef0920f6b1eaa66a143555b4f8ab79ba2b0f30ef55", "url": "https://files.pythonhosted.org/packages/23/90/087f6ad1bd3df059632ca3407a4e6552ed1053ee35354de0a771acf35423/coverage-7.16.2-cp315-cp315t-macosx_11_0_arm64.whl"}, + {"hash": "sha256:fb9d92ecfe2d5b494367c67f7446f8b75b68d8d0c8cf3bc3e6997478be25d9e2", "url": "https://files.pythonhosted.org/packages/7e/8e/285dcef0184358044e7cbcd810a1bdc9566bc620f54702d605477155df4a/coverage-7.16.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:eb57acff4a74246ae513c142d4b36e18c389c3aed8661914a53f7cd0071031b2", "url": "https://files.pythonhosted.org/packages/06/b2/cc83f3a6e5789a4e89059c69555bc641c2efcde568405a1c06fc702951ab/coverage-7.16.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:444889f7f66b74e4455c0a97e0e166dd41177f1dca8c0239a47cff25e05ba7e1", "url": "https://files.pythonhosted.org/packages/ac/41/f548c19530f5d66ac6e3c92bbcbc49da7261de3a458b9f3e54a3efb1a0b2/coverage-7.16.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:a740ea6f083c6db7b926534d159508f80ba275ab35e722522de0d18d0f56e55f", "url": "https://files.pythonhosted.org/packages/94/61/4dc27cf82ef96434d2874110ad0cc10ea4621025705dc5049862bd3bd181/coverage-7.16.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:8e209591f7c41ae4a9171335cf6156afda0b21de73b02f73f5aa95b2d5fbb08d", "url": "https://files.pythonhosted.org/packages/38/29/bf8072b1b8bd5f2de8b21460a404460b1a2b97e80a9464c78ec0271f6199/coverage-7.16.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:396bb16e04ce04efbb3df91456ae4e3da918e69ecdf67fb711b0a0fdf35ccce0", "url": "https://files.pythonhosted.org/packages/7c/2f/0aecb8721be5cdeb8afd9d6d9f6b463f074e4d8d37f00f4c42442522709f/coverage-7.16.2-cp315-cp315t-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:9cdf19874e0d247f32f03609200370343c3c7aa260b191d8c2bb251d36198283", "url": "https://files.pythonhosted.org/packages/ab/0b/92b4b7628268ee711249958e68fc0328779bd3d9a7ab4715379465aedb84/coverage-7.16.2-cp315-cp315t-musllinux_1_2_i686.whl"}, + {"hash": "sha256:fd3d72233eb8b48acc94fa57d44e2d32ce8e7abed02882ccb6d855ccc4ed33ec", "url": "https://files.pythonhosted.org/packages/7b/d9/41c95c1ab29b3dcd357cd1227181d1c98185632aca41ce670ce671b23a43/coverage-7.16.2-cp315-cp315t-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:bb4ffe96aa663cee727659db5a2afeb38c95f8677b747d447b90d6d4874ea2c5", "url": "https://files.pythonhosted.org/packages/80/07/ebeb259aa5362b033a137b86d7274ff4b109d59be8cc9913889b783bf75a/coverage-7.16.2-cp315-cp315t-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:dba2edfb054f6d4a08df9d1637c39a5aa3865bca6617c13c86be21e45658a59c", "url": "https://files.pythonhosted.org/packages/b2/18/8437620f90d023680a072eee02f968055f3658bbfb7d386d0ea34cfb7f30/coverage-7.16.2-cp315-cp315t-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:251aed777c47c77aba047096d4542889db089227655711dfc2b9c54ef0e15e35", "url": "https://files.pythonhosted.org/packages/28/6c/f08e8ee4293e6434035424180bef4d45e028e8ecc006c61bf9453e74405e/coverage-7.16.2-cp315-cp315t-win32.whl"}, + {"hash": "sha256:2aca0bdfa9e91621d5b09d815357bf63def4fc0e9cb66da67bf2cf93f3b1a6f5", "url": "https://files.pythonhosted.org/packages/f7/fd/3f939c2847f4a72c20cff8b1ac33da78ea91a2d38d9b43336e60db719103/coverage-7.16.2-cp315-cp315t-win_amd64.whl"}, + {"hash": "sha256:b88841e654f09732804809e435b3e005a929ffd9998b872b7b213957b8759cb8", "url": "https://files.pythonhosted.org/packages/5a/35/b98cdc354c952402132e675a87f2cc3227fb68f959c84aaa491fbe15933d/coverage-7.16.2-cp315-cp315t-win_arm64.whl"}, + {"hash": "sha256:11d28e9123a9156cb405d8d27b44256c9a58fb5decc2073a8f17862057e3aa0f", "url": "https://files.pythonhosted.org/packages/3f/0c/7a64e1ac90541a8edf50daef0914848011fb057a5bf55284a4811e21939a/coverage-7.16.2-py3-none-any.whl"} +]}, +{"name": "coverage", "resolution-markers": ["python_full_version >= '3.15'", "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'"], "sdist": {"hash": "sha256:d05a1bef52f4d7f1b484a1cf46a8929e50963f910e51396373f06cd3b6248f38", "url": "https://files.pythonhosted.org/packages/78/4a/740c3134227763ebc30a226d7f2f29b25e35898d162dd8b2dd792be83fb2/coverage-7.17.2.tar.gz"}, "source": {"registry": "https://pypi.org/simple"}, "version": "7.17.2", "wheels": [ + {"hash": "sha256:a3f49ac9c9e5fd4b450677eb44f4b14ea9ab71950b2896888c9f6a22d5cd1773", "url": "https://files.pythonhosted.org/packages/5e/9d/e3db9071ebeda48e47e5be0fd0404219954e7888f64e7c37df324a79dc3a/coverage-7.17.2-cp311-cp311-macosx_10_9_x86_64.whl"}, + {"hash": "sha256:3d3d9d79a2b6cdcdde5a1ab1ae89953faea5cb28c0eea17a413ad997bb3cc8a1", "url": "https://files.pythonhosted.org/packages/49/a1/260786d936940102052a51cb166f80f4a98e67fb6142aa050f1af48a2385/coverage-7.17.2-cp311-cp311-macosx_11_0_arm64.whl"}, + {"hash": "sha256:5b71aeaa4440cbece5498acda30838685793d3bb960aff87ec97fbb546715a5f", "url": "https://files.pythonhosted.org/packages/be/80/f5176b7f6aa9e05e37e1bb5eae473c5d015485c36206934f53b2b2795076/coverage-7.17.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:01303d9e68b6e6bdc5459b54e3bd0e3539c94c25b50ac7362b7cbcacbc815fbc", "url": "https://files.pythonhosted.org/packages/03/9f/4669ca11ce1932cc18fce766ead327ceb41a92055608c179b137067e2805/coverage-7.17.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:1947bd9d82e405c892c50b0d8dc0c1dea5df2a962129ca238c500a6bcf887509", "url": "https://files.pythonhosted.org/packages/ae/99/efefaa02a4b2876ffa4a389c81a8074c888442b12c64046b448718fa5590/coverage-7.17.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:2cd1a7472a972fed01c89589b1560846e32e942f8329a515c4059d4521c6579a", "url": "https://files.pythonhosted.org/packages/19/2f/a565096e5dcf8c511332d397d712b056f06091ab3efc10adb5a4d22b0cc2/coverage-7.17.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:2b7fe217808239437b8509b869fde2e0cac48776a68c626bd6b15411b087e3de", "url": "https://files.pythonhosted.org/packages/ab/09/87f9b02f97179f2c76fa5724e0ce00b9db2bbe16b7e21d89c8fc1f3a77d9/coverage-7.17.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:565f1f1a9632d82678525d668a10fe070a285603a9d3581505b0e1a66624ad50", "url": "https://files.pythonhosted.org/packages/db/12/70733b58d508bc80dc983408b06f1fd0c59fde0eb1379dd9473b3e290856/coverage-7.17.2-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:189cdffb3ebc53d6133de67952dac7cc88d1fcaff5480623fb00ef0bf1cef918", "url": "https://files.pythonhosted.org/packages/33/de/0495b108a3091f2389800455b615939867a3ac19f24f0fc186f4d066b4f3/coverage-7.17.2-cp311-cp311-musllinux_1_2_i686.whl"}, + {"hash": "sha256:f25209696631752468a25e5954fe119503468b3b29b6fa2c621ae766153c6f50", "url": "https://files.pythonhosted.org/packages/0f/ff/39cda949e0e9bbc16620d8b843fd78929a46edda2566e54b9597bda61625/coverage-7.17.2-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:20faaaf3a49601afbcd6d46c17a594b0ae62631498def44e5eed94789962ea18", "url": "https://files.pythonhosted.org/packages/85/e5/0eb1bbfc80e1a03318d81fc044dc1c4fc075921f7f618298d42837dafa66/coverage-7.17.2-cp311-cp311-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:7c123b6bbc183a14372036769f033098026aa75fc8fba37d028b0b772356e90b", "url": "https://files.pythonhosted.org/packages/a7/18/098ea8ee450922eec7ab48a35b4a7fd6a49c4a97416d1e4cf66092c24dc4/coverage-7.17.2-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:1c3146ef9d14c011517422489784a7ac344477e9cdacb05d9529e912721cd5d8", "url": "https://files.pythonhosted.org/packages/8e/1c/a6ee041c4bc8b6ec7bc9f417daf89e3ee702182a48d573267ccaaed17c64/coverage-7.17.2-cp311-cp311-win32.whl"}, + {"hash": "sha256:51c6a60e63ea993113ea5cd24a401cc2faddc54aa7acfddaffa5d3b8c0161b9d", "url": "https://files.pythonhosted.org/packages/5a/df/7f077fb2e71b1d73fe8285be7c6ba43dc085f9eaa2384d4e8e55560073a3/coverage-7.17.2-cp311-cp311-win_amd64.whl"}, + {"hash": "sha256:b08ee3ef53e80e5a50c68b3d9764363e373acba540e1b58daee0a9a7f23fdfb2", "url": "https://files.pythonhosted.org/packages/90/56/89dcd5907d0cc04b07746e62e1cb896c0f694e5af40aa0c980458825c6c5/coverage-7.17.2-cp311-cp311-win_arm64.whl"}, + {"hash": "sha256:289a24443352713745f79c1b6aae4e99ced2f5ff3e86bb630c33c631d50f217c", "url": "https://files.pythonhosted.org/packages/22/6a/a51808ac625c457326fb37b174d35e1badb1324c6def5d9668089c4b14ef/coverage-7.17.2-cp312-cp312-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:da4b0aef427eb96e422b444e5c2c3d38768fedb9141346294c38717a8b15b416", "url": "https://files.pythonhosted.org/packages/31/24/f32ec1a5ef947294252e77c5d40ac0903ba1de4c92638edfcca8629c067f/coverage-7.17.2-cp312-cp312-macosx_11_0_arm64.whl"}, + {"hash": "sha256:90c6c5f93606b8803bd7f8a0b2ae05df7e35da4504a1c53cfe9294a1731e1f49", "url": "https://files.pythonhosted.org/packages/01/8e/3097367327c69e65521b9d9bf8b9d2ddecefa8c21bd08e17bc068bd3db93/coverage-7.17.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:36314997533266bde3d07765289674304d61bfe49165bb1b4fa9be12cb4ba0d1", "url": "https://files.pythonhosted.org/packages/73/65/774fc4ec86807b6ee66b46465bc49b34bb6d291e9bff6ddfa9708ccb4053/coverage-7.17.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:1682d98ded28524bc6853cc4e7c65175b3e917f38b2e2ddebb28cafd8ab4de74", "url": "https://files.pythonhosted.org/packages/d8/72/17b5df5d8211e1880169b93197ba98b21b30ccf8f116284536113b7582c5/coverage-7.17.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:8eab0a5cff8dbc563d1416a6e079ba3c146712e47167c022a5df5ea01bd76df9", "url": "https://files.pythonhosted.org/packages/de/0c/603ba7fff1aa4c8064fdf4a1fff6d9fb51c72a5828429dfe858be48e38b3/coverage-7.17.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:635337a96895f2ffcfe2aa7554468a988f468fb67a8007c1a65e95148c1855a9", "url": "https://files.pythonhosted.org/packages/f4/ca/f2ae6e47e18b35a0dbddb9d8f51be34a6d66b41120f2e0f7363ccc0812f4/coverage-7.17.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:79cd2d9742419b6d1aaeb1f3fb77a8e6a01c09ebe5bd1b27011752410a5e3781", "url": "https://files.pythonhosted.org/packages/b2/4b/b09540a46395a6a1052ad114bf366e383c64e223374f4911a85620667930/coverage-7.17.2-cp312-cp312-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:b0209fff1ca92f1e2e344d54b5b3def74efbdb46ba807cd81976a5e9e569cac3", "url": "https://files.pythonhosted.org/packages/ec/c4/42783cb43481fafafffbc7be7fe6026258ffd30a575707733109c9ef0679/coverage-7.17.2-cp312-cp312-musllinux_1_2_i686.whl"}, + {"hash": "sha256:7a3984fb8c2407721f9bc05573fc9a770752efc756d55068ccbde1fe53e42233", "url": "https://files.pythonhosted.org/packages/37/07/192ee81dd1ec4da58ae0f03fdd0045d2b821007e4194f7704d6e495733f0/coverage-7.17.2-cp312-cp312-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:698ca1575135215be25ac9c5d14f0b5455135d29602b7ee81e511d64a0435c85", "url": "https://files.pythonhosted.org/packages/dd/94/8d983310d98299a19ba23efbb324492f4bf8d8668d610eacd304e451f138/coverage-7.17.2-cp312-cp312-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:686b6f8f9e57afc3a44096b17ee8de7e11e0a12f2da819e25047af1eb3b0fc6f", "url": "https://files.pythonhosted.org/packages/1a/85/6c923d71c76566c6c65a2af104d39d69d44ae708a700de017e6c56fedfc6/coverage-7.17.2-cp312-cp312-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:ee083412170118074bcc74361fcbc7104eb6d0b7c9c75eb3b84a4c324410c531", "url": "https://files.pythonhosted.org/packages/54/f9/fd3c37b09891bfdfece486a6145345bf2b32332045a678afd293a7f2f05d/coverage-7.17.2-cp312-cp312-win32.whl"}, + {"hash": "sha256:e269dddf5ebac20dfcc5a2a459528a9d221ab828cb31dfdf19c7cfacdbcee673", "url": "https://files.pythonhosted.org/packages/dc/b8/4d395163c9bfae0e991abbd14dea9c9bbb4ade904e5f297cbfb7b8150216/coverage-7.17.2-cp312-cp312-win_amd64.whl"}, + {"hash": "sha256:bac3c79c31f45274117ed64c65efeaed85665668aeba7d809c7c38801ada6ed4", "url": "https://files.pythonhosted.org/packages/a3/02/1024b8d4f60a9ecc0ee41a6000425caa63855d63e51da84c93c0b1dff894/coverage-7.17.2-cp312-cp312-win_arm64.whl"}, + {"hash": "sha256:882cade9b6dc43b2bc819bb444c0a13e4ed1f669a50e0de74adeec54fce486f3", "url": "https://files.pythonhosted.org/packages/e0/32/d750d7b10b0a9436349b3fb78491c5bb8f9b524f01ae6994d935d77d6ab8/coverage-7.17.2-cp313-cp313-android_24_arm64_v8a.whl"}, + {"hash": "sha256:f5a875e92f10c6463027d8151459f2d92b4c13527402ed1301eaddabd79d8cf5", "url": "https://files.pythonhosted.org/packages/63/33/468f8c63aff5deef81bfb8e44771f68ebba328c0aa46c8a6ff27dedd0fda/coverage-7.17.2-cp313-cp313-android_24_x86_64.whl"}, + {"hash": "sha256:e8da5e158d13556457f69245deacf6d8401a2c9c31afaddd18bea691e849b5fd", "url": "https://files.pythonhosted.org/packages/10/91/c8723e48c1497a3c56a3194973a416a6f0fb24560425685f960888e03b20/coverage-7.17.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl"}, + {"hash": "sha256:42133a2647982db7968db138c0f4a57f5836101d8d504600a87ef8ebfa22a4cf", "url": "https://files.pythonhosted.org/packages/61/4f/2f57b3dc6ef31e12a99e6dcb820c4ab04d973ea1466f73c856bf920a740c/coverage-7.17.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl"}, + {"hash": "sha256:e6c89d63d43738712ce1febddb7758ffeb56de08f1b2be68aeb9a9c7fee712c7", "url": "https://files.pythonhosted.org/packages/9c/fa/967e829238dda21c4bce17aef2f739187678c8e6e6fc880a8454945a7433/coverage-7.17.2-cp313-cp313-macosx_10_13_x86_64.whl"}, + {"hash": "sha256:f94d465ee00ebe694ca866d66bbe45fa04618b1afcb377de934694f624fa7cf6", "url": "https://files.pythonhosted.org/packages/9d/df/803ba08e8937759dc3f18b93ece82f81b2fb446e3846e4d60a06a4e77647/coverage-7.17.2-cp313-cp313-macosx_11_0_arm64.whl"}, + {"hash": "sha256:7ce0acb27a823e8e5169282f538f0e182854cc8b472b88d45456f09b6fe364fd", "url": "https://files.pythonhosted.org/packages/4a/12/92dcd9206e7bb817af42b8663cfeefdbb2c5a6c92cc7069d43e6892bbdeb/coverage-7.17.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:70daa8e4b49541b51c0baeba0d802b0d14d3214daeebc071b3db0b0c7dc84646", "url": "https://files.pythonhosted.org/packages/48/2d/6b028df8050caef4ce0df177686c92dba16ab7cf004040e5518f95b57021/coverage-7.17.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:95bd8dc5b356f78f10a967ec50f8902d56f24c623753ac9fce0aba9d6213d5c5", "url": "https://files.pythonhosted.org/packages/62/c3/429b500937ac446e36fcce517d040b0b8bd04a4bfa087bdf864175553fea/coverage-7.17.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:a5565a9dd715a5a94ffcf9a0ad4b6b1501b5441fd67d660e78742adff5d37a0a", "url": "https://files.pythonhosted.org/packages/c1/91/263224a77de3ab1ee42d110b09f1ac9a02638794d3e002a859822638256a/coverage-7.17.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:091eba4248d31b03828fc657c96d14adfaa8754f5aec7b73298b82894858db06", "url": "https://files.pythonhosted.org/packages/0f/44/99b72f2961f4ba54ac14ccb016f3b212cc9a9dfda4c37e07a208efe01f9f/coverage-7.17.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:d2742952851c7f88b1484385f68c42c6388153520efb40ff09fc852c3783aa5c", "url": "https://files.pythonhosted.org/packages/be/a7/e97f63a7c345077e4e398d15e3aa5068edc28cb061f997f71fd0e32703e1/coverage-7.17.2-cp313-cp313-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:1da1999e3c371edd669b48a1029c86dc0474cdc6ff152d58cb3f15cb3337af72", "url": "https://files.pythonhosted.org/packages/16/0c/73e470df7db83962e2cdd6c1a7ca7dc8f8632c17a6d378d6d9124ff8bc6b/coverage-7.17.2-cp313-cp313-musllinux_1_2_i686.whl"}, + {"hash": "sha256:7804a6022fcd5e1d4de4dc676e985d6aea0119249e88c041e943f3cd07953527", "url": "https://files.pythonhosted.org/packages/50/43/db8bc1b92d933b0b5f57d0e3377594c4f78ea31e84e8fbc22627fd17ecf4/coverage-7.17.2-cp313-cp313-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:6bdf3821fd0740b5369b9ab58b4e9d0d68a2c5c75a7ee0cd3beb624f9f15e4fa", "url": "https://files.pythonhosted.org/packages/c4/f4/3e9e5cc9ccc911f8fb9595053da1ce58b9de8941b27e2f2f7058cdfc87f1/coverage-7.17.2-cp313-cp313-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:e09c37817768bb71fe3e70b6ce5e6ac1a0cd7398536c2f30a4460218c7b66e93", "url": "https://files.pythonhosted.org/packages/72/9f/0d67883bf55adf50424728937cbee54d76fa137817e6bb20f97061d01816/coverage-7.17.2-cp313-cp313-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:c10f2a2748dd084e44811bb9b26979763af9003115d9bf93c1da69011944719f", "url": "https://files.pythonhosted.org/packages/a8/22/71965edc14736073b55a40e521791d9233d5c3194fa6887a94f4431fbbcc/coverage-7.17.2-cp313-cp313-win32.whl"}, + {"hash": "sha256:8dc07ed34bcb206f69d5b54fd3874f7bf39ac941c9f7aec2b53b29874fcfd77a", "url": "https://files.pythonhosted.org/packages/47/0d/715cb37f51f908a6f6f33fe8c6fc95ab638b17cb014b1650daae0893c160/coverage-7.17.2-cp313-cp313-win_amd64.whl"}, + {"hash": "sha256:bb74283661a239fdaf738ac0b07db4ed9b476c7532d3e713abf0f6a072781e60", "url": "https://files.pythonhosted.org/packages/39/f2/a5099bdc2a530f29c818a1c7e706669451c10e5f0eb62dcff76ef3568668/coverage-7.17.2-cp313-cp313-win_arm64.whl"}, + {"hash": "sha256:0439f77deb5c023003533ae8909a9aa1e174c847d655327035b1e542c7c8a54e", "url": "https://files.pythonhosted.org/packages/96/f9/30d6ff7a66e1519d79940fdee542ead9a63601de60be5cc9fe62c74bab58/coverage-7.17.2-cp314-cp314-android_24_arm64_v8a.whl"}, + {"hash": "sha256:e86a66f96d80c770daeed78394c1d4f6e8d03e5a3e08c5dab5275d56414f296a", "url": "https://files.pythonhosted.org/packages/1a/db/82c722c37dce98fe1abc953768903752bdd415dfe8cfeb7b744a7236a735/coverage-7.17.2-cp314-cp314-android_24_x86_64.whl"}, + {"hash": "sha256:87b30f3daa0bedda6b770b303410a6def007844772aaa143dc52675b3d95cd8a", "url": "https://files.pythonhosted.org/packages/7d/07/71b68d870975776cb185f11cd80e053b01739f3646488fe8b9721744f56a/coverage-7.17.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl"}, + {"hash": "sha256:3f219efbc3ec71c377e278dee201633e1d4ad0d2d9bdaddfd9489b2869d584c4", "url": "https://files.pythonhosted.org/packages/aa/23/ed60f79576175e108c77df2bdc116f46e367aab108542ddeecaab752f0f6/coverage-7.17.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl"}, + {"hash": "sha256:36248fb6c03341501169539bcab35eb3a59159100be1246fad3c14cb73c17685", "url": "https://files.pythonhosted.org/packages/10/7e/60a51850d3ff659b82cc211bc98fa126aab2ac9b81bd4ed6abc3e3720a09/coverage-7.17.2-cp314-cp314-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:2a6281011cf85699763bf88ab55e0774f6696f87b390f39152b14ecefadccb59", "url": "https://files.pythonhosted.org/packages/77/80/a9539c15076fb9b95d8cbdb7f0caec90857d80bddca3eeca439f5e2e64e2/coverage-7.17.2-cp314-cp314-macosx_11_0_arm64.whl"}, + {"hash": "sha256:3dfefb1ec16ca1a01e5da40ba162e268ced8bd3624b9eba4e9384ebfa14af7ec", "url": "https://files.pythonhosted.org/packages/7f/3b/0a84b6032511834105fd4f8a8b4e76927783cea03dc4796032d5c4bfec8b/coverage-7.17.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:34e5c7a2da36804c6e6f10b368eae01ad56f5a4ac5ecce7297300a3d46fdef2a", "url": "https://files.pythonhosted.org/packages/2e/e4/a3e6b8bcc219b00ea91e36f071c2659b253e5d6e1b258c31eb4524231718/coverage-7.17.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:e6bf531deaebf8f69ba6015bba3fb817cab6694dfb69ac2a708889dc59a3926e", "url": "https://files.pythonhosted.org/packages/05/54/a666b65c5bb145d8796b7902ff1c0f60600a6c2a08949e20f2fb7af15de3/coverage-7.17.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:88d4dc83b6ad09a2134cad9d8ec780b274b17ac7dfba8c2c6e92e3bc468fe85d", "url": "https://files.pythonhosted.org/packages/a6/73/ac9d06da9e0c143b874a68b17d8dfeedc45acdf1bdcee5b22a42bece098e/coverage-7.17.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:8dc17707fd463b4dfab53792e41d50a23316ac6b98539007aa2388a1d44a8535", "url": "https://files.pythonhosted.org/packages/87/b1/8a500e16cf787383b00b5a86eb50a490b4d8ebde505f92e1aaca515afc0a/coverage-7.17.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:df0a34d445eea64f3766e9ca2ff57bcde600a89c7c0330434a7a37e8d6e2549f", "url": "https://files.pythonhosted.org/packages/f7/be/642762c5973b89e2b8bf66c95778bdd80e96ceea442ae8fa753732597b73/coverage-7.17.2-cp314-cp314-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:c5a98aff9eb681312ed403ecb7beb5f15fd25d6e0acc2554b9ca4b4d6f0b6705", "url": "https://files.pythonhosted.org/packages/e4/b1/f7d6c0ef0a4aecf3f91f19df4ab3fa131fec8c89f554187511716584c9f5/coverage-7.17.2-cp314-cp314-musllinux_1_2_i686.whl"}, + {"hash": "sha256:7565c17092f2f4021abbe3f5007300c224f362364a388f5ddad9216dab44b611", "url": "https://files.pythonhosted.org/packages/c7/c0/3ae087d52cfa5119ad9ecbd193c40089924d1101cef7333f15a86dccb444/coverage-7.17.2-cp314-cp314-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:555257578e20a46020c27b2af4ef99059cd906aa9b8e163baf9da9a5afb65a64", "url": "https://files.pythonhosted.org/packages/c5/95/d324108991f892cb361ef8b2f91bd4cda9c2563e0954247409e9e6b911ab/coverage-7.17.2-cp314-cp314-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:460ba2139866e758c3a8e9d9422fdc889942bcbc5ccaf710dc4fbd8707d14aa8", "url": "https://files.pythonhosted.org/packages/14/b5/13a62eac66cecbaaa098bb753a3693b44454a4d21d53db4f1a0953f35f0b/coverage-7.17.2-cp314-cp314-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:e72cf9556f799c3fce7597c1bd7dbfc9104befbd08088deb3b9ae9fd98117c02", "url": "https://files.pythonhosted.org/packages/53/13/9ea31cfa72434449b9a98666760169d1e0ab31b3f6cdb12e68149106668e/coverage-7.17.2-cp314-cp314-win32.whl"}, + {"hash": "sha256:205f9e329d9d5e47c320f8712326a01620689edae50531cc8934511e2c5e8f7e", "url": "https://files.pythonhosted.org/packages/f6/cc/aa8f34b014979c973740f5a5ed6861ecfc6009082c70e72030ca1519e0f8/coverage-7.17.2-cp314-cp314-win_amd64.whl"}, + {"hash": "sha256:a5909dd087006c70c128297312ba67d877123e871100645d9bd73ae021a4d01d", "url": "https://files.pythonhosted.org/packages/49/64/16afaa19b3886dc157c3c5ebace5241b84b96a005cb28545584e66b80895/coverage-7.17.2-cp314-cp314-win_arm64.whl"}, + {"hash": "sha256:674bcd023a3743666d4d7a15238e57a00c968553780a36c80d3779fc410b912c", "url": "https://files.pythonhosted.org/packages/e8/95/acc865e1411c8ebc5c5d230367f3bfed5c605ed54bb41b84fc58f4e7f137/coverage-7.17.2-cp314-cp314t-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:ba45dc072bfbf1798e68ec81379b73473fc912174bff9f91924d77711a1d2b30", "url": "https://files.pythonhosted.org/packages/10/98/02c37c6a928ccfc84b7a43eda44d96d3b1ccbc93989c178754b7e67e7650/coverage-7.17.2-cp314-cp314t-macosx_11_0_arm64.whl"}, + {"hash": "sha256:0d8a5dc709da9b90ad5cc859a9b88c9b801e65ca2fbf7cbdbed5b9cbf457fcc9", "url": "https://files.pythonhosted.org/packages/28/ab/d503b0aaebb92b171260469463915af5e16a64457b289d4cb844e8c366ac/coverage-7.17.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:248873a144c067f981742ff4bbfb48b57e281a8cb7a3821d4e293bdb50b47970", "url": "https://files.pythonhosted.org/packages/f6/e5/6d6ba15bde22fcccd07e14305be14291d041f732771a73f787297445d3e7/coverage-7.17.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:d0332e2b27fc5cc761d6b8a33118b958e6a46be7f00fd30e03a5cf103350c0b3", "url": "https://files.pythonhosted.org/packages/63/e7/bd6dcec5b8130f5bd8679087a2bc3a50a53c4ee78d5ec449610020088ff3/coverage-7.17.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:27fc4384a1154f7df30d304ccddc05ef457368c065670ecbd392606279b3329a", "url": "https://files.pythonhosted.org/packages/55/c1/b3f06aa1f1c51f4295fdfa6500eb7409aaa6273cf5f27bafc25217f3547d/coverage-7.17.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:603140eeae3f9adc562f91b1b98d4d1fc491bd1f66c5082482c568d0b7891bb1", "url": "https://files.pythonhosted.org/packages/a7/f0/b68ab3654af3541488b26856e3043c13c782d8e6104fc8149746ecaa83f0/coverage-7.17.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:be0fec33b69c40f845c76f67fb4495b62a84da8c31a579dc6e18c5a63fa6a1f3", "url": "https://files.pythonhosted.org/packages/fd/16/4a9d56bd4ca4572cbd20f9843813623cef1b88b15f979ec4f381704d2f84/coverage-7.17.2-cp314-cp314t-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:960e199965e506d78f62252355397a05c8cee8072ad2d82d932745517d566539", "url": "https://files.pythonhosted.org/packages/78/d2/1c04b38bab92d2b7d62718d170b5bea43755fefee7e29cc056448c433d92/coverage-7.17.2-cp314-cp314t-musllinux_1_2_i686.whl"}, + {"hash": "sha256:d7a363c0788435ff5324d9582d37c63f215c75f974d755cfd6cfc9fbe0e64b04", "url": "https://files.pythonhosted.org/packages/26/c9/2c639e9aee78b0522cb3dc245de09685e34b479bcc45d4d2a277d0de1174/coverage-7.17.2-cp314-cp314t-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:afd81658f17b1a50bbc1d247a61fa435be0be471246136eb946f9cbfc437a4fa", "url": "https://files.pythonhosted.org/packages/79/1f/11bdaf03c1b12f88094b57420b4238d3974ebec6a7d674b645a54e24f90c/coverage-7.17.2-cp314-cp314t-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:d88fcc24470d73ed4360e910f8549718b913ba56fb8775d9ef929a647835dfea", "url": "https://files.pythonhosted.org/packages/bb/f6/43d40d1b4d4892e54c68a241cd5b92efed4d5934109e0e5e24a94759f732/coverage-7.17.2-cp314-cp314t-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:2c42c222939b8699e7bc0e453149627f211ebbbb2f74c1a04331b0cd9834e2c8", "url": "https://files.pythonhosted.org/packages/8e/33/6874185b791c71684c401fc74abd71bbdb102e8ee41c8f380cf25af41d29/coverage-7.17.2-cp314-cp314t-win32.whl"}, + {"hash": "sha256:b94c509e26603ba7f8cb1bf8bb63967c7be533493be5668d8442cd06ec853d76", "url": "https://files.pythonhosted.org/packages/16/e0/9057b4cf962ca6089b144f0d93a748f5e2d13876dd0f33f9d5a2c2ec49d2/coverage-7.17.2-cp314-cp314t-win_amd64.whl"}, + {"hash": "sha256:eca90e028932de597e2c50f454ad8cc631281e14d20b9f801a649ab555deb580", "url": "https://files.pythonhosted.org/packages/5e/4f/bd626297c942380279dff323e1d8a8937990d553119a4f887358488b1b54/coverage-7.17.2-cp314-cp314t-win_arm64.whl"}, + {"hash": "sha256:cbf751ae86ef0d80e719915c087b05776c8d9300141f6c3e56f536d78a62c301", "url": "https://files.pythonhosted.org/packages/ac/92/ff8badc05ff0c4ed26ca62fb3d155e9014ff65768b76d5f399e63d9e9a79/coverage-7.17.2-cp315-cp315-android_24_arm64_v8a.whl"}, + {"hash": "sha256:249c4b4eaac9ced6fb85e465027ca26348111c2760108fffc356a16be4f54cd9", "url": "https://files.pythonhosted.org/packages/5a/8b/c5a5392e5a5dceb68f3b1b8cdba925c7997699f351d811cd4a41add9c29e/coverage-7.17.2-cp315-cp315-android_24_x86_64.whl"}, + {"hash": "sha256:e2fd4e664a34854cae8db58ca6132fb7b5a7efc67b14233e797194bd0d63b6b0", "url": "https://files.pythonhosted.org/packages/7b/bc/9259a1fc49ad663861cbfb3df6a99d4c8f62a11b242a2db3358822d28b17/coverage-7.17.2-cp315-cp315-ios_13_0_arm64_iphoneos.whl"}, + {"hash": "sha256:0712a43e39410aa567f2e85846490f8dd6c839820e7edef2142767c5c03162ba", "url": "https://files.pythonhosted.org/packages/20/ad/83f03da8ab846af246d4a47b651e5d90229a4c3314e31e8eecade63b378f/coverage-7.17.2-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl"}, + {"hash": "sha256:298849ca0e385767096995cb57f72059b469f43f9927b8393f29d134f7bb1062", "url": "https://files.pythonhosted.org/packages/f3/08/e110687f24245edbbe9c0986a6ccd04f1f9a6e90e329cbd4347e2b81d76e/coverage-7.17.2-cp315-cp315-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:81a432793440edef268dbadd493393bd6f70fa4b87f76c58114f7f77f1ccfaea", "url": "https://files.pythonhosted.org/packages/93/24/7cfcd6adb4a0c18a10de12fd695edbf8cd121fdca0a6a63cea1bffc029fe/coverage-7.17.2-cp315-cp315-macosx_11_0_arm64.whl"}, + {"hash": "sha256:d14872ccef35972c4ac290f2432f43d0a7bb230cae2b5ed26871ab86221e8b5f", "url": "https://files.pythonhosted.org/packages/54/7a/af167a0bb6d0564209d35c8039b290327ecf7b3e34f838f9360b56d3a03c/coverage-7.17.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:3bd065e75c077d6de8cd3fe68699a8b47be3ec595af6b66e3629926839094609", "url": "https://files.pythonhosted.org/packages/f5/c6/4f3931c3ff80a6f2608209181adb00527970a4b43b64c3aa323383c37d51/coverage-7.17.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:8d3626b9816f7eadd0993c3703a1d8ed124b579cebe5201b446f1c0d551fa245", "url": "https://files.pythonhosted.org/packages/3e/71/31a4945459008952061147ea9b85853902507d865a7272a9f64be0c4a093/coverage-7.17.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:c9639c2ea019c948b52067a0f74485039eb84b7a2de828ba6d178e663fb2b8bd", "url": "https://files.pythonhosted.org/packages/82/6b/a4eb258b0373034f8f19aeea4c1bc4c89942b5b228812ee9952f033dc611/coverage-7.17.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:19d508b0611486f8e23e28abcba77795ee865ff0e0cd936349ba692c8d0d8ca4", "url": "https://files.pythonhosted.org/packages/02/ae/9410f2f4bbaa698ff3869c7c09e0fa10d74482a1ec8040063157612f07a0/coverage-7.17.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:cfe64d2501806a04507217852a3132a9a9076321435500bf3b11943a5c872732", "url": "https://files.pythonhosted.org/packages/ad/ee/97549ec1bdf6ff6d17612dbcf4b2205daa74510273ac1efd1460e3db275c/coverage-7.17.2-cp315-cp315-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:4274acd35aae5bbbd0908792260819b9720b171ccc099a8168f7c20ef5d6ab3a", "url": "https://files.pythonhosted.org/packages/36/72/76e04605522a6dd81bbf43e1b407ca611f415c04b67e18c123ee440b1d69/coverage-7.17.2-cp315-cp315-musllinux_1_2_i686.whl"}, + {"hash": "sha256:85bccf6fe19ea3747147387971465f6a06197d6b9f6b644a97ba90ca5b09c5cd", "url": "https://files.pythonhosted.org/packages/30/8d/163e027635790ab232f3ec0261d2a26bec34c377bea2096554261444d0f5/coverage-7.17.2-cp315-cp315-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:d5024913651813801fd43b65b6409b0d50f8bae95316c8f1512dfbd8ca96cb1f", "url": "https://files.pythonhosted.org/packages/18/35/11dd585a06be350285d42c17aa2fccd0af18d962b6979f03dd1a258eb705/coverage-7.17.2-cp315-cp315-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:99b64a12fe6017c254a8027790e0eaa22eaeec201e7dfda5eaac176a6984ef27", "url": "https://files.pythonhosted.org/packages/1a/ed/f1f3b4bf6081122bf1820b1f38352377a75a2d5f39bb6331598e165b64af/coverage-7.17.2-cp315-cp315-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:74d91af590514dda16165d5d7cfa94183150c6a6a80a99f61068369ae3959455", "url": "https://files.pythonhosted.org/packages/ab/bb/61e07a606a9f4d0f5391ac9b62b1f008a997f87860ad233f99833fd6e6bd/coverage-7.17.2-cp315-cp315-win32.whl"}, + {"hash": "sha256:075566a44289afb634ceaafa7d8d377bb2667a5b4a90fbee2609efdcd015c66e", "url": "https://files.pythonhosted.org/packages/86/40/2d4980d3374a023a34c5ea7cd5d2b93c02c5588a324ca7faf8a0a8c7dbac/coverage-7.17.2-cp315-cp315-win_amd64.whl"}, + {"hash": "sha256:be699e76b57fede8920a0f568eff23b270d9004414b7cf6744bdda851638be6e", "url": "https://files.pythonhosted.org/packages/f8/c8/b307327b1256f9ed8ffc3271db041913835cfee065fbaf45105978093c08/coverage-7.17.2-cp315-cp315-win_arm64.whl"}, + {"hash": "sha256:ffbaadf8612781358b3f949ad68dce26c5a35beaafcc48125fc231dbcce7d362", "url": "https://files.pythonhosted.org/packages/40/18/0912e37f23c101faebd7c5e544c6ba6bd750715f81f2a2c7ea66f83a2295/coverage-7.17.2-cp315-cp315t-macosx_10_15_x86_64.whl"}, + {"hash": "sha256:d4004fce7b3a8ae5ef2af835670763f12723e9b9d74837697607b89a17f77312", "url": "https://files.pythonhosted.org/packages/5e/a0/c4ce9bbf82b2e9dbf2e0326004c8c37d87a275f515fc7bb22344a6162050/coverage-7.17.2-cp315-cp315t-macosx_11_0_arm64.whl"}, + {"hash": "sha256:a838a10afb5b60a6cc6315348b176badaf2409d821208a3e3cda1dc608129e23", "url": "https://files.pythonhosted.org/packages/3f/05/9c7429c87589534755398a58fe9837fdb2870a22d99e87623fcb261e1d4e/coverage-7.17.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {"hash": "sha256:0fc17882975fe485cfc95fb2f20eed937e05e0c0d8ffa5048857cda3aa838e8a", "url": "https://files.pythonhosted.org/packages/e9/22/d1ad2ea7c8871d66f8453d33cdeec39986e6e1f2a104628e9bcb171f0209/coverage-7.17.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {"hash": "sha256:a65faa1ee98e090a5fd7c116f6d3ab5c6a85fcc46849b8494835608715836399", "url": "https://files.pythonhosted.org/packages/0d/1d/5038de4bf6549903ec3d86959c6f18aff54911616b47aa96f4d6a5660c93/coverage-7.17.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {"hash": "sha256:7c17f716c837db550a202e7732cdfaa726b8515cba88be42193616913970d223", "url": "https://files.pythonhosted.org/packages/8b/ea/68a26b4b5eb255d92de906fd66e8d720b1df15e705949ea9a9cef2cc7f8b/coverage-7.17.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {"hash": "sha256:545ca42488c7f0e62324fb2621aa05737db843b75e84e65a271bbf976f496f66", "url": "https://files.pythonhosted.org/packages/70/86/0f4238f09b22bc03a907346fabcabe5449626879cdedbc1edd362ab0e570/coverage-7.17.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"}, + {"hash": "sha256:8de787f533b13aeeea5d7139610eadb56f249cc739840b973603d1d18d0fb023", "url": "https://files.pythonhosted.org/packages/f1/ac/1885255425900f691c543d88394d9f0dacd2f2dc78422e660c747ca9c95c/coverage-7.17.2-cp315-cp315t-musllinux_1_2_aarch64.whl"}, + {"hash": "sha256:485b2ed9e13b7e9209276843d7147e2f1cd1c816582655650a64c8f922093c58", "url": "https://files.pythonhosted.org/packages/02/d6/0111601342d91e25d13e22a55f34600aa387c55b5027b90e78d941911821/coverage-7.17.2-cp315-cp315t-musllinux_1_2_i686.whl"}, + {"hash": "sha256:a472e23a7514478e1c8124ea056424d681e3173975650b0a06b6181658f1009a", "url": "https://files.pythonhosted.org/packages/70/48/d9bdc5346a9ad09711f55b6901ee05b57305e6b2b66e6edfd50f81f0d410/coverage-7.17.2-cp315-cp315t-musllinux_1_2_ppc64le.whl"}, + {"hash": "sha256:0a623de5c57c5c3f3aed830c73a3a5aad91dbf76d53bc30ec161be758c8ca408", "url": "https://files.pythonhosted.org/packages/98/75/3a4d8ce4aa59ed2e898364acf923b39471689d51876bb978c42e664cdb79/coverage-7.17.2-cp315-cp315t-musllinux_1_2_riscv64.whl"}, + {"hash": "sha256:2ad189c65706306644399e48530ad1aea1dfb311bd21d988df1ace422d09f3f8", "url": "https://files.pythonhosted.org/packages/4a/11/d15082e504e7852a25848ee0dcb59453579ec923c6d1f3010316e1fb6118/coverage-7.17.2-cp315-cp315t-musllinux_1_2_x86_64.whl"}, + {"hash": "sha256:6fd6f218e39a621a30dbaf9543831fa10fe713afc8a44b52fc42cfe277412609", "url": "https://files.pythonhosted.org/packages/f1/9d/6460101b0191afae9ed84c8d8136cc8cc09675f376ce08acb3118b8c27be/coverage-7.17.2-cp315-cp315t-win32.whl"}, + {"hash": "sha256:ef07aae0a666ef5edcab5bc1bb590468c6f4120a8cdad50a010d042a59ab6e77", "url": "https://files.pythonhosted.org/packages/9a/f2/3462d9616445a8cb36ec2e027d1c0ae82f6f90f224d9851ba0b22b7425d6/coverage-7.17.2-cp315-cp315t-win_amd64.whl"}, + {"hash": "sha256:c68f64d96857749a215657c172f7144ed026063489686b901774f271997923c2", "url": "https://files.pythonhosted.org/packages/4c/06/abeca0cf853e77aba9464d4ada4603c8b6e744b667c13e1d4eefecc487af/coverage-7.17.2-cp315-cp315t-win_arm64.whl"}, + {"hash": "sha256:e674027ceb6bfd959f1bb035000fd20fa8b717dc6765adaaf8bca347822c8c7d", "url": "https://files.pythonhosted.org/packages/da/91/1805cc144497d76a436f66a21fa57492bee7ed1257826fb992f03b9b2b6f/coverage-7.17.2-py3-none-any.whl"} +]}, +{"dependencies": [{"marker": "python_full_version < '3.10'", "name": "coverage", "source": {"registry": "https://pypi.org/simple"}, "version": "7.10.7"}, {"marker": "python_full_version == '3.10.*'", "name": "coverage", "source": {"registry": "https://pypi.org/simple"}, "version": "7.16.2"}, {"marker": "python_full_version >= '3.11'", "name": "coverage", "source": {"registry": "https://pypi.org/simple"}, "version": "7.17.2"}], "name": "rules-python-coverage", "source": {"virtual": "."}, "version": "0.0.0"} +]} +""" diff --git a/python/private/coverage/workspace_deps.bzl b/python/private/coverage/workspace_deps.bzl deleted file mode 100755 index 0759b7c2a2..0000000000 --- a/python/private/coverage/workspace_deps.bzl +++ /dev/null @@ -1,757 +0,0 @@ -"""The coverage wheels for the WORKSPACE toolchains. - -@generated by `bazel run //dev/update_deps:update_coverage_workspace_deps` from -the `uv.lock` file in this directory. DO NOT EDIT. -""" - -COVERAGE_WHEELS = { - "coverage_cp310_cp310_macosx_10_9_x86_64_23219888": { - "filename": "coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl", - "integrity": "sha256-IyGYiEd+3XNrb8rsEnLUfZO5JumZZB/+p+U6FzjnCys=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/60/72/db17bf5f87568ab524413693385be2eeb03e652ab56c54ea05fa85515675/coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl", - }, - "coverage_cp310_cp310_macosx_11_0_arm64_40c0f008": { - "filename": "coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl", - "integrity": "sha256-QMDwCJn+YYGuf0NM6yAOUfXuS47RDjtfC2BfDK4V2oc=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/d1/e1/285727a8a74d48de256e8605413ee4cf82301228253ea58e5ab4270138b0/coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl", - }, - "coverage_cp310_cp310_manylinux_2_17_aarch64_9fd670ac": { - "filename": "coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-n9ZwrEO3CcV1rvwlv1LYpZijvFAXvd/QoXkVKrBqLes=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/0e/19/14a8e44cbb2ad36ae62aaa03c5eec4a06a7f7df220ea3776af26927acbe2/coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp310_cp310_manylinux_2_17_ppc64le_705e5af1": { - "filename": "coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-cF5a8R00ZH79wXDHhAtoV8gc90vpZBmlU/I35o5iy3I=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/f9/72/f5bcad0d9a9b450080032344fbff7ec60c1b0e3d38019f7735dee2b69645/coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp310_cp310_manylinux_2_31_riscv64_8afd9bf3": { - "filename": "coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-iv2b81zGofIus2NICPqOC5GQJFnFch7y5EYd/ncdfwg=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/ae/9a/8c735234e8abb52bf5d063f98c780fa942e77c4c0225f9fcb9b33effc346/coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp310_cp310_manylinux_2_5_i686_a4624f80": { - "filename": "coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-pGJPgHMva0J6xY8fWcV3oJlKEugXS1r2oCe0tYeV1MM=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/50/c7/c737b73bac9bf5034f5ff45a4237b7a189d417a4e36751faf5d10c082cbe/coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp310_cp310_manylinux_2_5_x86_64_191803c4": { - "filename": "coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-GRgDxJlrSZ/NeMKtXl92fcxTy03G3m1qdBtEOhgh7wI=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/29/4e/e1d38d27817d91776ca543d01276a67eace3df675a673cac32b12c167d58/coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp310_cp310_musllinux_1_2_x86_64_c85d54e7": { - "filename": "coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", - "integrity": "sha256-yF1U5+iiypMv6DmTAa+bjVkH6ipFX/r/bn0SCNuDuUM=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/57/d3/84cd6a11e707e422194739e9734d947746efa5e5b3358ff2d601f823ee45/coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", - }, - "coverage_cp310_cp310_win32_5139009b": { - "filename": "coverage-7.16.2-cp310-cp310-win32.whl", - "integrity": "sha256-UTkAm179IZT8Fo7pNi8OGRumEu9dKSQvkmnCL5uPgMc=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/ab/bc/3d84c2e2a95f38e346ce3730d9f0e53114f4e8aace72e6e51e014afb9992/coverage-7.16.2-cp310-cp310-win32.whl", - }, - "coverage_cp310_cp310_win_amd64_c3305c38": { - "filename": "coverage-7.16.2-cp310-cp310-win_amd64.whl", - "integrity": "sha256-wzBcOKL6IaQlTyrOfdnvX8VpyaVYtm5wF2ULPWN/uV4=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/07/86/31f1f3170571a345ab9d8361a7b2f8e0c5698173fbecd1f7149b6e2089ee/coverage-7.16.2-cp310-cp310-win_amd64.whl", - }, - "coverage_cp311_cp311_macosx_10_9_x86_64_a3f49ac9": { - "filename": "coverage-7.17.2-cp311-cp311-macosx_10_9_x86_64.whl", - "integrity": "sha256-o/Saycnl/UtFBnfrRPSxTqmrcZULKJaIjJ9qItXNF3M=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/5e/9d/e3db9071ebeda48e47e5be0fd0404219954e7888f64e7c37df324a79dc3a/coverage-7.17.2-cp311-cp311-macosx_10_9_x86_64.whl", - }, - "coverage_cp311_cp311_macosx_11_0_arm64_3d3d9d79": { - "filename": "coverage-7.17.2-cp311-cp311-macosx_11_0_arm64.whl", - "integrity": "sha256-PT2deaK2zc3eWhqxromVP66lyyjA7qF6QTrZl7s8yKE=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/49/a1/260786d936940102052a51cb166f80f4a98e67fb6142aa050f1af48a2385/coverage-7.17.2-cp311-cp311-macosx_11_0_arm64.whl", - }, - "coverage_cp311_cp311_manylinux_2_17_aarch64_1947bd9d": { - "filename": "coverage-7.17.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-GUe9nYLkBciSxQsNjcDB3qXfKpYhKcojjFAKa8+IdQk=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/ae/99/efefaa02a4b2876ffa4a389c81a8074c888442b12c64046b448718fa5590/coverage-7.17.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp311_cp311_manylinux_2_17_ppc64le_2cd1a747": { - "filename": "coverage-7.17.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-LNGnRyqXL+0ByJWJsVYIRuMulC+DKaUVxAWdRSHGV5o=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/19/2f/a565096e5dcf8c511332d397d712b056f06091ab3efc10adb5a4d22b0cc2/coverage-7.17.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp311_cp311_manylinux_2_31_riscv64_2b7fe217": { - "filename": "coverage-7.17.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-K3/iF4CCOUN7hQm4af3i4MrEh3amjGJr1rFUEbCH494=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/ab/09/87f9b02f97179f2c76fa5724e0ce00b9db2bbe16b7e21d89c8fc1f3a77d9/coverage-7.17.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp311_cp311_manylinux_2_5_i686_5b71aeaa": { - "filename": "coverage-7.17.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-W3GuqkRAy+zlSYrNowg4aFeT07uWCv+H7Jf7tUZxWl8=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/be/80/f5176b7f6aa9e05e37e1bb5eae473c5d015485c36206934f53b2b2795076/coverage-7.17.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp311_cp311_manylinux_2_5_x86_64_01303d9e": { - "filename": "coverage-7.17.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-ATA9nmi25r3FRZtU470ONTnJTCW1Csc2K3y8rLyBX7w=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/03/9f/4669ca11ce1932cc18fce766ead327ceb41a92055608c179b137067e2805/coverage-7.17.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp311_cp311_musllinux_1_2_x86_64_7c123b6b": { - "filename": "coverage-7.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", - "integrity": "sha256-fBI7a7wYOhQ3IDZ2nwMwmAJqp1/I+6N9AosLdyNW6Qs=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/a7/18/098ea8ee450922eec7ab48a35b4a7fd6a49c4a97416d1e4cf66092c24dc4/coverage-7.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", - }, - "coverage_cp311_cp311_win32_1c3146ef": { - "filename": "coverage-7.17.2-cp311-cp311-win32.whl", - "integrity": "sha256-HDFG750UwBFRdCJIl4SnrDREd+nNrLBdlSnpEnIc1dg=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/8e/1c/a6ee041c4bc8b6ec7bc9f417daf89e3ee702182a48d573267ccaaed17c64/coverage-7.17.2-cp311-cp311-win32.whl", - }, - "coverage_cp311_cp311_win_amd64_51c6a60e": { - "filename": "coverage-7.17.2-cp311-cp311-win_amd64.whl", - "integrity": "sha256-UcamDmPqmTET6lzSSkAcwvrdxUqnrP3a/6XTuMAWG50=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/5a/df/7f077fb2e71b1d73fe8285be7c6ba43dc085f9eaa2384d4e8e55560073a3/coverage-7.17.2-cp311-cp311-win_amd64.whl", - }, - "coverage_cp311_cp311_win_arm64_b08ee3ef": { - "filename": "coverage-7.17.2-cp311-cp311-win_arm64.whl", - "integrity": "sha256-sI7j71PoDlpQxos9l2Q2Pjc6y6VA4bWNruCpp/I/37I=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/90/56/89dcd5907d0cc04b07746e62e1cb896c0f694e5af40aa0c980458825c6c5/coverage-7.17.2-cp311-cp311-win_arm64.whl", - }, - "coverage_cp312_cp312_macosx_10_13_x86_64_289a2444": { - "filename": "coverage-7.17.2-cp312-cp312-macosx_10_13_x86_64.whl", - "integrity": "sha256-KJokRDNScTdF95wbaq5Omc7S9f8+hrtjDDPGMdUPIXw=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/22/6a/a51808ac625c457326fb37b174d35e1badb1324c6def5d9668089c4b14ef/coverage-7.17.2-cp312-cp312-macosx_10_13_x86_64.whl", - }, - "coverage_cp312_cp312_macosx_11_0_arm64_da4b0aef": { - "filename": "coverage-7.17.2-cp312-cp312-macosx_11_0_arm64.whl", - "integrity": "sha256-2ksK70J+uW5CK0ROXCw9OHaP7bkUE0YpTDhxeosVtBY=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/31/24/f32ec1a5ef947294252e77c5d40ac0903ba1de4c92638edfcca8629c067f/coverage-7.17.2-cp312-cp312-macosx_11_0_arm64.whl", - }, - "coverage_cp312_cp312_manylinux_2_17_aarch64_1682d98d": { - "filename": "coverage-7.17.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-FoLZje0oUkvGhTzE58ZRdbPpF/OLLi3euyjK/Yq03nQ=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/d8/72/17b5df5d8211e1880169b93197ba98b21b30ccf8f116284536113b7582c5/coverage-7.17.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp312_cp312_manylinux_2_17_ppc64le_8eab0a5c": { - "filename": "coverage-7.17.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-jqsKXP+NvFY9FBam4Hm6PBRnEuRxZ8Aipd9eoBvXbfk=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/de/0c/603ba7fff1aa4c8064fdf4a1fff6d9fb51c72a5828429dfe858be48e38b3/coverage-7.17.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp312_cp312_manylinux_2_31_riscv64_635337a9": { - "filename": "coverage-7.17.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-Y1M3qWiV8v/P4qp1VEaKmI9Gj7Z6gAfBpl6VFIwYVak=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/f4/ca/f2ae6e47e18b35a0dbddb9d8f51be34a6d66b41120f2e0f7363ccc0812f4/coverage-7.17.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp312_cp312_manylinux_2_5_i686_90c6c5f9": { - "filename": "coverage-7.17.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-kMbF+TYGuIA71/igsq4F33412kUEocU8/pKUoXMeH0k=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/01/8e/3097367327c69e65521b9d9bf8b9d2ddecefa8c21bd08e17bc068bd3db93/coverage-7.17.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp312_cp312_manylinux_2_5_x86_64_36314997": { - "filename": "coverage-7.17.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-NjFJl1MyZr3j0HdlKJZ0ME1hv+SRZbsbT6m+EstLoNE=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/73/65/774fc4ec86807b6ee66b46465bc49b34bb6d291e9bff6ddfa9708ccb4053/coverage-7.17.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp312_cp312_musllinux_1_2_x86_64_686b6f8f": { - "filename": "coverage-7.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", - "integrity": "sha256-aGtvj55Xr8OkQJaxfujefhHgoS8tqBniUEevHrOw/G8=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/1a/85/6c923d71c76566c6c65a2af104d39d69d44ae708a700de017e6c56fedfc6/coverage-7.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", - }, - "coverage_cp312_cp312_win32_ee083412": { - "filename": "coverage-7.17.2-cp312-cp312-win32.whl", - "integrity": "sha256-7gg0EhcBGAdLzHQ2H8vHEE620LfJx16zuEpMMkQQxTE=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/54/f9/fd3c37b09891bfdfece486a6145345bf2b32332045a678afd293a7f2f05d/coverage-7.17.2-cp312-cp312-win32.whl", - }, - "coverage_cp312_cp312_win_amd64_e269dddf": { - "filename": "coverage-7.17.2-cp312-cp312-win_amd64.whl", - "integrity": "sha256-4mnd3166wg38xaKkWVKKnSIauCjLMd/fGcfPrNvO5nM=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/dc/b8/4d395163c9bfae0e991abbd14dea9c9bbb4ade904e5f297cbfb7b8150216/coverage-7.17.2-cp312-cp312-win_amd64.whl", - }, - "coverage_cp312_cp312_win_arm64_bac3c79c": { - "filename": "coverage-7.17.2-cp312-cp312-win_arm64.whl", - "integrity": "sha256-usPHnDH0UnQRftZMZe/q7YVmVmiuun2AnHw4gBrabtQ=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/a3/02/1024b8d4f60a9ecc0ee41a6000425caa63855d63e51da84c93c0b1dff894/coverage-7.17.2-cp312-cp312-win_arm64.whl", - }, - "coverage_cp313_cp313_macosx_10_13_x86_64_e6c89d63": { - "filename": "coverage-7.17.2-cp313-cp313-macosx_10_13_x86_64.whl", - "integrity": "sha256-5sidY9Q3OHEs4f6923dY/+tW3gjxsr5orrmpx/7nEsc=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/9c/fa/967e829238dda21c4bce17aef2f739187678c8e6e6fc880a8454945a7433/coverage-7.17.2-cp313-cp313-macosx_10_13_x86_64.whl", - }, - "coverage_cp313_cp313_macosx_11_0_arm64_f94d465e": { - "filename": "coverage-7.17.2-cp313-cp313-macosx_11_0_arm64.whl", - "integrity": "sha256-+U1GXuAOvmlMqGbWa75F+gRhixr8s3fek0aU9iT6fPY=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/9d/df/803ba08e8937759dc3f18b93ece82f81b2fb446e3846e4d60a06a4e77647/coverage-7.17.2-cp313-cp313-macosx_11_0_arm64.whl", - }, - "coverage_cp313_cp313_manylinux_2_17_aarch64_95bd8dc5": { - "filename": "coverage-7.17.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-lb2NxbNW948QqWfsUPiQLVbyTGI3U6yfzgq6nWIT1cU=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/62/c3/429b500937ac446e36fcce517d040b0b8bd04a4bfa087bdf864175553fea/coverage-7.17.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp313_cp313_manylinux_2_17_ppc64le_a5565a9d": { - "filename": "coverage-7.17.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-pVZandcVpalP/PmgrUtrFQG1RB/WfWYOeHQq3/XTego=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/c1/91/263224a77de3ab1ee42d110b09f1ac9a02638794d3e002a859822638256a/coverage-7.17.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp313_cp313_manylinux_2_31_riscv64_091eba42": { - "filename": "coverage-7.17.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-CR66QkjTGwOCj8ZXyW0UrfqodU9a7HtzKYuCiUhY2wY=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/0f/44/99b72f2961f4ba54ac14ccb016f3b212cc9a9dfda4c37e07a208efe01f9f/coverage-7.17.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp313_cp313_manylinux_2_5_i686_7ce0acb2": { - "filename": "coverage-7.17.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-fOCssnqCPo5RaSgvU48OGChUzItHK4jUVFbwm2/jZP0=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/4a/12/92dcd9206e7bb817af42b8663cfeefdbb2c5a6c92cc7069d43e6892bbdeb/coverage-7.17.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp313_cp313_manylinux_2_5_x86_64_70daa8e4": { - "filename": "coverage-7.17.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-cNqo5LSVQbUcC666DYArDRTTIU2u68Bxs9sLDH3IRkY=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/48/2d/6b028df8050caef4ce0df177686c92dba16ab7cf004040e5518f95b57021/coverage-7.17.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp313_cp313_musllinux_1_2_x86_64_e09c3781": { - "filename": "coverage-7.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", - "integrity": "sha256-4Jw3gXdou3H+PnC2zl5qwaDNc5hTbC8wpEYCGMe2bpM=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/72/9f/0d67883bf55adf50424728937cbee54d76fa137817e6bb20f97061d01816/coverage-7.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", - }, - "coverage_cp313_cp313_win32_c10f2a27": { - "filename": "coverage-7.17.2-cp313-cp313-win32.whl", - "integrity": "sha256-wQ8qJ0jdCE5EgRu5sml5djr5ADEV2b+TwdppARlEcZ8=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/a8/22/71965edc14736073b55a40e521791d9233d5c3194fa6887a94f4431fbbcc/coverage-7.17.2-cp313-cp313-win32.whl", - }, - "coverage_cp313_cp313_win_amd64_8dc07ed3": { - "filename": "coverage-7.17.2-cp313-cp313-win_amd64.whl", - "integrity": "sha256-jcB+00vLIG9p1bVP04dPe/OayUHJ967CtTsph0/P13o=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/47/0d/715cb37f51f908a6f6f33fe8c6fc95ab638b17cb014b1650daae0893c160/coverage-7.17.2-cp313-cp313-win_amd64.whl", - }, - "coverage_cp313_cp313_win_arm64_bb742836": { - "filename": "coverage-7.17.2-cp313-cp313-win_arm64.whl", - "integrity": "sha256-u3QoNmGiOf2vc4rAsH207ZtHbHUy0+cTq/D2oHJ4HmA=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/39/f2/a5099bdc2a530f29c818a1c7e706669451c10e5f0eb62dcff76ef3568668/coverage-7.17.2-cp313-cp313-win_arm64.whl", - }, - "coverage_cp314_cp314_macosx_10_15_x86_64_36248fb6": { - "filename": "coverage-7.17.2-cp314-cp314-macosx_10_15_x86_64.whl", - "integrity": "sha256-NiSPtsAzQVARaVObyrNes6WRWRAL4SRvrTwUy3PBdoU=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/10/7e/60a51850d3ff659b82cc211bc98fa126aab2ac9b81bd4ed6abc3e3720a09/coverage-7.17.2-cp314-cp314-macosx_10_15_x86_64.whl", - }, - "coverage_cp314_cp314_macosx_11_0_arm64_2a628101": { - "filename": "coverage-7.17.2-cp314-cp314-macosx_11_0_arm64.whl", - "integrity": "sha256-KmKBARz4Vpl2O/iKtV4HdPZpb4ezkPORUrFOzvrcy1k=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/77/80/a9539c15076fb9b95d8cbdb7f0caec90857d80bddca3eeca439f5e2e64e2/coverage-7.17.2-cp314-cp314-macosx_11_0_arm64.whl", - }, - "coverage_cp314_cp314_manylinux_2_17_aarch64_e6bf531d": { - "filename": "coverage-7.17.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-5r9THerr+PabpgFbuj+4F8q2aU37aawqcIiJ3Fmjkm4=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/05/54/a666b65c5bb145d8796b7902ff1c0f60600a6c2a08949e20f2fb7af15de3/coverage-7.17.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp314_cp314_manylinux_2_17_ppc64le_88d4dc83": { - "filename": "coverage-7.17.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-iNTcg7atCaITTK2djseAsnSxesffuowsbpLjvEaP6F0=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/a6/73/ac9d06da9e0c143b874a68b17d8dfeedc45acdf1bdcee5b22a42bece098e/coverage-7.17.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp314_cp314_manylinux_2_31_riscv64_8dc17707": { - "filename": "coverage-7.17.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-jcF3B/1GO036tTeS5B1QojMWrGuYU5AHqiOIodRKhTU=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/87/b1/8a500e16cf787383b00b5a86eb50a490b4d8ebde505f92e1aaca515afc0a/coverage-7.17.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp314_cp314_manylinux_2_5_i686_3dfefb1e": { - "filename": "coverage-7.17.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-Pf77HsFsoaAeXaQLoWLiaM7YvTYkueuk6ThOv6FK9+w=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/7f/3b/0a84b6032511834105fd4f8a8b4e76927783cea03dc4796032d5c4bfec8b/coverage-7.17.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp314_cp314_manylinux_2_5_x86_64_34e5c7a2": { - "filename": "coverage-7.17.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-NOXHoto2gExubxCzaOrgGtVvWkrF7M5ylzAKPUb97yo=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/2e/e4/a3e6b8bcc219b00ea91e36f071c2659b253e5d6e1b258c31eb4524231718/coverage-7.17.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp314_cp314_musllinux_1_2_x86_64_460ba213": { - "filename": "coverage-7.17.2-cp314-cp314-musllinux_1_2_x86_64.whl", - "integrity": "sha256-RguiE5hm51jDqOnZQi/ciJlCvLxcyvcQ3E+9hwfRSqg=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/14/b5/13a62eac66cecbaaa098bb753a3693b44454a4d21d53db4f1a0953f35f0b/coverage-7.17.2-cp314-cp314-musllinux_1_2_x86_64.whl", - }, - "coverage_cp314_cp314_win32_e72cf955": { - "filename": "coverage-7.17.2-cp314-cp314-win32.whl", - "integrity": "sha256-5yz5VW95nD/OdZfBvX2/yRBL770ICI3rO5rp/ZgRfAI=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/53/13/9ea31cfa72434449b9a98666760169d1e0ab31b3f6cdb12e68149106668e/coverage-7.17.2-cp314-cp314-win32.whl", - }, - "coverage_cp314_cp314_win_amd64_205f9e32": { - "filename": "coverage-7.17.2-cp314-cp314-win_amd64.whl", - "integrity": "sha256-IF+eMp2dXkfDIPhxIyagFiBontrlBTHMiTRRHixej34=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/f6/cc/aa8f34b014979c973740f5a5ed6861ecfc6009082c70e72030ca1519e0f8/coverage-7.17.2-cp314-cp314-win_amd64.whl", - }, - "coverage_cp314_cp314_win_arm64_a5909dd0": { - "filename": "coverage-7.17.2-cp314-cp314-win_arm64.whl", - "integrity": "sha256-pZCd0IcAbHDBKClzErpn2HcSPocRAGRdm9c64CGk0B0=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/49/64/16afaa19b3886dc157c3c5ebace5241b84b96a005cb28545584e66b80895/coverage-7.17.2-cp314-cp314-win_arm64.whl", - }, - "coverage_cp314_cp314t_macosx_10_15_x86_64_674bcd02": { - "filename": "coverage-7.17.2-cp314-cp314t-macosx_10_15_x86_64.whl", - "integrity": "sha256-Z0vNAjo3Q2ZtTXoVI45XoAyWhVN4CjbIDTd5/EELkSw=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/e8/95/acc865e1411c8ebc5c5d230367f3bfed5c605ed54bb41b84fc58f4e7f137/coverage-7.17.2-cp314-cp314t-macosx_10_15_x86_64.whl", - }, - "coverage_cp314_cp314t_macosx_11_0_arm64_ba45dc07": { - "filename": "coverage-7.17.2-cp314-cp314t-macosx_11_0_arm64.whl", - "integrity": "sha256-ukXcByv78XmOaOyBN5tzRz/JEhdL/5+Rkk13cRodKzA=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/10/98/02c37c6a928ccfc84b7a43eda44d96d3b1ccbc93989c178754b7e67e7650/coverage-7.17.2-cp314-cp314t-macosx_11_0_arm64.whl", - }, - "coverage_cp314_cp314t_manylinux_2_17_aarch64_d0332e2b": { - "filename": "coverage-7.17.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-0DMuKyf8XMdh1rijMRi5WOaka+fwD9MOA6XPEDNQwLM=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/63/e7/bd6dcec5b8130f5bd8679087a2bc3a50a53c4ee78d5ec449610020088ff3/coverage-7.17.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp314_cp314t_manylinux_2_17_ppc64le_27fc4384": { - "filename": "coverage-7.17.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-J/xDhKEVT33zDTBMzdwF70VzaMBlZw7L05JgYnmzMpo=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/55/c1/b3f06aa1f1c51f4295fdfa6500eb7409aaa6273cf5f27bafc25217f3547d/coverage-7.17.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp314_cp314t_manylinux_2_31_riscv64_603140ee": { - "filename": "coverage-7.17.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-YDFA7q4/mtxWL5GxuY1NH8SRvR9mxQgkgsVo0LeJG7E=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/a7/f0/b68ab3654af3541488b26856e3043c13c782d8e6104fc8149746ecaa83f0/coverage-7.17.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp314_cp314t_manylinux_2_5_i686_0d8a5dc7": { - "filename": "coverage-7.17.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-DYpdxwnam5CtXMhZqbiMm4AeZcovv3y9vtW5y/RX/Mk=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/28/ab/d503b0aaebb92b171260469463915af5e16a64457b289d4cb844e8c366ac/coverage-7.17.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp314_cp314t_manylinux_2_5_x86_64_248873a1": { - "filename": "coverage-7.17.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-JIhzoUTAZ/mBdC/0u/tItX4oGoy3o4IdTik721C0eXA=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/f6/e5/6d6ba15bde22fcccd07e14305be14291d041f732771a73f787297445d3e7/coverage-7.17.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp314_cp314t_musllinux_1_2_x86_64_d88fcc24": { - "filename": "coverage-7.17.2-cp314-cp314t-musllinux_1_2_x86_64.whl", - "integrity": "sha256-2I/MJEcNc+1DYOkQ+FSXGLkTulb7h3XZ75KaZHg13+o=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/bb/f6/43d40d1b4d4892e54c68a241cd5b92efed4d5934109e0e5e24a94759f732/coverage-7.17.2-cp314-cp314t-musllinux_1_2_x86_64.whl", - }, - "coverage_cp314_cp314t_win32_2c42c222": { - "filename": "coverage-7.17.2-cp314-cp314t-win32.whl", - "integrity": "sha256-LELCIpObhpnnvA5FMUlifyEeu7svdMGgQzGwzZg04sg=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/8e/33/6874185b791c71684c401fc74abd71bbdb102e8ee41c8f380cf25af41d29/coverage-7.17.2-cp314-cp314t-win32.whl", - }, - "coverage_cp314_cp314t_win_amd64_b94c509e": { - "filename": "coverage-7.17.2-cp314-cp314t-win_amd64.whl", - "integrity": "sha256-uUxQniZgO6f4yxv4u2OWfHvlM0k75WaNhELNBuyFPXY=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/16/e0/9057b4cf962ca6089b144f0d93a748f5e2d13876dd0f33f9d5a2c2ec49d2/coverage-7.17.2-cp314-cp314t-win_amd64.whl", - }, - "coverage_cp314_cp314t_win_arm64_eca90e02": { - "filename": "coverage-7.17.2-cp314-cp314t-win_arm64.whl", - "integrity": "sha256-7KkOAoky3ll+LFD0VK2MxjEoHhTSC5+AGmSatVXetYA=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/5e/4f/bd626297c942380279dff323e1d8a8937990d553119a4f887358488b1b54/coverage-7.17.2-cp314-cp314t-win_arm64.whl", - }, - "coverage_cp315_cp315_macosx_10_15_x86_64_298849ca": { - "filename": "coverage-7.17.2-cp315-cp315-macosx_10_15_x86_64.whl", - "integrity": "sha256-KYhJyg44V2cJaZXLV/cgWbRp9D+ZJ7g5PynRNPe7EGI=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/f3/08/e110687f24245edbbe9c0986a6ccd04f1f9a6e90e329cbd4347e2b81d76e/coverage-7.17.2-cp315-cp315-macosx_10_15_x86_64.whl", - }, - "coverage_cp315_cp315_macosx_11_0_arm64_81a43279": { - "filename": "coverage-7.17.2-cp315-cp315-macosx_11_0_arm64.whl", - "integrity": "sha256-gaQyeTRA7e8mjbrdSTOTvW9w+kuH92xYEU9/d/HM+uo=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/93/24/7cfcd6adb4a0c18a10de12fd695edbf8cd121fdca0a6a63cea1bffc029fe/coverage-7.17.2-cp315-cp315-macosx_11_0_arm64.whl", - }, - "coverage_cp315_cp315_manylinux_2_17_aarch64_8d3626b9": { - "filename": "coverage-7.17.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-jTYmuYFvfq3QmTw3A6HY7RJLV5zr5SAbRG8cDVUfokU=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/3e/71/31a4945459008952061147ea9b85853902507d865a7272a9f64be0c4a093/coverage-7.17.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp315_cp315_manylinux_2_17_ppc64le_c9639c2e": { - "filename": "coverage-7.17.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-yWOcLqAZyUi1IGeg90SFA564S3ot6Ci6bReOZj+yuL0=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/82/6b/a4eb258b0373034f8f19aeea4c1bc4c89942b5b228812ee9952f033dc611/coverage-7.17.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp315_cp315_manylinux_2_31_riscv64_19d508b0": { - "filename": "coverage-7.17.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-GdUIsGEUhvjiPiiry6d3le6GX/DgzZNjSbppLI0NjKQ=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/02/ae/9410f2f4bbaa698ff3869c7c09e0fa10d74482a1ec8040063157612f07a0/coverage-7.17.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp315_cp315_manylinux_2_5_i686_d14872cc": { - "filename": "coverage-7.17.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-0UhyzO81lyxKwpDyQy9D0Ke7IwyuK17SaHGrhiIei18=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/54/7a/af167a0bb6d0564209d35c8039b290327ecf7b3e34f838f9360b56d3a03c/coverage-7.17.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp315_cp315_manylinux_2_5_x86_64_3bd065e7": { - "filename": "coverage-7.17.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-O9Bl51wHfW3ozT/mhpmotHvj7Fla9rZuNimSaDkJRgk=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/f5/c6/4f3931c3ff80a6f2608209181adb00527970a4b43b64c3aa323383c37d51/coverage-7.17.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp315_cp315_musllinux_1_2_x86_64_99b64a12": { - "filename": "coverage-7.17.2-cp315-cp315-musllinux_1_2_x86_64.whl", - "integrity": "sha256-mbZKEv5gF8JUqAJ3kODqoi6u7CAeff2l6qwXammE7yc=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/1a/ed/f1f3b4bf6081122bf1820b1f38352377a75a2d5f39bb6331598e165b64af/coverage-7.17.2-cp315-cp315-musllinux_1_2_x86_64.whl", - }, - "coverage_cp315_cp315_win32_74d91af5": { - "filename": "coverage-7.17.2-cp315-cp315-win32.whl", - "integrity": "sha256-dNka9ZBRTdoWFl1dfPqUGDFQxqaoCpn2EGg2muOVlFU=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/ab/bb/61e07a606a9f4d0f5391ac9b62b1f008a997f87860ad233f99833fd6e6bd/coverage-7.17.2-cp315-cp315-win32.whl", - }, - "coverage_cp315_cp315_win_amd64_075566a4": { - "filename": "coverage-7.17.2-cp315-cp315-win_amd64.whl", - "integrity": "sha256-B1VmpEKJr7Y0zqr6fY03e7JmeltKkPvuJgnv3NAVxm4=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/86/40/2d4980d3374a023a34c5ea7cd5d2b93c02c5588a324ca7faf8a0a8c7dbac/coverage-7.17.2-cp315-cp315-win_amd64.whl", - }, - "coverage_cp315_cp315_win_arm64_be699e76": { - "filename": "coverage-7.17.2-cp315-cp315-win_arm64.whl", - "integrity": "sha256-vmmedrV/7eiSCg9Wjv8jsnDZAEQUt89nRL3ahRY4vm4=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/f8/c8/b307327b1256f9ed8ffc3271db041913835cfee065fbaf45105978093c08/coverage-7.17.2-cp315-cp315-win_arm64.whl", - }, - "coverage_cp315_cp315t_macosx_10_15_x86_64_ffbaadf8": { - "filename": "coverage-7.17.2-cp315-cp315t-macosx_10_15_x86_64.whl", - "integrity": "sha256-/7qt+GEngTWLP5Sa1o3OJsWjW+qvzEgSX8Ix28zn02I=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/40/18/0912e37f23c101faebd7c5e544c6ba6bd750715f81f2a2c7ea66f83a2295/coverage-7.17.2-cp315-cp315t-macosx_10_15_x86_64.whl", - }, - "coverage_cp315_cp315t_macosx_11_0_arm64_d4004fce": { - "filename": "coverage-7.17.2-cp315-cp315t-macosx_11_0_arm64.whl", - "integrity": "sha256-1ABPzns6iuXvKvg1Zwdj8Scj6bnXSDdpdge4mhf3cxI=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/5e/a0/c4ce9bbf82b2e9dbf2e0326004c8c37d87a275f515fc7bb22344a6162050/coverage-7.17.2-cp315-cp315t-macosx_11_0_arm64.whl", - }, - "coverage_cp315_cp315t_manylinux_2_17_aarch64_a65faa1e": { - "filename": "coverage-7.17.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-pl+qHumOCQpf18EW9tOrXGqF/MRoSbhJSDVghxWDY5k=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/0d/1d/5038de4bf6549903ec3d86959c6f18aff54911616b47aa96f4d6a5660c93/coverage-7.17.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp315_cp315t_manylinux_2_17_ppc64le_7c17f716": { - "filename": "coverage-7.17.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - "integrity": "sha256-fBf3Fsg321UKIC53Ms36pya4UVy6iL5CGTYWkTlw0iM=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/8b/ea/68a26b4b5eb255d92de906fd66e8d720b1df15e705949ea9a9cef2cc7f8b/coverage-7.17.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", - }, - "coverage_cp315_cp315t_manylinux_2_31_riscv64_545ca424": { - "filename": "coverage-7.17.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-VFykJIjH8OYjJPsmIaoFc324Q7dehOZaJxu/l29Jb2Y=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/70/86/0f4238f09b22bc03a907346fabcabe5449626879cdedbc1edd362ab0e570/coverage-7.17.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp315_cp315t_manylinux_2_5_i686_a838a10a": { - "filename": "coverage-7.17.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-qDihCvtbYKbMYxU0ixdrra8kCdghIIo+PNodxggSniM=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/3f/05/9c7429c87589534755398a58fe9837fdb2870a22d99e87623fcb261e1d4e/coverage-7.17.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp315_cp315t_manylinux_2_5_x86_64_0fc17882": { - "filename": "coverage-7.17.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-D8F4gpdf5IXPyV+y8g7tk34F4MDY/6UEiFfNo6qDjoo=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/e9/22/d1ad2ea7c8871d66f8453d33cdeec39986e6e1f2a104628e9bcb171f0209/coverage-7.17.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp315_cp315t_musllinux_1_2_x86_64_2ad189c6": { - "filename": "coverage-7.17.2-cp315-cp315t-musllinux_1_2_x86_64.whl", - "integrity": "sha256-KtGJxlcGMGZEOZ5IUwrRrqHfsxG9IdmI3xrOQi0J8/g=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/4a/11/d15082e504e7852a25848ee0dcb59453579ec923c6d1f3010316e1fb6118/coverage-7.17.2-cp315-cp315t-musllinux_1_2_x86_64.whl", - }, - "coverage_cp315_cp315t_win32_6fd6f218": { - "filename": "coverage-7.17.2-cp315-cp315t-win32.whl", - "integrity": "sha256-b9byGOOaYhow26+VQ4MfoQ/nE6/IpEtS/ELP4ndBJgk=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/f1/9d/6460101b0191afae9ed84c8d8136cc8cc09675f376ce08acb3118b8c27be/coverage-7.17.2-cp315-cp315t-win32.whl", - }, - "coverage_cp315_cp315t_win_amd64_ef07aae0": { - "filename": "coverage-7.17.2-cp315-cp315t-win_amd64.whl", - "integrity": "sha256-7weq4KZm717cq1vBu1kEaMb0EgqM2tUKAQ0EKlmrbnc=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/9a/f2/3462d9616445a8cb36ec2e027d1c0ae82f6f90f224d9851ba0b22b7425d6/coverage-7.17.2-cp315-cp315t-win_amd64.whl", - }, - "coverage_cp315_cp315t_win_arm64_c68f64d9": { - "filename": "coverage-7.17.2-cp315-cp315t-win_arm64.whl", - "integrity": "sha256-xo9k2WhXdJohVlfBcvcUTtAmBjSJaGuQF3TycZl5I8I=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/4c/06/abeca0cf853e77aba9464d4ada4603c8b6e744b667c13e1d4eefecc487af/coverage-7.17.2-cp315-cp315t-win_arm64.whl", - }, - "coverage_cp39_cp39_macosx_10_9_x86_64_fff7b9c3": { - "filename": "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", - "integrity": "sha256-//e5w/GZVwIMrFRscAJTMRE9LmFTf24kQbx2V5E959M=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", - }, - "coverage_cp39_cp39_macosx_11_0_arm64_bc91b314": { - "filename": "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", - "integrity": "sha256-vJGzFM7yd0LaSG1oObZ3s/J5Pf5StRu7t89zbVwpKBw=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", - }, - "coverage_cp39_cp39_manylinux_2_17_aarch64_c7315339": { - "filename": "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "integrity": "sha256-xzFTOerjskwtL6HtfXo4ZUy6NKE+8Z+8uUJdpG09xZQ=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - }, - "coverage_cp39_cp39_manylinux_2_31_riscv64_912e6ebc": { - "filename": "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - "integrity": "sha256-kS5uvHpuSt/bsa7Dca0ExohUzTvzYIs1FOf/kGKTHYo=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", - }, - "coverage_cp39_cp39_manylinux_2_5_i686_567f5c15": { - "filename": "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - "integrity": "sha256-Vn9cFV7ajfHT1DnUCkWmpfAptCmwZkgjXx5+UbUis5Y=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", - }, - "coverage_cp39_cp39_manylinux_2_5_x86_64_2af88def": { - "filename": "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - "integrity": "sha256-KviN7/zIpNWXTPLVAiUbw7LbhGHwtm2ApEnDN1eqn0A=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", - }, - "coverage_cp39_cp39_musllinux_1_2_x86_64_635adb9a": { - "filename": "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", - "integrity": "sha256-Y1rbmkUHyf0u1l85aT+jHJo+46jm3GTfAz6P31KnAD8=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", - }, - "coverage_cp39_cp39_win32_5a02d5a8": { - "filename": "coverage-7.10.7-cp39-cp39-win32.whl", - "integrity": "sha256-WgLVqFDil5sKAUxBJXOVOZUXR0Oj9/pOpabpo8VhdDE=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", - }, - "coverage_cp39_cp39_win_amd64_c134869d": { - "filename": "coverage-7.10.7-cp39-cp39-win_amd64.whl", - "integrity": "sha256-wTSGnV/+NFR9FOF0yGb9j+IlSRjMCpXpkFKQO8FUPgc=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", - }, - "coverage_py3_none_any_11d28e91": { - "filename": "coverage-7.16.2-py3-none-any.whl", - "integrity": "sha256-EdKOkSOpFWy0BdjSe0QlbJpY+13swgc6jxeGIFfjqg8=", - "requirement": "coverage==7.16.2", - "url": "https://files.pythonhosted.org/packages/3f/0c/7a64e1ac90541a8edf50daef0914848011fb057a5bf55284a4811e21939a/coverage-7.16.2-py3-none-any.whl", - }, - "coverage_py3_none_any_e674027c": { - "filename": "coverage-7.17.2-py3-none-any.whl", - "integrity": "sha256-5nQCfOtr/ZWfG7A1AA/SD6i3F9xnZa2q+LyjR4IsjH0=", - "requirement": "coverage==7.17.2", - "url": "https://files.pythonhosted.org/packages/da/91/1805cc144497d76a436f66a21fa57492bee7ed1257826fb992f03b9b2b6f/coverage-7.17.2-py3-none-any.whl", - }, - "coverage_py3_none_any_f7941f6f": { - "filename": "coverage-7.10.7-py3-none-any.whl", - "integrity": "sha256-95Qfby/m3WgHoSCHN7igy88cxtewfSSZitLWNZCGgmA=", - "requirement": "coverage==7.10.7", - "url": "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", - }, -} - -COVERAGE_TOOLS = { - "3.10": { - "linux_aarch64": "coverage_cp310_cp310_manylinux_2_17_aarch64_9fd670ac", - "linux_armv7": "coverage_py3_none_any_11d28e91", - "linux_i386": "coverage_cp310_cp310_manylinux_2_5_i686_a4624f80", - "linux_ppc": "coverage_cp310_cp310_manylinux_2_17_ppc64le_705e5af1", - "linux_riscv64": "coverage_cp310_cp310_manylinux_2_31_riscv64_8afd9bf3", - "linux_s390x": "coverage_py3_none_any_11d28e91", - "linux_x86_64": "coverage_cp310_cp310_manylinux_2_5_x86_64_191803c4", - "linux_x86_64_musl": "coverage_cp310_cp310_musllinux_1_2_x86_64_c85d54e7", - "osx_aarch64": "coverage_cp310_cp310_macosx_11_0_arm64_40c0f008", - "osx_arm64e": "coverage_py3_none_any_11d28e91", - "osx_x86_64": "coverage_cp310_cp310_macosx_10_9_x86_64_23219888", - "windows_x86_32": "coverage_cp310_cp310_win32_5139009b", - "windows_x86_64": "coverage_cp310_cp310_win_amd64_c3305c38", - }, - "3.11": { - "linux_aarch64": "coverage_cp311_cp311_manylinux_2_17_aarch64_1947bd9d", - "linux_armv7": "coverage_py3_none_any_e674027c", - "linux_i386": "coverage_cp311_cp311_manylinux_2_5_i686_5b71aeaa", - "linux_ppc": "coverage_cp311_cp311_manylinux_2_17_ppc64le_2cd1a747", - "linux_riscv64": "coverage_cp311_cp311_manylinux_2_31_riscv64_2b7fe217", - "linux_s390x": "coverage_py3_none_any_e674027c", - "linux_x86_64": "coverage_cp311_cp311_manylinux_2_5_x86_64_01303d9e", - "linux_x86_64_musl": "coverage_cp311_cp311_musllinux_1_2_x86_64_7c123b6b", - "osx_aarch64": "coverage_cp311_cp311_macosx_11_0_arm64_3d3d9d79", - "osx_arm64e": "coverage_py3_none_any_e674027c", - "osx_x86_64": "coverage_cp311_cp311_macosx_10_9_x86_64_a3f49ac9", - "windows_aarch64": "coverage_cp311_cp311_win_arm64_b08ee3ef", - "windows_x86_32": "coverage_cp311_cp311_win32_1c3146ef", - "windows_x86_64": "coverage_cp311_cp311_win_amd64_51c6a60e", - }, - "3.12": { - "linux_aarch64": "coverage_cp312_cp312_manylinux_2_17_aarch64_1682d98d", - "linux_armv7": "coverage_py3_none_any_e674027c", - "linux_i386": "coverage_cp312_cp312_manylinux_2_5_i686_90c6c5f9", - "linux_ppc": "coverage_cp312_cp312_manylinux_2_17_ppc64le_8eab0a5c", - "linux_riscv64": "coverage_cp312_cp312_manylinux_2_31_riscv64_635337a9", - "linux_s390x": "coverage_py3_none_any_e674027c", - "linux_x86_64": "coverage_cp312_cp312_manylinux_2_5_x86_64_36314997", - "linux_x86_64_musl": "coverage_cp312_cp312_musllinux_1_2_x86_64_686b6f8f", - "osx_aarch64": "coverage_cp312_cp312_macosx_11_0_arm64_da4b0aef", - "osx_arm64e": "coverage_py3_none_any_e674027c", - "osx_x86_64": "coverage_cp312_cp312_macosx_10_13_x86_64_289a2444", - "windows_aarch64": "coverage_cp312_cp312_win_arm64_bac3c79c", - "windows_x86_32": "coverage_cp312_cp312_win32_ee083412", - "windows_x86_64": "coverage_cp312_cp312_win_amd64_e269dddf", - }, - "3.13": { - "linux_aarch64": "coverage_cp313_cp313_manylinux_2_17_aarch64_95bd8dc5", - "linux_aarch64_freethreaded": "coverage_py3_none_any_e674027c", - "linux_armv7": "coverage_py3_none_any_e674027c", - "linux_armv7_freethreaded": "coverage_py3_none_any_e674027c", - "linux_i386": "coverage_cp313_cp313_manylinux_2_5_i686_7ce0acb2", - "linux_i386_freethreaded": "coverage_py3_none_any_e674027c", - "linux_ppc": "coverage_cp313_cp313_manylinux_2_17_ppc64le_a5565a9d", - "linux_ppc_freethreaded": "coverage_py3_none_any_e674027c", - "linux_riscv64": "coverage_cp313_cp313_manylinux_2_31_riscv64_091eba42", - "linux_riscv64_freethreaded": "coverage_py3_none_any_e674027c", - "linux_s390x": "coverage_py3_none_any_e674027c", - "linux_s390x_freethreaded": "coverage_py3_none_any_e674027c", - "linux_x86_64": "coverage_cp313_cp313_manylinux_2_5_x86_64_70daa8e4", - "linux_x86_64_freethreaded": "coverage_py3_none_any_e674027c", - "linux_x86_64_musl": "coverage_cp313_cp313_musllinux_1_2_x86_64_e09c3781", - "linux_x86_64_musl_freethreaded": "coverage_py3_none_any_e674027c", - "osx_aarch64": "coverage_cp313_cp313_macosx_11_0_arm64_f94d465e", - "osx_aarch64_freethreaded": "coverage_py3_none_any_e674027c", - "osx_arm64e": "coverage_py3_none_any_e674027c", - "osx_arm64e_freethreaded": "coverage_py3_none_any_e674027c", - "osx_x86_64": "coverage_cp313_cp313_macosx_10_13_x86_64_e6c89d63", - "osx_x86_64_freethreaded": "coverage_py3_none_any_e674027c", - "windows_aarch64": "coverage_cp313_cp313_win_arm64_bb742836", - "windows_aarch64_freethreaded": "coverage_py3_none_any_e674027c", - "windows_x86_32": "coverage_cp313_cp313_win32_c10f2a27", - "windows_x86_32_freethreaded": "coverage_py3_none_any_e674027c", - "windows_x86_64": "coverage_cp313_cp313_win_amd64_8dc07ed3", - "windows_x86_64_freethreaded": "coverage_py3_none_any_e674027c", - }, - "3.14": { - "linux_aarch64": "coverage_cp314_cp314_manylinux_2_17_aarch64_e6bf531d", - "linux_aarch64_freethreaded": "coverage_cp314_cp314t_manylinux_2_17_aarch64_d0332e2b", - "linux_armv7": "coverage_py3_none_any_e674027c", - "linux_armv7_freethreaded": "coverage_py3_none_any_e674027c", - "linux_i386": "coverage_cp314_cp314_manylinux_2_5_i686_3dfefb1e", - "linux_i386_freethreaded": "coverage_cp314_cp314t_manylinux_2_5_i686_0d8a5dc7", - "linux_ppc": "coverage_cp314_cp314_manylinux_2_17_ppc64le_88d4dc83", - "linux_ppc_freethreaded": "coverage_cp314_cp314t_manylinux_2_17_ppc64le_27fc4384", - "linux_riscv64": "coverage_cp314_cp314_manylinux_2_31_riscv64_8dc17707", - "linux_riscv64_freethreaded": "coverage_cp314_cp314t_manylinux_2_31_riscv64_603140ee", - "linux_s390x": "coverage_py3_none_any_e674027c", - "linux_s390x_freethreaded": "coverage_py3_none_any_e674027c", - "linux_x86_64": "coverage_cp314_cp314_manylinux_2_5_x86_64_34e5c7a2", - "linux_x86_64_freethreaded": "coverage_cp314_cp314t_manylinux_2_5_x86_64_248873a1", - "linux_x86_64_musl": "coverage_cp314_cp314_musllinux_1_2_x86_64_460ba213", - "linux_x86_64_musl_freethreaded": "coverage_cp314_cp314t_musllinux_1_2_x86_64_d88fcc24", - "osx_aarch64": "coverage_cp314_cp314_macosx_11_0_arm64_2a628101", - "osx_aarch64_freethreaded": "coverage_cp314_cp314t_macosx_11_0_arm64_ba45dc07", - "osx_arm64e": "coverage_py3_none_any_e674027c", - "osx_arm64e_freethreaded": "coverage_py3_none_any_e674027c", - "osx_x86_64": "coverage_cp314_cp314_macosx_10_15_x86_64_36248fb6", - "osx_x86_64_freethreaded": "coverage_cp314_cp314t_macosx_10_15_x86_64_674bcd02", - "windows_aarch64": "coverage_cp314_cp314_win_arm64_a5909dd0", - "windows_aarch64_freethreaded": "coverage_cp314_cp314t_win_arm64_eca90e02", - "windows_x86_32": "coverage_cp314_cp314_win32_e72cf955", - "windows_x86_32_freethreaded": "coverage_cp314_cp314t_win32_2c42c222", - "windows_x86_64": "coverage_cp314_cp314_win_amd64_205f9e32", - "windows_x86_64_freethreaded": "coverage_cp314_cp314t_win_amd64_b94c509e", - }, - "3.15": { - "linux_aarch64": "coverage_cp315_cp315_manylinux_2_17_aarch64_8d3626b9", - "linux_aarch64_freethreaded": "coverage_cp315_cp315t_manylinux_2_17_aarch64_a65faa1e", - "linux_armv7": "coverage_py3_none_any_e674027c", - "linux_armv7_freethreaded": "coverage_py3_none_any_e674027c", - "linux_i386": "coverage_cp315_cp315_manylinux_2_5_i686_d14872cc", - "linux_i386_freethreaded": "coverage_cp315_cp315t_manylinux_2_5_i686_a838a10a", - "linux_ppc": "coverage_cp315_cp315_manylinux_2_17_ppc64le_c9639c2e", - "linux_ppc_freethreaded": "coverage_cp315_cp315t_manylinux_2_17_ppc64le_7c17f716", - "linux_riscv64": "coverage_cp315_cp315_manylinux_2_31_riscv64_19d508b0", - "linux_riscv64_freethreaded": "coverage_cp315_cp315t_manylinux_2_31_riscv64_545ca424", - "linux_s390x": "coverage_py3_none_any_e674027c", - "linux_s390x_freethreaded": "coverage_py3_none_any_e674027c", - "linux_x86_64": "coverage_cp315_cp315_manylinux_2_5_x86_64_3bd065e7", - "linux_x86_64_freethreaded": "coverage_cp315_cp315t_manylinux_2_5_x86_64_0fc17882", - "linux_x86_64_musl": "coverage_cp315_cp315_musllinux_1_2_x86_64_99b64a12", - "linux_x86_64_musl_freethreaded": "coverage_cp315_cp315t_musllinux_1_2_x86_64_2ad189c6", - "osx_aarch64": "coverage_cp315_cp315_macosx_11_0_arm64_81a43279", - "osx_aarch64_freethreaded": "coverage_cp315_cp315t_macosx_11_0_arm64_d4004fce", - "osx_arm64e": "coverage_py3_none_any_e674027c", - "osx_arm64e_freethreaded": "coverage_py3_none_any_e674027c", - "osx_x86_64": "coverage_cp315_cp315_macosx_10_15_x86_64_298849ca", - "osx_x86_64_freethreaded": "coverage_cp315_cp315t_macosx_10_15_x86_64_ffbaadf8", - "windows_aarch64": "coverage_cp315_cp315_win_arm64_be699e76", - "windows_aarch64_freethreaded": "coverage_cp315_cp315t_win_arm64_c68f64d9", - "windows_x86_32": "coverage_cp315_cp315_win32_74d91af5", - "windows_x86_32_freethreaded": "coverage_cp315_cp315t_win32_6fd6f218", - "windows_x86_64": "coverage_cp315_cp315_win_amd64_075566a4", - "windows_x86_64_freethreaded": "coverage_cp315_cp315t_win_amd64_ef07aae0", - }, - "3.9": { - "linux_aarch64": "coverage_cp39_cp39_manylinux_2_17_aarch64_c7315339", - "linux_armv7": "coverage_py3_none_any_f7941f6f", - "linux_i386": "coverage_cp39_cp39_manylinux_2_5_i686_567f5c15", - "linux_ppc": "coverage_py3_none_any_f7941f6f", - "linux_riscv64": "coverage_cp39_cp39_manylinux_2_31_riscv64_912e6ebc", - "linux_s390x": "coverage_py3_none_any_f7941f6f", - "linux_x86_64": "coverage_cp39_cp39_manylinux_2_5_x86_64_2af88def", - "linux_x86_64_musl": "coverage_cp39_cp39_musllinux_1_2_x86_64_635adb9a", - "osx_aarch64": "coverage_cp39_cp39_macosx_11_0_arm64_bc91b314", - "osx_arm64e": "coverage_py3_none_any_f7941f6f", - "osx_x86_64": "coverage_cp39_cp39_macosx_10_9_x86_64_fff7b9c3", - "windows_x86_32": "coverage_cp39_cp39_win32_5a02d5a8", - "windows_x86_64": "coverage_cp39_cp39_win_amd64_c134869d", - }, -} diff --git a/python/private/coverage/workspace_deps_gen.bzl b/python/private/coverage/workspace_deps_gen.bzl deleted file mode 100644 index 87faf55487..0000000000 --- a/python/private/coverage/workspace_deps_gen.bzl +++ /dev/null @@ -1,119 +0,0 @@ -"""A dev-only repository rule to generate the `workspace_deps.bzl` file. - -The WORKSPACE macros cannot read the `uv.lock` file, so the wheels that are -selected for each Python version and platform are written to a checked-in -`.bzl` file. This uses the same wheel selection code as the `pip.parse` bzlmod -extension. - -Run `bazel run //dev/update_deps:update_coverage_workspace_deps` to update the -checked-in file. -""" - -load("@toml.bzl", "toml") -load("//python:versions.bzl", "MINOR_MAPPING") -load("//python/private:repo_utils.bzl", "repo_utils") -load("//python/private:text_util.bzl", "render") -load("//python/private:version.bzl", "version") -load("//python/private/pypi:default_platforms.bzl", "default_platforms") -load("//python/private/pypi:hash.bzl", "hash") -load("//python/private/pypi:parse_requirements.bzl", "parse_requirements") -load("//python/private/pypi:pep508_env.bzl", "env") -load("//python/private/pypi:pep508_evaluate.bzl", "evaluate") -load("//python/private/pypi:platform.bzl", "platform") -load("//python/private/pypi:whl_repo_name.bzl", "whl_repo_name") - -_HEADER = '''\ -"""The coverage wheels for the WORKSPACE toolchains. - -@generated by `bazel run //dev/update_deps:update_coverage_workspace_deps` from -the `uv.lock` file in this directory. DO NOT EDIT. -""" -''' - -def _platforms(python_version): - """Return the platforms to select the wheels for. - - Args: - python_version: {type}`str` the full Python version. - - Returns: - {type}`dict[str, struct]` the platforms. - """ - ver = version.parse(python_version, strict = True) - ret = {} - for name, values in default_platforms().items(): - p = platform(**values) - env_ = env( - env = p.env, - os = p.os_name, - arch = p.arch_name, - python_version = python_version, - ) - if p.marker and not evaluate(p.marker, env = env_): - continue - - ret[name] = struct( - env = env_, - whl_abi_tags = [ - tag.format(major = ver.release[0], minor = ver.release[1]) - for tag in p.whl_abi_tags - ], - whl_platform_tags = p.whl_platform_tags, - ) - return ret - -def _workspace_deps_gen_impl(rctx): - logger = repo_utils.logger(rctx) - - wheels = {} - tools = {} - for minor_version, full_version in sorted( - MINOR_MAPPING.items(), - key = lambda x: version.parse(x[0]).key(), - ): - platforms = _platforms(full_version) - requirements = parse_requirements( - rctx, - platforms = platforms, - uv_lock = rctx.attr.uv_lock, - toml_decode = toml.decode, - logger = logger, - ) - for whl in requirements: - if whl.name != "coverage": - continue - - for src in whl.srcs: - name = whl_repo_name(src.filename, src.digest) - wheels[name] = { - "filename": src.filename, - "integrity": hash.integrity(src.digest), - "requirement": src.requirement_line, - "url": src.url, - } - for p in src.target_platforms: - tools.setdefault(minor_version, {})[p] = name - - rctx.file("BUILD.bazel", 'exports_files(["workspace_deps.bzl"])\n') - rctx.file("workspace_deps.bzl", "\n".join([ - _HEADER, - "COVERAGE_WHEELS = " + render.dict_dict( - {k: wheels[k] for k in sorted(wheels)}, - ), - "", - "COVERAGE_TOOLS = " + render.dict_dict( - { - minor_version: {p: tools[minor_version][p] for p in sorted(tools[minor_version])} - for minor_version in sorted(tools) - }, - ), - "", - ])) - -workspace_deps_gen = repository_rule( - implementation = _workspace_deps_gen_impl, - attrs = { - "uv_lock": attr.label(mandatory = True, allow_single_file = True), - }, - doc = "Generate the `workspace_deps.bzl` file from the coverage `uv.lock` file.", -) diff --git a/python/private/internal_dev_deps.bzl b/python/private/internal_dev_deps.bzl index dd6195808b..5bbee232f9 100644 --- a/python/private/internal_dev_deps.bzl +++ b/python/private/internal_dev_deps.bzl @@ -14,7 +14,6 @@ """Module extension for internal dev_dependency=True setup.""" load("@bazel_ci_rules//:rbe_repo.bzl", "rbe_preconfig") -load("//python/private/coverage:workspace_deps_gen.bzl", "workspace_deps_gen") load("//python/private/pypi:whl_library.bzl", "whl_library") load("//tests/support/whl_from_dir:whl_from_dir_repo.bzl", "whl_from_dir_repo") load(":runtime_env_repo.bzl", "runtime_env_repo") @@ -31,12 +30,6 @@ def _internal_dev_deps_impl(mctx): ) runtime_env_repo(name = "rules_python_runtime_env_tc_info") - # Setup for //tests/coverage:workspace_deps_test - workspace_deps_gen( - name = "rules_python_coverage_workspace_deps", - uv_lock = "//python/private/coverage:uv.lock", - ) - # Setup for //tests/whl_with_build_files whl_from_dir_repo( name = "whl_with_build_files", diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel index 8a53a4f3e2..d2ff5fec50 100644 --- a/tests/coverage/BUILD.bazel +++ b/tests/coverage/BUILD.bazel @@ -1,8 +1,8 @@ -load("@bazel_skylib//rules:diff_test.bzl", "diff_test") load("//python:py_library.bzl", "py_library") load("//python:pytest_test.bzl", "pytest_test") load("//tests/support:py_reconfig.bzl", "py_reconfig_test") load("//tests/support:support.bzl", "NOT_WINDOWS", "SUPPORTS_BZLMOD") +load(":coverage_workspace_tests.bzl", "coverage_workspace_test_suite") py_library( name = "lib", @@ -37,16 +37,6 @@ pytest_test( deps = ["//python/runfiles"], ) -# The WORKSPACE toolchains use the wheels from the checked-in file, which is -# generated with bzlmod. -diff_test( - name = "workspace_deps_test", - failure_message = "Run `bazel run //dev/update_deps:update_coverage_workspace_deps` to update the file.", - file1 = "//python/private/coverage:workspace_deps.bzl", - file2 = "@rules_python_coverage_workspace_deps//:workspace_deps.bzl", - target_compatible_with = SUPPORTS_BZLMOD, -) - # The same program, but the coverage tool is the `coverage` py_library from the # `@dev_pip` hub, see the `MODULE.bazel` file. py_reconfig_test( @@ -70,3 +60,5 @@ pytest_test( target_compatible_with = NOT_WINDOWS + SUPPORTS_BZLMOD, deps = ["//python/runfiles"], ) + +coverage_workspace_test_suite(name = "coverage_workspace_tests") diff --git a/tests/coverage/coverage_workspace_tests.bzl b/tests/coverage/coverage_workspace_tests.bzl new file mode 100644 index 0000000000..5445ab3af8 --- /dev/null +++ b/tests/coverage/coverage_workspace_tests.bzl @@ -0,0 +1,113 @@ +"""Tests for the wheel selection of the WORKSPACE coverage tool.""" + +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("//python/private/coverage:coverage_workspace.bzl", "select_wheels") # buildifier: disable=bzl-visibility +load("//python/private/pypi:default_platforms.bzl", "default_platforms") # buildifier: disable=bzl-visibility + +_tests = [] + +_URL = "https://files.pythonhosted.org/packages/" + +def _platforms(*names): + platforms = default_platforms() + return {name: platforms[name] for name in names} + +def _wheel(filename, digest): + return {"hash": "sha256:" + digest, "url": _URL + filename} + +def _test_select_platform_specific_wheels(env): + got = select_wheels( + uv_lock = {"package": [ + { + "name": "coverage", + "source": {"registry": "https://pypi.org/simple"}, + "version": "7.0", + "wheels": [ + _wheel("coverage-7.0-cp313-cp313-manylinux_2_17_x86_64.whl", "deadb001"), + _wheel("coverage-7.0-cp313-cp313-macosx_11_0_arm64.whl", "deadb002"), + _wheel("coverage-7.0-py3-none-any.whl", "deadb003"), + ], + }, + ]}, + minor_mapping = {"3.13": "3.13.1"}, + platforms = _platforms("linux_s390x", "linux_x86_64", "osx_aarch64"), + ) + + # Each platform selects its own wheel and the platforms without a + # platform specific wheel use the `none-any` wheel. + env.expect.that_dict(got.tools).contains_exactly({ + "3.13": { + "linux_s390x": "coverage_py3_none_any_deadb003", + "linux_x86_64": "coverage_cp313_cp313_manylinux_2_17_x86_64_deadb001", + "osx_aarch64": "coverage_cp313_cp313_macosx_11_0_arm64_deadb002", + }, + }) + env.expect.that_dict(got.wheels["coverage_cp313_cp313_manylinux_2_17_x86_64_deadb001"]).contains_exactly({ + "filename": "coverage-7.0-cp313-cp313-manylinux_2_17_x86_64.whl", + "integrity": "sha256-3q2wAQ==", + "url": _URL + "coverage-7.0-cp313-cp313-manylinux_2_17_x86_64.whl", + }) + env.expect.that_collection(got.wheels.keys()).contains_exactly([ + "coverage_cp313_cp313_macosx_11_0_arm64_deadb002", + "coverage_cp313_cp313_manylinux_2_17_x86_64_deadb001", + "coverage_py3_none_any_deadb003", + ]) + +_tests.append(_test_select_platform_specific_wheels) + +def _test_select_per_python_version(env): + got = select_wheels( + uv_lock = {"package": [ + { + "name": "coverage", + "resolution-markers": ["python_full_version < '3.10'"], + "source": {"registry": "https://pypi.org/simple"}, + "version": "7.0", + "wheels": [_wheel("coverage-7.0-py3-none-any.whl", "deadb001")], + }, + { + "name": "coverage", + "resolution-markers": ["python_full_version >= '3.10'"], + "source": {"registry": "https://pypi.org/simple"}, + "version": "8.0", + "wheels": [_wheel("coverage-8.0-py3-none-any.whl", "deadb002")], + }, + ]}, + minor_mapping = {"3.13": "3.13.1", "3.9": "3.9.1"}, + platforms = _platforms("linux_x86_64"), + ) + + # Newer coverage versions drop the support for older Python versions. + env.expect.that_dict(got.tools).contains_exactly({ + "3.13": {"linux_x86_64": "coverage_py3_none_any_deadb002"}, + "3.9": {"linux_x86_64": "coverage_py3_none_any_deadb001"}, + }) + +_tests.append(_test_select_per_python_version) + +def _test_select_skips_other_packages(env): + got = select_wheels( + uv_lock = {"package": [ + { + "name": "other", + "source": {"registry": "https://pypi.org/simple"}, + "version": "1.0", + "wheels": [_wheel("other-1.0-py3-none-any.whl", "deadb001")], + }, + ]}, + minor_mapping = {"3.13": "3.13.1"}, + platforms = _platforms("linux_x86_64"), + ) + + env.expect.that_dict(got.tools).contains_exactly({}) + env.expect.that_dict(got.wheels).contains_exactly({}) + +_tests.append(_test_select_skips_other_packages) + +def coverage_workspace_test_suite(name): + """Create the test suite. + + Args: + name: the name of the test suite + """ + test_suite(name = name, basic_tests = _tests) From 5a6c48c14242aee1b1c6e4a09de7b6da545c0a1b Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:09:27 +0900 Subject: [PATCH 34/39] fix: use LF in the vendored uv.lock and fix the vendor_uv_lock docstring --- .gitattributes | 1 + python/uv/private/vendor_uv_lock.bzl | 10 +++++++--- python/uv/private/vendor_uv_lock.py | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index d777e53d3a..dfd78bdac3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ python/features.bzl export-subst tests/uv/lock/testdata/requirements.txt text eol=lf python/private/runtimes_manifest.bzl text eol=lf +python/private/coverage/uv_lock.bzl text eol=lf *.bat text eol=crlf diff --git a/python/uv/private/vendor_uv_lock.bzl b/python/uv/private/vendor_uv_lock.bzl index 1e9d95d6a3..24dd9059ec 100644 --- a/python/uv/private/vendor_uv_lock.bzl +++ b/python/uv/private/vendor_uv_lock.bzl @@ -66,6 +66,13 @@ def vendor_uv_lock( - `name_test`: a `diff_test` that `out` is up to date. It only depends on `src`, so it does not need the network. + The vendored file always uses LF line endings, so add ` text eol=lf` + to the `.gitattributes` file if git converts the line endings on checkout, + e.g. on Windows. + + :::{versionadded} VERSION_NEXT_FEATURE + ::: + Args: name: {type}`str` the name of the targets. lock: {type}`str` the name of the {obj}`lock` target in the same @@ -75,9 +82,6 @@ def vendor_uv_lock( out: {type}`str` the vendored `.bzl` file, relative to the package. constant: {type}`str` the name of the JSON string constant. **kwargs: common kwargs passed to the rules. - - :::{versionadded} VERSION_NEXT_FEATURE - ::: """ pkg = native.package_name() prefix = pkg + "/" if pkg else "" diff --git a/python/uv/private/vendor_uv_lock.py b/python/uv/private/vendor_uv_lock.py index be2ec139d4..2d14b78abe 100644 --- a/python/uv/private/vendor_uv_lock.py +++ b/python/uv/private/vendor_uv_lock.py @@ -104,13 +104,15 @@ def render(packages: list, *, constant: str, src: str, update_target: str) -> st def vendor(src: pathlib.Path, out: pathlib.Path, args: argparse.Namespace) -> None: lock = tomllib.loads(src.read_text()) + # Always use LF, so that the vendored file is the same on all platforms. out.write_text( render( trim(lock), constant=args.constant, src=args.src_label, update_target=args.update_target, - ) + ), + newline="\n", ) From 0eaf95074d4f1a1a98c29224f2a6b4b19a7a8b08 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:18:57 +0900 Subject: [PATCH 35/39] refactor(pypi): compute the whl extraction features without rules_python_internal The `patch_whl.bzl` and `whl_extract.bzl` files loaded the `@rules_python_internal` repository only to get the `supports_whl_extraction` and `extract_needs_chmod` values, which only depend on the Bazel version. Compute them from `native.bazel_version` in a new `extract_features` function, which `internal_config_repo` also uses. This allows loading `whl_library` from the files that are loaded before `py_repositories` defines `@rules_python_internal` in WORKSPACE, e.g. `python/repositories.bzl`. --- python/private/BUILD.bazel | 6 +++ python/private/extract_features.bzl | 38 +++++++++++++++++++ python/private/internal_config_repo.bzl | 17 +++------ python/private/pypi/BUILD.bazel | 4 +- python/private/pypi/patch_whl.bzl | 6 +-- python/private/pypi/whl_extract.bzl | 6 +-- tests/extract_features/BUILD.bazel | 3 ++ .../extract_features_tests.bzl | 35 +++++++++++++++++ 8 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 python/private/extract_features.bzl create mode 100644 tests/extract_features/BUILD.bazel create mode 100644 tests/extract_features/extract_features_tests.bzl diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index dc0758efa5..7be464360b 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -410,6 +410,7 @@ bzl_library( name = "internal_config_repo", srcs = ["internal_config_repo.bzl"], deps = [ + ":extract_features", ":repo_utils", ":text_util", ], @@ -975,6 +976,11 @@ bzl_library( srcs = ["envsubst.bzl"], ) +bzl_library( + name = "extract_features", + srcs = ["extract_features.bzl"], +) + bzl_library( name = "full_version", srcs = ["full_version.bzl"], diff --git a/python/private/extract_features.bzl b/python/private/extract_features.bzl new file mode 100644 index 0000000000..8e07c69ed3 --- /dev/null +++ b/python/private/extract_features.bzl @@ -0,0 +1,38 @@ +"""The archive extraction features of the running Bazel version. + +This only uses `native.bazel_version`, so that the repository rules can use it +without loading the `@rules_python_internal` repository, which may not be +defined yet when the WORKSPACE macros are loaded. +""" + +def _major_minor(bazel_version): + # An empty version signifies a development build, which is treated as + # the latest version. + if not bazel_version: + return 99999, 99999 + + parts = bazel_version.split(".") + return int(parts[0]), int(parts[1]) + +def extract_features(bazel_version = None): + """Return the archive extraction features of the Bazel version. + + Args: + bazel_version: {type}`str | None` the Bazel version, defaults to + `native.bazel_version`. + + Returns: + {type}`struct` with: + * `supports_whl_extraction`: {type}`bool` whether `.whl` files can + be extracted, which requires Bazel 8.3.0 or later. + * `extract_needs_chmod`: {type}`bool` whether the extracted files need + their permissions fixed, which is the case before Bazel 8.6.0. + """ + if bazel_version == None: + bazel_version = native.bazel_version + + major, minor = _major_minor(bazel_version) + return struct( + supports_whl_extraction = major > 8 or (major == 8 and minor >= 3), + extract_needs_chmod = major < 8 or (major == 8 and minor < 6), + ) diff --git a/python/private/internal_config_repo.bzl b/python/private/internal_config_repo.bzl index 8f203bb89e..762160c839 100644 --- a/python/private/internal_config_repo.bzl +++ b/python/private/internal_config_repo.bzl @@ -18,6 +18,7 @@ such as globals available to Bazel versions, or propagating user environment settings for rules to later use. """ +load("//python/private:extract_features.bzl", "extract_features") load("//python/private:text_util.bzl", "render") load(":repo_utils.bzl", "repo_utils") @@ -81,20 +82,12 @@ def _internal_config_repo_impl(rctx): # An empty version signifies a development build, which is treated as # the latest version. if native.bazel_version: - version_parts = native.bazel_version.split(".") - bazel_major_version = int(version_parts[0]) - bazel_minor_version = int(version_parts[1]) + bazel_major_version = int(native.bazel_version.split(".")[0]) else: bazel_major_version = 99999 - bazel_minor_version = 99999 - supports_whl_extraction = False - extract_needs_chmod = bazel_major_version < 8 or (bazel_major_version == 8 and bazel_minor_version < 6) + features = extract_features() if bazel_major_version >= 8: - # Extracting .whl files requires Bazel 8.3.0 or later. - if bazel_major_version > 8 or bazel_minor_version >= 3: - supports_whl_extraction = True - builtin_py_info_symbol = "None" builtin_py_runtime_info_symbol = "None" builtin_py_cc_link_params_provider = "None" @@ -109,8 +102,8 @@ def _internal_config_repo_impl(rctx): enable_deprecation_warnings = _bool_from_environ(rctx, _ENABLE_DEPRECATION_WARNINGS_ENVVAR_NAME, _ENABLE_DEPRECATION_WARNINGS_DEFAULT), builtin_py_info_symbol = builtin_py_info_symbol, builtin_py_runtime_info_symbol = builtin_py_runtime_info_symbol, - supports_whl_extraction = str(supports_whl_extraction), - extract_needs_chmod = str(extract_needs_chmod), + supports_whl_extraction = str(features.supports_whl_extraction), + extract_needs_chmod = str(features.extract_needs_chmod), builtin_py_cc_link_params_provider = builtin_py_cc_link_params_provider, modules_using_explicit_initpy = str(explicit_init_py_modules), tool_deps_dev_only = str(rctx.attr.tool_deps_dev_only), diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel index f94d24594f..e841b6893f 100644 --- a/python/private/pypi/BUILD.bazel +++ b/python/private/pypi/BUILD.bazel @@ -265,8 +265,8 @@ bzl_library( srcs = ["patch_whl.bzl"], deps = [ ":parse_whl_name", + "//python/private:extract_features", "//python/private:repo_utils", - "@rules_python_internal//:rules_python_config", ], ) @@ -475,8 +475,8 @@ bzl_library( srcs = ["whl_extract.bzl"], deps = [ ":whl_metadata", + "//python/private:extract_features", "//python/private:repo_utils", - "@rules_python_internal//:rules_python_config", ], ) diff --git a/python/private/pypi/patch_whl.bzl b/python/private/pypi/patch_whl.bzl index b257f2b4fe..fea1421150 100644 --- a/python/private/pypi/patch_whl.bzl +++ b/python/private/pypi/patch_whl.bzl @@ -19,7 +19,7 @@ repack patched wheels. Instead, after extracting and patching, we fix the RECORD file natively in Starlark and repack using system zip. """ -load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config") +load("//python/private:extract_features.bzl", "extract_features") load("//python/private:repo_utils.bzl", "repo_utils") load(":parse_whl_name.bzl", "parse_whl_name") @@ -96,8 +96,8 @@ def patch_whl(rctx, *, whl_path, patches): repo_utils.extract( rctx, archive = whl_input, - supports_whl_extraction = rp_config.supports_whl_extraction, - extract_needs_chmod = rp_config.extract_needs_chmod, + supports_whl_extraction = extract_features().supports_whl_extraction, + extract_needs_chmod = extract_features().extract_needs_chmod, ) if not patches: diff --git a/python/private/pypi/whl_extract.bzl b/python/private/pypi/whl_extract.bzl index d7195fd681..a7a8d58dd5 100644 --- a/python/private/pypi/whl_extract.bzl +++ b/python/private/pypi/whl_extract.bzl @@ -1,6 +1,6 @@ """A simple whl extractor.""" -load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config") +load("//python/private:extract_features.bzl", "extract_features") load("//python/private:repo_utils.bzl", "repo_utils") load(":whl_metadata.bzl", "find_whl_metadata") @@ -32,8 +32,8 @@ def whl_extract(rctx, *, whl_path, logger): rctx, archive = whl_path, output = install_dir_path, - supports_whl_extraction = rp_config.supports_whl_extraction, - extract_needs_chmod = rp_config.extract_needs_chmod, + supports_whl_extraction = extract_features().supports_whl_extraction, + extract_needs_chmod = extract_features().extract_needs_chmod, ) metadata_file = find_whl_metadata( diff --git a/tests/extract_features/BUILD.bazel b/tests/extract_features/BUILD.bazel new file mode 100644 index 0000000000..9b2fc599b3 --- /dev/null +++ b/tests/extract_features/BUILD.bazel @@ -0,0 +1,3 @@ +load(":extract_features_tests.bzl", "extract_features_test_suite") + +extract_features_test_suite(name = "extract_features_tests") diff --git a/tests/extract_features/extract_features_tests.bzl b/tests/extract_features/extract_features_tests.bzl new file mode 100644 index 0000000000..0a8c66cb75 --- /dev/null +++ b/tests/extract_features/extract_features_tests.bzl @@ -0,0 +1,35 @@ +"""Tests for the archive extraction features of the Bazel versions.""" + +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("//python/private:extract_features.bzl", "extract_features") # buildifier: disable=bzl-visibility + +_tests = [] + +def _test_extract_features(env): + for bazel_version, (supports_whl_extraction, extract_needs_chmod) in { + # A development build is treated as the latest version. + "": (True, False), + "7.4.1": (False, True), + "8.2.1": (False, True), + "8.3.0": (True, True), + "8.5.1": (True, True), + "8.6.0": (True, False), + "9.1.1": (True, False), + }.items(): + got = extract_features(bazel_version) + env.expect.where(bazel_version = bazel_version).that_bool( + got.supports_whl_extraction, + ).equals(supports_whl_extraction) + env.expect.where(bazel_version = bazel_version).that_bool( + got.extract_needs_chmod, + ).equals(extract_needs_chmod) + +_tests.append(_test_extract_features) + +def extract_features_test_suite(name): + """Create the test suite. + + Args: + name: the name of the test suite + """ + test_suite(name = name, basic_tests = _tests) From 4a55555032bee7308d94dfb3f97326025448617a Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:24:55 +0900 Subject: [PATCH 36/39] refactor(pypi): do not load bazel_skylib from the whl_library load chain The `pypi_repo_utils.bzl` and `util.bzl` files only used `types.is_string` and `types.is_tuple` from `@bazel_skylib`, which are simple `type()` checks. Without them, `whl_library` can be loaded from the files that are loaded before the `WORKSPACE` defines `@bazel_skylib`, e.g. `python/repositories.bzl`. --- python/private/BUILD.bazel | 5 +---- python/private/pypi/BUILD.bazel | 1 - python/private/pypi/pypi_repo_utils.bzl | 3 +-- python/private/util.bzl | 3 +-- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index 7be464360b..f79c433074 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -908,10 +908,7 @@ bzl_library( bzl_library( name = "util", srcs = ["util.bzl"], - deps = [ - ":py_internal", - "@bazel_skylib//lib:types", - ], + deps = [":py_internal"], ) bzl_library( diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel index e841b6893f..3a920c6bce 100644 --- a/python/private/pypi/BUILD.bazel +++ b/python/private/pypi/BUILD.bazel @@ -371,7 +371,6 @@ bzl_library( deps = [ "//python/private:repo_utils", "//python/private:util", - "@bazel_skylib//lib:types", ], ) diff --git a/python/private/pypi/pypi_repo_utils.bzl b/python/private/pypi/pypi_repo_utils.bzl index 8ec7bd1dbe..d9a074ca96 100644 --- a/python/private/pypi/pypi_repo_utils.bzl +++ b/python/private/pypi/pypi_repo_utils.bzl @@ -14,7 +14,6 @@ "" -load("@bazel_skylib//lib:types.bzl", "types") load("//python/private:repo_utils.bzl", "repo_utils") load("//python/private:util.bzl", "is_importable_name") @@ -113,7 +112,7 @@ def _execute_prep(mrctx, *, python, srcs, **kwargs): environment = kwargs.pop("environment", {}) pythonpath = environment.get("PYTHONPATH", "") - if pythonpath and not types.is_string(pythonpath): + if pythonpath and type(pythonpath) != "string": environment["PYTHONPATH"] = _construct_pypath(mrctx, entries = pythonpath) kwargs["environment"] = environment diff --git a/python/private/util.bzl b/python/private/util.bzl index 31f317fedf..575f27c1ae 100644 --- a/python/private/util.bzl +++ b/python/private/util.bzl @@ -14,7 +14,6 @@ """Functionality shared by multiple pieces of code.""" -load("@bazel_skylib//lib:types.bzl", "types") load("//python/private:py_internal.bzl", "py_internal") def copy_propagating_kwargs(from_kwargs, into_kwargs = None): @@ -62,7 +61,7 @@ def add_tag(attrs, tag): # Preserve the input type: this allows a test verifying the underlying # rule can accept the tuple for the tags argument. - if types.is_tuple(tags): + if type(tags) == "tuple": attrs["tags"] = tags + (tag,) else: # List concatenation is necessary because the original value From 98aa18483bad82a0534a96d957c5c0b4b372ae4b Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:34:21 +0900 Subject: [PATCH 37/39] refactor: add bazel_features to //python:features.bzl Replace the private `extract_features` function with a public `bazel_features(bazel_version)` function in `//python:features.bzl`, which returns the features of a Bazel version, e.g. `bazel_features(native.bazel_version).supports_whl_extraction`. The Bazel version is only available when a repository rule or a module extension runs, so this is a function and not fields of the `features` struct. The values are no longer written to the `@rules_python_internal` config, as nothing reads them from there anymore. --- python/features.bzl | 39 +++++++++++++++++++ python/private/BUILD.bazel | 6 --- python/private/extract_features.bzl | 38 ------------------ python/private/internal_config_repo.bzl | 6 --- python/private/pypi/BUILD.bazel | 4 +- python/private/pypi/patch_whl.bzl | 6 +-- python/private/pypi/whl_extract.bzl | 6 +-- tests/bazel_features/BUILD.bazel | 3 ++ .../bazel_features_tests.bzl} | 12 +++--- tests/extract_features/BUILD.bazel | 3 -- 10 files changed, 56 insertions(+), 67 deletions(-) delete mode 100644 python/private/extract_features.bzl create mode 100644 tests/bazel_features/BUILD.bazel rename tests/{extract_features/extract_features_tests.bzl => bazel_features/bazel_features_tests.bzl} (72%) delete mode 100644 tests/extract_features/BUILD.bazel diff --git a/python/features.bzl b/python/features.bzl index 99050a7ca5..8e295ec747 100644 --- a/python/features.bzl +++ b/python/features.bzl @@ -148,6 +148,45 @@ _LOADABLE_SYMBOLS = { ], } +def _major_minor(bazel_version): + # An empty version signifies a development build, which is treated as + # the latest version. + if not bazel_version: + return 99999, 99999 + + parts = bazel_version.split(".") + return int(parts[0]), int(parts[1]) + +def bazel_features(bazel_version): + """Return the features of a Bazel version that `rules_python` uses. + + The Bazel version is only available when a repository rule or a module + extension runs, so this is a function instead of fields of {obj}`features`, + e.g. `bazel_features(native.bazel_version).supports_whl_extraction`. + + :::{versionadded} VERSION_NEXT_FEATURE + ::: + + Args: + bazel_version: {type}`str` the Bazel version, e.g. + `native.bazel_version`. An empty string is a development build, + which is treated as the latest version. + + Returns: + {type}`struct` with: + * `supports_whl_extraction`: {type}`bool` whether + `repository_ctx.extract` can extract `.whl` files, which requires + Bazel 8.3.0 or later. + * `extract_needs_chmod`: {type}`bool` whether the files extracted by + `repository_ctx.extract` need their permissions fixed, which is the + case before Bazel 8.6.0. + """ + major, minor = _major_minor(bazel_version) + return struct( + supports_whl_extraction = major > 8 or (major == 8 and minor >= 3), + extract_needs_chmod = major < 8 or (major == 8 and minor < 6), + ) + features = struct( TYPEDEF = _features_typedef, # keep sorted diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index f79c433074..1cdd28a847 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -410,7 +410,6 @@ bzl_library( name = "internal_config_repo", srcs = ["internal_config_repo.bzl"], deps = [ - ":extract_features", ":repo_utils", ":text_util", ], @@ -973,11 +972,6 @@ bzl_library( srcs = ["envsubst.bzl"], ) -bzl_library( - name = "extract_features", - srcs = ["extract_features.bzl"], -) - bzl_library( name = "full_version", srcs = ["full_version.bzl"], diff --git a/python/private/extract_features.bzl b/python/private/extract_features.bzl deleted file mode 100644 index 8e07c69ed3..0000000000 --- a/python/private/extract_features.bzl +++ /dev/null @@ -1,38 +0,0 @@ -"""The archive extraction features of the running Bazel version. - -This only uses `native.bazel_version`, so that the repository rules can use it -without loading the `@rules_python_internal` repository, which may not be -defined yet when the WORKSPACE macros are loaded. -""" - -def _major_minor(bazel_version): - # An empty version signifies a development build, which is treated as - # the latest version. - if not bazel_version: - return 99999, 99999 - - parts = bazel_version.split(".") - return int(parts[0]), int(parts[1]) - -def extract_features(bazel_version = None): - """Return the archive extraction features of the Bazel version. - - Args: - bazel_version: {type}`str | None` the Bazel version, defaults to - `native.bazel_version`. - - Returns: - {type}`struct` with: - * `supports_whl_extraction`: {type}`bool` whether `.whl` files can - be extracted, which requires Bazel 8.3.0 or later. - * `extract_needs_chmod`: {type}`bool` whether the extracted files need - their permissions fixed, which is the case before Bazel 8.6.0. - """ - if bazel_version == None: - bazel_version = native.bazel_version - - major, minor = _major_minor(bazel_version) - return struct( - supports_whl_extraction = major > 8 or (major == 8 and minor >= 3), - extract_needs_chmod = major < 8 or (major == 8 and minor < 6), - ) diff --git a/python/private/internal_config_repo.bzl b/python/private/internal_config_repo.bzl index 762160c839..620be5b4d1 100644 --- a/python/private/internal_config_repo.bzl +++ b/python/private/internal_config_repo.bzl @@ -18,7 +18,6 @@ such as globals available to Bazel versions, or propagating user environment settings for rules to later use. """ -load("//python/private:extract_features.bzl", "extract_features") load("//python/private:text_util.bzl", "render") load(":repo_utils.bzl", "repo_utils") @@ -28,10 +27,8 @@ _ENABLE_DEPRECATION_WARNINGS_DEFAULT = "0" _CONFIG_TEMPLATE = """ config = struct( build_python_zip_default = {build_python_zip_default}, - supports_whl_extraction = {supports_whl_extraction}, enable_pystar = True, enable_deprecation_warnings = {enable_deprecation_warnings}, - extract_needs_chmod = {extract_needs_chmod}, bazel_8_or_later = {bazel_8_or_later}, bazel_9_or_later = {bazel_9_or_later}, bazel_10_or_later = {bazel_10_or_later}, @@ -86,7 +83,6 @@ def _internal_config_repo_impl(rctx): else: bazel_major_version = 99999 - features = extract_features() if bazel_major_version >= 8: builtin_py_info_symbol = "None" builtin_py_runtime_info_symbol = "None" @@ -102,8 +98,6 @@ def _internal_config_repo_impl(rctx): enable_deprecation_warnings = _bool_from_environ(rctx, _ENABLE_DEPRECATION_WARNINGS_ENVVAR_NAME, _ENABLE_DEPRECATION_WARNINGS_DEFAULT), builtin_py_info_symbol = builtin_py_info_symbol, builtin_py_runtime_info_symbol = builtin_py_runtime_info_symbol, - supports_whl_extraction = str(features.supports_whl_extraction), - extract_needs_chmod = str(features.extract_needs_chmod), builtin_py_cc_link_params_provider = builtin_py_cc_link_params_provider, modules_using_explicit_initpy = str(explicit_init_py_modules), tool_deps_dev_only = str(rctx.attr.tool_deps_dev_only), diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel index 3a920c6bce..eece782ef3 100644 --- a/python/private/pypi/BUILD.bazel +++ b/python/private/pypi/BUILD.bazel @@ -265,7 +265,7 @@ bzl_library( srcs = ["patch_whl.bzl"], deps = [ ":parse_whl_name", - "//python/private:extract_features", + "//python:features", "//python/private:repo_utils", ], ) @@ -474,7 +474,7 @@ bzl_library( srcs = ["whl_extract.bzl"], deps = [ ":whl_metadata", - "//python/private:extract_features", + "//python:features", "//python/private:repo_utils", ], ) diff --git a/python/private/pypi/patch_whl.bzl b/python/private/pypi/patch_whl.bzl index fea1421150..f8c01f94b7 100644 --- a/python/private/pypi/patch_whl.bzl +++ b/python/private/pypi/patch_whl.bzl @@ -19,7 +19,7 @@ repack patched wheels. Instead, after extracting and patching, we fix the RECORD file natively in Starlark and repack using system zip. """ -load("//python/private:extract_features.bzl", "extract_features") +load("//python:features.bzl", "bazel_features") load("//python/private:repo_utils.bzl", "repo_utils") load(":parse_whl_name.bzl", "parse_whl_name") @@ -96,8 +96,8 @@ def patch_whl(rctx, *, whl_path, patches): repo_utils.extract( rctx, archive = whl_input, - supports_whl_extraction = extract_features().supports_whl_extraction, - extract_needs_chmod = extract_features().extract_needs_chmod, + supports_whl_extraction = bazel_features(native.bazel_version).supports_whl_extraction, + extract_needs_chmod = bazel_features(native.bazel_version).extract_needs_chmod, ) if not patches: diff --git a/python/private/pypi/whl_extract.bzl b/python/private/pypi/whl_extract.bzl index a7a8d58dd5..c2b4a1a19b 100644 --- a/python/private/pypi/whl_extract.bzl +++ b/python/private/pypi/whl_extract.bzl @@ -1,6 +1,6 @@ """A simple whl extractor.""" -load("//python/private:extract_features.bzl", "extract_features") +load("//python:features.bzl", "bazel_features") load("//python/private:repo_utils.bzl", "repo_utils") load(":whl_metadata.bzl", "find_whl_metadata") @@ -32,8 +32,8 @@ def whl_extract(rctx, *, whl_path, logger): rctx, archive = whl_path, output = install_dir_path, - supports_whl_extraction = extract_features().supports_whl_extraction, - extract_needs_chmod = extract_features().extract_needs_chmod, + supports_whl_extraction = bazel_features(native.bazel_version).supports_whl_extraction, + extract_needs_chmod = bazel_features(native.bazel_version).extract_needs_chmod, ) metadata_file = find_whl_metadata( diff --git a/tests/bazel_features/BUILD.bazel b/tests/bazel_features/BUILD.bazel new file mode 100644 index 0000000000..2af882e20d --- /dev/null +++ b/tests/bazel_features/BUILD.bazel @@ -0,0 +1,3 @@ +load(":bazel_features_tests.bzl", "bazel_features_test_suite") + +bazel_features_test_suite(name = "bazel_features_tests") diff --git a/tests/extract_features/extract_features_tests.bzl b/tests/bazel_features/bazel_features_tests.bzl similarity index 72% rename from tests/extract_features/extract_features_tests.bzl rename to tests/bazel_features/bazel_features_tests.bzl index 0a8c66cb75..f452d65817 100644 --- a/tests/extract_features/extract_features_tests.bzl +++ b/tests/bazel_features/bazel_features_tests.bzl @@ -1,11 +1,11 @@ -"""Tests for the archive extraction features of the Bazel versions.""" +"""Tests for the features of the Bazel versions.""" load("@rules_testing//lib:test_suite.bzl", "test_suite") -load("//python/private:extract_features.bzl", "extract_features") # buildifier: disable=bzl-visibility +load("//python:features.bzl", "bazel_features") _tests = [] -def _test_extract_features(env): +def _test_bazel_features(env): for bazel_version, (supports_whl_extraction, extract_needs_chmod) in { # A development build is treated as the latest version. "": (True, False), @@ -16,7 +16,7 @@ def _test_extract_features(env): "8.6.0": (True, False), "9.1.1": (True, False), }.items(): - got = extract_features(bazel_version) + got = bazel_features(bazel_version) env.expect.where(bazel_version = bazel_version).that_bool( got.supports_whl_extraction, ).equals(supports_whl_extraction) @@ -24,9 +24,9 @@ def _test_extract_features(env): got.extract_needs_chmod, ).equals(extract_needs_chmod) -_tests.append(_test_extract_features) +_tests.append(_test_bazel_features) -def extract_features_test_suite(name): +def bazel_features_test_suite(name): """Create the test suite. Args: diff --git a/tests/extract_features/BUILD.bazel b/tests/extract_features/BUILD.bazel deleted file mode 100644 index 9b2fc599b3..0000000000 --- a/tests/extract_features/BUILD.bazel +++ /dev/null @@ -1,3 +0,0 @@ -load(":extract_features_tests.bzl", "extract_features_test_suite") - -extract_features_test_suite(name = "extract_features_tests") From 0bb36ad6805cdc6b0dec9366d20fdf97c66ccecd Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:34:57 +0900 Subject: [PATCH 38/39] chore: explain why the skylib types helpers are not used --- python/private/pypi/pypi_repo_utils.bzl | 4 ++++ python/private/util.bzl | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/python/private/pypi/pypi_repo_utils.bzl b/python/private/pypi/pypi_repo_utils.bzl index d9a074ca96..d8ad340340 100644 --- a/python/private/pypi/pypi_repo_utils.bzl +++ b/python/private/pypi/pypi_repo_utils.bzl @@ -112,6 +112,10 @@ def _execute_prep(mrctx, *, python, srcs, **kwargs): environment = kwargs.pop("environment", {}) pythonpath = environment.get("PYTHONPATH", "") + + # NOTE: `types.is_string` from `@bazel_skylib` is not used, so that this + # file can be loaded before the `WORKSPACE` defines `@bazel_skylib`, e.g. + # via `whl_library` from `python/repositories.bzl`. if pythonpath and type(pythonpath) != "string": environment["PYTHONPATH"] = _construct_pypath(mrctx, entries = pythonpath) kwargs["environment"] = environment diff --git a/python/private/util.bzl b/python/private/util.bzl index 575f27c1ae..e96bd569e9 100644 --- a/python/private/util.bzl +++ b/python/private/util.bzl @@ -61,6 +61,10 @@ def add_tag(attrs, tag): # Preserve the input type: this allows a test verifying the underlying # rule can accept the tuple for the tags argument. + # NOTE: `types.is_tuple` from `@bazel_skylib` is not used, so that + # this file can be loaded before the `WORKSPACE` defines + # `@bazel_skylib`, e.g. via `whl_library` from + # `python/repositories.bzl`. if type(tags) == "tuple": attrs["tags"] = tags + (tag,) else: From b29e48e12e7fd812a62e59b0e1df4c7f79ab0bde Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:37:27 +0900 Subject: [PATCH 39/39] refactor(toolchain): use whl_library for the WORKSPACE coverage wheels Now that `whl_library` can be loaded from `python/repositories.bzl` (#4458), use it for the coverage wheels instead of a hand-crafted `http_archive`, so the wheels are extracted the same way as for the pip hubs. --- python/private/coverage/BUILD.bazel | 1 + .../private/coverage/coverage_workspace.bzl | 43 +++++++------------ tests/coverage/coverage_workspace_tests.bzl | 1 + 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/python/private/coverage/BUILD.bazel b/python/private/coverage/BUILD.bazel index 87e96dfc56..8ef81174bc 100644 --- a/python/private/coverage/BUILD.bazel +++ b/python/private/coverage/BUILD.bazel @@ -29,6 +29,7 @@ bzl_library( "//python/private/pypi:pep508_env", "//python/private/pypi:pep508_evaluate", "//python/private/pypi:platform", + "//python/private/pypi:whl_library", "//python/private/pypi:whl_repo_name", ], ) diff --git a/python/private/coverage/coverage_workspace.bzl b/python/private/coverage/coverage_workspace.bzl index 8afc9aebe5..cb247a1c12 100644 --- a/python/private/coverage/coverage_workspace.bzl +++ b/python/private/coverage/coverage_workspace.bzl @@ -11,7 +11,6 @@ is a target for each Python version in the hub, which only selects the wheel based on the target platform. """ -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") load("//python:versions.bzl", "MINOR_MAPPING") load("//python/private:text_util.bzl", "render") @@ -22,6 +21,7 @@ load("//python/private/pypi:parse_requirements.bzl", "parse_requirements") load("//python/private/pypi:pep508_env.bzl", "env") load("//python/private/pypi:pep508_evaluate.bzl", "evaluate") load("//python/private/pypi:platform.bzl", "platform") +load("//python/private/pypi:whl_library.bzl", "whl_library") load("//python/private/pypi:whl_repo_name.bzl", "whl_repo_name") load(":uv_lock.bzl", "UV_LOCK_JSON") @@ -56,6 +56,8 @@ def _coverage_hub_repo_impl(rctx): "", """package(default_visibility = ["//visibility:public"])""", "", + """exports_files(["config.bzl"])""", + "", ] + [ render.call( "selects.config_setting_group", @@ -65,6 +67,9 @@ def _coverage_hub_repo_impl(rctx): for p in used_platforms ])) + # The `whl_library` repositories load the packages from the hub. + rctx.file("config.bzl", "packages = {\"coverage\": None}\n") + rctx.file("coverage/BUILD.bazel", "\n".join([ """package(default_visibility = ["//visibility:public"])""", "", @@ -101,25 +106,6 @@ coverage_hub_repo = repository_rule( doc = "The hub repository for the coverage tool of the WORKSPACE toolchains.", ) -# The BUILD file for the extracted wheel. The `whl_library` is not used, -# because this is loaded before `py_repositories` defines the repositories that -# it needs. -_WHL_BUILD_FILE = """\ -load("{py_library}", "py_library") - -package(default_visibility = ["//visibility:public"]) - -py_library( - name = "pkg", - srcs = glob(["coverage/**/*.py"]), - data = glob( - ["coverage/**"], - exclude = ["coverage/**/*.py"], - ), - imports = ["."], -) -""" - def _platforms(python_version, platforms): """Return the platforms to select the wheels for. @@ -167,7 +153,8 @@ def select_wheels(*, uv_lock, minor_mapping, platforms, package = "coverage"): Returns: {type}`struct` with `wheels`, a dict of repository name suffixes to - the wheel `filename`, `integrity` and `url`, and `tools`, a dict of + the wheel `filename`, `integrity`, `requirement` and `url`, and + `tools`, a dict of `major.minor` versions to platform names to the repository name suffixes. """ @@ -197,6 +184,7 @@ def select_wheels(*, uv_lock, minor_mapping, platforms, package = "coverage"): wheels[name] = { "filename": src.filename, "integrity": hash.integrity(src.digest), + "requirement": src.requirement_line, "url": src.url, } for p in src.target_platforms: @@ -235,17 +223,16 @@ def coverage_repositories(*, name, uv_lock, python_version, minor_mapping = MINO platforms = platforms, ) tools = selected.tools - build_file_content = _WHL_BUILD_FILE.format( - py_library = str(Label("//python:py_library.bzl")), - ) for repo, whl in selected.wheels.items(): maybe( - http_archive, + whl_library, name = "{}_{}".format(name, repo), - build_file_content = build_file_content, - integrity = whl["integrity"], - type = "zip", + requirement = whl["requirement"], urls = [whl["url"]], + filename = whl["filename"], + integrity = whl["integrity"], + dep_template = "@{}//{{name}}:{{target}}".format(name), + config_load = "@{}//:config.bzl".format(name), ) coverage_hub_repo( diff --git a/tests/coverage/coverage_workspace_tests.bzl b/tests/coverage/coverage_workspace_tests.bzl index 5445ab3af8..db6f880aff 100644 --- a/tests/coverage/coverage_workspace_tests.bzl +++ b/tests/coverage/coverage_workspace_tests.bzl @@ -45,6 +45,7 @@ def _test_select_platform_specific_wheels(env): env.expect.that_dict(got.wheels["coverage_cp313_cp313_manylinux_2_17_x86_64_deadb001"]).contains_exactly({ "filename": "coverage-7.0-cp313-cp313-manylinux_2_17_x86_64.whl", "integrity": "sha256-3q2wAQ==", + "requirement": "coverage==7.0", "url": _URL + "coverage-7.0-cp313-cp313-manylinux_2_17_x86_64.whl", }) env.expect.that_collection(got.wheels.keys()).contains_exactly([