Upgrade bazelbuild-rules_python to 0.36.0 am: b42a503ff3

Original change: https://android-review.googlesource.com/c/platform/external/bazelbuild-rules_python/+/3362429

Change-Id: I96f30149f65294e4159ef88e58f907488882d039
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index b778ac4..631ad86 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -38,11 +38,18 @@
     - "..."
   test_flags:
     - "--test_tag_filters=-integration-test"
-.common_workspace_flags: &common_workspace_flags
+.common_workspace_flags_min_bazel: &common_workspace_flags_min_bazel
   test_flags:
     - "--noenable_bzlmod"
   build_flags:
     - "--noenable_bzlmod"
+.common_workspace_flags: &common_workspace_flags
+  test_flags:
+    - "--noenable_bzlmod"
+    - "--enable_workspace"
+  build_flags:
+    - "--noenable_bzlmod"
+    - "--enable_workspace"
 .common_bazelinbazel_config: &common_bazelinbazel_config
     build_flags:
       - "--build_tag_filters=integration-test"
@@ -58,6 +65,15 @@
 .reusable_build_test_all: &reusable_build_test_all
   build_targets: ["..."]
   test_targets: ["..."]
+.lockfile_mode_error: &lockfile_mode_error
+  # For testing lockfile support
+  skip_in_bazel_downstream_pipeline: "Lockfile depends on the bazel version"
+  build_flags:
+    - "--lockfile_mode=error"
+  test_flags:
+    - "--lockfile_mode=error"
+  coverage_flags:
+    - "--lockfile_mode=error"
 .coverage_targets_example_bzlmod: &coverage_targets_example_bzlmod
   coverage_targets: ["..."]
 .coverage_targets_example_bzlmod_build_file_generation: &coverage_targets_example_bzlmod_build_file_generation
@@ -84,7 +100,7 @@
     - "--test_tag_filters=-integration-test,-doc_check_test"
 tasks:
   gazelle_extension_min:
-    <<: *common_workspace_flags
+    <<: *common_workspace_flags_min_bazel
     <<: *minimum_supported_version
     name: "Gazelle: workspace, minumum supported Bazel version"
     platform: ubuntu2004
@@ -108,7 +124,7 @@
   ubuntu_min_workspace:
     <<: *minimum_supported_version
     <<: *reusable_config
-    <<: *common_workspace_flags
+    <<: *common_workspace_flags_min_bazel
     name: "Default: Ubuntu, workspace, minimum Bazel"
     platform: ubuntu2004
   ubuntu_min_bzlmod:
@@ -187,7 +203,7 @@
   integration_test_build_file_generation_ubuntu_minimum_supported_workspace:
     <<: *minimum_supported_version
     <<: *reusable_build_test_all
-    <<: *common_workspace_flags
+    <<: *common_workspace_flags_min_bazel
     name: "examples/build_file_generation: Ubuntu, workspace, minimum Bazel"
     working_directory: examples/build_file_generation
     platform: ubuntu2004
@@ -247,6 +263,20 @@
     name: "examples/bzlmod: Windows"
     working_directory: examples/bzlmod
     platform: windows
+  integration_test_bzlmod_ubuntu_lockfile:
+    <<: *reusable_build_test_all
+    <<: *coverage_targets_example_bzlmod
+    <<: *lockfile_mode_error
+    name: "examples/bzlmod: Ubuntu with lockfile"
+    working_directory: examples/bzlmod
+    platform: ubuntu2004
+  integration_test_bzlmod_macos_lockfile:
+    <<: *reusable_build_test_all
+    <<: *coverage_targets_example_bzlmod
+    <<: *lockfile_mode_error
+    name: "examples/bzlmod: macOS with lockfile"
+    working_directory: examples/bzlmod
+    platform: macos
 
   integration_test_bzlmod_generate_build_file_generation_ubuntu_min:
     <<: *minimum_supported_version
@@ -319,7 +349,7 @@
 
   integration_test_pip_parse_ubuntu_min_workspace:
     <<: *minimum_supported_version
-    <<: *common_workspace_flags
+    <<: *common_workspace_flags_min_bazel
     <<: *reusable_build_test_all
     name: "examples/pip_parse: Ubuntu, workspace, minimum supporte Bazel version"
     working_directory: examples/pip_parse
@@ -353,7 +383,7 @@
 
   integration_test_pip_parse_vendored_ubuntu_min_workspace:
     <<: *minimum_supported_version
-    <<: *common_workspace_flags
+    <<: *common_workspace_flags_min_bazel
     <<: *reusable_build_test_all
     name: "examples/pip_parse_vendored: Ubuntu, workspace, minimum Bazel"
     working_directory: examples/pip_parse_vendored
@@ -532,7 +562,7 @@
 
   integration_compile_pip_requirements_test_from_external_repo_ubuntu_min_workspace:
     <<: *minimum_supported_version
-    <<: *common_workspace_flags
+    <<: *common_workspace_flags_min_bazel
     name: "compile_pip_requirements_test_from_external_repo: Ubuntu, workspace, minimum Bazel"
     working_directory: tests/integration/compile_pip_requirements_test_from_external_repo
     platform: ubuntu2004
diff --git a/.bazelrc b/.bazelrc
index b484751..1ca469c 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -4,8 +4,8 @@
 # (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
 # To update these lines, execute
 # `bazel run @rules_bazel_integration_test//tools:update_deleted_packages`
-build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
-query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
+build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
+query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
 
 test --test_output=errors
 
diff --git a/.bazelversion b/.bazelversion
index 66ce77b..a3fcc71 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-7.0.0
+7.1.0
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 9632f4e..5733fc1 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -10,7 +10,7 @@
   - package-ecosystem: "pip"
     directories:
       # Maintain dependencies for our tools
-      - "/docs/sphinx"
+      - "/docs"
       - "/tools/publish"
     schedule:
       interval: "weekly"
diff --git a/.gitignore b/.gitignore
index 863b0e9..92b5801 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,3 +52,4 @@
 # MODULE.bazel.lock is ignored for now as per recommendation from upstream.
 # See https://github.com/bazelbuild/bazel/issues/20369
 MODULE.bazel.lock
+!/examples/bzlmod/MODULE.bazel.lock
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 54aa043..38b9161 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -45,3 +45,9 @@
         entry: bazel run @rules_bazel_integration_test//tools:update_deleted_packages
         files: ^((examples|tests)/.*/(MODULE.bazel|WORKSPACE|WORKSPACE.bzlmod|BUILD.bazel)|.bazelrc)$
         pass_filenames: false
+      - id: update-bzlmod-lockfiles
+        name: Update bzlmod lockfiles
+        language: script
+        entry: ./tools/private/update_bzlmod_lockfiles.sh
+        files: ^python/
+        pass_filenames: false
diff --git a/.readthedocs.yml b/.readthedocs.yml
index f68ccc8..6613d49 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -11,4 +11,4 @@
     - bazel version
     # Put the actual build behind a shell script because its easier to modify than
     # the yaml config.
-    - docs/sphinx/readthedocs_build.sh
+    - docs/readthedocs_build.sh
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b457c4..8ea9565 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,12 +27,89 @@
 ### Changed
 * Nothing yet
 
+### Fixed
+* Nothing yet
+
 ### Added
 * Nothing yet
 
 ### Removed
 * Nothing yet
 
+## [0.36.0] - 2024-09-24
+
+[0.36.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.36.0
+
+### Changed
+* (gazelle): Update error messages when unable to resolve a dependency to be more human-friendly.
+* (flags) The {obj}`--python_version` flag now also returns
+  {obj}`config_common.FeatureFlagInfo`.
+* (toolchain): The toolchain patches now expose the `patch_strip` attribute
+  that one should use when patching toolchains. Please set it if you are
+  patching python interpreter. In the next release the default will be set to
+  `0` which better reflects the defaults used in public `bazel` APIs.
+* (toolchains) When {obj}`py_runtime.interpreter_version_info` isn't specified,
+  the {obj}`--python_version` flag will determine the value. This allows
+  specifying the build-time Python version for the
+  {obj}`runtime_env_toolchains`.
+* (toolchains) {obj}`py_cc_toolchain.libs` and {obj}`PyCcToolchainInfo.libs` is
+  optional. This is to support situations where only the Python headers are
+  available.
+* (bazel) Minimum bazel 7 version that we test against has been bumped to `7.1`.
+
+### Fixed
+* (whl_library): Remove `--no-index` and add `--no-build-isolation` to the
+  `pip install` command when installing a wheel from a local file, which happens
+  when `experimental_index_url` flag is used.
+* (bzlmod) get the path to the host python interpreter in a way that results in
+  platform non-dependent hashes in the lock file when the requirement markers need
+  to be evaluated.
+* (bzlmod) correctly watch sources used for evaluating requirement markers for
+  any changes so that the repository rule or module extensions can be
+  re-evaluated when the said files change.
+* (gazelle): Fix incorrect use of `t.Fatal`/`t.Fatalf` in tests.
+* (toolchain) Omit third-party python packages from coverage reports from
+  stage2 bootstrap template.
+* (bzlmod) Properly handle relative path URLs in parse_simpleapi_html.bzl
+* (gazelle) Correctly resolve deps that have top-level module overlap with a gazelle_python.yaml dep module
+* (rules) Make `RUNFILES_MANIFEST_FILE`-based invocations work when used with
+  {obj}`--bootstrap_impl=script`. This fixes invocations using non-sandboxed
+  test execution with `--enable_runfiles=false --build_runfile_manifests=true`.
+  ([#2186](https://github.com/bazelbuild/rules_python/issues/2186)).
+* (py_wheel) Fix incorrectly generated `Required-Dist` when specifying requirements with markers
+  in extra_requires in py_wheel rule.
+* (rules) Prevent pytest from trying run the generated stage2
+  bootstrap .py file when using {obj}`--bootstrap_impl=script`
+* (toolchain) The {bzl:obj}`gen_python_config_settings` has been fixed to include
+  the flag_values from the platform definitions.
+
+
+### Added
+* (bzlmod): Toolchain overrides can now be done using the new
+  {bzl:obj}`python.override`, {bzl:obj}`python.single_version_override` and
+  {bzl:obj}`python.single_version_platform_override` tag classes.
+  See [#2081](https://github.com/bazelbuild/rules_python/issues/2081).
+* (rules) Executables provide {obj}`PyExecutableInfo`, which contains
+  executable-specific information useful for packaging an executable or
+  or deriving a new one from the original.
+* (py_wheel) Removed use of bash to avoid failures on Windows machines which do not
+  have it installed.
+* (docs) Automatically generated documentation for {bzl:obj}`python_register_toolchains`
+  and related symbols.
+* (toolchains) Added {attr}`python_repository.patch_strip` attribute for
+  allowing values that are other than `1`, which has been hard-coded up until
+  now. If you are relying on the undocumented `patches` support in
+  `TOOL_VERSIONS` for registering patched toolchains please consider setting
+  the `patch_strip` explicitly to `1` if you depend on this value - in the
+  future the value may change to default to `0`.
+* (toolchains) Added `//python:none`, a special target for use with
+  {obj}`py_exec_tools_toolchain.exec_interpreter` to treat the value as `None`.
+
+### Removed
+* (toolchains): Removed accidentally exposed `http_archive` symbol from
+  `python/repositories.bzl`.
+* (toolchains): An internal _is_python_config_setting_ macro has been removed.
+
 ## [0.35.0] - 2024-08-15
 
 [0.35.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.35.0
@@ -114,7 +191,6 @@
 [pytest_bazel]: https://pypi.org/project/pytest-bazel
 [20240726]: https://github.com/indygreg/python-build-standalone/releases/tag/20240726
 
-
 ## [0.34.0] - 2024-07-04
 
 [0.34.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.34.0
@@ -227,7 +303,7 @@
   be automatically deleted correctly. For example, if `python_generation_mode`
   is set to package, when `__init__.py` is deleted, the `py_library` generated
   for this package before will be deleted automatically.
-* (whl_library): Use `is_python_config_setting` to correctly handle multi-python
+* (whl_library): Use _is_python_config_setting_ to correctly handle multi-python
   version dependency select statements when the `experimental_target_platforms`
   includes the Python ABI. The default python version case within the select is
   also now handled correctly, stabilizing the implementation.
@@ -745,8 +821,7 @@
 
 ### Added
 
-* (bzlmod, entry_point) Added
-  [`py_console_script_binary`](./docs/py_console_script_binary.md), which
+* (bzlmod, entry_point) Added {obj}`py_console_script_binary`, which
   allows adding custom dependencies to a package's entry points and customizing
   the `py_binary` rule used to build it.
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cb123bf..33b296f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -148,7 +148,7 @@
 
 * **requirements lock files**: These are usually generated by a
   `compile_pip_requirements` update target, which is usually in the same directory.
-  e.g. `bazel run //docs/sphinx:requirements.update`
+  e.g. `bazel run //docs:requirements.update`
 
 ## Core rules
 
diff --git a/METADATA b/METADATA
index e534c8d..1d87185 100644
--- a/METADATA
+++ b/METADATA
@@ -8,12 +8,12 @@
   license_type: NOTICE
   last_upgrade_date {
     year: 2024
-    month: 9
-    day: 19
+    month: 11
+    day: 18
   }
   identifier {
     type: "Git"
     value: "https://github.com/bazelbuild/rules_python"
-    version: "0.35.0"
+    version: "0.36.0"
   }
 }
diff --git a/MODULE.bazel b/MODULE.bazel
index c4d0e5f..58c7ae2 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -81,7 +81,7 @@
     "python",
     dev_dependency = True,
 )
-dev_python.rules_python_private_testing(
+dev_python.override(
     register_all_versions = True,
 )
 
@@ -93,7 +93,7 @@
 dev_pip.parse(
     hub_name = "dev_pip",
     python_version = "3.11",
-    requirements_lock = "//docs/sphinx:requirements.txt",
+    requirements_lock = "//docs:requirements.txt",
 )
 dev_pip.parse(
     hub_name = "pypiserver",
@@ -116,6 +116,7 @@
     path = "tests/integration/bazel_from_env",
 )
 bazel_binaries.download(version = "6.4.0")
+bazel_binaries.download(version = "7.3.1")
 bazel_binaries.download(version = "rolling")
 use_repo(
     bazel_binaries,
@@ -124,6 +125,7 @@
     # that should be use_repo()'d, so we add them as requested
     "bazel_binaries_bazelisk",
     "build_bazel_bazel_6_4_0",
+    "build_bazel_bazel_7_3_1",
     "build_bazel_bazel_rolling",
     "build_bazel_bazel_self",
 )
diff --git a/WORKSPACE b/WORKSPACE
index 695b0e9..02b3b6e 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -122,7 +122,7 @@
 pip_parse(
     name = "dev_pip",
     python_interpreter_target = interpreter,
-    requirements_lock = "//docs/sphinx:requirements.txt",
+    requirements_lock = "//docs:requirements.txt",
 )
 
 load("@dev_pip//:requirements.bzl", docs_install_deps = "install_deps")
diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index c334fbc..149e2c5 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -1,10 +1,10 @@
-# Copyright 2017 The Bazel Authors. All rights reserved.
+# 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
+#     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,
@@ -13,31 +13,172 @@
 # limitations under the License.
 
 load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("@dev_pip//:requirements.bzl", "requirement")
+load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")  # buildifier: disable=bzl-visibility
+load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
+load("//python/uv/private:lock.bzl", "lock")  # buildifier: disable=bzl-visibility
+load("//sphinxdocs:readthedocs.bzl", "readthedocs_install")
+load("//sphinxdocs:sphinx.bzl", "sphinx_build_binary", "sphinx_docs")
+load("//sphinxdocs:sphinx_docs_library.bzl", "sphinx_docs_library")
+load("//sphinxdocs:sphinx_stardoc.bzl", "sphinx_stardoc", "sphinx_stardocs")
 
-# NOTE: Only public visibility for historical reasons.
-# This package is only for rules_python to generate its own docs.
-package(default_visibility = ["//visibility:public"])
+package(default_visibility = ["//:__subpackages__"])
 
 licenses(["notice"])  # Apache 2.0
 
+# We only build for Linux and Mac because:
+# 1. The actual doc process only runs on Linux
+# 2. Mac is a common development platform, and is close enough to Linux
+#    it's feasible to make work.
+# Making CI happy under Windows is too much of a headache, though, so we don't
+# bother with that.
+_TARGET_COMPATIBLE_WITH = select({
+    "@platforms//os:linux": [],
+    "@platforms//os:macos": [],
+    "//conditions:default": ["@platforms//:incompatible"],
+}) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"]
+
+# See README.md for instructions. Short version:
+# * `bazel run //docs:docs.serve` in a separate terminal
+# * `ibazel build //docs:docs` to automatically rebuild docs
+sphinx_docs(
+    name = "docs",
+    srcs = glob(
+        include = [
+            "*.md",
+            "**/*.md",
+            "_static/**",
+            "_includes/**",
+        ],
+        exclude = [
+            "README.md",
+            "_*",
+            "*.inv*",
+        ],
+    ),
+    config = "conf.py",
+    formats = [
+        "html",
+    ],
+    renamed_srcs = {
+        "//:CHANGELOG.md": "changelog.md",
+        "//:CONTRIBUTING.md": "contributing.md",
+        "//sphinxdocs/inventories:bazel_inventory": "bazel_inventory.inv",
+    },
+    sphinx = ":sphinx-build",
+    strip_prefix = package_name() + "/",
+    tags = ["docs"],
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+    deps = [
+        ":bzl_api_docs",
+        ":py_api_srcs",
+        ":py_runtime_pair",
+        "//sphinxdocs/docs:docs_lib",
+    ],
+)
+
+sphinx_stardocs(
+    name = "bzl_api_docs",
+    srcs = [
+        "//python:defs_bzl",
+        "//python:packaging_bzl",
+        "//python:pip_bzl",
+        "//python:py_binary_bzl",
+        "//python:py_cc_link_params_info_bzl",
+        "//python:py_exec_tools_info_bzl",
+        "//python:py_exec_tools_toolchain_bzl",
+        "//python:py_executable_info_bzl",
+        "//python:py_library_bzl",
+        "//python:py_runtime_bzl",
+        "//python:py_runtime_info_bzl",
+        "//python:py_test_bzl",
+        "//python:repositories_bzl",
+        "//python/cc:py_cc_toolchain_bzl",
+        "//python/cc:py_cc_toolchain_info_bzl",
+        "//python/entry_points:py_console_script_binary_bzl",
+        "//python/private:py_cc_toolchain_rule_bzl",
+        "//python/private/common:py_binary_rule_bazel_bzl",
+        "//python/private/common:py_library_rule_bazel_bzl",
+        "//python/private/common:py_runtime_rule_bzl",
+        "//python/private/common:py_test_rule_bazel_bzl",
+    ] + ([
+        # Bazel 6 + Stardoc isn't able to parse something about the python bzlmod extension
+        "//python/extensions:python_bzl",
+    ] if IS_BAZEL_7_OR_HIGHER else []) + ([
+        # This depends on @pythons_hub, which is only created under bzlmod,
+        "//python/extensions:pip_bzl",
+    ] if IS_BAZEL_7_OR_HIGHER and BZLMOD_ENABLED else []),
+    prefix = "api/rules_python/",
+    tags = ["docs"],
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+)
+
+sphinx_stardoc(
+    name = "py_runtime_pair",
+    src = "//python/private:py_runtime_pair_rule_bzl",
+    prefix = "api/rules_python/",
+    tags = ["docs"],
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+)
+
+sphinx_docs_library(
+    name = "py_api_srcs",
+    srcs = [
+        "//python/runfiles",
+    ],
+    strip_prefix = "python/",
+)
+
+readthedocs_install(
+    name = "readthedocs_install",
+    docs = [":docs"],
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+)
+
+sphinx_build_binary(
+    name = "sphinx-build",
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+    deps = [
+        requirement("sphinx"),
+        requirement("sphinx_rtd_theme"),
+        requirement("myst_parser"),
+        requirement("readthedocs_sphinx_ext"),
+        requirement("typing_extensions"),
+        requirement("sphinx_autodoc2"),
+        requirement("sphinx_reredirects"),
+        "//sphinxdocs/src/sphinx_bzl",
+    ],
+)
+
+# Run bazel run //docs:requirements.update
+lock(
+    name = "requirements",
+    srcs = ["pyproject.toml"],
+    out = "requirements.txt",
+    upgrade = True,
+)
+
 # Temporary compatibility aliases for some other projects depending on the old
 # bzl_library targets.
 alias(
     name = "defs",
     actual = "//python:defs_bzl",
     deprecation = "Use //python:defs_bzl instead; targets under //docs are internal.",
+    visibility = ["//visibility:public"],
 )
 
 alias(
     name = "bazel_repo_tools",
     actual = "//python/private:bazel_tools_bzl",
     deprecation = "Use @bazel_tools//tools:bzl_srcs instead; targets under //docs are internal.",
+    visibility = ["//visibility:public"],
 )
 
 bzl_library(
     name = "pip_install_bzl",
     deprecation = "Use //python:pip_bzl or //python/pip_install:pip_repository_bzl instead; " +
                   "targets under //docs are internal.",
+    visibility = ["//visibility:public"],
     deps = [
         "//python:pip_bzl",
         "//python/pip_install:pip_repository_bzl",
@@ -49,4 +190,5 @@
     actual = "//python/pip_install:pip_repository_bzl",
     deprecation = "Use //python/pip_install:pip_repository_bzl instead; Both the requirements " +
                   "parser and targets under //docs are internal",
+    visibility = ["//visibility:public"],
 )
diff --git a/docs/sphinx/README.md b/docs/README.md
similarity index 90%
rename from docs/sphinx/README.md
rename to docs/README.md
index 98420e4..d98be41 100644
--- a/docs/sphinx/README.md
+++ b/docs/README.md
@@ -18,8 +18,8 @@
 change. The quick start is:
 
 ```
-bazel run //docs/sphinx:docs.serve  # Run in separate terminal
-ibazel build //docs/sphinx:docs  # Automatically rebuilds docs
+bazel run //docs:docs.serve  # Run in separate terminal
+ibazel build //docs:docs  # Automatically rebuilds docs
 ```
 
 This will build the docs and start a local webserver at http://localhost:8000
@@ -47,14 +47,14 @@
 creating indexes, and using concise markup to generate rich documentation.
 
 MyST features and behaviors are controlled by the Sphinx configuration file,
-`docs/sphinx/conf.py`. For more info, see https://myst-parser.readthedocs.io.
+`docs/conf.py`. For more info, see https://myst-parser.readthedocs.io.
 
 ## Sphinx configuration
 
 The Sphinx-specific configuration files and input doc files live in
-docs/sphinx.
+docs/.
 
-The Sphinx configuration is `docs/sphinx/conf.py`. See
+The Sphinx configuration is `docs/conf.py`. See
 https://www.sphinx-doc.org/ for details about the configuration file.
 
 ## Readthedocs configuration
diff --git a/docs/sphinx/_includes/py_console_script_binary.md b/docs/_includes/py_console_script_binary.md
similarity index 100%
rename from docs/sphinx/_includes/py_console_script_binary.md
rename to docs/_includes/py_console_script_binary.md
diff --git a/docs/sphinx/_static/css/custom.css b/docs/_static/css/custom.css
similarity index 100%
rename from docs/sphinx/_static/css/custom.css
rename to docs/_static/css/custom.css
diff --git a/docs/sphinx/api/index.md b/docs/api/index.md
similarity index 83%
rename from docs/sphinx/api/index.md
rename to docs/api/index.md
index 028fab7..0a5f1ed 100644
--- a/docs/sphinx/api/index.md
+++ b/docs/api/index.md
@@ -2,5 +2,5 @@
 
 ```{toctree}
 :glob:
-**
+*/index
 ```
diff --git a/docs/api/rules_python/index.md b/docs/api/rules_python/index.md
new file mode 100644
index 0000000..7e4d1ff
--- /dev/null
+++ b/docs/api/rules_python/index.md
@@ -0,0 +1,8 @@
+# rules_python Bazel APIs
+
+API documentation for rules_python Bazel objects.
+
+```{toctree}
+:glob:
+**
+```
diff --git a/docs/sphinx/api/python/cc/index.md b/docs/api/rules_python/python/cc/index.md
similarity index 76%
rename from docs/sphinx/api/python/cc/index.md
rename to docs/api/rules_python/python/cc/index.md
index acaaf4f..82c5934 100644
--- a/docs/sphinx/api/python/cc/index.md
+++ b/docs/api/rules_python/python/cc/index.md
@@ -1,3 +1,5 @@
+:::{default-domain} bzl
+:::
 :::{bzl:currentfile} //python/cc:BUILD.bazel
 :::
 # //python/cc
@@ -25,3 +27,15 @@
 
 * `CcInfo`: The C++ information about the Python libraries.
 :::
+
+:::{bzl:target} toolchain_type
+
+Toolchain type identifier for the Python C toolchain.
+
+This toolchain type is typically implemented by {obj}`py_cc_toolchain`.
+
+::::{seealso}
+{any}`Custom Toolchains` for how to define custom toolchains
+::::
+
+:::
diff --git a/docs/sphinx/api/python/config_settings/index.md b/docs/api/rules_python/python/config_settings/index.md
similarity index 95%
rename from docs/sphinx/api/python/config_settings/index.md
rename to docs/api/rules_python/python/config_settings/index.md
index 50647ab..e102baa 100644
--- a/docs/sphinx/api/python/config_settings/index.md
+++ b/docs/api/rules_python/python/config_settings/index.md
@@ -30,13 +30,13 @@
 Determines if Python source files should be compiled at build time.
 
 :::{note}
-The flag value is overridden by the target level `precompile` attribute,
+The flag value is overridden by the target level {attr}`precompile` attribute,
 except for the case of `force_enabled` and `forced_disabled`.
 :::
 
 Values:
 
-* `auto`: Automatically decide the effective value based on environment,
+* `auto`: (default) Automatically decide the effective value based on environment,
   target platform, etc.
 * `enabled`: Compile Python source files at build time. Note that
   {bzl:obj}`--precompile_add_to_runfiles` affects how the compiled files are included into
@@ -65,12 +65,18 @@
 
 Values:
 
+* `auto`: (default) Automatically decide the effective value based on environment,
+  target platform, etc.
 * `keep_source`: Include the original Python source.
 * `omit_source`: Don't include the orignal py source.
 * `omit_if_generated_source`: Keep the original source if it's a regular source
   file, but omit it if it's a generated file.
+
 :::{versionadded} 0.33.0
 :::
+:::{versionadded} 0.36.0
+The `auto` value
+:::
 ::::
 
 ::::{bzl:flag} precompile_add_to_runfiles
diff --git a/docs/api/rules_python/python/index.md b/docs/api/rules_python/python/index.md
new file mode 100644
index 0000000..bc5a731
--- /dev/null
+++ b/docs/api/rules_python/python/index.md
@@ -0,0 +1,65 @@
+:::{default-domain} bzl
+:::
+:::{bzl:currentfile} //python:BUILD.bazel
+:::
+
+# //python
+
+:::{bzl:target} toolchain_type
+
+Identifier for the toolchain type for the target platform.
+
+This toolchain type gives information about the runtime for the target platform.
+It is typically implemented by the {obj}`py_runtime` rule.
+
+::::{seealso}
+{any}`Custom Toolchains` for how to define custom toolchains
+::::
+
+:::
+
+:::{bzl:target} exec_tools_toolchain_type
+
+Identifier for the toolchain type for exec tools used to build Python targets.
+
+This toolchain type gives information about tools needed to build Python targets
+at build time. It is typically implemented by the {obj}`py_exec_tools_toolchain`
+rule.
+
+::::{seealso}
+{any}`Custom Toolchains` for how to define custom toolchains
+::::
+:::
+
+:::{bzl:target} current_py_toolchain
+
+Helper target to resolve to the consumer's current Python toolchain. This target
+provides:
+
+* {obj}`PyRuntimeInfo`: The consuming target's target toolchain information
+
+:::
+
+::::{target} autodetecting_toolchain
+
+Legacy toolchain; despite its name, it doesn't autodetect anything.
+
+:::{deprecated} 0.34.0
+
+Use {obj}`@rules_python//python/runtime_env_toolchains:all` instead.
+:::
+::::
+
+:::{target} none
+A special target so that label attributes with default values can be set to
+`None`.
+
+Bazel interprets `None` to mean "use the default value", which
+makes it impossible to have a label attribute with a default value that is
+optional. To work around this, a target with a special provider is used;
+internally rules check for this, then treat the value as `None`.
+
+::::{versionadded} 0.36.0
+::::
+
+:::
diff --git a/docs/sphinx/api/python/runtime_env_toolchains/index.md b/docs/api/rules_python/python/runtime_env_toolchains/index.md
similarity index 91%
rename from docs/sphinx/api/python/runtime_env_toolchains/index.md
rename to docs/api/rules_python/python/runtime_env_toolchains/index.md
index ef31f08..7d6e1fb 100644
--- a/docs/sphinx/api/python/runtime_env_toolchains/index.md
+++ b/docs/api/rules_python/python/runtime_env_toolchains/index.md
@@ -1,9 +1,9 @@
 :::{default-domain} bzl
 :::
-:::{bzl:currentfile} //python/runtime_env_toolchain:BUILD.bazel
+:::{bzl:currentfile} //python/runtime_env_toolchains:BUILD.bazel
 :::
 
-# //python/runtime_env_toolchain
+# //python/runtime_env_toolchains
 
 ::::{target} all
 
diff --git a/docs/sphinx/api/tools/precompiler/index.md b/docs/api/rules_python/tools/precompiler/index.md
similarity index 100%
rename from docs/sphinx/api/tools/precompiler/index.md
rename to docs/api/rules_python/tools/precompiler/index.md
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..d65d5b5
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,223 @@
+# Configuration file for the Sphinx documentation builder.
+
+import os
+
+# -- Project information
+project = "rules_python"
+copyright = "2023, The Bazel Authors"
+author = "Bazel"
+
+# NOTE: These are overriden by -D flags via --//sphinxdocs:extra_defines
+version = "0.0.0"
+release = version
+
+# -- General configuration
+# See https://www.sphinx-doc.org/en/master/usage/configuration.html
+# for more settings
+
+# Any extensions here not built into Sphinx must also be added to
+# the dependencies of //docs:sphinx-builder
+extensions = [
+    "autodoc2",
+    "sphinx.ext.autosectionlabel",
+    "sphinx.ext.doctest",
+    "sphinx.ext.duration",
+    "sphinx.ext.extlinks",
+    "sphinx.ext.intersphinx",
+    "myst_parser",
+    "sphinx_rtd_theme",  # Necessary to get jquery to make flyout work
+    "sphinx_bzl.bzl",
+    "sphinx_reredirects",
+]
+
+autodoc2_packages = [
+    "sphinx_bzl",
+    "runfiles",
+]
+
+autodoc2_output_dir = "api/py"
+autodoc2_sort_names = True
+autodoc2_class_docstring = "both"
+autodoc2_index_template = """
+Python APIs
+====================
+
+This page contains auto-generated API reference documentation [#f1]_.
+
+.. toctree::
+   :titlesonly:
+
+{% for package in top_level %}
+   {{ package }}
+{%- endfor %}
+
+.. [#f1] Created with `sphinx-autodoc2 <https://github.com/chrisjsewell/sphinx-autodoc2>`_
+
+"""
+
+
+autodoc2_docstring_parser_regexes = [
+    (".*", "myst"),
+]
+ 
+# NOTE: The redirects generation will clobber existing files.
+redirects = {
+    "api/tools/precompiler/index": "/api/rules_python/tools/precompiler/index.html",
+    "api/python/py_library": "/api/rules_python/python/py_library.html",
+    "api/python/py_binary": "/api/rules_python/python/py_binary.html",
+    "api/python/py_test": "/api/rules_python/python/py_test.html",
+    "api/python/defs": "/api/rules_python/python/defs.html",
+    "api/python/index": "/api/rules_python/python/index.html",
+    "api/python/py_runtime_info": "/api/rules_python/python/py_runtime_info.html",
+    "api/python/private/common/py_library_rule_bazel": "/api/rules_python/python/private/common/py_library_rule_bazel.html",
+    "api/python/private/common/py_test_rule_bazel": "/api/rules_python/python/private/common/py_test_rule_bazel.html",
+    "api/python/private/common/py_binary_rule_bazel": "/api/rules_python/python/private/common/py_binary_rule_bazel.html",
+    "api/python/private/common/py_runtime_rule": "/api/rules_python/python/private/common/py_runtime_rule.html",
+    "api/python/extensions/pip": "/api/rules_python/python/extensions/pip.html",
+    "api/python/extensions/python": "/api/rules_python/python/extensions/python.html",
+    "api/python/entry_points/py_console_script_binary": "/api/rules_python/python/entry_points/py_console_script_binary.html",
+    "api/python/cc/py_cc_toolchain_info": "/api/rules_python/python/cc/py_cc_toolchain_info.html",
+    "api/python/cc/index": "/api/rules_python/python/cc/index.html",
+    "api/python/py_cc_link_params_info": "/api/rules_python/python/py_cc_link_params_info.html",
+    "api/python/runtime_env_toolchains/index": "/api/rules_python/python/runtime_env_toolchains/index.html",
+    "api/python/pip": "/api/rules_python/python/pip.html",
+    "api/python/config_settings/index": "/api/rules_python/python/config_settings/index.html",
+    "api/python/packaging": "/api/rules_python/python/packaging.html",
+    "api/python/py_runtime": "/api/rules_python/python/py_runtime.html",
+    "api/sphinxdocs/sphinx": "/api/sphinxdocs/sphinxdocs/sphinx.html",
+    "api/sphinxdocs/sphinx_stardoc": "/api/sphinxdocs/sphinxdocs/sphinx_stardoc.html",
+    "api/sphinxdocs/readthedocs": "/api/sphinxdocs/sphinxdocs/readthedocs.html",
+    "api/sphinxdocs/index": "/api/sphinxdocs/sphinxdocs/index.html",
+    "api/sphinxdocs/private/sphinx_docs_library": "/api/sphinxdocs/sphinxdocs/private/sphinx_docs_library.html",
+    "api/sphinxdocs/sphinx_docs_library": "/api/sphinxdocs/sphinxdocs/sphinx_docs_library.html",
+    "api/sphinxdocs/inventories/index": "/api/sphinxdocs/sphinxdocs/inventories/index.html",
+}
+
+# Adapted from the template code:
+# https://github.com/readthedocs/readthedocs.org/blob/main/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl
+if os.environ.get("READTHEDOCS") == "True":
+    # Must come first because it can interfere with other extensions, according
+    # to the original conf.py template comments
+    extensions.insert(0, "readthedocs_ext.readthedocs")
+
+    if os.environ.get("READTHEDOCS_VERSION_TYPE") == "external":
+        # Insert after the main extension
+        extensions.insert(1, "readthedocs_ext.external_version_warning")
+        readthedocs_vcs_url = (
+            "http://github.com/bazelbuild/rules_python/pull/{}".format(
+                os.environ.get("READTHEDOCS_VERSION", "")
+            )
+        )
+        # The build id isn't directly available, but it appears to be encoded
+        # into the host name, so we can parse it from that. The format appears
+        # to be `build-X-project-Y-Z`, where:
+        # * X is an integer build id
+        # * Y is an integer project id
+        # * Z is the project name
+        _build_id = os.environ.get("HOSTNAME", "build-0-project-0-rules-python")
+        _build_id = _build_id.split("-")[1]
+        readthedocs_build_url = (
+            f"https://readthedocs.org/projects/rules-python/builds/{_build_id}"
+        )
+
+exclude_patterns = ["_includes/*"]
+templates_path = ["_templates"]
+primary_domain = None  # The default is 'py', which we don't make much use of
+nitpicky = True
+
+# --- Intersphinx configuration
+
+intersphinx_mapping = {
+    "bazel": ("https://bazel.build/", "bazel_inventory.inv"),
+}
+
+# --- Extlinks configuration
+extlinks = {
+    "gh-path": (f"https://github.com/bazelbuild/rules_python/tree/main/%s", "%s"),
+}
+
+# --- MyST configuration
+# See https://myst-parser.readthedocs.io/en/latest/configuration.html
+# for more settings
+
+# See https://myst-parser.readthedocs.io/en/latest/syntax/optional.html
+# for additional extensions.
+myst_enable_extensions = [
+    "fieldlist",
+    "attrs_block",
+    "attrs_inline",
+    "colon_fence",
+    "deflist",
+    "substitution",
+]
+
+myst_substitutions = {}
+
+# --- sphinx_stardoc configuration
+
+bzl_default_repository_name = "@rules_python"
+
+# -- Options for HTML output
+# See https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+# For additional html settings
+
+# See https://sphinx-rtd-theme.readthedocs.io/en/stable/configuring.html for
+# them-specific options
+html_theme = "sphinx_rtd_theme"
+html_theme_options = {}
+
+# The html_context settings are part of the jinja context used by the themes.
+html_context = {
+    # This controls whether the flyout menu is shown. It is always false
+    # because:
+    # * For local builds, the flyout menu is empty and doesn't show in the
+    #   same place as for RTD builds. No point in showing it locally.
+    # * For RTD builds, the flyout menu is always automatically injected,
+    #   so having it be True makes the flyout show up twice.
+    "READTHEDOCS": False,
+    "PRODUCTION_DOMAIN": "readthedocs.org",
+    # This is the path to a page's source (after the github user/repo/commit)
+    "conf_py_path": "/docs/",
+    "github_user": "bazelbuild",
+    "github_repo": "rules_python",
+    # The git version that was checked out, e.g. the tag or branch name
+    "github_version": os.environ.get("READTHEDOCS_GIT_IDENTIFIER", ""),
+    # For local builds, the github link won't work. Disabling it replaces
+    # it with a "view source" link to view the source Sphinx saw, which
+    # is useful for local development.
+    "display_github": os.environ.get("READTHEDOCS") == "True",
+    "commit": os.environ.get("READTHEDOCS_GIT_COMMIT_HASH", "unknown commit"),
+    # Used by readthedocs_ext.external_version_warning extension
+    # This is the PR number being built
+    "current_version": os.environ.get("READTHEDOCS_VERSION", ""),
+}
+
+# Keep this in sync with the stardoc templates
+html_permalinks_icon = "¶"
+
+# These folders are copied to the documentation's HTML output
+html_static_path = ["_static"]
+
+# These paths are either relative to html_static_path
+# or fully qualified paths (eg. https://...)
+html_css_files = [
+    "css/custom.css",
+]
+
+# -- Options for EPUB output
+epub_show_urls = "footnote"
+
+suppress_warnings = [
+    # The autosectionlabel extension turns header titles into referencable
+    # names. Unfortunately, CHANGELOG.md has many duplicate header titles,
+    # which creates lots of warning spam. Just ignore them.
+    "autosectionlabel.*"
+]
+
+
+def setup(app):
+    # Pygments says it supports starlark, but it doesn't seem to actually
+    # recognize `starlark` as a name. So just manually map it to python.
+    from sphinx.highlighting import lexer_classes
+
+    app.add_lexer("starlark", lexer_classes["python"])
diff --git a/docs/sphinx/coverage.md b/docs/coverage.md
similarity index 100%
rename from docs/sphinx/coverage.md
rename to docs/coverage.md
diff --git a/docs/sphinx/environment-variables.md b/docs/environment-variables.md
similarity index 100%
rename from docs/sphinx/environment-variables.md
rename to docs/environment-variables.md
diff --git a/docs/sphinx/gazelle.md b/docs/gazelle.md
similarity index 100%
rename from docs/sphinx/gazelle.md
rename to docs/gazelle.md
diff --git a/docs/sphinx/getting-started.md b/docs/getting-started.md
similarity index 100%
rename from docs/sphinx/getting-started.md
rename to docs/getting-started.md
diff --git a/docs/sphinx/glossary.md b/docs/glossary.md
similarity index 100%
rename from docs/sphinx/glossary.md
rename to docs/glossary.md
diff --git a/docs/sphinx/index.md b/docs/index.md
similarity index 97%
rename from docs/sphinx/index.md
rename to docs/index.md
index 8405eac..c06c31e 100644
--- a/docs/sphinx/index.md
+++ b/docs/index.md
@@ -57,7 +57,7 @@
 self
 getting-started
 pypi-dependencies
-toolchains
+Toolchains <toolchains>
 pip
 coverage
 precompiling
@@ -67,6 +67,7 @@
 Changelog <changelog>
 api/index
 environment-variables
+Sphinxdocs <sphinxdocs/index>
 glossary
 genindex
 ```
diff --git a/docs/sphinx/pip.md b/docs/pip.md
similarity index 100%
rename from docs/sphinx/pip.md
rename to docs/pip.md
diff --git a/docs/sphinx/precompiling.md b/docs/precompiling.md
similarity index 100%
rename from docs/sphinx/precompiling.md
rename to docs/precompiling.md
diff --git a/docs/sphinx/pypi-dependencies.md b/docs/pypi-dependencies.md
similarity index 99%
rename from docs/sphinx/pypi-dependencies.md
rename to docs/pypi-dependencies.md
index db017d2..636fefb 100644
--- a/docs/sphinx/pypi-dependencies.md
+++ b/docs/pypi-dependencies.md
@@ -324,7 +324,7 @@
 When using this feature during the `pip` extension evaluation you will see the accessed indexes similar to below:
 ```console
 Loading: 0 packages loaded
-    currently loading: docs/sphinx
+    currently loading: docs/
     Fetching module extension pip in @@//python/extensions:pip.bzl; starting
     Fetching https://pypi.org/simple/twine/
 ```
diff --git a/docs/sphinx/pyproject.toml b/docs/pyproject.toml
similarity index 67%
rename from docs/sphinx/pyproject.toml
rename to docs/pyproject.toml
index 03279c5..2bcb31b 100644
--- a/docs/sphinx/pyproject.toml
+++ b/docs/pyproject.toml
@@ -5,10 +5,12 @@
 dependencies = [
     # NOTE: This is only used as input to create the resolved requirements.txt
     # file, which is what builds, both Bazel and Readthedocs, both use.
+    "sphinx-autodoc2",
     "sphinx",
     "myst-parser",
-    "sphinx_rtd_theme",
+    "sphinx_rtd_theme >=2.0", # uv insists on downgrading for some reason
     "readthedocs-sphinx-ext",
     "absl-py",
-    "typing-extensions"
+    "typing-extensions",
+    "sphinx-reredirects"
 ]
diff --git a/docs/sphinx/readthedocs_build.sh b/docs/readthedocs_build.sh
similarity index 92%
rename from docs/sphinx/readthedocs_build.sh
rename to docs/readthedocs_build.sh
index c611b7c..3f67310 100755
--- a/docs/sphinx/readthedocs_build.sh
+++ b/docs/readthedocs_build.sh
@@ -17,4 +17,4 @@
   --config=rtd \
   "--//sphinxdocs:extra_defines=version=$READTHEDOCS_VERSION" \
   "${extra_env[@]}" \
-  //docs/sphinx:readthedocs_install
+  //docs:readthedocs_install
diff --git a/docs/sphinx/requirements.txt b/docs/requirements.txt
similarity index 71%
rename from docs/sphinx/requirements.txt
rename to docs/requirements.txt
index e0d3bba..7b5681d 100644
--- a/docs/sphinx/requirements.txt
+++ b/docs/requirements.txt
@@ -1,22 +1,26 @@
 # This file was autogenerated by uv via the following command:
-#    bazel run //docs/sphinx:requirements.update
+#    bazel run //docs:requirements.update
 --index-url https://pypi.org/simple
 
 absl-py==2.1.0 \
     --hash=sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308 \
     --hash=sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff
-    # via rules-python-docs (docs/sphinx/pyproject.toml)
-alabaster==0.7.16 \
-    --hash=sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65 \
-    --hash=sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92
+    # via rules-python-docs (docs/pyproject.toml)
+alabaster==1.0.0 \
+    --hash=sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e \
+    --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b
     # via sphinx
-babel==2.15.0 \
-    --hash=sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb \
-    --hash=sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413
+astroid==3.3.2 \
+    --hash=sha256:99e9b5b602cbb005434084309213d6af32bf7a9b743c836749168b8e2b330cbd \
+    --hash=sha256:9f8136ce9770e0f912401b25a0f15d5c2ec20b50e99b1b413ac0778fe53ff6f1
+    # via sphinx-autodoc2
+babel==2.16.0 \
+    --hash=sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b \
+    --hash=sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316
     # via sphinx
-certifi==2024.7.4 \
-    --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \
-    --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90
+certifi==2024.8.30 \
+    --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
+    --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
     # via requests
 charset-normalizer==3.3.2 \
     --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
@@ -114,16 +118,16 @@
     --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
     --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
     # via sphinx
-docutils==0.20.1 \
-    --hash=sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6 \
-    --hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b
+docutils==0.21.2 \
+    --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \
+    --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2
     # via
     #   myst-parser
     #   sphinx
     #   sphinx-rtd-theme
-idna==3.7 \
-    --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
-    --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0
+idna==3.8 \
+    --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \
+    --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603
     # via requests
 imagesize==1.4.1 \
     --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \
@@ -204,18 +208,18 @@
     --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \
     --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68
     # via jinja2
-mdit-py-plugins==0.4.1 \
-    --hash=sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a \
-    --hash=sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c
+mdit-py-plugins==0.4.2 \
+    --hash=sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636 \
+    --hash=sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5
     # via myst-parser
 mdurl==0.1.2 \
     --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
     --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
     # via markdown-it-py
-myst-parser==3.0.1 \
-    --hash=sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1 \
-    --hash=sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87
-    # via rules-python-docs (docs/sphinx/pyproject.toml)
+myst-parser==4.0.0 \
+    --hash=sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531 \
+    --hash=sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d
+    # via rules-python-docs (docs/pyproject.toml)
 packaging==24.1 \
     --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \
     --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124
@@ -226,63 +230,65 @@
     --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \
     --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a
     # via sphinx
-pyyaml==6.0.1 \
-    --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
-    --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
-    --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
-    --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
-    --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
-    --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
-    --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
-    --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
-    --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
-    --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
-    --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
-    --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
-    --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
-    --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
-    --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
-    --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
-    --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
-    --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
-    --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
-    --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
-    --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
-    --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
-    --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
-    --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
-    --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
-    --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
-    --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
-    --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
-    --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
-    --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
-    --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
-    --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
-    --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
-    --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
-    --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
-    --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
-    --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
-    --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
-    --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
-    --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
-    --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
-    --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
-    --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
-    --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
-    --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
-    --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
-    --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
-    --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
-    --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
-    --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
-    --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
+pyyaml==6.0.2 \
+    --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
+    --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
+    --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
+    --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
+    --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
+    --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
+    --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
+    --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
+    --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
+    --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
+    --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
+    --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
+    --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
+    --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
+    --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
+    --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
+    --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
+    --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
+    --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
+    --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
+    --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
+    --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
+    --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
+    --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
+    --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
+    --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
+    --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
+    --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
+    --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
+    --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
+    --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
+    --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
+    --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
+    --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
+    --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
+    --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
+    --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
+    --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
+    --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
+    --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
+    --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
+    --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
+    --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
+    --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
+    --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
+    --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
+    --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
+    --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
+    --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
+    --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
+    --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
+    --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
+    --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
     # via myst-parser
 readthedocs-sphinx-ext==2.2.5 \
     --hash=sha256:ee5fd5b99db9f0c180b2396cbce528aa36671951b9526bb0272dbfce5517bd27 \
     --hash=sha256:f8c56184ea011c972dd45a90122568587cc85b0127bc9cf064d17c68bc809daa
-    # via rules-python-docs (docs/sphinx/pyproject.toml)
+    # via rules-python-docs (docs/pyproject.toml)
 requests==2.32.3 \
     --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
     --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
@@ -293,18 +299,27 @@
     --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
     --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
     # via sphinx
-sphinx==7.4.7 \
-    --hash=sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe \
-    --hash=sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239
+sphinx==8.0.2 \
+    --hash=sha256:0cce1ddcc4fd3532cf1dd283bc7d886758362c5c1de6598696579ce96d8ffa5b \
+    --hash=sha256:56173572ae6c1b9a38911786e206a110c9749116745873feae4f9ce88e59391d
     # via
-    #   rules-python-docs (docs/sphinx/pyproject.toml)
+    #   rules-python-docs (docs/pyproject.toml)
     #   myst-parser
+    #   sphinx-reredirects
     #   sphinx-rtd-theme
     #   sphinxcontrib-jquery
+sphinx-autodoc2==0.5.0 \
+    --hash=sha256:7d76044aa81d6af74447080182b6868c7eb066874edc835e8ddf810735b6565a \
+    --hash=sha256:e867013b1512f9d6d7e6f6799f8b537d6884462acd118ef361f3f619a60b5c9e
+    # via rules-python-docs (docs/pyproject.toml)
+sphinx-reredirects==0.1.5 \
+    --hash=sha256:444ae1438fba4418242ca76d6a6de3eaee82aaf0d8f2b0cac71a15d32ce6eba2 \
+    --hash=sha256:cfa753b441020a22708ce8eb17d4fd553a28fc87a609330092917ada2a6da0d8
+    # via rules-python-docs (docs/pyproject.toml)
 sphinx-rtd-theme==2.0.0 \
     --hash=sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b \
     --hash=sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586
-    # via rules-python-docs (docs/sphinx/pyproject.toml)
+    # via rules-python-docs (docs/pyproject.toml)
 sphinxcontrib-applehelp==2.0.0 \
     --hash=sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1 \
     --hash=sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5
@@ -336,7 +351,9 @@
 typing-extensions==4.12.2 \
     --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
     --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
-    # via rules-python-docs (docs/sphinx/pyproject.toml)
+    # via
+    #   rules-python-docs (docs/pyproject.toml)
+    #   sphinx-autodoc2
 urllib3==2.2.2 \
     --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
     --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
diff --git a/docs/sphinx/BUILD.bazel b/docs/sphinx/BUILD.bazel
deleted file mode 100644
index 947ebba..0000000
--- a/docs/sphinx/BUILD.bazel
+++ /dev/null
@@ -1,194 +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.
-
-load("@bazel_skylib//rules:write_file.bzl", "write_file")
-load("@dev_pip//:requirements.bzl", "requirement")
-load("//python:py_binary.bzl", "py_binary")
-load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")  # buildifier: disable=bzl-visibility
-load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
-load("//sphinxdocs:readthedocs.bzl", "readthedocs_install")
-load("//sphinxdocs:sphinx.bzl", "sphinx_build_binary", "sphinx_docs")
-load("//sphinxdocs:sphinx_stardoc.bzl", "sphinx_stardocs")
-
-# We only build for Linux and Mac because:
-# 1. The actual doc process only runs on Linux
-# 2. Mac is a common development platform, and is close enough to Linux
-#    it's feasible to make work.
-# Making CI happy under Windows is too much of a headache, though, so we don't
-# bother with that.
-_TARGET_COMPATIBLE_WITH = select({
-    "@platforms//os:linux": [],
-    "@platforms//os:macos": [],
-    "//conditions:default": ["@platforms//:incompatible"],
-}) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"]
-
-# See README.md for instructions. Short version:
-# * `bazel run //docs/sphinx:docs.serve` in a separate terminal
-# * `ibazel build //docs/sphinx:docs` to automatically rebuild docs
-sphinx_docs(
-    name = "docs",
-    srcs = [
-        ":bzl_api_docs",
-    ] + glob(
-        include = [
-            "*.md",
-            "**/*.md",
-            "_static/**",
-            "_includes/**",
-        ],
-        exclude = [
-            "README.md",
-            "_*",
-            "*.inv*",
-        ],
-    ),
-    config = "conf.py",
-    formats = [
-        "html",
-    ],
-    renamed_srcs = {
-        "//:CHANGELOG.md": "changelog.md",
-        "//:CONTRIBUTING.md": "contributing.md",
-        "//sphinxdocs/inventories:bazel_inventory": "bazel_inventory.inv",
-    },
-    sphinx = ":sphinx-build",
-    strip_prefix = package_name() + "/",
-    tags = ["docs"],
-    target_compatible_with = _TARGET_COMPATIBLE_WITH,
-)
-
-sphinx_stardocs(
-    name = "bzl_api_docs",
-    docs = {
-        "api/python/cc/py_cc_toolchain.md": dict(
-            dep = "//python/private:py_cc_toolchain_bzl",
-            input = "//python/private:py_cc_toolchain_rule.bzl",
-            public_load_path = "//python/cc:py_cc_toolchain.bzl",
-        ),
-        "api/python/cc/py_cc_toolchain_info.md": "//python/cc:py_cc_toolchain_info_bzl",
-        "api/python/defs.md": "//python:defs_bzl",
-        "api/python/entry_points/py_console_script_binary.md": "//python/entry_points:py_console_script_binary_bzl",
-        "api/python/packaging.md": "//python:packaging_bzl",
-        "api/python/pip.md": "//python:pip_bzl",
-        "api/python/private/common/py_binary_rule_bazel.md": "//python/private/common:py_binary_rule_bazel_bzl",
-        "api/python/private/common/py_library_rule_bazel.md": "//python/private/common:py_library_rule_bazel_bzl",
-        "api/python/private/common/py_runtime_rule.md": "//python/private/common:py_runtime_rule_bzl",
-        "api/python/private/common/py_test_rule_bazel.md": "//python/private/common:py_test_rule_bazel_bzl",
-        "api/python/py_binary.md": "//python:py_binary_bzl",
-        "api/python/py_cc_link_params_info.md": "//python:py_cc_link_params_info_bzl",
-        "api/python/py_library.md": "//python:py_library_bzl",
-        "api/python/py_runtime.md": "//python:py_runtime_bzl",
-        "api/python/py_runtime_info.md": "//python:py_runtime_info_bzl",
-        "api/python/py_runtime_pair.md": dict(
-            dep = "//python/private:py_runtime_pair_rule_bzl",
-            input = "//python/private:py_runtime_pair_rule.bzl",
-            public_load_path = "//python:py_runtime_pair.bzl",
-        ),
-        "api/python/py_test.md": "//python:py_test_bzl",
-    } | ({
-        # Bazel 6 + Stardoc isn't able to parse something about the python bzlmod extension
-        "api/python/extensions/python.md": "//python/extensions:python_bzl",
-    } if IS_BAZEL_7_OR_HIGHER else {}) | ({
-        # This depends on @pythons_hub, which is only created under bzlmod,
-        "api/python/extensions/pip.md": "//python/extensions:pip_bzl",
-    } if IS_BAZEL_7_OR_HIGHER and BZLMOD_ENABLED else {}),
-    tags = ["docs"],
-    target_compatible_with = _TARGET_COMPATIBLE_WITH,
-)
-
-readthedocs_install(
-    name = "readthedocs_install",
-    docs = [":docs"],
-    target_compatible_with = _TARGET_COMPATIBLE_WITH,
-)
-
-sphinx_build_binary(
-    name = "sphinx-build",
-    target_compatible_with = _TARGET_COMPATIBLE_WITH,
-    deps = [
-        requirement("sphinx"),
-        requirement("sphinx_rtd_theme"),
-        requirement("myst_parser"),
-        requirement("readthedocs_sphinx_ext"),
-        requirement("typing_extensions"),
-        "//sphinxdocs/src/sphinx_bzl",
-    ],
-)
-
-_REQUIREMENTS_TARGET_COMPATIBLE_WITH = select({
-    "@platforms//os:linux": [],
-    "@platforms//os:macos": [],
-    "@platforms//os:windows": [],
-    "//conditions:default": ["@platforms//:incompatible"],
-}) if BZLMOD_ENABLED else ["@platforms//:incompatible"]
-
-# Run bazel run //docs/sphinx:requirements.update
-genrule(
-    name = "requirements",
-    srcs = ["pyproject.toml"],
-    outs = ["_requirements.txt"],
-    cmd = "$(UV_BIN) pip compile " + " ".join([
-        "--custom-compile-command='bazel run //docs/sphinx:requirements.update'",
-        "--generate-hashes",
-        "--universal",
-        "--emit-index-url",
-        "--no-strip-extras",
-        "--no-build",
-        "--python=$(PYTHON3)",
-        "$<",
-        "--output-file=$@",
-        # Always try upgrading
-        "--upgrade",
-    ]),
-    tags = [
-        "local",
-        "manual",
-        "no-cache",
-    ],
-    target_compatible_with = _REQUIREMENTS_TARGET_COMPATIBLE_WITH,
-    toolchains = [
-        "//python/uv:current_toolchain",
-        "//python:current_py_toolchain",
-    ],
-)
-
-# Write a script that can be used for updating the in-tree version of the
-# requirements file
-write_file(
-    name = "gen_update_requirements",
-    out = "requirements.update.py",
-    content = [
-        "from os import environ",
-        "from pathlib import Path",
-        "from sys import stderr",
-        "",
-        'src = Path(environ["REQUIREMENTS_FILE"])',
-        'dst = Path(environ["BUILD_WORKSPACE_DIRECTORY"]) / "docs" / "sphinx" / "requirements.txt"',
-        'print(f"Writing requirements contents from {src} to {dst}", file=stderr)',
-        "dst.write_text(src.read_text())",
-        'print("Success!", file=stderr)',
-    ],
-    target_compatible_with = _REQUIREMENTS_TARGET_COMPATIBLE_WITH,
-)
-
-py_binary(
-    name = "requirements.update",
-    srcs = ["requirements.update.py"],
-    data = [":requirements"],
-    env = {
-        "REQUIREMENTS_FILE": "$(location :requirements)",
-    },
-    tags = ["manual"],
-    target_compatible_with = _REQUIREMENTS_TARGET_COMPATIBLE_WITH,
-)
diff --git a/docs/sphinx/_stardoc_footer.md b/docs/sphinx/_stardoc_footer.md
deleted file mode 100644
index 7aa33f7..0000000
--- a/docs/sphinx/_stardoc_footer.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-[`Action`]: https://bazel.build/rules/lib/Action
-[`bool`]: https://bazel.build/rules/lib/bool
-[`depset`]: https://bazel.build/rules/lib/depset
-[`dict`]: https://bazel.build/rules/lib/dict
-[`File`]: https://bazel.build/rules/lib/File
-[`Label`]: https://bazel.build/rules/lib/Label
-[`list`]: https://bazel.build/rules/lib/list
-[`str`]: https://bazel.build/rules/lib/string
-[str]: https://bazel.build/rules/lib/string
-[`int`]: https://bazel.build/rules/lib/int
-[`struct`]: https://bazel.build/rules/lib/builtins/struct
-[`Target`]: https://bazel.build/rules/lib/Target
-[target-name]: https://bazel.build/concepts/labels#target-names
-[attr-label]: https://bazel.build/concepts/labels
diff --git a/docs/sphinx/api/python/index.md b/docs/sphinx/api/python/index.md
deleted file mode 100644
index 6c79447..0000000
--- a/docs/sphinx/api/python/index.md
+++ /dev/null
@@ -1,36 +0,0 @@
-:::{default-domain} bzl
-:::
-:::{bzl:currentfile} //python:BUILD.bazel
-:::
-
-# //python
-
-:::{bzl:target} toolchain_type
-
-Identifier for the toolchain type for the target platform.
-:::
-
-:::{bzl:target} exec_tools_toolchain_type
-
-Identifier for the toolchain type for exec tools used to build Python targets.
-:::
-
-:::{bzl:target} current_py_toolchain
-
-Helper target to resolve to the consumer's current Python toolchain. This target
-provides:
-
-* `PyRuntimeInfo`: The consuming target's target toolchain information
-
-:::
-
-::::{target} autodetecting_toolchain
-
-Legacy toolchain; despite its name, it doesn't autodetect anything.
-
-:::{deprecated} 0.34.0
-
-Use {obj}`@rules_python//python/runtime_env_toolchain:all` instead.
-:::
-::::
-
diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py
deleted file mode 100644
index b315577..0000000
--- a/docs/sphinx/conf.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Configuration file for the Sphinx documentation builder.
-
-import os
-
-# -- Project information
-project = "rules_python"
-copyright = "2023, The Bazel Authors"
-author = "Bazel"
-
-# NOTE: These are overriden by -D flags via --//sphinxdocs:extra_defines
-version = "0.0.0"
-release = version
-
-# -- General configuration
-# See https://www.sphinx-doc.org/en/master/usage/configuration.html
-# for more settings
-
-# Any extensions here not built into Sphinx must also be added to
-# the dependencies of //docs/sphinx:sphinx-builder
-extensions = [
-    "sphinx.ext.autodoc",
-    "sphinx.ext.autosectionlabel",
-    "sphinx.ext.autosummary",
-    "sphinx.ext.doctest",
-    "sphinx.ext.duration",
-    "sphinx.ext.extlinks",
-    "sphinx.ext.intersphinx",
-    "myst_parser",
-    "sphinx_rtd_theme",  # Necessary to get jquery to make flyout work
-    "sphinx_bzl.bzl",
-]
-
-# Adapted from the template code:
-# https://github.com/readthedocs/readthedocs.org/blob/main/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl
-if os.environ.get("READTHEDOCS") == "True":
-    # Must come first because it can interfere with other extensions, according
-    # to the original conf.py template comments
-    extensions.insert(0, "readthedocs_ext.readthedocs")
-
-    if os.environ.get("READTHEDOCS_VERSION_TYPE") == "external":
-        # Insert after the main extension
-        extensions.insert(1, "readthedocs_ext.external_version_warning")
-        readthedocs_vcs_url = (
-            "http://github.com/bazelbuild/rules_python/pull/{}".format(
-                os.environ.get("READTHEDOCS_VERSION", "")
-            )
-        )
-        # The build id isn't directly available, but it appears to be encoded
-        # into the host name, so we can parse it from that. The format appears
-        # to be `build-X-project-Y-Z`, where:
-        # * X is an integer build id
-        # * Y is an integer project id
-        # * Z is the project name
-        _build_id = os.environ.get("HOSTNAME", "build-0-project-0-rules-python")
-        _build_id = _build_id.split("-")[1]
-        readthedocs_build_url = (
-            f"https://readthedocs.org/projects/rules-python/builds/{_build_id}"
-        )
-
-exclude_patterns = ["_includes/*"]
-templates_path = ["_templates"]
-primary_domain = None  # The default is 'py', which we don't make much use of
-nitpicky = True
-
-# --- Intersphinx configuration
-
-intersphinx_mapping = {
-    "bazel": ("https://bazel.build/", "bazel_inventory.inv"),
-}
-
-# --- Extlinks configuration
-extlinks = {
-    "gh-path": (f"https://github.com/bazelbuild/rules_python/tree/main/%s", "%s"),
-}
-
-# --- MyST configuration
-# See https://myst-parser.readthedocs.io/en/latest/configuration.html
-# for more settings
-
-# See https://myst-parser.readthedocs.io/en/latest/syntax/optional.html
-# for additional extensions.
-myst_enable_extensions = [
-    "fieldlist",
-    "attrs_block",
-    "attrs_inline",
-    "colon_fence",
-    "deflist",
-    "substitution",
-]
-
-myst_substitutions = {}
-
-# --- sphinx_stardoc configuration
-
-bzl_default_repository_name = "@rules_python"
-
-# -- Options for HTML output
-# See https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
-# For additional html settings
-
-# See https://sphinx-rtd-theme.readthedocs.io/en/stable/configuring.html for
-# them-specific options
-html_theme = "sphinx_rtd_theme"
-html_theme_options = {}
-
-# The html_context settings are part of the jinja context used by the themes.
-html_context = {
-    # This controls whether the flyout menu is shown. It is always false
-    # because:
-    # * For local builds, the flyout menu is empty and doesn't show in the
-    #   same place as for RTD builds. No point in showing it locally.
-    # * For RTD builds, the flyout menu is always automatically injected,
-    #   so having it be True makes the flyout show up twice.
-    "READTHEDOCS": False,
-    "PRODUCTION_DOMAIN": "readthedocs.org",
-    # This is the path to a page's source (after the github user/repo/commit)
-    "conf_py_path": "/docs/sphinx/",
-    "github_user": "bazelbuild",
-    "github_repo": "rules_python",
-    # The git version that was checked out, e.g. the tag or branch name
-    "github_version": os.environ.get("READTHEDOCS_GIT_IDENTIFIER", ""),
-    # For local builds, the github link won't work. Disabling it replaces
-    # it with a "view source" link to view the source Sphinx saw, which
-    # is useful for local development.
-    "display_github": os.environ.get("READTHEDOCS") == "True",
-    "commit": os.environ.get("READTHEDOCS_GIT_COMMIT_HASH", "unknown commit"),
-    # Used by readthedocs_ext.external_version_warning extension
-    # This is the PR number being built
-    "current_version": os.environ.get("READTHEDOCS_VERSION", ""),
-}
-
-# Keep this in sync with the stardoc templates
-html_permalinks_icon = "¶"
-
-# These folders are copied to the documentation's HTML output
-html_static_path = ["_static"]
-
-# These paths are either relative to html_static_path
-# or fully qualified paths (eg. https://...)
-html_css_files = [
-    "css/custom.css",
-]
-
-# -- Options for EPUB output
-epub_show_urls = "footnote"
-
-suppress_warnings = []
-
-
-def setup(app):
-    # Pygments says it supports starlark, but it doesn't seem to actually
-    # recognize `starlark` as a name. So just manually map it to python.
-    from sphinx.highlighting import lexer_classes
-
-    app.add_lexer("starlark", lexer_classes["python"])
diff --git a/docs/sphinx/toolchains.md b/docs/sphinx/toolchains.md
deleted file mode 100644
index fac1bfc..0000000
--- a/docs/sphinx/toolchains.md
+++ /dev/null
@@ -1,244 +0,0 @@
-:::{default-domain} bzl
-:::
-
-# Configuring Python toolchains and runtimes
-
-This documents how to configure the Python toolchain and runtimes for different
-use cases.
-
-## Bzlmod MODULE configuration
-
-How to configure `rules_python` in your MODULE.bazel file depends on how and why
-you're using Python. There are 4 basic use cases:
-
-1. A root module that always uses Python. For example, you're building a
-   Python application.
-2. A library module with dev-only uses of Python. For example, a Java project
-   that only uses Python as part of testing itself.
-3. A library module without version constraints. For example, a rule set with
-   Python build tools, but defers to the user as to what Python version is used
-   for the tools.
-4. A library module with version constraints. For example, a rule set with
-   Python build tools, and the module requires a specific version of Python
-   be used with its tools.
-
-### Root modules
-
-Root modules are always the top-most module. These are special in two ways:
-
-1. Some `rules_python` bzlmod APIs are only respected by the root module.
-2. The root module can force module overrides and specific module dependency
-   ordering.
-
-When configuring `rules_python` for a root module, you typically want to
-explicitly specify the Python version you want to use. This ensures that
-dependencies don't change the Python version out from under you. Remember that
-`rules_python` will set a version by default, but it will change regularly as
-it tracks a recent Python version.
-
-NOTE: If your root module only uses Python for development of the module itself,
-you should read the dev-only library module section.
-
-```
-bazel_dep(name="rules_python", version=...)
-python = use_extension("@rules_python//python/extensions:python.bzl", "python")
-
-python.toolchain(python_version = "3.12", is_default = True)
-```
-
-### Library modules
-
-A library module is a module that can show up in arbitrary locations in the
-bzlmod module graph -- it's unknown where in the breadth-first search order the
-module will be relative to other modules. For example, `rules_python` is a
-library module.
-
-#### Library modules with dev-only Python usage
-
-A library module with dev-only Python usage is usually one where Python is only
-used as part of its tests. For example, a module for Java rules might run some
-Python program to generate test data, but real usage of the rules don't need
-Python to work. To configure this, follow the root-module setup, but remember to
-specify `dev_dependency = True` to the bzlmod APIs:
-
-```
-# MODULE.bazel
-bazel_dep(name = "rules_python", version=..., dev_dependency = True)
-
-python = use_extension(
-    "@rules_python//python/extensions:python.bzl",
-    "python",
-    dev_dependency = True
-)
-
-python.toolchain(python_version = "3.12", is_default=True)
-```
-
-#### Library modules without version constraints
-
-A library module without version constraints is one where the version of Python
-used for the Python programs it runs isn't chosen by the module itself. Instead,
-it's up to the root module to pick an appropriate version of Python.
-
-For this case, configuration is simple: just depend on `rules_python` and use
-the normal `//python:py_binary.bzl` et al rules. There is no need to call
-`python.toolchain` -- rules_python ensures _some_ Python version is available,
-but more often the root module will specify some version.
-
-```
-# MODULE.bazel
-bazel_dep(name = "rules_python", version=...)
-```
-
-#### Library modules with version constraints
-
-A library module with version constraints is one where the module requires a
-specific Python version be used with its tools. This has some pros/cons:
-
-* It allows the library's tools to use a different version of Python than
-  the rest of the build. For example, a user's program could use Python 3.12,
-  while the library module's tools use Python 3.10.
-* It reduces the support burden for the library module because the library only needs
-  to test for the particular Python version they intend to run as.
-* It raises the support burden for the library module because the version of
-  Python being used needs to be regularly incremented.
-* It has higher build overhead because additional runtimes and libraries need
-  to be downloaded, and Bazel has to keep additional configuration state.
-
-To configure this, request the Python versions needed in MODULE.bazel and use
-the version-aware rules for `py_binary`.
-
-```
-# MODULE.bazel
-bazel_dep(name = "rules_python", version=...)
-
-python = use_extension("@rules_python//python/extensions:python.bzl", "python")
-python.toolchain(python_version = "3.12")
-
-# BUILD.bazel
-load("@python_versions//3.12:defs.bzl", "py_binary")
-
-py_binary(...)
-```
-
-### Pinning to a Python version
-
-Pinning to a version allows targets to force that a specific Python version is
-used, even if the root module configures a different version as a default. This
-is most useful for two cases:
-
-1. For submodules to ensure they run with the appropriate Python version
-2. To allow incremental, per-target, upgrading to newer Python versions,
-   typically in a mono-repo situation.
-
-To configure a submodule with the version-aware rules, request the particular
-version you need, then use the `@python_versions` repo to use the rules that
-force specific versions:
-
-```starlark
-python = use_extension("@rules_python//python/extensions:python.bzl", "python")
-
-python.toolchain(
-    python_version = "3.11",
-)
-use_repo(python, "python_versions")
-```
-
-Then use e.g. `load("@python_versions//3.11:defs.bzl", "py_binary")` to use
-the rules that force that particular version. Multiple versions can be specified
-and use within a single build.
-
-For more documentation, see the bzlmod examples under the {gh-path}`examples`
-folder.  Look for the examples that contain a `MODULE.bazel` file.
-
-### Other toolchain details
-
-The `python.toolchain()` call makes its contents available under a repo named
-`python_X_Y`, where X and Y are the major and minor versions. For example,
-`python.toolchain(python_version="3.11")` creates the repo `@python_3_11`.
-Remember to call `use_repo()` to make repos visible to your module:
-`use_repo(python, "python_3_11")`
-
-#### Toolchain usage in other rules
-
-Python toolchains can be utilized in other bazel rules, such as `genrule()`, by adding the `toolchains=["@rules_python//python:current_py_toolchain"]` attribute. You can obtain the path to the Python interpreter using the `$(PYTHON2)` and `$(PYTHON3)` ["Make" Variables](https://bazel.build/reference/be/make-variables). See the
-{gh-path}`test_current_py_toolchain <tests/load_from_macro/BUILD.bazel>` target for an example.
-
-
-## Workspace configuration
-
-To import rules_python in your project, you first need to add it to your
-`WORKSPACE` file, using the snippet provided in the
-[release you choose](https://github.com/bazelbuild/rules_python/releases)
-
-To depend on a particular unreleased version, you can do the following:
-
-```starlark
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-
-# Update the SHA and VERSION to the lastest version available here:
-# https://github.com/bazelbuild/rules_python/releases.
-
-SHA="84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841"
-
-VERSION="0.23.1"
-
-http_archive(
-    name = "rules_python",
-    sha256 = SHA,
-    strip_prefix = "rules_python-{}".format(VERSION),
-    url = "https://github.com/bazelbuild/rules_python/releases/download/{}/rules_python-{}.tar.gz".format(VERSION,VERSION),
-)
-
-load("@rules_python//python:repositories.bzl", "py_repositories")
-
-py_repositories()
-```
-
-### Workspace toolchain registration
-
-To register a hermetic Python toolchain rather than rely on a system-installed interpreter for runtime execution, you can add to the `WORKSPACE` file:
-
-```starlark
-load("@rules_python//python:repositories.bzl", "python_register_toolchains")
-
-python_register_toolchains(
-    name = "python_3_11",
-    # Available versions are listed in @rules_python//python:versions.bzl.
-    # We recommend using the same version your team is already standardized on.
-    python_version = "3.11",
-)
-
-load("@python_3_11//:defs.bzl", "interpreter")
-
-load("@rules_python//python:pip.bzl", "pip_parse")
-
-pip_parse(
-    ...
-    python_interpreter_target = interpreter,
-    ...
-)
-```
-
-After registration, your Python targets will use the toolchain's interpreter during execution, but a system-installed interpreter
-is still used to 'bootstrap' Python targets (see https://github.com/bazelbuild/rules_python/issues/691).
-You may also find some quirks while using this toolchain. Please refer to [python-build-standalone documentation's _Quirks_ section](https://gregoryszorc.com/docs/python-build-standalone/main/quirks.html).
-
-## Autodetecting toolchain
-
-The autodetecting toolchain is a deprecated toolchain that is built into Bazel.
-It's name is a bit misleading: it doesn't autodetect anything. All it does is
-use `python3` from the environment a binary runs within. This provides extremely
-limited functionality to the rules (at build time, nothing is knowable about
-the Python runtime).
-
-Bazel itself automatically registers `@bazel_tools//tools/python:autodetecting_toolchain`
-as the lowest priority toolchain. For WORKSPACE builds, if no other toolchain
-is registered, that toolchain will be used. For bzlmod builds, rules_python
-automatically registers a higher-priority toolchain; it won't be used unless
-there is a toolchain misconfiguration somewhere.
-
-To aid migration off the Bazel-builtin toolchain, rules_python provides
-{obj}`@rules_python//python/runtime_env_toolchains:all`. This is an equivalent
-toolchain, but is implemented using rules_python's objects.
diff --git a/docs/sphinx/support.md b/docs/support.md
similarity index 100%
rename from docs/sphinx/support.md
rename to docs/support.md
diff --git a/docs/toolchains.md b/docs/toolchains.md
new file mode 100644
index 0000000..6df6f22
--- /dev/null
+++ b/docs/toolchains.md
@@ -0,0 +1,453 @@
+:::{default-domain} bzl
+:::
+
+# Configuring Python toolchains and runtimes
+
+This documents how to configure the Python toolchain and runtimes for different
+use cases.
+
+## Bzlmod MODULE configuration
+
+How to configure `rules_python` in your MODULE.bazel file depends on how and why
+you're using Python. There are 4 basic use cases:
+
+1. A root module that always uses Python. For example, you're building a
+   Python application.
+2. A library module with dev-only uses of Python. For example, a Java project
+   that only uses Python as part of testing itself.
+3. A library module without version constraints. For example, a rule set with
+   Python build tools, but defers to the user as to what Python version is used
+   for the tools.
+4. A library module with version constraints. For example, a rule set with
+   Python build tools, and the module requires a specific version of Python
+   be used with its tools.
+
+### Root modules
+
+Root modules are always the top-most module. These are special in two ways:
+
+1. Some `rules_python` bzlmod APIs are only respected by the root module.
+2. The root module can force module overrides and specific module dependency
+   ordering.
+
+When configuring `rules_python` for a root module, you typically want to
+explicitly specify the Python version you want to use. This ensures that
+dependencies don't change the Python version out from under you. Remember that
+`rules_python` will set a version by default, but it will change regularly as
+it tracks a recent Python version.
+
+NOTE: If your root module only uses Python for development of the module itself,
+you should read the dev-only library module section.
+
+```
+bazel_dep(name="rules_python", version=...)
+python = use_extension("@rules_python//python/extensions:python.bzl", "python")
+
+python.toolchain(python_version = "3.12", is_default = True)
+```
+
+### Library modules
+
+A library module is a module that can show up in arbitrary locations in the
+bzlmod module graph -- it's unknown where in the breadth-first search order the
+module will be relative to other modules. For example, `rules_python` is a
+library module.
+
+#### Library modules with dev-only Python usage
+
+A library module with dev-only Python usage is usually one where Python is only
+used as part of its tests. For example, a module for Java rules might run some
+Python program to generate test data, but real usage of the rules don't need
+Python to work. To configure this, follow the root-module setup, but remember to
+specify `dev_dependency = True` to the bzlmod APIs:
+
+```
+# MODULE.bazel
+bazel_dep(name = "rules_python", version=..., dev_dependency = True)
+
+python = use_extension(
+    "@rules_python//python/extensions:python.bzl",
+    "python",
+    dev_dependency = True
+)
+
+python.toolchain(python_version = "3.12", is_default=True)
+```
+
+#### Library modules without version constraints
+
+A library module without version constraints is one where the version of Python
+used for the Python programs it runs isn't chosen by the module itself. Instead,
+it's up to the root module to pick an appropriate version of Python.
+
+For this case, configuration is simple: just depend on `rules_python` and use
+the normal `//python:py_binary.bzl` et al rules. There is no need to call
+`python.toolchain` -- rules_python ensures _some_ Python version is available,
+but more often the root module will specify some version.
+
+```
+# MODULE.bazel
+bazel_dep(name = "rules_python", version=...)
+```
+
+#### Library modules with version constraints
+
+A library module with version constraints is one where the module requires a
+specific Python version be used with its tools. This has some pros/cons:
+
+* It allows the library's tools to use a different version of Python than
+  the rest of the build. For example, a user's program could use Python 3.12,
+  while the library module's tools use Python 3.10.
+* It reduces the support burden for the library module because the library only needs
+  to test for the particular Python version they intend to run as.
+* It raises the support burden for the library module because the version of
+  Python being used needs to be regularly incremented.
+* It has higher build overhead because additional runtimes and libraries need
+  to be downloaded, and Bazel has to keep additional configuration state.
+
+To configure this, request the Python versions needed in MODULE.bazel and use
+the version-aware rules for `py_binary`.
+
+```
+# MODULE.bazel
+bazel_dep(name = "rules_python", version=...)
+
+python = use_extension("@rules_python//python/extensions:python.bzl", "python")
+python.toolchain(python_version = "3.12")
+
+# BUILD.bazel
+load("@python_versions//3.12:defs.bzl", "py_binary")
+
+py_binary(...)
+```
+
+### Pinning to a Python version
+
+Pinning to a version allows targets to force that a specific Python version is
+used, even if the root module configures a different version as a default. This
+is most useful for two cases:
+
+1. For submodules to ensure they run with the appropriate Python version
+2. To allow incremental, per-target, upgrading to newer Python versions,
+   typically in a mono-repo situation.
+
+To configure a submodule with the version-aware rules, request the particular
+version you need, then use the `@python_versions` repo to use the rules that
+force specific versions:
+
+```starlark
+python = use_extension("@rules_python//python/extensions:python.bzl", "python")
+
+python.toolchain(
+    python_version = "3.11",
+)
+use_repo(python, "python_versions")
+```
+
+Then use e.g. `load("@python_versions//3.11:defs.bzl", "py_binary")` to use
+the rules that force that particular version. Multiple versions can be specified
+and use within a single build.
+
+For more documentation, see the bzlmod examples under the {gh-path}`examples`
+folder.  Look for the examples that contain a `MODULE.bazel` file.
+
+### Other toolchain details
+
+The `python.toolchain()` call makes its contents available under a repo named
+`python_X_Y`, where X and Y are the major and minor versions. For example,
+`python.toolchain(python_version="3.11")` creates the repo `@python_3_11`.
+Remember to call `use_repo()` to make repos visible to your module:
+`use_repo(python, "python_3_11")`
+
+#### Toolchain usage in other rules
+
+Python toolchains can be utilized in other bazel rules, such as `genrule()`, by
+adding the `toolchains=["@rules_python//python:current_py_toolchain"]`
+attribute. You can obtain the path to the Python interpreter using the
+`$(PYTHON2)` and `$(PYTHON3)` ["Make"
+Variables](https://bazel.build/reference/be/make-variables). See the
+{gh-path}`test_current_py_toolchain <tests/load_from_macro/BUILD.bazel>` target
+for an example.
+
+### Overriding toolchain defaults and adding more versions
+
+One can perform various overrides for the registered toolchains from the root
+module. For example, the following use cases would be supported using the
+existing attributes:
+
+* Limiting the available toolchains for the entire `bzlmod` transitive graph
+  via {attr}`python.override.available_python_versions`.
+* Setting particular `X.Y.Z` Python versions when modules request `X.Y` version
+  via {attr}`python.override.minor_mapping`.
+* Per-version control of the coverage tool used using
+  {attr}`python.single_version_platform_override.coverage_tool`.
+* Adding additional Python versions via {bzl:obj}`python.single_version_override` or
+  {bzl:obj}`python.single_version_platform_override`.
+
+## Workspace configuration
+
+To import rules_python in your project, you first need to add it to your
+`WORKSPACE` file, using the snippet provided in the
+[release you choose](https://github.com/bazelbuild/rules_python/releases)
+
+To depend on a particular unreleased version, you can do the following:
+
+```starlark
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+
+# Update the SHA and VERSION to the lastest version available here:
+# https://github.com/bazelbuild/rules_python/releases.
+
+SHA="84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841"
+
+VERSION="0.23.1"
+
+http_archive(
+    name = "rules_python",
+    sha256 = SHA,
+    strip_prefix = "rules_python-{}".format(VERSION),
+    url = "https://github.com/bazelbuild/rules_python/releases/download/{}/rules_python-{}.tar.gz".format(VERSION,VERSION),
+)
+
+load("@rules_python//python:repositories.bzl", "py_repositories")
+
+py_repositories()
+```
+
+### Workspace toolchain registration
+
+To register a hermetic Python toolchain rather than rely on a system-installed interpreter for runtime execution, you can add to the `WORKSPACE` file:
+
+```starlark
+load("@rules_python//python:repositories.bzl", "python_register_toolchains")
+
+python_register_toolchains(
+    name = "python_3_11",
+    # Available versions are listed in @rules_python//python:versions.bzl.
+    # We recommend using the same version your team is already standardized on.
+    python_version = "3.11",
+)
+
+load("@python_3_11//:defs.bzl", "interpreter")
+
+load("@rules_python//python:pip.bzl", "pip_parse")
+
+pip_parse(
+    ...
+    python_interpreter_target = interpreter,
+    ...
+)
+```
+
+After registration, your Python targets will use the toolchain's interpreter during execution, but a system-installed interpreter
+is still used to 'bootstrap' Python targets (see https://github.com/bazelbuild/rules_python/issues/691).
+You may also find some quirks while using this toolchain. Please refer to [python-build-standalone documentation's _Quirks_ section](https://gregoryszorc.com/docs/python-build-standalone/main/quirks.html).
+
+## Autodetecting toolchain
+
+The autodetecting toolchain is a deprecated toolchain that is built into Bazel.
+It's name is a bit misleading: it doesn't autodetect anything. All it does is
+use `python3` from the environment a binary runs within. This provides extremely
+limited functionality to the rules (at build time, nothing is knowable about
+the Python runtime).
+
+Bazel itself automatically registers `@bazel_tools//tools/python:autodetecting_toolchain`
+as the lowest priority toolchain. For WORKSPACE builds, if no other toolchain
+is registered, that toolchain will be used. For bzlmod builds, rules_python
+automatically registers a higher-priority toolchain; it won't be used unless
+there is a toolchain misconfiguration somewhere.
+
+To aid migration off the Bazel-builtin toolchain, rules_python provides
+{bzl:obj}`@rules_python//python/runtime_env_toolchains:all`. This is an equivalent
+toolchain, but is implemented using rules_python's objects.
+
+
+## Custom toolchains
+
+While rules_python provides toolchains by default, it is not required to use
+them, and you can define your own toolchains to use instead. This section
+gives an introduction for how to define them yourself.
+
+:::{note}
+* Defining your own toolchains is an advanced feature.
+* APIs used for defining them are less stable and may change more often.
+:::
+
+Under the hood, there are multiple toolchains that comprise the different
+information necessary to build Python targets. Each one has an
+associated _toolchain type_ that identifies it. We call the collection of these
+toolchains a "toolchain suite".
+
+One of the underlying design goals of the toolchains is to support complex and
+bespoke environments. Such environments may use an arbitrary combination of
+{obj}`RBE`, cross-platform building, multiple Python versions,
+building Python from source, embeding Python (as opposed to building separate
+interpreters), using prebuilt binaries, or using binaries built from source. To
+that end, many of the attributes they accept, and fields they provide, are
+optional.
+
+### Target toolchain type
+
+The target toolchain type is {obj}`//python:toolchain_type`, and it
+is for _target configuration_ runtime information, e.g., the Python version
+and interpreter binary that a program will use.
+
+The is typically implemented using {obj}`py_runtime()`, which
+provides the {obj}`PyRuntimeInfo` provider. For historical reasons from the
+Python 2 transition, `py_runtime` is wrapped in {obj}`py_runtime_pair`,
+which provides {obj}`ToolchainInfo` with the field `py3_runtime`, which is an
+instance of `PyRuntimeInfo`.
+
+This toolchain type is intended to hold only _target configuration_ values. As
+such, when defining its associated {external:bzl:obj}`toolchain` target, only
+set {external:bzl:obj}`toolchain.target_compatible_with` and/or
+{external:bzl:obj}`toolchain.target_settings` constraints; there is no need to
+set {external:bzl:obj}`toolchain.exec_compatible_with`.
+
+### Python C toolchain type
+
+The Python C toolchain type ("py cc") is {obj}`//python/cc:toolchain_type`, and
+it has C/C++ information for the _target configuration_, e.g. the C headers that
+provide `Python.h`.
+
+This is typically implemented using {obj}`py_cc_toolchain()`, which provides
+{obj}`ToolchainInfo` with the field `py_cc_toolchain` set, which is a
+{obj}`PyCcToolchainInfo` provider instance. 
+
+This toolchain type is intended to hold only _target configuration_ values
+relating to the C/C++ information for the Python runtime. As such, when defining
+its associated {external:obj}`toolchain` target, only set
+{external:bzl:obj}`toolchain.target_compatible_with` and/or
+{external:bzl:obj}`toolchain.target_settings` constraints; there is no need to
+set {external:bzl:obj}`toolchain.exec_compatible_with`.
+
+### Exec tools toolchain type
+
+The exec tools toolchain type is {obj}`//python:exec_tools_toolchain_type`,
+and it is for supporting tools for _building_ programs, e.g. the binary to
+precompile code at build time.
+
+This toolchain type is intended to hold only _exec configuration_ values --
+usually tools (prebuilt or from-source) used to build Python targets.
+
+This is typically implemented using {obj}`py_exec_tools_toolchain`, which
+provides {obj}`ToolchainInfo` with the field `exec_tools` set, which is an
+instance of {obj}`PyExecToolsInfo`.
+
+The toolchain constraints of this toolchain type can be a bit more nuanced than
+the other toolchain types. Typically, you set
+{external:bzl:obj}`toolchain.target_settings` to the Python version the tools
+are for, and {external:bzl:obj}`toolchain.exec_compatible_with` to the platform
+they can run on. This allows the toolchain to first be considered based on the
+target configuration (e.g. Python version), then for one to be chosen based on
+finding one compatible with the available host platforms to run the tool on.
+
+However, what `target_compatible_with`/`target_settings` and
+`exec_compatible_with` values to use depend on details of the tools being used.
+For example:
+* If you had a precompiler that supported any version of Python, then
+  putting the Python version in `target_settings` is unnecessary.
+* If you had a prebuilt polyglot precompiler binary that could run on any
+  platform, then setting `exec_compatible_with` is unnecessary.
+
+This can work because, when the rules invoke these build tools, they pass along
+all necessary information so that the tool can be entirely independent of the
+target configuration being built for.
+
+Alternatively, if you had a precompiler that only ran on linux, and only
+produced valid output for programs intended to run on linux, then _both_
+`exec_compatible_with` and `target_compatible_with` must be set to linux.
+
+### Custom toolchain example
+
+Here, we show an example for a semi-complicated toolchain suite, one that is:
+
+* A CPython-based interpreter
+* For Python version 3.12.0
+* Using an in-build interpreter built from source
+* That only runs on Linux
+* Using a prebuilt precompiler that only runs on Linux, and only produces byte
+  code valid for 3.12
+* With the exec tools interpreter disabled (unnecessary with a prebuild
+  precompiler)
+* Providing C headers and libraries
+
+Defining toolchains for this might look something like this:
+
+```
+# File: toolchain_impls/BUILD
+load("@rules_python//python:py_cc_toolchain.bzl", "py_cc_toolchain")
+load("@rules_python//python:py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain")
+load("@rules_python//python:py_runtime.bzl", "py_runtime")
+load("@rules_python//python:py_runtime_pair.bzl", "py_runtime_pair")
+
+MAJOR = 3
+MINOR = 12
+MICRO = 0
+
+py_runtime(
+    name = "runtime",
+    interpreter = ":python",
+    interpreter_version_info = {
+        "major": str(MAJOR),
+        "minor": str(MINOR),
+        "micro": str(MICRO),
+    }
+    implementation = "cpython"
+)
+py_runtime_pair(
+    name = "runtime_pair",
+    py3_runtime = ":runtime"
+)
+
+py_cc_toolchain(
+    name = "py_cc_toolchain_impl",
+    headers = ":headers",
+    libs = ":libs",
+    python_version = "{}.{}".format(MAJOR, MINOR)
+)
+
+py_exec_tools_toolchain(
+    name = "exec_tools_toolchain_impl",
+    exec_interpreter = "@rules_python/python:none",
+    precompiler = "precompiler-cpython-3.12"
+)
+
+cc_binary(name = "python3.12", ...)
+cc_library(name = "headers", ...)
+cc_library(name = "libs", ...)
+
+# File: toolchains/BUILD
+# Putting toolchain() calls in a separate package from the toolchain
+# implementations minimizes Bazel loading overhead
+
+toolchain(
+    name = "runtime_toolchain",
+    toolchain = "//toolchain_impl:runtime_pair",
+    toolchain_type = "@rules_python//python:toolchain_type",
+    target_compatible_with = ["@platforms/os:linux"]
+)
+toolchain(
+    name = "py_cc_toolchain",
+    toolchain = "//toolchain_impl:py_cc_toolchain_impl",
+    toolchain_type = "@rules_python//python/cc:toolchain_type",
+    target_compatible_with = ["@platforms/os:linux"]
+)
+
+toolchain(
+    name = "exec_tools_toolchain",
+    toolchain = "//toolchain_impl:exec_tools_toolchain_impl",
+    toolchain_type = "@rules_python//python:exec_tools_toolchain_type",
+    target_settings = [
+        "@rules_python//python/config_settings:is_python_3.12",
+    ],
+    exec_comaptible_with = ["@platforms/os:linux"]
+)
+```
+
+:::{note}
+The toolchain() calls should be in a separate BUILD file from everything else.
+This avoids Bazel having to perform unnecessary work when it discovers the list
+of available toolchains.
+:::
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel
index f6372ea..92ca8e7 100644
--- a/examples/BUILD.bazel
+++ b/examples/BUILD.bazel
@@ -12,4 +12,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# The following is experimental API and currently not intended for use outside this example.
+load("@rules_python//python/uv/private:lock.bzl", "lock")  # buildifier: disable=bzl-visibility
+
 licenses(["notice"])  # Apache 2.0
+
+lock(
+    name = "bzlmod_requirements_3_9",
+    srcs = ["bzlmod/requirements.in"],
+    out = "bzlmod/requirements_lock_3_9.txt",
+    python_version = "3.9.19",
+)
diff --git a/examples/bzlmod/.bazelrc b/examples/bzlmod/.bazelrc
index 578342d..fd16095 100644
--- a/examples/bzlmod/.bazelrc
+++ b/examples/bzlmod/.bazelrc
@@ -1,4 +1,5 @@
 common --enable_bzlmod
+common --lockfile_mode=update
 
 coverage --java_runtime_version=remotejdk_11
 
diff --git a/examples/bzlmod/.bazelversion b/examples/bzlmod/.bazelversion
new file mode 100644
index 0000000..643916c
--- /dev/null
+++ b/examples/bzlmod/.bazelversion
@@ -0,0 +1 @@
+7.3.1
diff --git a/examples/bzlmod/BUILD.bazel b/examples/bzlmod/BUILD.bazel
index bb16f98..d684b9c 100644
--- a/examples/bzlmod/BUILD.bazel
+++ b/examples/bzlmod/BUILD.bazel
@@ -9,20 +9,10 @@
 load("@pip//:requirements.bzl", "all_data_requirements", "all_requirements", "all_whl_requirements", "requirement")
 load("@python_3_9//:defs.bzl", py_test_with_transition = "py_test")
 load("@python_versions//3.10:defs.bzl", compile_pip_requirements_3_10 = "compile_pip_requirements")
-load("@python_versions//3.9:defs.bzl", compile_pip_requirements_3_9 = "compile_pip_requirements")
 load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")
 
 # This stanza calls a rule that generates targets for managing pip dependencies
-# with pip-compile.
-compile_pip_requirements_3_9(
-    name = "requirements_3_9",
-    src = "requirements.in",
-    requirements_txt = "requirements_lock_3_9.txt",
-    requirements_windows = "requirements_windows_3_9.txt",
-)
-
-# This stanza calls a rule that generates targets for managing pip dependencies
-# with pip-compile.
+# with pip-compile for a particular python version.
 compile_pip_requirements_3_10(
     name = "requirements_3_10",
     timeout = "moderate",
diff --git a/examples/bzlmod/MODULE.bazel b/examples/bzlmod/MODULE.bazel
index 3da17a6..4ac8191 100644
--- a/examples/bzlmod/MODULE.bazel
+++ b/examples/bzlmod/MODULE.bazel
@@ -22,7 +22,7 @@
 python = use_extension("@rules_python//python/extensions:python.bzl", "python")
 python.toolchain(
     configure_coverage_tool = True,
-    # Only set when you have mulitple toolchain versions.
+    # Only set when you have multiple toolchain versions.
     is_default = True,
     python_version = "3.9",
 )
@@ -37,6 +37,55 @@
     python_version = "3.10",
 )
 
+# One can override the actual toolchain versions that are available, which can be useful
+# when optimizing what gets downloaded and when.
+python.override(
+    available_python_versions = [
+        "3.10.9",
+        "3.9.19",
+        # The following is used by the `other_module` and we need to include it here
+        # as well.
+        "3.11.8",
+    ],
+    # Also override the `minor_mapping` so that the root module,
+    # instead of rules_python's defaults, controls what full version
+    # is used when `3.x` is requested.
+    minor_mapping = {
+        "3.10": "3.10.9",
+        "3.11": "3.11.8",
+        "3.9": "3.9.19",
+    },
+)
+
+# Or the sources that the toolchains come from for all platforms
+python.single_version_override(
+    patch_strip = 1,
+    # The user can specify patches to be applied to all interpreters.
+    patches = [],
+    python_version = "3.10.2",
+    sha256 = {
+        "aarch64-apple-darwin": "1409acd9a506e2d1d3b65c1488db4e40d8f19d09a7df099667c87a506f71c0ef",
+        "aarch64-unknown-linux-gnu": "8f351a8cc348bb45c0f95b8634c8345ec6e749e483384188ad865b7428342703",
+        "x86_64-apple-darwin": "8146ad4390710ec69b316a5649912df0247d35f4a42e2aa9615bffd87b3e235a",
+        "x86_64-pc-windows-msvc": "a1d9a594cd3103baa24937ad9150c1a389544b4350e859200b3e5c036ac352bd",
+        "x86_64-unknown-linux-gnu": "9b64eca2a94f7aff9409ad70bdaa7fbbf8148692662e764401883957943620dd",
+    },
+    urls = ["20220227/cpython-{python_version}+20220227-{platform}-{build}.tar.gz"],
+)
+
+# Or a single platform. This can be used in combination with the
+# `single_version_override` and `single_version_platform_override` will be
+# applied after `single_version_override`. Any values present in this override
+# will overwrite the values set by the `single_version_override`
+python.single_version_platform_override(
+    patch_strip = 1,
+    patches = [],
+    platform = "aarch64-apple-darwin",
+    python_version = "3.10.2",
+    sha256 = "1409acd9a506e2d1d3b65c1488db4e40d8f19d09a7df099667c87a506f71c0ef",
+    urls = ["20220227/cpython-{python_version}+20220227-{platform}-{build}.tar.gz"],
+)
+
 # You only need to load this repositories if you are using multiple Python versions.
 # See the tests folder for various examples on using multiple Python versions.
 # The names "python_3_9" and "python_3_10" are autmatically created by the repo
@@ -135,17 +184,7 @@
     ],
     hub_name = "pip",
     python_version = "3.9",
-    # The requirements files for each platform that we want to support.
-    requirements_by_platform = {
-        # Default requirements file for needs to explicitly provide the platforms
-        "//:requirements_lock_3_9.txt": "linux_*,osx_*",
-        # This API allows one to specify additional platforms that the users
-        # configure the toolchains for themselves. In this example we add
-        # `windows_aarch64` to illustrate that `rules_python` won't fail to
-        # process the value, but it does not mean that this example will work
-        # on Windows ARM.
-        "//:requirements_windows_3_9.txt": "windows_x86_64,windows_aarch64",
-    },
+    requirements_lock = "requirements_lock_3_9.txt",
     # These modifications were created above and we
     # are providing pip.parse with the label of the mod
     # and the name of the wheel.
@@ -179,8 +218,17 @@
     ],
     hub_name = "pip",
     python_version = "3.10",
-    requirements_lock = "//:requirements_lock_3_10.txt",
-    requirements_windows = "//:requirements_windows_3_10.txt",
+    # The requirements files for each platform that we want to support.
+    requirements_by_platform = {
+        # Default requirements file for needs to explicitly provide the platforms
+        "//:requirements_lock_3_10.txt": "linux_*,osx_*",
+        # This API allows one to specify additional platforms that the users
+        # configure the toolchains for themselves. In this example we add
+        # `windows_aarch64` to illustrate that `rules_python` won't fail to
+        # process the value, but it does not mean that this example will work
+        # on Windows ARM.
+        "//:requirements_windows_3_10.txt": "windows_x86_64,windows_aarch64",
+    },
     # These modifications were created above and we
     # are providing pip.parse with the label of the mod
     # and the name of the wheel.
diff --git a/examples/bzlmod/MODULE.bazel.lock b/examples/bzlmod/MODULE.bazel.lock
new file mode 100644
index 0000000..cb8fbe2
--- /dev/null
+++ b/examples/bzlmod/MODULE.bazel.lock
@@ -0,0 +1,8551 @@
+{
+  "lockFileVersion": 11,
+  "registryFileHashes": {
+    "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
+    "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
+    "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
+    "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
+    "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb",
+    "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/source.json": "14892cc698e02ffedf4967546e6bedb7245015906888d3465fcf27c90a26da10",
+    "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef",
+    "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862",
+    "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
+    "https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015",
+    "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/source.json": "082ed5f9837901fada8c68c2f3ddc958bb22b6d654f71dd73f3df30d45d4b749",
+    "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
+    "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
+    "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
+    "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
+    "https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5",
+    "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
+    "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
+    "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
+    "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
+    "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
+    "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
+    "https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6",
+    "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
+    "https://bcr.bazel.build/modules/protobuf/23.1/MODULE.bazel": "88b393b3eb4101d18129e5db51847cd40a5517a53e81216144a8c32dfeeca52a",
+    "https://bcr.bazel.build/modules/protobuf/24.4/MODULE.bazel": "7bc7ce5f2abf36b3b7b7c8218d3acdebb9426aeb35c2257c96445756f970eb12",
+    "https://bcr.bazel.build/modules/protobuf/24.4/source.json": "ace4b8c65d4cfe64efe544f09fc5e5df77faf3a67fbb29c5341e0d755d9b15d6",
+    "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
+    "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430",
+    "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
+    "https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel": "30d9135a2b6561c761bd67bd4990da591e6bdc128790ce3e7afd6a3558b2fb64",
+    "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1",
+    "https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d",
+    "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
+    "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
+    "https://bcr.bazel.build/modules/rules_jvm_external/5.1/source.json": "5abb45cc9beb27b77aec6a65a11855ef2b55d95dfdc358e9f312b78ae0ba32d5",
+    "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
+    "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
+    "https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a",
+    "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
+    "https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c",
+    "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
+    "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
+    "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483",
+    "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/source.json": "8d8448e71706df7450ced227ca6b3812407ff5e2ccad74a43a9fbe79c84e34e0",
+    "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
+    "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
+    "https://bcr.bazel.build/modules/stardoc/0.5.3/source.json": "cd53fe968dc8cd98197c052db3db6d82562960c87b61e7a90ee96f8e4e0dda97",
+    "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
+    "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9",
+    "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/source.json": "b2150404947339e8b947c6b16baa39fa75657f4ddec5e37272c7b11c7ab533bc",
+    "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
+    "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27",
+    "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79",
+    "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d"
+  },
+  "selectedYankedVersions": {},
+  "moduleExtensions": {
+    "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": {
+      "general": {
+        "bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=",
+        "usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "local_config_apple_cc": {
+            "bzlFile": "@@apple_support~//crosstool:setup.bzl",
+            "ruleClassName": "_apple_cc_autoconf",
+            "attributes": {}
+          },
+          "local_config_apple_cc_toolchains": {
+            "bzlFile": "@@apple_support~//crosstool:setup.bzl",
+            "ruleClassName": "_apple_cc_autoconf_toolchains",
+            "attributes": {}
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "apple_support~",
+            "bazel_tools",
+            "bazel_tools"
+          ]
+        ]
+      }
+    },
+    "@@platforms//host:extension.bzl%host_platform": {
+      "general": {
+        "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=",
+        "usagesDigest": "meSzxn3DUCcYEhq4HQwExWkWtU4EjriRBQLsZN+Q0SU=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "host_platform": {
+            "bzlFile": "@@platforms//host:extension.bzl",
+            "ruleClassName": "host_platform_repo",
+            "attributes": {}
+          }
+        },
+        "recordedRepoMappingEntries": []
+      }
+    },
+    "@@protobuf~//:non_module_deps.bzl%non_module_deps": {
+      "general": {
+        "bzlTransitiveDigest": "jsbfONl9OksDWiAs7KDFK5chH/tYI3DngdM30NKdk5Y=",
+        "usagesDigest": "eVrT3hFCIZNRuTKpfWDzSIwTi2p6U6PWbt+tNWl/Tqk=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "utf8_range": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "urls": [
+                "https://github.com/protocolbuffers/utf8_range/archive/de0b4a8ff9b5d4c98108bdfe723291a33c52c54f.zip"
+              ],
+              "strip_prefix": "utf8_range-de0b4a8ff9b5d4c98108bdfe723291a33c52c54f",
+              "sha256": "5da960e5e5d92394c809629a03af3c7709d2d3d0ca731dacb3a9fb4bf28f7702"
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "protobuf~",
+            "bazel_tools",
+            "bazel_tools"
+          ]
+        ]
+      }
+    },
+    "@@rules_jvm_external~//:extensions.bzl%maven": {
+      "general": {
+        "bzlTransitiveDigest": "4ijz6uc3T4E+d+U8LQv4EAt+8OqZNVY/lzvhLx3y1yg=",
+        "usagesDigest": "WfVTcbopbu3jyxPgDWx1iqIv1QV6L/T7utvDxAj5k84=",
+        "recordedFileInputs": {
+          "@@rules_jvm_external~//rules_jvm_external_deps_install.json": "3ab1f67b0de4815df110bc72ccd6c77882b3b21d3d1e0a84445847b6ce3235a3"
+        },
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "org_slf4j_slf4j_api_1_7_30": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "cdba07964d1bb40a0761485c6b1e8c2f8fd9eb1d19c53928ac0d7f9510105c57",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar",
+                "https://maven.google.com/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar"
+              ],
+              "downloaded_file_path": "org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar"
+            }
+          },
+          "com_google_api_grpc_proto_google_common_protos_2_0_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "5ce71656118618731e34a5d4c61aa3a031be23446dc7de8b5a5e77b66ebcd6ef",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/api/grpc/proto-google-common-protos/2.0.1/proto-google-common-protos-2.0.1.jar",
+                "https://maven.google.com/com/google/api/grpc/proto-google-common-protos/2.0.1/proto-google-common-protos-2.0.1.jar"
+              ],
+              "downloaded_file_path": "com/google/api/grpc/proto-google-common-protos/2.0.1/proto-google-common-protos-2.0.1.jar"
+            }
+          },
+          "com_google_api_gax_1_60_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "02f37d4ff1a7b8d71dff8064cf9568aa4f4b61bcc4485085d16130f32afa5a79",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/api/gax/1.60.0/gax-1.60.0.jar",
+                "https://maven.google.com/com/google/api/gax/1.60.0/gax-1.60.0.jar"
+              ],
+              "downloaded_file_path": "com/google/api/gax/1.60.0/gax-1.60.0.jar"
+            }
+          },
+          "com_google_guava_failureaccess_1_0_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar",
+                "https://maven.google.com/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar"
+              ],
+              "downloaded_file_path": "com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar"
+            }
+          },
+          "commons_logging_commons_logging_1_2": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636",
+              "urls": [
+                "https://repo1.maven.org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.jar",
+                "https://maven.google.com/commons-logging/commons-logging/1.2/commons-logging-1.2.jar"
+              ],
+              "downloaded_file_path": "commons-logging/commons-logging/1.2/commons-logging-1.2.jar"
+            }
+          },
+          "com_google_http_client_google_http_client_appengine_1_38_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "f97b495fd97ac3a3d59099eb2b55025f4948230da15a076f189b9cff37c6b4d2",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/http-client/google-http-client-appengine/1.38.0/google-http-client-appengine-1.38.0.jar",
+                "https://maven.google.com/com/google/http-client/google-http-client-appengine/1.38.0/google-http-client-appengine-1.38.0.jar"
+              ],
+              "downloaded_file_path": "com/google/http-client/google-http-client-appengine/1.38.0/google-http-client-appengine-1.38.0.jar"
+            }
+          },
+          "com_google_cloud_google_cloud_storage_1_113_4": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "796833e9bdab80c40bbc820e65087eb8f28c6bfbca194d2e3e00d98cb5bc55d6",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/cloud/google-cloud-storage/1.113.4/google-cloud-storage-1.113.4.jar",
+                "https://maven.google.com/com/google/cloud/google-cloud-storage/1.113.4/google-cloud-storage-1.113.4.jar"
+              ],
+              "downloaded_file_path": "com/google/cloud/google-cloud-storage/1.113.4/google-cloud-storage-1.113.4.jar"
+            }
+          },
+          "io_grpc_grpc_context_1_33_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "99b8aea2b614fe0e61c3676e681259dc43c2de7f64620998e1a8435eb2976496",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/grpc/grpc-context/1.33.1/grpc-context-1.33.1.jar",
+                "https://maven.google.com/io/grpc/grpc-context/1.33.1/grpc-context-1.33.1.jar"
+              ],
+              "downloaded_file_path": "io/grpc/grpc-context/1.33.1/grpc-context-1.33.1.jar"
+            }
+          },
+          "com_google_api_grpc_proto_google_iam_v1_1_0_3": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "64cee7383a97e846da8d8e160e6c8fe30561e507260552c59e6ccfc81301fdc8",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/api/grpc/proto-google-iam-v1/1.0.3/proto-google-iam-v1-1.0.3.jar",
+                "https://maven.google.com/com/google/api/grpc/proto-google-iam-v1/1.0.3/proto-google-iam-v1-1.0.3.jar"
+              ],
+              "downloaded_file_path": "com/google/api/grpc/proto-google-iam-v1/1.0.3/proto-google-iam-v1-1.0.3.jar"
+            }
+          },
+          "com_google_api_api_common_1_10_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "2a033f24bb620383eda440ad307cb8077cfec1c7eadc684d65216123a1b9613a",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/api/api-common/1.10.1/api-common-1.10.1.jar",
+                "https://maven.google.com/com/google/api/api-common/1.10.1/api-common-1.10.1.jar"
+              ],
+              "downloaded_file_path": "com/google/api/api-common/1.10.1/api-common-1.10.1.jar"
+            }
+          },
+          "com_google_auth_google_auth_library_oauth2_http_0_22_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "1722d895c42dc42ea1d1f392ddbec1fbb28f7a979022c3a6c29acc39cc777ad1",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/auth/google-auth-library-oauth2-http/0.22.0/google-auth-library-oauth2-http-0.22.0.jar",
+                "https://maven.google.com/com/google/auth/google-auth-library-oauth2-http/0.22.0/google-auth-library-oauth2-http-0.22.0.jar"
+              ],
+              "downloaded_file_path": "com/google/auth/google-auth-library-oauth2-http/0.22.0/google-auth-library-oauth2-http-0.22.0.jar"
+            }
+          },
+          "com_typesafe_netty_netty_reactive_streams_2_0_5": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "f949849fc8ee75fde468ba3a35df2e04577fa31a2940b83b2a7dc9d14dac13d6",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/typesafe/netty/netty-reactive-streams/2.0.5/netty-reactive-streams-2.0.5.jar",
+                "https://maven.google.com/com/typesafe/netty/netty-reactive-streams/2.0.5/netty-reactive-streams-2.0.5.jar"
+              ],
+              "downloaded_file_path": "com/typesafe/netty/netty-reactive-streams/2.0.5/netty-reactive-streams-2.0.5.jar"
+            }
+          },
+          "com_typesafe_netty_netty_reactive_streams_http_2_0_5": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "b39224751ad936758176e9d994230380ade5e9079e7c8ad778e3995779bcf303",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/typesafe/netty/netty-reactive-streams-http/2.0.5/netty-reactive-streams-http-2.0.5.jar",
+                "https://maven.google.com/com/typesafe/netty/netty-reactive-streams-http/2.0.5/netty-reactive-streams-http-2.0.5.jar"
+              ],
+              "downloaded_file_path": "com/typesafe/netty/netty-reactive-streams-http/2.0.5/netty-reactive-streams-http-2.0.5.jar"
+            }
+          },
+          "javax_annotation_javax_annotation_api_1_3_2": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b",
+              "urls": [
+                "https://repo1.maven.org/maven2/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar",
+                "https://maven.google.com/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar"
+              ],
+              "downloaded_file_path": "javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar"
+            }
+          },
+          "com_google_j2objc_j2objc_annotations_1_3": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar",
+                "https://maven.google.com/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar"
+              ],
+              "downloaded_file_path": "com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar"
+            }
+          },
+          "software_amazon_awssdk_metrics_spi_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "08a11dc8c4ba464beafbcc7ac05b8c724c1ccb93da99482e82a68540ac704e4a",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/metrics-spi/2.17.183/metrics-spi-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/metrics-spi/2.17.183/metrics-spi-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/metrics-spi/2.17.183/metrics-spi-2.17.183.jar"
+            }
+          },
+          "org_reactivestreams_reactive_streams_1_0_3": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "1dee0481072d19c929b623e155e14d2f6085dc011529a0a0dbefc84cf571d865",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar",
+                "https://maven.google.com/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar"
+              ],
+              "downloaded_file_path": "org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar"
+            }
+          },
+          "com_google_http_client_google_http_client_jackson2_1_38_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "e6504a82425fcc2168a4ca4175138ddcc085168daed8cdedb86d8f6fdc296e1e",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/http-client/google-http-client-jackson2/1.38.0/google-http-client-jackson2-1.38.0.jar",
+                "https://maven.google.com/com/google/http-client/google-http-client-jackson2/1.38.0/google-http-client-jackson2-1.38.0.jar"
+              ],
+              "downloaded_file_path": "com/google/http-client/google-http-client-jackson2/1.38.0/google-http-client-jackson2-1.38.0.jar"
+            }
+          },
+          "io_netty_netty_transport_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "c5fb68e9a65b6e8a516adfcb9fa323479ee7b4d9449d8a529d2ecab3d3711d5a",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-transport/4.1.72.Final/netty-transport-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-transport/4.1.72.Final/netty-transport-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-transport/4.1.72.Final/netty-transport-4.1.72.Final.jar"
+            }
+          },
+          "io_netty_netty_codec_http2_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "c89a70500f59e8563e720aaa808263a514bd9e2bd91ba84eab8c2ccb45f234b2",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-codec-http2/4.1.72.Final/netty-codec-http2-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-codec-http2/4.1.72.Final/netty-codec-http2-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-codec-http2/4.1.72.Final/netty-codec-http2-4.1.72.Final.jar"
+            }
+          },
+          "io_opencensus_opencensus_api_0_24_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "f561b1cc2673844288e596ddf5bb6596868a8472fd2cb8993953fc5c034b2352",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar",
+                "https://maven.google.com/io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar"
+              ],
+              "downloaded_file_path": "io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar"
+            }
+          },
+          "rules_jvm_external_deps": {
+            "bzlFile": "@@rules_jvm_external~//:coursier.bzl",
+            "ruleClassName": "pinned_coursier_fetch",
+            "attributes": {
+              "repositories": [
+                "{ \"repo_url\": \"https://repo1.maven.org/maven2\" }"
+              ],
+              "artifacts": [
+                "{ \"group\": \"com.google.auth\", \"artifact\": \"google-auth-library-credentials\", \"version\": \"0.22.0\" }",
+                "{ \"group\": \"com.google.auth\", \"artifact\": \"google-auth-library-oauth2-http\", \"version\": \"0.22.0\" }",
+                "{ \"group\": \"com.google.cloud\", \"artifact\": \"google-cloud-core\", \"version\": \"1.93.10\" }",
+                "{ \"group\": \"com.google.cloud\", \"artifact\": \"google-cloud-storage\", \"version\": \"1.113.4\" }",
+                "{ \"group\": \"com.google.code.gson\", \"artifact\": \"gson\", \"version\": \"2.9.0\" }",
+                "{ \"group\": \"com.google.googlejavaformat\", \"artifact\": \"google-java-format\", \"version\": \"1.15.0\" }",
+                "{ \"group\": \"com.google.guava\", \"artifact\": \"guava\", \"version\": \"31.1-jre\" }",
+                "{ \"group\": \"org.apache.maven\", \"artifact\": \"maven-artifact\", \"version\": \"3.8.6\" }",
+                "{ \"group\": \"software.amazon.awssdk\", \"artifact\": \"s3\", \"version\": \"2.17.183\" }"
+              ],
+              "fetch_sources": true,
+              "fetch_javadoc": false,
+              "generate_compat_repositories": false,
+              "maven_install_json": "@@rules_jvm_external~//:rules_jvm_external_deps_install.json",
+              "override_targets": {},
+              "strict_visibility": false,
+              "strict_visibility_value": [
+                "@@//visibility:private"
+              ],
+              "jetify": false,
+              "jetify_include_list": [
+                "*"
+              ],
+              "additional_netrc_lines": [],
+              "fail_if_repin_required": false,
+              "use_starlark_android_rules": false,
+              "aar_import_bzl_label": "@build_bazel_rules_android//android:rules.bzl",
+              "duplicate_version_warning": "warn"
+            }
+          },
+          "org_threeten_threetenbp_1_5_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "dcf9c0f940739f2a825cd8626ff27113459a2f6eb18797c7152f93fff69c264f",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/threeten/threetenbp/1.5.0/threetenbp-1.5.0.jar",
+                "https://maven.google.com/org/threeten/threetenbp/1.5.0/threetenbp-1.5.0.jar"
+              ],
+              "downloaded_file_path": "org/threeten/threetenbp/1.5.0/threetenbp-1.5.0.jar"
+            }
+          },
+          "software_amazon_awssdk_http_client_spi_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "fe7120f175df9e47ebcc5d946d7f40110faf2ba0a30364f3b935d5b8a5a6c3c6",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/http-client-spi/2.17.183/http-client-spi-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/http-client-spi/2.17.183/http-client-spi-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/http-client-spi/2.17.183/http-client-spi-2.17.183.jar"
+            }
+          },
+          "software_amazon_awssdk_third_party_jackson_core_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "1bc27c9960993c20e1ab058012dd1ae04c875eec9f0f08f2b2ca41e578dee9a4",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/third-party-jackson-core/2.17.183/third-party-jackson-core-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/third-party-jackson-core/2.17.183/third-party-jackson-core-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/third-party-jackson-core/2.17.183/third-party-jackson-core-2.17.183.jar"
+            }
+          },
+          "software_amazon_eventstream_eventstream_1_0_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "0c37d8e696117f02c302191b8110b0d0eb20fa412fce34c3a269ec73c16ce822",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/eventstream/eventstream/1.0.1/eventstream-1.0.1.jar",
+                "https://maven.google.com/software/amazon/eventstream/eventstream/1.0.1/eventstream-1.0.1.jar"
+              ],
+              "downloaded_file_path": "software/amazon/eventstream/eventstream/1.0.1/eventstream-1.0.1.jar"
+            }
+          },
+          "com_google_oauth_client_google_oauth_client_1_31_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "4ed4e2948251dbda66ce251bd7f3b32cd8570055e5cdb165a3c7aea8f43da0ff",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/oauth-client/google-oauth-client/1.31.1/google-oauth-client-1.31.1.jar",
+                "https://maven.google.com/com/google/oauth-client/google-oauth-client/1.31.1/google-oauth-client-1.31.1.jar"
+              ],
+              "downloaded_file_path": "com/google/oauth-client/google-oauth-client/1.31.1/google-oauth-client-1.31.1.jar"
+            }
+          },
+          "maven": {
+            "bzlFile": "@@rules_jvm_external~//:coursier.bzl",
+            "ruleClassName": "coursier_fetch",
+            "attributes": {
+              "repositories": [
+                "{ \"repo_url\": \"https://repo1.maven.org/maven2\" }"
+              ],
+              "artifacts": [
+                "{ \"group\": \"com.google.code.findbugs\", \"artifact\": \"jsr305\", \"version\": \"3.0.2\" }",
+                "{ \"group\": \"com.google.code.gson\", \"artifact\": \"gson\", \"version\": \"2.8.9\" }",
+                "{ \"group\": \"com.google.errorprone\", \"artifact\": \"error_prone_annotations\", \"version\": \"2.3.2\" }",
+                "{ \"group\": \"com.google.j2objc\", \"artifact\": \"j2objc-annotations\", \"version\": \"1.3\" }",
+                "{ \"group\": \"com.google.guava\", \"artifact\": \"guava\", \"version\": \"31.1-jre\" }",
+                "{ \"group\": \"com.google.guava\", \"artifact\": \"guava-testlib\", \"version\": \"31.1-jre\" }",
+                "{ \"group\": \"com.google.truth\", \"artifact\": \"truth\", \"version\": \"1.1.2\" }",
+                "{ \"group\": \"junit\", \"artifact\": \"junit\", \"version\": \"4.13.2\" }",
+                "{ \"group\": \"org.mockito\", \"artifact\": \"mockito-core\", \"version\": \"4.3.1\" }"
+              ],
+              "fail_on_missing_checksum": true,
+              "fetch_sources": true,
+              "fetch_javadoc": false,
+              "excluded_artifacts": [],
+              "generate_compat_repositories": false,
+              "version_conflict_policy": "default",
+              "override_targets": {},
+              "strict_visibility": false,
+              "strict_visibility_value": [
+                "@@//visibility:private"
+              ],
+              "resolve_timeout": 600,
+              "jetify": false,
+              "jetify_include_list": [
+                "*"
+              ],
+              "use_starlark_android_rules": false,
+              "aar_import_bzl_label": "@build_bazel_rules_android//android:rules.bzl",
+              "duplicate_version_warning": "warn"
+            }
+          },
+          "software_amazon_awssdk_aws_xml_protocol_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "566bba05d49256fa6994efd68fa625ae05a62ea45ee74bb9130d20ea20988363",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/aws-xml-protocol/2.17.183/aws-xml-protocol-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/aws-xml-protocol/2.17.183/aws-xml-protocol-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/aws-xml-protocol/2.17.183/aws-xml-protocol-2.17.183.jar"
+            }
+          },
+          "software_amazon_awssdk_annotations_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "8e4d72361ca805a0bd8bbd9017cd7ff77c8d170f2dd469c7d52d5653330bb3fd",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/annotations/2.17.183/annotations-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/annotations/2.17.183/annotations-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/annotations/2.17.183/annotations-2.17.183.jar"
+            }
+          },
+          "software_amazon_awssdk_netty_nio_client_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "a6d356f364c56d7b90006b0b7e503b8630010993a5587ce42e74b10b8dca2238",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/netty-nio-client/2.17.183/netty-nio-client-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/netty-nio-client/2.17.183/netty-nio-client-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/netty-nio-client/2.17.183/netty-nio-client-2.17.183.jar"
+            }
+          },
+          "com_google_guava_guava_31_1_jre": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "a42edc9cab792e39fe39bb94f3fca655ed157ff87a8af78e1d6ba5b07c4a00ab",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar",
+                "https://maven.google.com/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar"
+              ],
+              "downloaded_file_path": "com/google/guava/guava/31.1-jre/guava-31.1-jre.jar"
+            }
+          },
+          "com_google_auto_value_auto_value_annotations_1_7_4": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "fedd59b0b4986c342f6ab2d182f2a4ee9fceb2c7e2d5bdc4dc764c92394a23d3",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/auto/value/auto-value-annotations/1.7.4/auto-value-annotations-1.7.4.jar",
+                "https://maven.google.com/com/google/auto/value/auto-value-annotations/1.7.4/auto-value-annotations-1.7.4.jar"
+              ],
+              "downloaded_file_path": "com/google/auto/value/auto-value-annotations/1.7.4/auto-value-annotations-1.7.4.jar"
+            }
+          },
+          "io_netty_netty_transport_native_unix_common_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "6f8f1cc29b5a234eeee9439a63eb3f03a5994aa540ff555cb0b2c88cefaf6877",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-transport-native-unix-common/4.1.72.Final/netty-transport-native-unix-common-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-transport-native-unix-common/4.1.72.Final/netty-transport-native-unix-common-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-transport-native-unix-common/4.1.72.Final/netty-transport-native-unix-common-4.1.72.Final.jar"
+            }
+          },
+          "io_opencensus_opencensus_contrib_http_util_0_24_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "7155273bbb1ed3d477ea33cf19d7bbc0b285ff395f43b29ae576722cf247000f",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar",
+                "https://maven.google.com/io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar"
+              ],
+              "downloaded_file_path": "io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar"
+            }
+          },
+          "com_fasterxml_jackson_core_jackson_core_2_11_3": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "78cd0a6b936232e06dd3e38da8a0345348a09cd1ff9c4d844c6ee72c75cfc402",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.11.3/jackson-core-2.11.3.jar",
+                "https://maven.google.com/com/fasterxml/jackson/core/jackson-core/2.11.3/jackson-core-2.11.3.jar"
+              ],
+              "downloaded_file_path": "com/fasterxml/jackson/core/jackson-core/2.11.3/jackson-core-2.11.3.jar"
+            }
+          },
+          "com_google_cloud_google_cloud_core_1_93_10": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "832d74eca66f4601e162a8460d6f59f50d1d23f93c18b02654423b6b0d67c6ea",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/cloud/google-cloud-core/1.93.10/google-cloud-core-1.93.10.jar",
+                "https://maven.google.com/com/google/cloud/google-cloud-core/1.93.10/google-cloud-core-1.93.10.jar"
+              ],
+              "downloaded_file_path": "com/google/cloud/google-cloud-core/1.93.10/google-cloud-core-1.93.10.jar"
+            }
+          },
+          "com_google_auth_google_auth_library_credentials_0_22_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "42c76031276de5b520909e9faf88c5b3c9a722d69ee9cfdafedb1c52c355dfc5",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/auth/google-auth-library-credentials/0.22.0/google-auth-library-credentials-0.22.0.jar",
+                "https://maven.google.com/com/google/auth/google-auth-library-credentials/0.22.0/google-auth-library-credentials-0.22.0.jar"
+              ],
+              "downloaded_file_path": "com/google/auth/google-auth-library-credentials/0.22.0/google-auth-library-credentials-0.22.0.jar"
+            }
+          },
+          "software_amazon_awssdk_profiles_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "78833b32fde3f1c5320373b9ea955c1bbc28f2c904010791c4784e610193ee56",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/profiles/2.17.183/profiles-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/profiles/2.17.183/profiles-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/profiles/2.17.183/profiles-2.17.183.jar"
+            }
+          },
+          "org_apache_httpcomponents_httpcore_4_4_13": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "e06e89d40943245fcfa39ec537cdbfce3762aecde8f9c597780d2b00c2b43424",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/apache/httpcomponents/httpcore/4.4.13/httpcore-4.4.13.jar",
+                "https://maven.google.com/org/apache/httpcomponents/httpcore/4.4.13/httpcore-4.4.13.jar"
+              ],
+              "downloaded_file_path": "org/apache/httpcomponents/httpcore/4.4.13/httpcore-4.4.13.jar"
+            }
+          },
+          "io_netty_netty_common_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "8adb4c291260ceb2859a68c49f0adeed36bf49587608e2b81ecff6aaf06025e9",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-common/4.1.72.Final/netty-common-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-common/4.1.72.Final/netty-common-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-common/4.1.72.Final/netty-common-4.1.72.Final.jar"
+            }
+          },
+          "io_netty_netty_transport_classes_epoll_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "e1528a9751c1285aa7beaf3a1eb0597151716426ce38598ac9bc0891209b9e68",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-transport-classes-epoll/4.1.72.Final/netty-transport-classes-epoll-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-transport-classes-epoll/4.1.72.Final/netty-transport-classes-epoll-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-transport-classes-epoll/4.1.72.Final/netty-transport-classes-epoll-4.1.72.Final.jar"
+            }
+          },
+          "org_checkerframework_checker_qual_3_12_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar",
+                "https://maven.google.com/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar"
+              ],
+              "downloaded_file_path": "org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar"
+            }
+          },
+          "com_google_cloud_google_cloud_core_http_1_93_10": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "81ac67c14c7c4244d2b7db2607ad352416aca8d3bb2adf338964e8fea25b1b3c",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/cloud/google-cloud-core-http/1.93.10/google-cloud-core-http-1.93.10.jar",
+                "https://maven.google.com/com/google/cloud/google-cloud-core-http/1.93.10/google-cloud-core-http-1.93.10.jar"
+              ],
+              "downloaded_file_path": "com/google/cloud/google-cloud-core-http/1.93.10/google-cloud-core-http-1.93.10.jar"
+            }
+          },
+          "software_amazon_awssdk_utils_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "7bd849bb5aa71bfdf6b849643736ecab3a7b3f204795804eefe5754104231ec6",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/utils/2.17.183/utils-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/utils/2.17.183/utils-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/utils/2.17.183/utils-2.17.183.jar"
+            }
+          },
+          "org_apache_commons_commons_lang3_3_8_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "dac807f65b07698ff39b1b07bfef3d87ae3fd46d91bbf8a2bc02b2a831616f68",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1.jar",
+                "https://maven.google.com/org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1.jar"
+              ],
+              "downloaded_file_path": "org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1.jar"
+            }
+          },
+          "software_amazon_awssdk_aws_core_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "bccbdbea689a665a702ff19828662d87fb7fe81529df13f02ef1e4c474ea9f93",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/aws-core/2.17.183/aws-core-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/aws-core/2.17.183/aws-core-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/aws-core/2.17.183/aws-core-2.17.183.jar"
+            }
+          },
+          "com_google_api_gax_httpjson_0_77_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "fd4dae47fa016d3b26e8d90b67ddc6c23c4c06e8bcdf085c70310ab7ef324bd6",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/api/gax-httpjson/0.77.0/gax-httpjson-0.77.0.jar",
+                "https://maven.google.com/com/google/api/gax-httpjson/0.77.0/gax-httpjson-0.77.0.jar"
+              ],
+              "downloaded_file_path": "com/google/api/gax-httpjson/0.77.0/gax-httpjson-0.77.0.jar"
+            }
+          },
+          "unpinned_rules_jvm_external_deps": {
+            "bzlFile": "@@rules_jvm_external~//:coursier.bzl",
+            "ruleClassName": "coursier_fetch",
+            "attributes": {
+              "repositories": [
+                "{ \"repo_url\": \"https://repo1.maven.org/maven2\" }"
+              ],
+              "artifacts": [
+                "{ \"group\": \"com.google.auth\", \"artifact\": \"google-auth-library-credentials\", \"version\": \"0.22.0\" }",
+                "{ \"group\": \"com.google.auth\", \"artifact\": \"google-auth-library-oauth2-http\", \"version\": \"0.22.0\" }",
+                "{ \"group\": \"com.google.cloud\", \"artifact\": \"google-cloud-core\", \"version\": \"1.93.10\" }",
+                "{ \"group\": \"com.google.cloud\", \"artifact\": \"google-cloud-storage\", \"version\": \"1.113.4\" }",
+                "{ \"group\": \"com.google.code.gson\", \"artifact\": \"gson\", \"version\": \"2.9.0\" }",
+                "{ \"group\": \"com.google.googlejavaformat\", \"artifact\": \"google-java-format\", \"version\": \"1.15.0\" }",
+                "{ \"group\": \"com.google.guava\", \"artifact\": \"guava\", \"version\": \"31.1-jre\" }",
+                "{ \"group\": \"org.apache.maven\", \"artifact\": \"maven-artifact\", \"version\": \"3.8.6\" }",
+                "{ \"group\": \"software.amazon.awssdk\", \"artifact\": \"s3\", \"version\": \"2.17.183\" }"
+              ],
+              "fail_on_missing_checksum": true,
+              "fetch_sources": true,
+              "fetch_javadoc": false,
+              "excluded_artifacts": [],
+              "generate_compat_repositories": false,
+              "version_conflict_policy": "default",
+              "override_targets": {},
+              "strict_visibility": false,
+              "strict_visibility_value": [
+                "@@//visibility:private"
+              ],
+              "maven_install_json": "@@rules_jvm_external~//:rules_jvm_external_deps_install.json",
+              "resolve_timeout": 600,
+              "jetify": false,
+              "jetify_include_list": [
+                "*"
+              ],
+              "use_starlark_android_rules": false,
+              "aar_import_bzl_label": "@build_bazel_rules_android//android:rules.bzl",
+              "duplicate_version_warning": "warn"
+            }
+          },
+          "com_google_errorprone_error_prone_annotations_2_11_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "721cb91842b46fa056847d104d5225c8b8e1e8b62263b993051e1e5a0137b7ec",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar",
+                "https://maven.google.com/com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar"
+              ],
+              "downloaded_file_path": "com/google/errorprone/error_prone_annotations/2.11.0/error_prone_annotations-2.11.0.jar"
+            }
+          },
+          "software_amazon_awssdk_regions_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "d3079395f3ffc07d04ffcce16fca29fb5968197f6e9ea3dbff6be297102b40a5",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/regions/2.17.183/regions-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/regions/2.17.183/regions-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/regions/2.17.183/regions-2.17.183.jar"
+            }
+          },
+          "io_netty_netty_handler_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "9cb6012af7e06361d738ac4e3bdc49a158f8cf87d9dee0f2744056b7d99c28d5",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-handler/4.1.72.Final/netty-handler-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-handler/4.1.72.Final/netty-handler-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-handler/4.1.72.Final/netty-handler-4.1.72.Final.jar"
+            }
+          },
+          "software_amazon_awssdk_aws_query_protocol_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "4dace03c76f80f3dec920cb3dedb2a95984c4366ef4fda728660cb90bed74848",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/aws-query-protocol/2.17.183/aws-query-protocol-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/aws-query-protocol/2.17.183/aws-query-protocol-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/aws-query-protocol/2.17.183/aws-query-protocol-2.17.183.jar"
+            }
+          },
+          "io_netty_netty_codec_http_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "fa6fec88010bfaf6a7415b5364671b6b18ffb6b35a986ab97b423fd8c3a0174b",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-codec-http/4.1.72.Final/netty-codec-http-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-codec-http/4.1.72.Final/netty-codec-http-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-codec-http/4.1.72.Final/netty-codec-http-4.1.72.Final.jar"
+            }
+          },
+          "io_netty_netty_resolver_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "6474598aab7cc9d8d6cfa06c05bd1b19adbf7f8451dbdd73070b33a6c60b1b90",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-resolver/4.1.72.Final/netty-resolver-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-resolver/4.1.72.Final/netty-resolver-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-resolver/4.1.72.Final/netty-resolver-4.1.72.Final.jar"
+            }
+          },
+          "software_amazon_awssdk_protocol_core_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "10e7c4faa1f05e2d73055d0390dbd0bb6450e2e6cb85beda051b1e4693c826ce",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/protocol-core/2.17.183/protocol-core-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/protocol-core/2.17.183/protocol-core-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/protocol-core/2.17.183/protocol-core-2.17.183.jar"
+            }
+          },
+          "org_checkerframework_checker_compat_qual_2_5_5": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar",
+                "https://maven.google.com/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar"
+              ],
+              "downloaded_file_path": "org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar"
+            }
+          },
+          "com_google_apis_google_api_services_storage_v1_rev20200927_1_30_10": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "52d26a9d105f8d8a0850807285f307a76cea8f3e0cdb2be4d3b15b1adfa77351",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/apis/google-api-services-storage/v1-rev20200927-1.30.10/google-api-services-storage-v1-rev20200927-1.30.10.jar",
+                "https://maven.google.com/com/google/apis/google-api-services-storage/v1-rev20200927-1.30.10/google-api-services-storage-v1-rev20200927-1.30.10.jar"
+              ],
+              "downloaded_file_path": "com/google/apis/google-api-services-storage/v1-rev20200927-1.30.10/google-api-services-storage-v1-rev20200927-1.30.10.jar"
+            }
+          },
+          "com_google_api_client_google_api_client_1_30_11": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "ee6f97865cc7de6c7c80955c3f37372cf3887bd75e4fc06f1058a6b4cd9bf4da",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/api-client/google-api-client/1.30.11/google-api-client-1.30.11.jar",
+                "https://maven.google.com/com/google/api-client/google-api-client/1.30.11/google-api-client-1.30.11.jar"
+              ],
+              "downloaded_file_path": "com/google/api-client/google-api-client/1.30.11/google-api-client-1.30.11.jar"
+            }
+          },
+          "software_amazon_awssdk_s3_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "ab073b91107a9e4ed9f030314077d137fe627e055ad895fabb036980a050e360",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/s3/2.17.183/s3-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/s3/2.17.183/s3-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/s3/2.17.183/s3-2.17.183.jar"
+            }
+          },
+          "org_apache_maven_maven_artifact_3_8_6": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "de22a4c6f54fe31276a823b1bbd3adfd6823529e732f431b5eff0852c2b9252b",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/apache/maven/maven-artifact/3.8.6/maven-artifact-3.8.6.jar",
+                "https://maven.google.com/org/apache/maven/maven-artifact/3.8.6/maven-artifact-3.8.6.jar"
+              ],
+              "downloaded_file_path": "org/apache/maven/maven-artifact/3.8.6/maven-artifact-3.8.6.jar"
+            }
+          },
+          "com_google_googlejavaformat_google_java_format_1_15_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "4f546cfe159547ac3b9547daa9649e728f6abc254979c975f1cb9971793692c3",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/googlejavaformat/google-java-format/1.15.0/google-java-format-1.15.0.jar",
+                "https://maven.google.com/com/google/googlejavaformat/google-java-format/1.15.0/google-java-format-1.15.0.jar"
+              ],
+              "downloaded_file_path": "com/google/googlejavaformat/google-java-format/1.15.0/google-java-format-1.15.0.jar"
+            }
+          },
+          "org_apache_httpcomponents_httpclient_4_5_13": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "6fe9026a566c6a5001608cf3fc32196641f6c1e5e1986d1037ccdbd5f31ef743",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar",
+                "https://maven.google.com/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar"
+              ],
+              "downloaded_file_path": "org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar"
+            }
+          },
+          "com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar",
+                "https://maven.google.com/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar"
+              ],
+              "downloaded_file_path": "com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar"
+            }
+          },
+          "com_google_http_client_google_http_client_1_38_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "411f4a42519b6b78bdc0fcfdf74c9edcef0ee97afa4a667abe04045a508d6302",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/http-client/google-http-client/1.38.0/google-http-client-1.38.0.jar",
+                "https://maven.google.com/com/google/http-client/google-http-client/1.38.0/google-http-client-1.38.0.jar"
+              ],
+              "downloaded_file_path": "com/google/http-client/google-http-client/1.38.0/google-http-client-1.38.0.jar"
+            }
+          },
+          "software_amazon_awssdk_apache_client_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "78ceae502fce6a97bbe5ff8f6a010a52ab7ea3ae66cb1a4122e18185fce45022",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/apache-client/2.17.183/apache-client-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/apache-client/2.17.183/apache-client-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/apache-client/2.17.183/apache-client-2.17.183.jar"
+            }
+          },
+          "software_amazon_awssdk_arns_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "659a185e191d66c71de81209490e66abeaccae208ea7b2831a738670823447aa",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/arns/2.17.183/arns-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/arns/2.17.183/arns-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/arns/2.17.183/arns-2.17.183.jar"
+            }
+          },
+          "com_google_code_gson_gson_2_9_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "c96d60551331a196dac54b745aa642cd078ef89b6f267146b705f2c2cbef052d",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.9.0/gson-2.9.0.jar",
+                "https://maven.google.com/com/google/code/gson/gson/2.9.0/gson-2.9.0.jar"
+              ],
+              "downloaded_file_path": "com/google/code/gson/gson/2.9.0/gson-2.9.0.jar"
+            }
+          },
+          "io_netty_netty_buffer_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "568ff7cd9d8e2284ec980730c88924f686642929f8f219a74518b4e64755f3a1",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-buffer/4.1.72.Final/netty-buffer-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-buffer/4.1.72.Final/netty-buffer-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-buffer/4.1.72.Final/netty-buffer-4.1.72.Final.jar"
+            }
+          },
+          "com_google_code_findbugs_jsr305_3_0_2": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar",
+                "https://maven.google.com/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar"
+              ],
+              "downloaded_file_path": "com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar"
+            }
+          },
+          "commons_codec_commons_codec_1_11": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "e599d5318e97aa48f42136a2927e6dfa4e8881dff0e6c8e3109ddbbff51d7b7d",
+              "urls": [
+                "https://repo1.maven.org/maven2/commons-codec/commons-codec/1.11/commons-codec-1.11.jar",
+                "https://maven.google.com/commons-codec/commons-codec/1.11/commons-codec-1.11.jar"
+              ],
+              "downloaded_file_path": "commons-codec/commons-codec/1.11/commons-codec-1.11.jar"
+            }
+          },
+          "software_amazon_awssdk_auth_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "8820c6636e5c14efc29399fb5565ce50212b0c1f4ed720a025a2c402d54e0978",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/auth/2.17.183/auth-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/auth/2.17.183/auth-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/auth/2.17.183/auth-2.17.183.jar"
+            }
+          },
+          "software_amazon_awssdk_json_utils_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "51ab7f550adc06afcb49f5270cdf690f1bfaaee243abaa5d978095e2a1e4e1a5",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/json-utils/2.17.183/json-utils-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/json-utils/2.17.183/json-utils-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/json-utils/2.17.183/json-utils-2.17.183.jar"
+            }
+          },
+          "org_codehaus_plexus_plexus_utils_3_3_1": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "4b570fcdbe5a894f249d2eb9b929358a9c88c3e548d227a80010461930222f2a",
+              "urls": [
+                "https://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/3.3.1/plexus-utils-3.3.1.jar",
+                "https://maven.google.com/org/codehaus/plexus/plexus-utils/3.3.1/plexus-utils-3.3.1.jar"
+              ],
+              "downloaded_file_path": "org/codehaus/plexus/plexus-utils/3.3.1/plexus-utils-3.3.1.jar"
+            }
+          },
+          "com_google_protobuf_protobuf_java_util_3_13_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "d9de66b8c9445905dfa7064f6d5213d47ce88a20d34e21d83c4a94a229e14e62",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java-util/3.13.0/protobuf-java-util-3.13.0.jar",
+                "https://maven.google.com/com/google/protobuf/protobuf-java-util/3.13.0/protobuf-java-util-3.13.0.jar"
+              ],
+              "downloaded_file_path": "com/google/protobuf/protobuf-java-util/3.13.0/protobuf-java-util-3.13.0.jar"
+            }
+          },
+          "io_netty_netty_codec_4_1_72_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "5d8591ca271a1e9c224e8de3873aa9936acb581ee0db514e7dc18523df36d16c",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-codec/4.1.72.Final/netty-codec-4.1.72.Final.jar",
+                "https://maven.google.com/io/netty/netty-codec/4.1.72.Final/netty-codec-4.1.72.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-codec/4.1.72.Final/netty-codec-4.1.72.Final.jar"
+            }
+          },
+          "com_google_protobuf_protobuf_java_3_13_0": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "97d5b2758408690c0dc276238707492a0b6a4d71206311b6c442cdc26c5973ff",
+              "urls": [
+                "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.13.0/protobuf-java-3.13.0.jar",
+                "https://maven.google.com/com/google/protobuf/protobuf-java/3.13.0/protobuf-java-3.13.0.jar"
+              ],
+              "downloaded_file_path": "com/google/protobuf/protobuf-java/3.13.0/protobuf-java-3.13.0.jar"
+            }
+          },
+          "io_netty_netty_tcnative_classes_2_0_46_Final": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "d3ec888dcc4ac7915bf88b417c5e04fd354f4311032a748a6882df09347eed9a",
+              "urls": [
+                "https://repo1.maven.org/maven2/io/netty/netty-tcnative-classes/2.0.46.Final/netty-tcnative-classes-2.0.46.Final.jar",
+                "https://maven.google.com/io/netty/netty-tcnative-classes/2.0.46.Final/netty-tcnative-classes-2.0.46.Final.jar"
+              ],
+              "downloaded_file_path": "io/netty/netty-tcnative-classes/2.0.46.Final/netty-tcnative-classes-2.0.46.Final.jar"
+            }
+          },
+          "software_amazon_awssdk_sdk_core_2_17_183": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_file",
+            "attributes": {
+              "sha256": "677e9cc90fdd82c1f40f97b99cb115b13ad6c3f58beeeab1c061af6954d64c77",
+              "urls": [
+                "https://repo1.maven.org/maven2/software/amazon/awssdk/sdk-core/2.17.183/sdk-core-2.17.183.jar",
+                "https://maven.google.com/software/amazon/awssdk/sdk-core/2.17.183/sdk-core-2.17.183.jar"
+              ],
+              "downloaded_file_path": "software/amazon/awssdk/sdk-core/2.17.183/sdk-core-2.17.183.jar"
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "rules_jvm_external~",
+            "bazel_tools",
+            "bazel_tools"
+          ],
+          [
+            "rules_jvm_external~",
+            "rules_jvm_external",
+            "rules_jvm_external~"
+          ]
+        ]
+      }
+    },
+    "@@rules_jvm_external~//:non-module-deps.bzl%non_module_deps": {
+      "general": {
+        "bzlTransitiveDigest": "l6SlNloqPvd60dcuPdWiJNi3g3jfK76fcZc0i/Yr0dQ=",
+        "usagesDigest": "pX61d12AFioOtqChQDmxvlNGDYT69e5MrKT2E/S6TeQ=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "io_bazel_rules_kotlin": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "sha256": "946747acdbeae799b085d12b240ec346f775ac65236dfcf18aa0cd7300f6de78",
+              "urls": [
+                "https://github.com/bazelbuild/rules_kotlin/releases/download/v1.7.0-RC-2/rules_kotlin_release.tgz"
+              ]
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "rules_jvm_external~",
+            "bazel_tools",
+            "bazel_tools"
+          ]
+        ]
+      }
+    },
+    "@@rules_python~//python/extensions:pip.bzl%pip": {
+      "general": {
+        "bzlTransitiveDigest": "ED3oUrLQz/MTptq8JOZ03sjD7HZ3naUeFS3XFpxz4tg=",
+        "usagesDigest": "MChlcSw99EuW3K7OOoMcXQIdcJnEh6YmfyjJm+9mxIg=",
+        "recordedFileInputs": {
+          "@@other_module~//requirements_lock_3_11.txt": "a7d0061366569043d5efcf80e34a32c732679367cb3c831c4cdc606adc36d314",
+          "@@rules_python~//python/private/pypi/whl_installer/platform.py": "b944b908b25a2f97d6d9f491504ad5d2507402d7e37c802ee878783f87f2aa11",
+          "@@//requirements_lock_3_10.txt": "5e7083982a7e60f34998579a0ae83b520d46ab8f2552cc51337217f024e6def5",
+          "@@rules_python~~internal_deps~pypi__packaging//BUILD.bazel": "8d36246aeefaab4b26fb9c1175cfaf13df5b6f1587e6753f1e78b132bad74795",
+          "@@//whl_mods/appended_build_content.BUILD": "87745b00382c66e5efbd7cb44a08fc3edbf7fd5099cf593f87599188f1557a9e",
+          "@@//requirements_lock_3_9.txt": "6a4990586366467d1e7d56d9f2ec9bafdd7e17fb29dc959aa5a6b0395c22eac7",
+          "@@rules_python~~internal_deps~pypi__packaging//packaging-24.0.dist-info/RECORD": "be1aea790359b4c2c9ea83d153c1a57c407742a35b95ee36d00723509f5ed5dd",
+          "@@//requirements_windows_3_10.txt": "c79f04bfaca147b8330275911a3328b81fc80828b9050a6bebdb15477627dabc",
+          "@@rules_python~//BUILD.bazel": "c421a2c2f3f428d2685a16eb9cc3fb8662605aba4ef151a87a356678bb7e866d",
+          "@@rules_python~~python~python_3_9_host//BUILD.bazel": "cf97d5763b728ce5ba8fdc3243350b967658ba4e3879734504aee002cec0d2b3",
+          "@@rules_python~//python/private/pypi/requirements_parser/resolve_target_platforms.py": "42bf51980528302373529bcdfddb8014e485182d6bc9d2f7d3bbe1f11d8d923d"
+        },
+        "recordedDirentsInputs": {},
+        "envVariables": {
+          "PIP_INDEX_URL": null,
+          "RULES_PYTHON_REPO_DEBUG": null,
+          "RULES_PYTHON_REPO_DEBUG_VERBOSITY": null
+        },
+        "generatedRepoSpecs": {
+          "pip_39_zipp_sdist_0145e43d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "zipp-3.20.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "zipp==3.20.0 ;python_version < '3.10'",
+              "sha256": "0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31",
+              "urls": [
+                "https://files.pythonhosted.org/packages/0e/af/9f2de5bd32549a1b705af7a7c054af3878816a1267cb389c03cc4f342a51/zipp-3.20.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_wrapt_cp39_cp39_musllinux_1_1_aarch64_b9b7a708": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3",
+              "urls": [
+                "https://files.pythonhosted.org/packages/e0/20/9716fb522d17a726364c4d032c8806ffe312268773dd46a394436b2787cc/wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl"
+              ]
+            }
+          },
+          "pip_39_snowballstemmer_py2_none_any_c8e1716e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "snowballstemmer-2.2.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "snowballstemmer==2.2.0",
+              "sha256": "c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_astroid_py3_none_any_10e0ad5f": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "astroid-2.12.13-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "astroid==2.12.13",
+              "sha256": "10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b1/61/42e075b7d29ed4d452d91cbaaca142710d50d04e68eb7161ce5807a00a30/astroid-2.12.13-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_sphinx": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "sphinx==7.2.6     --hash=sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560     --hash=sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"
+            }
+          },
+          "pip_310_docutils": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "docutils==0.20.1     --hash=sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6     --hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"
+            }
+          },
+          "pip_39_tomlkit_py3_none_any_07de26b0": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "tomlkit-0.11.6-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "tomlkit==0.11.6",
+              "sha256": "07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/2b/df/971fa5db3250bb022105d17f340339370f73d502e65e687a94ca1a4c4b1f/tomlkit-0.11.6-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_sphinx_sdist_9a5160e1": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinx-7.2.6.tar.gz",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinx==7.2.6",
+              "sha256": "9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5",
+              "urls": [
+                "https://files.pythonhosted.org/packages/73/8e/6e51da4b26665b4b92b1944ea18b2d9c825e753e19180cc5bdc818d0ed3b/sphinx-7.2.6.tar.gz"
+              ]
+            }
+          },
+          "pip_39_s3cmd_sdist_966b0a49": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "s3cmd-2.1.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "s3cmd==2.1.0",
+              "sha256": "966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c7/eb/5143fe1884af2303cb7b23f453e5c9f337af46c2281581fc40ab5322dee4/s3cmd-2.1.0.tar.gz"
+              ]
+            }
+          },
+          "pip_310_sphinxcontrib_serializinghtml": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "sphinxcontrib-serializinghtml==1.1.9     --hash=sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54     --hash=sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"
+            }
+          },
+          "pip_39_wrapt_cp39_cp39_manylinux_2_5_x86_64_40e7bc81": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/e0/6a/3c660fa34c8106aa9719f2a6636c1c3ea7afd5931ae665eb197fdf4def84/wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_pygments_py3_none_any_13fc09fa": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "Pygments-2.16.1-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pygments==2.16.1",
+              "sha256": "13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692",
+              "urls": [
+                "https://files.pythonhosted.org/packages/43/88/29adf0b44ba6ac85045e63734ae0997d3c58d8b1a91c914d240828d0d73d/Pygments-2.16.1-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_idna": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "idna==2.10     --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6     --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
+            }
+          },
+          "pip_39_lazy_object_proxy_sdist_78247b6d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "lazy-object-proxy-1.10.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "lazy-object-proxy==1.10.0",
+              "sha256": "78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69",
+              "urls": [
+                "https://files.pythonhosted.org/packages/2c/f0/f02e2d150d581a294efded4020094a371bbab42423fe78625ac18854d89b/lazy-object-proxy-1.10.0.tar.gz"
+              ]
+            }
+          },
+          "pip_310_astroid": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "astroid==2.13.5     --hash=sha256:6891f444625b6edb2ac798829b689e95297e100ddf89dbed5a8c610e34901501     --hash=sha256:df164d5ac811b9f44105a72b8f9d5edfb7b5b2d7e979b04ea377a77b3229114a"
+            }
+          },
+          "pip_39_websockets_sdist_88fc51d9": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d8/3b/2ed38e52eed4cf277f9df5f0463a99199a04d9e29c9e227cfafa57bd3993/websockets-11.0.3.tar.gz"
+              ]
+            }
+          },
+          "pip_39_pyyaml_cp39_cp39_macosx_10_9_x86_64_9eb6caa9": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/57/c5/5d09b66b41d549914802f482a2118d925d876dc2a35b2d127694c1345c34/PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_markupsafe_cp39_cp39_manylinux_2_17_x86_64_05fb2117": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e",
+              "urls": [
+                "https://files.pythonhosted.org/packages/de/63/cb7e71984e9159ec5f45b5e81e896c8bdd0e45fe3fc6ce02ab497f0d790e/MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_websockets_py3_none_any_6681ba9e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/47/96/9d5749106ff57629b54360664ae7eb9afd8302fad1680ead385383e33746/websockets-11.0.3-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_markupsafe_cp39_cp39_macosx_10_9_universal2_8023faf4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198",
+              "urls": [
+                "https://files.pythonhosted.org/packages/6a/86/654dc431513cd4417dfcead8102f22bece2d6abf2f584f0e1cc1524f7b94/MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl"
+              ]
+            }
+          },
+          "pip_39_urllib3_sdist_f8ecc1bb": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "urllib3-1.26.18.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "urllib3==1.26.18",
+              "sha256": "f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0",
+              "urls": [
+                "https://files.pythonhosted.org/packages/0c/39/64487bf07df2ed854cc06078c27c0d0abc59bd27b32232876e403c333a08/urllib3-1.26.18.tar.gz"
+              ]
+            }
+          },
+          "pip_39_platformdirs_py3_none_any_1a89a123": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "platformdirs-2.6.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "platformdirs==2.6.0",
+              "sha256": "1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca",
+              "urls": [
+                "https://files.pythonhosted.org/packages/87/69/cd019a9473bcdfb38983e2d550ccb239264fc4c2fc32c42ac1b1cc2506b6/platformdirs-2.6.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_applehelp_sdist_39fdc8d7": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_applehelp-1.0.7.tar.gz",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-applehelp==1.0.7",
+              "sha256": "39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa",
+              "urls": [
+                "https://files.pythonhosted.org/packages/1c/5a/fce19be5d4db26edc853a0c34832b39db7b769b7689da027529767b0aa98/sphinxcontrib_applehelp-1.0.7.tar.gz"
+              ]
+            }
+          },
+          "pip_39_pyyaml_cp39_cp39_win_amd64_510c9dee": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1-cp39-cp39-win_amd64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486",
+              "urls": [
+                "https://files.pythonhosted.org/packages/84/4d/82704d1ab9290b03da94e6425f5e87396b999fd7eb8e08f3a92c158402bf/PyYAML-6.0.1-cp39-cp39-win_amd64.whl"
+              ]
+            }
+          },
+          "pip_310_requests": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "annotation": "@@rules_python~~pip~whl_mods_hub//:requests.json",
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "requests==2.25.1     --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804     --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e",
+              "whl_patches": {
+                "@@//patches:empty.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}",
+                "@@//patches:requests_metadata.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}",
+                "@@//patches:requests_record.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}"
+              }
+            }
+          },
+          "pip_39_pyyaml_cp39_cp39_manylinux_2_17_aarch64_5773183b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ac/6c/967d91a8edf98d2b2b01d149bd9e51b8f9fb527c98d80ebb60c6b21d60c4/PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "pip_39_typing_extensions_py3_none_any_04e5ca03": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "typing_extensions-4.12.2-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "typing-extensions==4.12.2 ;python_version < '3.10'",
+              "sha256": "04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_snowballstemmer": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "snowballstemmer==2.2.0     --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1     --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"
+            }
+          },
+          "pip_310_sphinxcontrib_devhelp": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "sphinxcontrib-devhelp==1.0.5     --hash=sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212     --hash=sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"
+            }
+          },
+          "pip_39_yamllint_py2_none_any_89bb5b5a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "yamllint-1.28.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "yamllint==1.28.0",
+              "sha256": "89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2",
+              "urls": [
+                "https://files.pythonhosted.org/packages/40/f9/882281af7c40a99bfa5b14585071c5aa13f48961582ebe067ae38221d0d9/yamllint-1.28.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_jinja2_sdist_4a3aee7a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "jinja2-3.1.4.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "jinja2==3.1.4",
+              "sha256": "4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz"
+              ]
+            }
+          },
+          "pip_39_websockets_cp39_cp39_musllinux_1_1_aarch64_1fdf26fa": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c4/f5/15998b164c183af0513bba744b51ecb08d396ff86c0db3b55d62624d1f15/websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_qthelp_sdist_62b9d1a1": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_qthelp-1.0.6.tar.gz",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-qthelp==1.0.6",
+              "sha256": "62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/4f/a2/53129fc967ac8402d5e4e83e23c959c3f7a07362ec154bdb2e197d8cc270/sphinxcontrib_qthelp-1.0.6.tar.gz"
+              ]
+            }
+          },
+          "pip_39_setuptools_sdist_a7620757": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "setuptools-65.6.3.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "setuptools==65.6.3",
+              "sha256": "a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b6/21/cb9a8d0b2c8597c83fce8e9c02884bce3d4951e41e807fc35791c6b23d9a/setuptools-65.6.3.tar.gz"
+              ]
+            }
+          },
+          "pip_310_alabaster": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "alabaster==0.7.13     --hash=sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3     --hash=sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"
+            }
+          },
+          "pip_310_python_magic": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "python-magic==0.4.27     --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b     --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"
+            }
+          },
+          "pip_39_mccabe_sdist_348e0240": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "mccabe-0.7.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "mccabe==0.7.0",
+              "sha256": "348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
+              "urls": [
+                "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_chardet_sdist_0d6f53a1": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "chardet-4.0.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "chardet==4.0.0",
+              "sha256": "0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ee/2d/9cdc2b527e127b4c9db64b86647d567985940ac3698eeabc7ffaccb4ea61/chardet-4.0.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_serializinghtml_sdist_0c64ff89": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_serializinghtml-1.1.9.tar.gz",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-serializinghtml==1.1.9",
+              "sha256": "0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54",
+              "urls": [
+                "https://files.pythonhosted.org/packages/5c/41/df4cd017e8234ded544228f60f74fac1fe1c75bdb1e87b33a83c91a10530/sphinxcontrib_serializinghtml-1.1.9.tar.gz"
+              ]
+            }
+          },
+          "pip_39_docutils_sdist_f08a4e27": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "docutils-0.20.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "docutils==0.20.1",
+              "sha256": "f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/1f/53/a5da4f2c5739cf66290fac1431ee52aff6851c7c8ffd8264f13affd7bcdd/docutils-0.20.1.tar.gz"
+              ]
+            }
+          },
+          "pip_310_tabulate": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "tabulate==0.9.0     --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c     --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"
+            }
+          },
+          "pip_39_packaging_py3_none_any_8c491190": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "packaging-23.2-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "packaging==23.2",
+              "sha256": "8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_lazy_object_proxy": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "lazy-object-proxy==1.9.0     --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382     --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82     --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9     --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494     --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46     --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30     --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63     --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4     --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae     --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be     --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701     --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd     --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006     --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a     --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586     --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8     --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821     --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07     --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b     --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171     --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b     --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2     --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7     --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4     --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8     --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e     --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f     --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda     --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4     --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e     --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671     --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11     --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455     --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734     --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb     --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"
+            }
+          },
+          "pip_39_tomli_sdist_de526c12": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "tomli-2.0.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "tomli==2.0.1 ;python_version < '3.11'",
+              "sha256": "de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz"
+              ]
+            }
+          },
+          "pip_310_sphinxcontrib_htmlhelp": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "sphinxcontrib-htmlhelp==2.0.4     --hash=sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a     --hash=sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"
+            }
+          },
+          "pip_310_pylint": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "pylint==2.15.10     --hash=sha256:9df0d07e8948a1c3ffa3b6e2d7e6e63d9fb457c5da5b961ed63106594780cc7e     --hash=sha256:b3dc5ef7d33858f297ac0d06cc73862f01e4f2e74025ec3eff347ce0bc60baf5"
+            }
+          },
+          "pip_39_wheel_sdist_cd1196f3": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "annotation": "@@rules_python~~pip~whl_mods_hub//:wheel.json",
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wheel-0.40.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wheel==0.40.0",
+              "sha256": "cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873",
+              "urls": [
+                "https://files.pythonhosted.org/packages/fc/ef/0335f7217dd1e8096a9e8383e1d472aa14717878ffe07c4772e68b6e8735/wheel-0.40.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_pygments_sdist_1daff049": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "Pygments-2.16.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pygments==2.16.1",
+              "sha256": "1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d6/f7/4d461ddf9c2bcd6a4d7b2b139267ca32a69439387cc1f02a924ff8883825/Pygments-2.16.1.tar.gz"
+              ]
+            }
+          },
+          "pip_39_idna_py2_none_any_b97d804b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "idna-2.10-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "idna==2.10",
+              "sha256": "b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0",
+              "urls": [
+                "https://files.pythonhosted.org/packages/a2/38/928ddce2273eaa564f6f50de919327bf3a00f091b5baba8dfa9460f3a8a8/idna-2.10-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_pylint_py3_none_any_349c8cd3": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "pylint-2.15.9-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pylint==2.15.9",
+              "sha256": "349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb",
+              "urls": [
+                "https://files.pythonhosted.org/packages/7d/df/0e50d5640ed4c6a492cdc6df0c281afee3f85d98209e7ec7b31243838b40/pylint-2.15.9-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_babel": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "babel==2.13.1     --hash=sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900     --hash=sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"
+            }
+          },
+          "pip_39_jinja2_py3_none_any_bc5dd2ab": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "jinja2-3.1.4-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "jinja2==3.1.4",
+              "sha256": "bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_colorama_sdist_08695f5c": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "colorama-0.4.6.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "colorama==0.4.6",
+              "sha256": "08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz"
+              ]
+            }
+          },
+          "other_module_pip_311_absl_py": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@other_module_pip//{name}:{target}",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "other_module_pip_311",
+              "requirement": "absl-py==1.4.0     --hash=sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47     --hash=sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"
+            }
+          },
+          "pip_39_pathspec_py3_none_any_3c95343a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "pathspec-0.10.3-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pathspec==0.10.3",
+              "sha256": "3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/3c/29/c07c3a976dbe37c56e381e058c11e8738cb3a0416fc842a310461f8bb695/pathspec-0.10.3-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_yamllint": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "yamllint==1.32.0     --hash=sha256:d01dde008c65de5b235188ab3110bebc59d18e5c65fc8a58267cd211cd9df34a     --hash=sha256:d97a66e48da820829d96077d76b8dfbe6c6140f106e558dae87e81ac4e6b30b7"
+            }
+          },
+          "pip_39_markupsafe_sdist_af598ed3": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad",
+              "urls": [
+                "https://files.pythonhosted.org/packages/6d/7c/59a3248f411813f8ccba92a55feaac4bf360d29e2ff05ee7d8e1ef2d7dbf/MarkupSafe-2.1.3.tar.gz"
+              ]
+            }
+          },
+          "pip_39_python_magic_py2_none_any_c212960a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "python_magic-0.4.27-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "python-magic==0.4.27",
+              "sha256": "c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3",
+              "urls": [
+                "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_htmlhelp_sdist_6c26a118": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_htmlhelp-2.0.4.tar.gz",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-htmlhelp==2.0.4",
+              "sha256": "6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a",
+              "urls": [
+                "https://files.pythonhosted.org/packages/fd/2d/abf5cd4cc1d5cd9842748b15a28295e4c4a927facfa8a0e173bd3f151bc5/sphinxcontrib_htmlhelp-2.0.4.tar.gz"
+              ]
+            }
+          },
+          "pip_39_lazy_object_proxy_cp39_cp39_musllinux_1_1_x86_64_9a3a87cf": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "lazy-object-proxy==1.10.0",
+              "sha256": "9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658",
+              "urls": [
+                "https://files.pythonhosted.org/packages/8e/ae/3e15cffacbdb64ac49930cdbc23cb0c67e1bb9e8a8ca7765fd8a8d2510c3/lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_typing_extensions_sdist_1a7ead55": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "typing_extensions-4.12.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "typing-extensions==4.12.2 ;python_version < '3.10'",
+              "sha256": "1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz"
+              ]
+            }
+          },
+          "pip_39_tabulate_py3_none_any_024ca478": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "tabulate-0.9.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "tabulate==0.9.0",
+              "sha256": "024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f",
+              "urls": [
+                "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "other_module_pip": {
+            "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl",
+            "ruleClassName": "hub_repository",
+            "attributes": {
+              "repo_name": "other_module_pip",
+              "whl_map": {
+                "absl_py": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":null,\"repo\":\"other_module_pip_311_absl_py\",\"target_platforms\":null,\"version\":\"3.11\"}]"
+              },
+              "default_version": "3.9",
+              "packages": [],
+              "groups": {}
+            }
+          },
+          "pip_39_markupsafe_cp39_cp39_manylinux_2_17_aarch64_9dcdfd0e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58",
+              "urls": [
+                "https://files.pythonhosted.org/packages/68/8d/c33c43c499c19f4b51181e196c9a497010908fc22c5de33551e298aa6a21/MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "pip_39_wrapt_cp39_cp39_win_amd64_dee60e1d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1-cp39-cp39-win_amd64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb",
+              "urls": [
+                "https://files.pythonhosted.org/packages/5b/02/5ac7ea3b6722c84a2882d349ac581a9711b4047fe7a58475903832caa295/wrapt-1.14.1-cp39-cp39-win_amd64.whl"
+              ]
+            }
+          },
+          "pip_39_lazy_object_proxy_cp39_cp39_musllinux_1_1_aarch64_21713819": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "lazy-object-proxy==1.10.0",
+              "sha256": "217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d4/f8/d2d0d5caadf41c2d1fc9044dfc0e10d2831fb4ab6a077e68d25ea5bbff3b/lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl"
+              ]
+            }
+          },
+          "pip_310_pylint_print": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "pylint-print==1.0.1     --hash=sha256:30aa207e9718ebf4ceb47fb87012092e6d8743aab932aa07aa14a73e750ad3d0     --hash=sha256:a2b2599e7887b93e551db2624c523c1e6e9e58c3be8416cd98d41e4427e2669b"
+            }
+          },
+          "pip_39_tomlkit_sdist_71b952e5": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "tomlkit-0.11.6.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "tomlkit==0.11.6",
+              "sha256": "71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ff/04/58b4c11430ed4b7b8f1723a5e4f20929d59361e9b17f0872d69681fd8ffd/tomlkit-0.11.6.tar.gz"
+              ]
+            }
+          },
+          "pip_39_sphinx_py3_none_any_1e09160a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinx-7.2.6-py3-none-any.whl",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinx==7.2.6",
+              "sha256": "1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b2/b6/8ed35256aa530a9d3da15d20bdc0ba888d5364441bb50a5a83ee7827affe/sphinx-7.2.6-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_pylint_sdist_18783cca": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "pylint-2.15.9.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pylint==2.15.9",
+              "sha256": "18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4",
+              "urls": [
+                "https://files.pythonhosted.org/packages/68/3a/1e61444eb8276ad962a7f300b6920b7ad391f4fbe551d34443f093a18899/pylint-2.15.9.tar.gz"
+              ]
+            }
+          },
+          "pip_39_pathspec_sdist_56200de4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "pathspec-0.10.3.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pathspec==0.10.3",
+              "sha256": "56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/32/1a/6baf904503c3e943cae9605c9c88a43b964dea5b59785cf956091b341b08/pathspec-0.10.3.tar.gz"
+              ]
+            }
+          },
+          "pip_39_alabaster_py3_none_any_1ee19aca": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "alabaster-0.7.13-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "alabaster==0.7.13",
+              "sha256": "1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3",
+              "urls": [
+                "https://files.pythonhosted.org/packages/64/88/c7083fc61120ab661c5d0b82cb77079fc1429d3f913a456c1c82cf4658f7/alabaster-0.7.13-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_wrapt_cp39_cp39_musllinux_1_1_x86_64_34aa51c4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe",
+              "urls": [
+                "https://files.pythonhosted.org/packages/f9/3c/110e52b9da396a4ef3a0521552a1af9c7875a762361f48678c1ac272fd7e/wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_markupsafe_cp39_cp39_musllinux_1_1_aarch64_ab4a0df4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636",
+              "urls": [
+                "https://files.pythonhosted.org/packages/03/65/3473d2cb84bb2cda08be95b97fc4f53e6bcd701a2d50ba7b7c905e1e9273/MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl"
+              ]
+            }
+          },
+          "pip_39_websockets_cp39_cp39_manylinux_2_17_aarch64_6f1a3f10": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d9/36/5741e62ccf629c8e38cc20f930491f8a33ce7dba972cae93dba3d6f02552/websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "pip_39_s3cmd_py2_none_any_49cd23d5": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "s3cmd-2.1.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "s3cmd==2.1.0",
+              "sha256": "49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa",
+              "urls": [
+                "https://files.pythonhosted.org/packages/26/44/19e08f69b2169003f7307565f19449d997895251c6a6566ce21d5d636435/s3cmd-2.1.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_packaging_sdist_048fb0e9": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "packaging-23.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "packaging==23.2",
+              "sha256": "048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
+              "urls": [
+                "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz"
+              ]
+            }
+          },
+          "pip_39_idna_sdist_b307872f": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "idna-2.10.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "idna==2.10",
+              "sha256": "b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ea/b7/e0e3c1c467636186c39925827be42f16fee389dc404ac29e930e9136be70/idna-2.10.tar.gz"
+              ]
+            }
+          },
+          "pip_39_snowballstemmer_sdist_09b16deb": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "snowballstemmer-2.2.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "snowballstemmer==2.2.0",
+              "sha256": "09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1",
+              "urls": [
+                "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz"
+              ]
+            }
+          },
+          "pip_310_s3cmd": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "s3cmd==2.1.0     --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa     --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03"
+            }
+          },
+          "pip_39_imagesize_py2_none_any_0d8d18d0": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "imagesize-1.4.1-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "imagesize==1.4.1",
+              "sha256": "0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_setuptools_py3_none_any_57f6f22b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "setuptools-65.6.3-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "setuptools==65.6.3",
+              "sha256": "57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ef/e3/29d6e1a07e8d90ace4a522d9689d03e833b67b50d1588e693eec15f26251/setuptools-65.6.3-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_lazy_object_proxy_cp39_cp39_win_amd64_a899b10e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "lazy-object-proxy==1.10.0",
+              "sha256": "a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd",
+              "urls": [
+                "https://files.pythonhosted.org/packages/fe/30/40879041ed6a3364bfa862c4237aa7fe94dcd4affa2175718acbbf4d29b9/lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_devhelp_py3_none_any_fe8009ae": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-devhelp==1.0.5",
+              "sha256": "fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c0/03/010ac733ec7b7f71c1dc88e7115743ee466560d6d85373b56fb9916e4586/sphinxcontrib_devhelp-1.0.5-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_dill_sdist_e5db55f3": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "dill-0.3.6.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "dill==0.3.6",
+              "sha256": "e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373",
+              "urls": [
+                "https://files.pythonhosted.org/packages/7c/e7/364a09134e1062d4d5ff69b853a56cf61c223e0afcc6906b6832bcd51ea8/dill-0.3.6.tar.gz"
+              ]
+            }
+          },
+          "pip_39_colorama_py2_none_any_4f1d9991": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "colorama-0.4.6-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "colorama==0.4.6",
+              "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_chardet_py2_none_any_f864054d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "chardet-4.0.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "chardet==4.0.0",
+              "sha256": "f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5",
+              "urls": [
+                "https://files.pythonhosted.org/packages/19/c7/fa589626997dd07bd87d9269342ccb74b1720384a4d739a1872bd84fbe68/chardet-4.0.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_packaging": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "packaging==23.2     --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5     --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
+            }
+          },
+          "pip_310_colorama": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "colorama==0.4.6     --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44     --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
+            }
+          },
+          "pip_39_lazy_object_proxy_cp39_cp39_manylinux_2_5_x86_64_18dd842b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "lazy-object-proxy==1.10.0",
+              "sha256": "18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ab/be/d0a76dd4404ee68c7dd611c9b48e58b5c70ac5458e4c951b2c8923c24dd9/lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "pip_310_pathspec": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "pathspec==0.11.1     --hash=sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687     --hash=sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"
+            }
+          },
+          "pip_39_markupsafe_cp39_cp39_macosx_10_9_x86_64_6b2b5695": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/62/9b/4908a57acf39d8811836bc6776b309c2e07d63791485589acf0b6d7bc0c6/MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_platformdirs_sdist_b46ffafa": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "platformdirs-2.6.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "platformdirs==2.6.0",
+              "sha256": "b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ec/4c/9af851448e55c57b30a13a72580306e628c3b431d97fdae9e0b8d4fa3685/platformdirs-2.6.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_lazy_object_proxy_cp39_cp39_macosx_10_9_x86_64_366c32fe": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "lazy-object-proxy==1.10.0",
+              "sha256": "366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94",
+              "urls": [
+                "https://files.pythonhosted.org/packages/bc/2f/b9230d00c2eaa629e67cc69f285bf6b5692cb1d0179a1f8764edd451da86/lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_markupsafe_cp39_cp39_win_amd64_3fd4abcb": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba",
+              "urls": [
+                "https://files.pythonhosted.org/packages/a2/b2/624042cb58cc6b3529a6c3a7b7d230766e3ecb768cba118ba7befd18ed6f/MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl"
+              ]
+            }
+          },
+          "pip_39_wheel_py3_none_any_d236b20e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "annotation": "@@rules_python~~pip~whl_mods_hub//:wheel.json",
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wheel-0.40.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wheel==0.40.0",
+              "sha256": "d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247",
+              "urls": [
+                "https://files.pythonhosted.org/packages/61/86/cc8d1ff2ca31a312a25a708c891cf9facbad4eae493b3872638db6785eb5/wheel-0.40.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_certifi_py3_none_any_92d60375": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "certifi-2023.7.22-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "certifi==2023.7.22",
+              "sha256": "92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9",
+              "urls": [
+                "https://files.pythonhosted.org/packages/4c/dd/2234eab22353ffc7d94e8d13177aaa050113286e93e7b40eae01fbf7c3d9/certifi-2023.7.22-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_wrapt_cp39_cp39_macosx_11_0_arm64_988635d1": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7",
+              "urls": [
+                "https://files.pythonhosted.org/packages/bb/70/73c54e24ea69a8b06ae9649e61d5e64f2b4bdfc6f202fc7794abeac1ed20/wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl"
+              ]
+            }
+          },
+          "pip_310_typing_extensions": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "typing-extensions==4.6.3     --hash=sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26     --hash=sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"
+            }
+          },
+          "pip_39_websockets_cp39_cp39_musllinux_1_1_x86_64_97b52894": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311",
+              "urls": [
+                "https://files.pythonhosted.org/packages/72/89/0d150939f2e592ed78c071d69237ac1c872462cc62a750c5f592f3d4ab18/websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "pip_310_isort": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "isort==5.12.0     --hash=sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504     --hash=sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"
+            }
+          },
+          "pip_39_wrapt_cp39_cp39_macosx_10_9_x86_64_3232822c": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d9/ab/3ba5816dd466ffd7242913708771d258569825ab76fd29d7fd85b9361311/wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_pyyaml_cp39_cp39_musllinux_1_1_x86_64_04ac92ad": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5",
+              "urls": [
+                "https://files.pythonhosted.org/packages/40/da/a175a35cf5583580e90ac3e2a3dbca90e43011593ae62ce63f79d7b28d92/PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_jsmath_py2_none_any_2ec2eaeb": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-jsmath==1.0.1",
+              "sha256": "2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_sphinxcontrib_qthelp": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "sphinxcontrib-qthelp==1.0.6     --hash=sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d     --hash=sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"
+            }
+          },
+          "pip_310_wrapt": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "wrapt==1.15.0     --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0     --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420     --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a     --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c     --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079     --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923     --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f     --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1     --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8     --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86     --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0     --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364     --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e     --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c     --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e     --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c     --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727     --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff     --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e     --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29     --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7     --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72     --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475     --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a     --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317     --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2     --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd     --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640     --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98     --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248     --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e     --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d     --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec     --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1     --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e     --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9     --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92     --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb     --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094     --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46     --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29     --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd     --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705     --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8     --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975     --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb     --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e     --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b     --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418     --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019     --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1     --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba     --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6     --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2     --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3     --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7     --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752     --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416     --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f     --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1     --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc     --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145     --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee     --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a     --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7     --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b     --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653     --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0     --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90     --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29     --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6     --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034     --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09     --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559     --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"
+            }
+          },
+          "pip_39_yamllint_sdist_9e3d8ddd": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "yamllint-1.28.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "yamllint==1.28.0",
+              "sha256": "9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c8/82/4cd3ec8f98d821e7cc7ef504add450623d5c86b656faf65e9b0cc46f4be6/yamllint-1.28.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_importlib_metadata_py3_none_any_66f342cc": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "importlib_metadata-8.4.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "importlib-metadata==8.4.0 ;python_version < '3.10'",
+              "sha256": "66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c0/14/362d31bf1076b21e1bcdcb0dc61944822ff263937b804a79231df2774d28/importlib_metadata-8.4.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_python_dateutil_sdist_0123cacc": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "python-dateutil-2.8.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "python-dateutil==2.8.2",
+              "sha256": "0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
+              "urls": [
+                "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz"
+              ]
+            }
+          },
+          "pip": {
+            "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl",
+            "ruleClassName": "hub_repository",
+            "attributes": {
+              "repo_name": "pip",
+              "whl_map": {
+                "six": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"six-1.16.0-py2.py3-none-any.whl\",\"repo\":\"pip_39_six_py2_none_any_8abb2f1d\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"six-1.16.0.tar.gz\",\"repo\":\"pip_39_six_sdist_1e61c374\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_six\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "dill": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"dill-0.3.6-py3-none-any.whl\",\"repo\":\"pip_39_dill_py3_none_any_a07ffd23\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"dill-0.3.6.tar.gz\",\"repo\":\"pip_39_dill_sdist_e5db55f3\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_dill\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "idna": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"idna-2.10-py2.py3-none-any.whl\",\"repo\":\"pip_39_idna_py2_none_any_b97d804b\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"idna-2.10.tar.gz\",\"repo\":\"pip_39_idna_sdist_b307872f\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_idna\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "zipp": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"zipp-3.20.0-py3-none-any.whl\",\"repo\":\"pip_39_zipp_py3_none_any_58da6168\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"zipp-3.20.0.tar.gz\",\"repo\":\"pip_39_zipp_sdist_0145e43d\",\"target_platforms\":null,\"version\":\"3.9\"}]",
+                "babel": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"Babel-2.13.1-py3-none-any.whl\",\"repo\":\"pip_39_babel_py3_none_any_7077a498\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"Babel-2.13.1.tar.gz\",\"repo\":\"pip_39_babel_sdist_33e0952d\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_babel\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "isort": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"isort-5.11.4-py3-none-any.whl\",\"repo\":\"pip_39_isort_py3_none_any_c033fd0e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"isort-5.11.4.tar.gz\",\"repo\":\"pip_39_isort_sdist_6db30c5d\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_isort\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "s3cmd": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"s3cmd-2.1.0-py2.py3-none-any.whl\",\"repo\":\"pip_39_s3cmd_py2_none_any_49cd23d5\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"s3cmd-2.1.0.tar.gz\",\"repo\":\"pip_39_s3cmd_sdist_966b0a49\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_s3cmd\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "tomli": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"tomli-2.0.1-py3-none-any.whl\",\"repo\":\"pip_39_tomli_py3_none_any_939de3e7\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"tomli-2.0.1.tar.gz\",\"repo\":\"pip_39_tomli_sdist_de526c12\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_tomli\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "wheel": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wheel-0.40.0-py3-none-any.whl\",\"repo\":\"pip_39_wheel_py3_none_any_d236b20e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wheel-0.40.0.tar.gz\",\"repo\":\"pip_39_wheel_sdist_cd1196f3\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_wheel\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "wrapt": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl\",\"repo\":\"pip_39_wrapt_cp39_cp39_macosx_10_9_x86_64_3232822c\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl\",\"repo\":\"pip_39_wrapt_cp39_cp39_musllinux_1_1_x86_64_34aa51c4\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"pip_39_wrapt_cp39_cp39_manylinux_2_5_x86_64_40e7bc81\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl\",\"repo\":\"pip_39_wrapt_cp39_cp39_macosx_11_0_arm64_988635d1\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"pip_39_wrapt_cp39_cp39_manylinux_2_17_aarch64_9cca3c2c\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl\",\"repo\":\"pip_39_wrapt_cp39_cp39_musllinux_1_1_aarch64_b9b7a708\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1-cp39-cp39-win_amd64.whl\",\"repo\":\"pip_39_wrapt_cp39_cp39_win_amd64_dee60e1d\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"wrapt-1.14.1.tar.gz\",\"repo\":\"pip_39_wrapt_sdist_380a85cf\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_wrapt\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "jinja2": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"jinja2-3.1.4-py3-none-any.whl\",\"repo\":\"pip_39_jinja2_py3_none_any_bc5dd2ab\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"jinja2-3.1.4.tar.gz\",\"repo\":\"pip_39_jinja2_sdist_4a3aee7a\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_jinja2\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "mccabe": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"mccabe-0.7.0-py2.py3-none-any.whl\",\"repo\":\"pip_39_mccabe_py2_none_any_6c2d30ab\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"mccabe-0.7.0.tar.gz\",\"repo\":\"pip_39_mccabe_sdist_348e0240\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_mccabe\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "pylint": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"pylint-2.15.9-py3-none-any.whl\",\"repo\":\"pip_39_pylint_py3_none_any_349c8cd3\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"pylint-2.15.9.tar.gz\",\"repo\":\"pip_39_pylint_sdist_18783cca\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_pylint\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "pyyaml": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl\",\"repo\":\"pip_39_pyyaml_cp39_cp39_musllinux_1_1_x86_64_04ac92ad\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1-cp39-cp39-win_amd64.whl\",\"repo\":\"pip_39_pyyaml_cp39_cp39_win_amd64_510c9dee\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"pip_39_pyyaml_cp39_cp39_manylinux_2_17_aarch64_5773183b\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl\",\"repo\":\"pip_39_pyyaml_cp39_cp39_macosx_10_9_x86_64_9eb6caa9\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl\",\"repo\":\"pip_39_pyyaml_cp39_cp39_manylinux_2_17_s390x_b786eecb\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"pip_39_pyyaml_cp39_cp39_manylinux_2_17_x86_64_bc1bf292\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl\",\"repo\":\"pip_39_pyyaml_cp39_cp39_macosx_11_0_arm64_c8098ddc\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"PyYAML-6.0.1.tar.gz\",\"repo\":\"pip_39_pyyaml_sdist_bfdf460b\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_pyyaml\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "sphinx": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinx-7.2.6-py3-none-any.whl\",\"repo\":\"pip_39_sphinx_py3_none_any_1e09160a\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinx-7.2.6.tar.gz\",\"repo\":\"pip_39_sphinx_sdist_9a5160e1\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_sphinx\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "astroid": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"astroid-2.12.13-py3-none-any.whl\",\"repo\":\"pip_39_astroid_py3_none_any_10e0ad5f\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"astroid-2.12.13.tar.gz\",\"repo\":\"pip_39_astroid_sdist_1493fe8b\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_astroid\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "certifi": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"certifi-2023.7.22-py3-none-any.whl\",\"repo\":\"pip_39_certifi_py3_none_any_92d60375\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"certifi-2023.7.22.tar.gz\",\"repo\":\"pip_39_certifi_sdist_539cc1d1\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_certifi\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "chardet": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"chardet-4.0.0-py2.py3-none-any.whl\",\"repo\":\"pip_39_chardet_py2_none_any_f864054d\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"chardet-4.0.0.tar.gz\",\"repo\":\"pip_39_chardet_sdist_0d6f53a1\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_chardet\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "tomlkit": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"tomlkit-0.11.6-py3-none-any.whl\",\"repo\":\"pip_39_tomlkit_py3_none_any_07de26b0\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"tomlkit-0.11.6.tar.gz\",\"repo\":\"pip_39_tomlkit_sdist_71b952e5\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_tomlkit\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "urllib3": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"urllib3-1.26.18-py2.py3-none-any.whl\",\"repo\":\"pip_39_urllib3_py2_none_any_34b97092\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"urllib3-1.26.18.tar.gz\",\"repo\":\"pip_39_urllib3_sdist_f8ecc1bb\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_urllib3\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "colorama": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"colorama-0.4.6-py2.py3-none-any.whl\",\"repo\":\"pip_39_colorama_py2_none_any_4f1d9991\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"colorama-0.4.6.tar.gz\",\"repo\":\"pip_39_colorama_sdist_08695f5c\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_colorama\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "docutils": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"docutils-0.20.1-py3-none-any.whl\",\"repo\":\"pip_39_docutils_py3_none_any_96f387a2\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"docutils-0.20.1.tar.gz\",\"repo\":\"pip_39_docutils_sdist_f08a4e27\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_docutils\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "pathspec": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"pathspec-0.10.3-py3-none-any.whl\",\"repo\":\"pip_39_pathspec_py3_none_any_3c95343a\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"pathspec-0.10.3.tar.gz\",\"repo\":\"pip_39_pathspec_sdist_56200de4\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_pathspec\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "pygments": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"Pygments-2.16.1-py3-none-any.whl\",\"repo\":\"pip_39_pygments_py3_none_any_13fc09fa\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"Pygments-2.16.1.tar.gz\",\"repo\":\"pip_39_pygments_sdist_1daff049\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_pygments\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "requests": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"requests-2.25.1-py2.py3-none-any.whl\",\"repo\":\"pip_39_requests_py2_none_any_c210084e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"requests-2.25.1.tar.gz\",\"repo\":\"pip_39_requests_sdist_27973dd4\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_requests\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "tabulate": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"tabulate-0.9.0-py3-none-any.whl\",\"repo\":\"pip_39_tabulate_py3_none_any_024ca478\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"tabulate-0.9.0.tar.gz\",\"repo\":\"pip_39_tabulate_sdist_0095b12b\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_tabulate\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "yamllint": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"yamllint-1.28.0-py2.py3-none-any.whl\",\"repo\":\"pip_39_yamllint_py2_none_any_89bb5b5a\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"yamllint-1.28.0.tar.gz\",\"repo\":\"pip_39_yamllint_sdist_9e3d8ddd\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_yamllint\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "alabaster": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"alabaster-0.7.13-py3-none-any.whl\",\"repo\":\"pip_39_alabaster_py3_none_any_1ee19aca\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"alabaster-0.7.13.tar.gz\",\"repo\":\"pip_39_alabaster_sdist_a27a4a08\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_alabaster\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "imagesize": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"imagesize-1.4.1-py2.py3-none-any.whl\",\"repo\":\"pip_39_imagesize_py2_none_any_0d8d18d0\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"imagesize-1.4.1.tar.gz\",\"repo\":\"pip_39_imagesize_sdist_69150444\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_imagesize\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "packaging": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"packaging-23.2-py3-none-any.whl\",\"repo\":\"pip_39_packaging_py3_none_any_8c491190\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"packaging-23.2.tar.gz\",\"repo\":\"pip_39_packaging_sdist_048fb0e9\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_packaging\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "markupsafe": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"pip_39_markupsafe_cp39_cp39_manylinux_2_17_x86_64_05fb2117\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl\",\"repo\":\"pip_39_markupsafe_cp39_cp39_musllinux_1_1_x86_64_0a4e4a1a\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl\",\"repo\":\"pip_39_markupsafe_cp39_cp39_win_amd64_3fd4abcb\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl\",\"repo\":\"pip_39_markupsafe_cp39_cp39_macosx_10_9_x86_64_6b2b5695\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl\",\"repo\":\"pip_39_markupsafe_cp39_cp39_macosx_10_9_universal2_8023faf4\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"pip_39_markupsafe_cp39_cp39_manylinux_2_17_aarch64_9dcdfd0e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl\",\"repo\":\"pip_39_markupsafe_cp39_cp39_musllinux_1_1_aarch64_ab4a0df4\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"MarkupSafe-2.1.3.tar.gz\",\"repo\":\"pip_39_markupsafe_sdist_af598ed3\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_markupsafe\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "setuptools": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"setuptools-65.6.3-py3-none-any.whl\",\"repo\":\"pip_39_setuptools_py3_none_any_57f6f22b\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"setuptools-65.6.3.tar.gz\",\"repo\":\"pip_39_setuptools_sdist_a7620757\",\"target_platforms\":null,\"version\":\"3.9\"}]",
+                "websockets": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_musllinux_1_1_aarch64_1fdf26fa\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_manylinux_2_5_x86_64_279e5de4\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_macosx_11_0_arm64_3580dd9c\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-py3-none-any.whl\",\"repo\":\"pip_39_websockets_py3_none_any_6681ba9e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_manylinux_2_17_aarch64_6f1a3f10\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_macosx_10_9_universal2_777354ee\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_macosx_10_9_x86_64_8c82f119\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_musllinux_1_1_x86_64_97b52894\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3-cp39-cp39-win_amd64.whl\",\"repo\":\"pip_39_websockets_cp39_cp39_win_amd64_c792ea4e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"websockets-11.0.3.tar.gz\",\"repo\":\"pip_39_websockets_sdist_88fc51d9\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_websockets\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "platformdirs": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"platformdirs-2.6.0-py3-none-any.whl\",\"repo\":\"pip_39_platformdirs_py3_none_any_1a89a123\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"platformdirs-2.6.0.tar.gz\",\"repo\":\"pip_39_platformdirs_sdist_b46ffafa\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_platformdirs\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "pylint_print": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"pylint_print-1.0.1-py3-none-any.whl\",\"repo\":\"pip_39_pylint_print_py3_none_any_a2b2599e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"pylint-print-1.0.1.tar.gz\",\"repo\":\"pip_39_pylint_print_sdist_30aa207e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_pylint_print\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "python_magic": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"python_magic-0.4.27-py2.py3-none-any.whl\",\"repo\":\"pip_39_python_magic_py2_none_any_c212960a\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"python-magic-0.4.27.tar.gz\",\"repo\":\"pip_39_python_magic_sdist_c1ba14b0\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_python_magic\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "python_dateutil": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"python_dateutil-2.8.2-py2.py3-none-any.whl\",\"repo\":\"pip_39_python_dateutil_py2_none_any_961d03dc\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"python-dateutil-2.8.2.tar.gz\",\"repo\":\"pip_39_python_dateutil_sdist_0123cacc\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_python_dateutil\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "snowballstemmer": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"snowballstemmer-2.2.0-py2.py3-none-any.whl\",\"repo\":\"pip_39_snowballstemmer_py2_none_any_c8e1716e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"snowballstemmer-2.2.0.tar.gz\",\"repo\":\"pip_39_snowballstemmer_sdist_09b16deb\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_snowballstemmer\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "lazy_object_proxy": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"pip_39_lazy_object_proxy_cp39_cp39_manylinux_2_5_x86_64_18dd842b\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl\",\"repo\":\"pip_39_lazy_object_proxy_cp39_cp39_musllinux_1_1_aarch64_21713819\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"pip_39_lazy_object_proxy_cp39_cp39_manylinux_2_17_aarch64_2297f08f\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl\",\"repo\":\"pip_39_lazy_object_proxy_cp39_cp39_macosx_10_9_x86_64_366c32fe\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl\",\"repo\":\"pip_39_lazy_object_proxy_cp39_cp39_musllinux_1_1_x86_64_9a3a87cf\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl\",\"repo\":\"pip_39_lazy_object_proxy_cp39_cp39_win_amd64_a899b10e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"lazy-object-proxy-1.10.0.tar.gz\",\"repo\":\"pip_39_lazy_object_proxy_sdist_78247b6d\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_lazy_object_proxy\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "typing_extensions": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"typing_extensions-4.12.2-py3-none-any.whl\",\"repo\":\"pip_39_typing_extensions_py3_none_any_04e5ca03\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"typing_extensions-4.12.2.tar.gz\",\"repo\":\"pip_39_typing_extensions_sdist_1a7ead55\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_typing_extensions\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "importlib_metadata": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"importlib_metadata-8.4.0-py3-none-any.whl\",\"repo\":\"pip_39_importlib_metadata_py3_none_any_66f342cc\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"importlib_metadata-8.4.0.tar.gz\",\"repo\":\"pip_39_importlib_metadata_sdist_9a547d3b\",\"target_platforms\":null,\"version\":\"3.9\"}]",
+                "sphinxcontrib_jsmath": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl\",\"repo\":\"pip_39_sphinxcontrib_jsmath_py2_none_any_2ec2eaeb\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib-jsmath-1.0.1.tar.gz\",\"repo\":\"pip_39_sphinxcontrib_jsmath_sdist_a9925e4a\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_sphinxcontrib_jsmath\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "sphinxcontrib_qthelp": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_qthelp-1.0.6-py3-none-any.whl\",\"repo\":\"pip_39_sphinxcontrib_qthelp_py3_none_any_bf76886e\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_qthelp-1.0.6.tar.gz\",\"repo\":\"pip_39_sphinxcontrib_qthelp_sdist_62b9d1a1\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_sphinxcontrib_qthelp\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "sphinxcontrib_devhelp": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_devhelp-1.0.5-py3-none-any.whl\",\"repo\":\"pip_39_sphinxcontrib_devhelp_py3_none_any_fe8009ae\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_devhelp-1.0.5.tar.gz\",\"repo\":\"pip_39_sphinxcontrib_devhelp_sdist_63b41e0d\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_sphinxcontrib_devhelp\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "sphinxcontrib_htmlhelp": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl\",\"repo\":\"pip_39_sphinxcontrib_htmlhelp_py3_none_any_8001661c\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_htmlhelp-2.0.4.tar.gz\",\"repo\":\"pip_39_sphinxcontrib_htmlhelp_sdist_6c26a118\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_sphinxcontrib_htmlhelp\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "sphinxcontrib_applehelp": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_applehelp-1.0.7-py3-none-any.whl\",\"repo\":\"pip_39_sphinxcontrib_applehelp_py3_none_any_094c4d56\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_applehelp-1.0.7.tar.gz\",\"repo\":\"pip_39_sphinxcontrib_applehelp_sdist_39fdc8d7\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_sphinxcontrib_applehelp\",\"target_platforms\":null,\"version\":\"3.10\"}]",
+                "sphinxcontrib_serializinghtml": "[{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl\",\"repo\":\"pip_39_sphinxcontrib_serializinghtml_py3_none_any_9b36e503\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.9\",\"filename\":\"sphinxcontrib_serializinghtml-1.1.9.tar.gz\",\"repo\":\"pip_39_sphinxcontrib_serializinghtml_sdist_0c64ff89\",\"target_platforms\":null,\"version\":\"3.9\"},{\"config_setting\":\"//_config:is_python_3.10\",\"filename\":null,\"repo\":\"pip_310_sphinxcontrib_serializinghtml\",\"target_platforms\":null,\"version\":\"3.10\"}]"
+              },
+              "default_version": "3.9",
+              "packages": [
+                "alabaster",
+                "astroid",
+                "babel",
+                "certifi",
+                "chardet",
+                "colorama",
+                "dill",
+                "docutils",
+                "idna",
+                "imagesize",
+                "importlib_metadata",
+                "isort",
+                "jinja2",
+                "lazy_object_proxy",
+                "markupsafe",
+                "mccabe",
+                "packaging",
+                "pathspec",
+                "platformdirs",
+                "pygments",
+                "pylint",
+                "pylint_print",
+                "python_dateutil",
+                "python_magic",
+                "pyyaml",
+                "requests",
+                "s3cmd",
+                "setuptools",
+                "six",
+                "snowballstemmer",
+                "sphinx",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_jsmath",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_serializinghtml",
+                "tabulate",
+                "tomli",
+                "tomlkit",
+                "typing_extensions",
+                "urllib3",
+                "websockets",
+                "wheel",
+                "wrapt",
+                "yamllint",
+                "zipp"
+              ],
+              "groups": {
+                "sphinx": [
+                  "sphinx",
+                  "sphinxcontrib-qthelp",
+                  "sphinxcontrib-htmlhelp",
+                  "sphinxcontrib-devhelp",
+                  "sphinxcontrib-applehelp",
+                  "sphinxcontrib-serializinghtml"
+                ]
+              }
+            }
+          },
+          "pip_39_pyyaml_cp39_cp39_manylinux_2_17_x86_64_bc1bf292": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c",
+              "urls": [
+                "https://files.pythonhosted.org/packages/7d/39/472f2554a0f1e825bd7c5afc11c817cd7a2f3657460f7159f691fbb37c51/PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_importlib_metadata_sdist_9a547d3b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "importlib_metadata-8.4.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "importlib-metadata==8.4.0 ;python_version < '3.10'",
+              "sha256": "9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c0/bd/fa8ce65b0a7d4b6d143ec23b0f5fd3f7ab80121078c465bc02baeaab22dc/importlib_metadata-8.4.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_tomli_py3_none_any_939de3e7": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "tomli-2.0.1-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "tomli==2.0.1 ;python_version < '3.11'",
+              "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
+              "urls": [
+                "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_websockets_cp39_cp39_win_amd64_c792ea4e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-win_amd64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e",
+              "urls": [
+                "https://files.pythonhosted.org/packages/f4/3f/65dfa50084a06ab0a05f3ca74195c2c17a1c075b8361327d831ccce0a483/websockets-11.0.3-cp39-cp39-win_amd64.whl"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_qthelp_py3_none_any_bf76886e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-qthelp==1.0.6",
+              "sha256": "bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4",
+              "urls": [
+                "https://files.pythonhosted.org/packages/1f/e5/1850f3f118e95581c1e30b57028ac979badee1eb29e70ee72b0241f5a185/sphinxcontrib_qthelp-1.0.6-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_python_dateutil": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "python-dateutil==2.8.2     --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86     --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
+            }
+          },
+          "pip_310_tomli": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "tomli==2.0.1     --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc     --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
+            }
+          },
+          "pip_310_imagesize": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "imagesize==1.4.1     --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b     --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"
+            }
+          },
+          "pip_39_sphinxcontrib_jsmath_sdist_a9925e4a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib-jsmath-1.0.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-jsmath==1.0.1",
+              "sha256": "a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz"
+              ]
+            }
+          },
+          "pip_39_alabaster_sdist_a27a4a08": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "alabaster-0.7.13.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "alabaster==0.7.13",
+              "sha256": "a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2",
+              "urls": [
+                "https://files.pythonhosted.org/packages/94/71/a8ee96d1fd95ca04a0d2e2d9c4081dac4c2d2b12f7ddb899c8cb9bfd1532/alabaster-0.7.13.tar.gz"
+              ]
+            }
+          },
+          "pip_39_pylint_print_py3_none_any_a2b2599e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "pylint_print-1.0.1-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pylint-print==1.0.1",
+              "sha256": "a2b2599e7887b93e551db2624c523c1e6e9e58c3be8416cd98d41e4427e2669b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/8f/a9/6f0687b575d502b4fa770cd52231e23462c548829e5f2e6f43a3d2b9c939/pylint_print-1.0.1-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_six_sdist_1e61c374": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "six-1.16.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "six==1.16.0",
+              "sha256": "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+              "urls": [
+                "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_requests_sdist_27973dd4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "annotation": "@@rules_python~~pip~whl_mods_hub//:requests.json",
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "requests-2.25.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "requests==2.25.1",
+              "sha256": "27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
+              "urls": [
+                "https://files.pythonhosted.org/packages/6b/47/c14abc08432ab22dc18b9892252efaf005ab44066de871e72a38d6af464b/requests-2.25.1.tar.gz"
+              ],
+              "whl_patches": {
+                "@@//patches:empty.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}",
+                "@@//patches:requests_metadata.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}",
+                "@@//patches:requests_record.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}"
+              }
+            }
+          },
+          "pip_310_platformdirs": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "platformdirs==3.5.1     --hash=sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f     --hash=sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"
+            }
+          },
+          "pip_39_six_py2_none_any_8abb2f1d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "six-1.16.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "six==1.16.0",
+              "sha256": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_urllib3_py2_none_any_34b97092": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "urllib3-1.26.18-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "urllib3==1.26.18",
+              "sha256": "34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b0/53/aa91e163dcfd1e5b82d8a890ecf13314e3e149c05270cc644581f77f17fd/urllib3-1.26.18-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_websockets_cp39_cp39_macosx_11_0_arm64_3580dd9c": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016",
+              "urls": [
+                "https://files.pythonhosted.org/packages/a0/1a/3da73e69ebc00649d11ed836541c92c1a2df0b8a8aa641a2c8746e7c2b9c/websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl"
+              ]
+            }
+          },
+          "pip_310_dill": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "dill==0.3.6     --hash=sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0     --hash=sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"
+            }
+          },
+          "pip_39_babel_sdist_33e0952d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "Babel-2.13.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "babel==2.13.1",
+              "sha256": "33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900",
+              "urls": [
+                "https://files.pythonhosted.org/packages/aa/6c/737d2345d86741eeb594381394016b9c74c1253b4cbe274bb1e7b5e2138e/Babel-2.13.1.tar.gz"
+              ]
+            }
+          },
+          "pip_310_sphinxcontrib_jsmath": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "sphinxcontrib-jsmath==1.0.1     --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178     --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
+            }
+          },
+          "pip_39_mccabe_py2_none_any_6c2d30ab": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "mccabe-0.7.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "mccabe==0.7.0",
+              "sha256": "6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e",
+              "urls": [
+                "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_tomlkit": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "tomlkit==0.11.8     --hash=sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171     --hash=sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"
+            }
+          },
+          "pip_310_markupsafe": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "markupsafe==2.1.3     --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e     --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e     --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431     --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686     --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c     --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559     --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc     --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb     --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939     --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c     --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0     --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4     --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9     --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575     --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba     --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d     --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd     --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3     --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00     --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155     --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac     --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52     --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f     --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8     --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b     --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007     --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24     --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea     --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198     --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0     --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee     --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be     --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2     --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1     --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707     --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6     --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c     --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58     --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823     --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779     --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636     --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c     --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad     --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee     --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc     --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2     --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48     --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7     --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e     --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b     --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa     --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5     --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e     --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb     --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9     --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57     --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc     --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc     --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2     --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"
+            }
+          },
+          "pip_39_isort_sdist_6db30c5d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "isort-5.11.4.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "isort==5.11.4",
+              "sha256": "6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/76/46/004e2dd6c312e8bb7cb40a6c01b770956e0ef137857e82d47bd9c829356b/isort-5.11.4.tar.gz"
+              ]
+            }
+          },
+          "pip_39_python_magic_sdist_c1ba14b0": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "python-magic-0.4.27.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "python-magic==0.4.27",
+              "sha256": "c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz"
+              ]
+            }
+          },
+          "pip_39_wrapt_cp39_cp39_manylinux_2_17_aarch64_9cca3c2c": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86",
+              "urls": [
+                "https://files.pythonhosted.org/packages/38/38/5b338163b3b4f1ab718306984678c3d180b85a25d72654ea4c61aa6b0968/wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "pip_39_lazy_object_proxy_cp39_cp39_manylinux_2_17_aarch64_2297f08f": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "lazy-object-proxy==1.10.0",
+              "sha256": "2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/20/44/7d3b51ada1ddf873b136e2fa1d68bf3ee7b406b0bd9eeb97445932e2bfe1/lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "pip_310_mccabe": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "mccabe==0.7.0     --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325     --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
+            }
+          },
+          "pip_39_certifi_sdist_539cc1d1": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "certifi-2023.7.22.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "certifi==2023.7.22",
+              "sha256": "539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082",
+              "urls": [
+                "https://files.pythonhosted.org/packages/98/98/c2ff18671db109c9f10ed27f5ef610ae05b73bd876664139cf95bd1429aa/certifi-2023.7.22.tar.gz"
+              ]
+            }
+          },
+          "pip_39_python_dateutil_py2_none_any_961d03dc": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "python_dateutil-2.8.2-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "python-dateutil==2.8.2",
+              "sha256": "961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9",
+              "urls": [
+                "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_sphinxcontrib_applehelp": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "sphinxcontrib-applehelp==1.0.7     --hash=sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d     --hash=sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"
+            }
+          },
+          "pip_39_pyyaml_sdist_bfdf460b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43",
+              "urls": [
+                "https://files.pythonhosted.org/packages/cd/e5/af35f7ea75cf72f2cd079c95ee16797de7cd71f29ea7c68ae5ce7be1eda0/PyYAML-6.0.1.tar.gz"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_devhelp_sdist_63b41e0d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_devhelp-1.0.5.tar.gz",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-devhelp==1.0.5",
+              "sha256": "63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212",
+              "urls": [
+                "https://files.pythonhosted.org/packages/2e/f2/6425b6db37e7c2254ad661c90a871061a078beaddaf9f15a00ba9c3a1529/sphinxcontrib_devhelp-1.0.5.tar.gz"
+              ]
+            }
+          },
+          "pip_310_pygments": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "pygments==2.16.1     --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692     --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"
+            }
+          },
+          "pip_39_tabulate_sdist_0095b12b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "tabulate-0.9.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "tabulate==0.9.0",
+              "sha256": "0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_serializinghtml_py3_none_any_9b36e503": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-serializinghtml==1.1.9",
+              "sha256": "9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1",
+              "urls": [
+                "https://files.pythonhosted.org/packages/95/d6/2e0bda62b2a808070ac922d21a950aa2cb5e4fcfb87e5ff5f86bc43a2201/sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl"
+              ]
+            }
+          },
+          "whl_mods_hub": {
+            "bzlFile": "@@rules_python~//python/private/pypi:extension.bzl",
+            "ruleClassName": "_whl_mods_repo",
+            "attributes": {
+              "whl_mods": {
+                "requests": "{\"additive_build_content\":\"load(\\\"@bazel_skylib//rules:write_file.bzl\\\", \\\"write_file\\\")\\n\\nwrite_file(\\n    name = \\\"generated_file\\\",\\n    out = \\\"generated_file.txt\\\",\\n    content = [\\\"Hello world from requests\\\"],\\n)\\n\\nfilegroup(\\n    name = \\\"whl_orig\\\",\\n    srcs = glob(\\n        [\\\"*.whl\\\"],\\n        allow_empty = False,\\n        exclude = [\\\"*-patched-*.whl\\\"],\\n    ),\\n)\\n\",\"copy_executables\":{},\"copy_files\":{},\"data\":[\":generated_file\"],\"data_exclude_glob\":[],\"srcs_exclude_glob\":[]}",
+                "wheel": "{\"additive_build_content\":\"load(\\\"@bazel_skylib//rules:write_file.bzl\\\", \\\"write_file\\\")\\nwrite_file(\\n    name = \\\"generated_file\\\",\\n    out = \\\"generated_file.txt\\\",\\n    content = [\\\"Hello world from build content file\\\"],\\n)\\n\",\"copy_executables\":{\"@@//whl_mods:data/copy_executable.py\":\"copied_content/executable.py\"},\"copy_files\":{\"@@//whl_mods:data/copy_file.txt\":\"copied_content/file.txt\"},\"data\":[\":generated_file\"],\"data_exclude_glob\":[\"site-packages/*.dist-info/WHEEL\"],\"srcs_exclude_glob\":[]}"
+              }
+            }
+          },
+          "pip_39_markupsafe_cp39_cp39_musllinux_1_1_x86_64_0a4e4a1a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "markupsafe==2.1.3",
+              "sha256": "0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ab/20/f59423543a8422cb8c69a579ebd0ef2c9dafa70cc8142b7372b5b4073caa/MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_imagesize_sdist_69150444": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "imagesize-1.4.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "imagesize==1.4.1",
+              "sha256": "69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a",
+              "urls": [
+                "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz"
+              ]
+            }
+          },
+          "pip_39_websockets_cp39_cp39_manylinux_2_5_x86_64_279e5de4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd",
+              "urls": [
+                "https://files.pythonhosted.org/packages/a6/9c/2356ecb952fd3992b73f7a897d65e57d784a69b94bb8d8fd5f97531e5c02/websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_applehelp_py3_none_any_094c4d56": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-applehelp==1.0.7",
+              "sha256": "094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c0/0c/261c0949083c0ac635853528bb0070c89e927841d4e533ba0b5563365c06/sphinxcontrib_applehelp-1.0.7-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_zipp_py3_none_any_58da6168": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "zipp-3.20.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "zipp==3.20.0 ;python_version < '3.10'",
+              "sha256": "58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/da/cc/b9958af9f9c86b51f846d8487440af495ecf19b16e426fce1ed0b0796175/zipp-3.20.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_certifi": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "certifi==2023.7.22     --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082     --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"
+            }
+          },
+          "pip_310_pyyaml": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "pyyaml==6.0     --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf     --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293     --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b     --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57     --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b     --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4     --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07     --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba     --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9     --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287     --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513     --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0     --hash=sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782     --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0     --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92     --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f     --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2     --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc     --hash=sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1     --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c     --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86     --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4     --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c     --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34     --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b     --hash=sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d     --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c     --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb     --hash=sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7     --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737     --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3     --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d     --hash=sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358     --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53     --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78     --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803     --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a     --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f     --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174     --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
+            }
+          },
+          "pip_39_requests_py2_none_any_c210084e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "annotation": "@@rules_python~~pip~whl_mods_hub//:requests.json",
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "requests-2.25.1-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "requests==2.25.1",
+              "sha256": "c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e",
+              "urls": [
+                "https://files.pythonhosted.org/packages/29/c1/24814557f1d22c56d50280771a17307e6bf87b70727d975fd6b2ce6b014a/requests-2.25.1-py2.py3-none-any.whl"
+              ],
+              "whl_patches": {
+                "@@//patches:empty.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}",
+                "@@//patches:requests_metadata.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}",
+                "@@//patches:requests_record.patch": "{\"patch_strip\":1,\"whls\":[\"requests-2.25.1-py2.py3-none-any.whl\"]}"
+              }
+            }
+          },
+          "pip_39_docutils_py3_none_any_96f387a2": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "docutils-0.20.1-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "docutils==0.20.1",
+              "sha256": "96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/26/87/f238c0670b94533ac0353a4e2a1a771a0cc73277b88bff23d3ae35a256c1/docutils-0.20.1-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_isort_py3_none_any_c033fd0e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "isort-5.11.4-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "isort==5.11.4",
+              "sha256": "c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/91/3b/a63bafb8141b67c397841b36ad46e7469716af2b2d00cb0be2dfb9667130/isort-5.11.4-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_astroid_sdist_1493fe8b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "astroid-2.12.13.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "astroid==2.12.13",
+              "sha256": "1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7",
+              "urls": [
+                "https://files.pythonhosted.org/packages/61/d0/e7cfca72ec7d6c5e0da725c003db99bb056e9b6c2f4ee6fae1145adf28a6/astroid-2.12.13.tar.gz"
+              ]
+            }
+          },
+          "pip_39_sphinxcontrib_htmlhelp_py3_none_any_8001661c": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl",
+              "group_deps": [
+                "sphinx",
+                "sphinxcontrib_qthelp",
+                "sphinxcontrib_htmlhelp",
+                "sphinxcontrib_devhelp",
+                "sphinxcontrib_applehelp",
+                "sphinxcontrib_serializinghtml"
+              ],
+              "group_name": "sphinx",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "sphinxcontrib-htmlhelp==2.0.4",
+              "sha256": "8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9",
+              "urls": [
+                "https://files.pythonhosted.org/packages/28/7a/958f8e3e6abe8219d0d1f1224886de847ab227b218f4a07b61bc337f64be/sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_websockets_cp39_cp39_macosx_10_9_universal2_777354ee": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c0/21/cb9dfbbea8dc0ad89ced52630e7e61edb425fb9fdc6002f8d0c5dd26b94b/websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl"
+              ]
+            }
+          },
+          "pip_310_chardet": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "chardet==4.0.0     --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa     --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
+            }
+          },
+          "pip_39_pyyaml_cp39_cp39_manylinux_2_17_s390x_b786eecb": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0",
+              "urls": [
+                "https://files.pythonhosted.org/packages/4a/4b/c71ef18ef83c82f99e6da8332910692af78ea32bd1d1d76c9787dfa36aea/PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl"
+              ]
+            }
+          },
+          "pip_39_pylint_print_sdist_30aa207e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "pylint-print-1.0.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pylint-print==1.0.1",
+              "sha256": "30aa207e9718ebf4ceb47fb87012092e6d8743aab932aa07aa14a73e750ad3d0",
+              "urls": [
+                "https://files.pythonhosted.org/packages/60/76/8fd24bfcbd5130b487990c6ec5eab2a053f1ec8f7d33ef6c38fee7e22b70/pylint-print-1.0.1.tar.gz"
+              ]
+            }
+          },
+          "pip_39_websockets_cp39_cp39_macosx_10_9_x86_64_8c82f119": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "websockets==11.0.3",
+              "sha256": "8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd",
+              "urls": [
+                "https://files.pythonhosted.org/packages/8f/f2/8a3eb016be19743c7eb9e67c855df0fdfa5912534ffaf83a05b62667d761/websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl"
+              ]
+            }
+          },
+          "pip_310_urllib3": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "urllib3==1.26.18     --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07     --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"
+            }
+          },
+          "pip_310_six": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "six==1.16.0     --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926     --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
+            }
+          },
+          "pip_39_wrapt_sdist_380a85cf": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "wrapt-1.14.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "wrapt==1.14.1",
+              "sha256": "380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/11/eb/e06e77394d6cf09977d92bff310cb0392930c08a338f99af6066a5a98f92/wrapt-1.14.1.tar.gz"
+              ]
+            }
+          },
+          "pip_39_pyyaml_cp39_cp39_macosx_11_0_arm64_c8098ddc": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "pyyaml==6.0.1",
+              "sha256": "c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859",
+              "urls": [
+                "https://files.pythonhosted.org/packages/0e/88/21b2f16cb2123c1e9375f2c93486e35fdc86e63f02e274f0e99c589ef153/PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl"
+              ]
+            }
+          },
+          "pip_310_wheel": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "annotation": "@@rules_python~~pip~whl_mods_hub//:wheel.json",
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "wheel==0.40.0     --hash=sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873     --hash=sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"
+            }
+          },
+          "pip_39_dill_py3_none_any_a07ffd23": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "dill-0.3.6-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "dill==0.3.6",
+              "sha256": "a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0",
+              "urls": [
+                "https://files.pythonhosted.org/packages/be/e3/a84bf2e561beed15813080d693b4b27573262433fced9c1d1fea59e60553/dill-0.3.6-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_39_babel_py3_none_any_7077a498": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "envsubst": [
+                "PIP_INDEX_URL"
+              ],
+              "experimental_target_platforms": [
+                "cp39_linux_aarch64",
+                "cp39_linux_arm",
+                "cp39_linux_ppc",
+                "cp39_linux_s390x",
+                "cp39_linux_x86_64",
+                "cp39_osx_aarch64",
+                "cp39_osx_x86_64",
+                "cp39_windows_x86_64"
+              ],
+              "filename": "Babel-2.13.1-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python",
+              "repo": "pip_39",
+              "requirement": "babel==2.13.1",
+              "sha256": "7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed",
+              "urls": [
+                "https://files.pythonhosted.org/packages/86/14/5dc2eb02b7cc87b2f95930310a2cc5229198414919a116b564832c747bc1/Babel-2.13.1-py3-none-any.whl"
+              ]
+            }
+          },
+          "pip_310_jinja2": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "jinja2==3.1.4     --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369     --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"
+            }
+          },
+          "pip_310_websockets": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@pip//{name}:{target}",
+              "experimental_target_platforms": [
+                "linux_*",
+                "osx_*",
+                "windows_*",
+                "linux_x86_64",
+                "host"
+              ],
+              "extra_pip_args": [
+                "--extra-index-url",
+                "https://pypi.org/simple/"
+              ],
+              "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python",
+              "repo": "pip_310",
+              "requirement": "websockets==11.0.3     --hash=sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd     --hash=sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f     --hash=sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998     --hash=sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82     --hash=sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788     --hash=sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa     --hash=sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f     --hash=sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4     --hash=sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7     --hash=sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f     --hash=sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd     --hash=sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69     --hash=sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb     --hash=sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b     --hash=sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016     --hash=sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac     --hash=sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4     --hash=sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb     --hash=sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99     --hash=sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e     --hash=sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54     --hash=sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf     --hash=sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007     --hash=sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3     --hash=sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6     --hash=sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86     --hash=sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1     --hash=sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61     --hash=sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11     --hash=sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8     --hash=sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f     --hash=sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931     --hash=sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526     --hash=sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016     --hash=sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae     --hash=sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd     --hash=sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b     --hash=sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311     --hash=sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af     --hash=sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152     --hash=sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288     --hash=sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de     --hash=sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97     --hash=sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d     --hash=sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d     --hash=sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca     --hash=sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0     --hash=sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9     --hash=sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b     --hash=sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e     --hash=sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128     --hash=sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d     --hash=sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c     --hash=sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5     --hash=sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6     --hash=sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b     --hash=sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b     --hash=sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280     --hash=sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c     --hash=sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c     --hash=sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f     --hash=sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20     --hash=sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8     --hash=sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb     --hash=sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602     --hash=sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf     --hash=sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0     --hash=sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74     --hash=sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0     --hash=sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"
+            }
+          }
+        },
+        "moduleExtensionMetadata": {
+          "useAllRepos": "NO",
+          "reproducible": false
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "bazel_features~",
+            "bazel_features_globals",
+            "bazel_features~~version_extension~bazel_features_globals"
+          ],
+          [
+            "bazel_features~",
+            "bazel_features_version",
+            "bazel_features~~version_extension~bazel_features_version"
+          ],
+          [
+            "rules_python~",
+            "bazel_features",
+            "bazel_features~"
+          ],
+          [
+            "rules_python~",
+            "bazel_skylib",
+            "bazel_skylib~"
+          ],
+          [
+            "rules_python~",
+            "bazel_tools",
+            "bazel_tools"
+          ],
+          [
+            "rules_python~",
+            "pypi__build",
+            "rules_python~~internal_deps~pypi__build"
+          ],
+          [
+            "rules_python~",
+            "pypi__click",
+            "rules_python~~internal_deps~pypi__click"
+          ],
+          [
+            "rules_python~",
+            "pypi__colorama",
+            "rules_python~~internal_deps~pypi__colorama"
+          ],
+          [
+            "rules_python~",
+            "pypi__importlib_metadata",
+            "rules_python~~internal_deps~pypi__importlib_metadata"
+          ],
+          [
+            "rules_python~",
+            "pypi__installer",
+            "rules_python~~internal_deps~pypi__installer"
+          ],
+          [
+            "rules_python~",
+            "pypi__more_itertools",
+            "rules_python~~internal_deps~pypi__more_itertools"
+          ],
+          [
+            "rules_python~",
+            "pypi__packaging",
+            "rules_python~~internal_deps~pypi__packaging"
+          ],
+          [
+            "rules_python~",
+            "pypi__pep517",
+            "rules_python~~internal_deps~pypi__pep517"
+          ],
+          [
+            "rules_python~",
+            "pypi__pip",
+            "rules_python~~internal_deps~pypi__pip"
+          ],
+          [
+            "rules_python~",
+            "pypi__pip_tools",
+            "rules_python~~internal_deps~pypi__pip_tools"
+          ],
+          [
+            "rules_python~",
+            "pypi__pyproject_hooks",
+            "rules_python~~internal_deps~pypi__pyproject_hooks"
+          ],
+          [
+            "rules_python~",
+            "pypi__setuptools",
+            "rules_python~~internal_deps~pypi__setuptools"
+          ],
+          [
+            "rules_python~",
+            "pypi__tomli",
+            "rules_python~~internal_deps~pypi__tomli"
+          ],
+          [
+            "rules_python~",
+            "pypi__wheel",
+            "rules_python~~internal_deps~pypi__wheel"
+          ],
+          [
+            "rules_python~",
+            "pypi__zipp",
+            "rules_python~~internal_deps~pypi__zipp"
+          ],
+          [
+            "rules_python~",
+            "pythons_hub",
+            "rules_python~~python~pythons_hub"
+          ],
+          [
+            "rules_python~~python~pythons_hub",
+            "python_3_10_host",
+            "rules_python~~python~python_3_10_host"
+          ],
+          [
+            "rules_python~~python~pythons_hub",
+            "python_3_11_host",
+            "rules_python~~python~python_3_11_host"
+          ],
+          [
+            "rules_python~~python~pythons_hub",
+            "python_3_9_host",
+            "rules_python~~python~python_3_9_host"
+          ]
+        ]
+      }
+    },
+    "@@rules_python~//python/private/pypi:pip.bzl%pip_internal": {
+      "general": {
+        "bzlTransitiveDigest": "vEOIMpxlh8qbHkABunGFRr+IDbabjCM/hUF0V3GGTus=",
+        "usagesDigest": "Y8ihY+R57BAFhalrVLVdJFrpwlbsiKz9JPJ99ljF7HA=",
+        "recordedFileInputs": {
+          "@@rules_python~//tools/publish/requirements.txt": "031e35d03dde03ae6305fe4b3d1f58ad7bdad857379752deede0f93649991b8a",
+          "@@rules_python~//tools/publish/requirements_windows.txt": "15472d5a28e068d31ba9e2dc389459698afaff366e9db06e15890283a3ea252e",
+          "@@rules_python~//tools/publish/requirements_darwin.txt": "61cf602ff33b58c5f42a6cee30112985e9b502209605314e313157f8aad679f9"
+        },
+        "recordedDirentsInputs": {},
+        "envVariables": {
+          "RULES_PYTHON_REPO_DEBUG": null,
+          "RULES_PYTHON_REPO_DEBUG_VERBOSITY": null
+        },
+        "generatedRepoSpecs": {
+          "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_aarch64_3548db28": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cffi==1.15.1",
+              "sha256": "3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
+              "urls": [
+                "https://files.pythonhosted.org/packages/91/bc/b7723c2fe7a22eee71d7edf2102cd43423d5f95ff3932ebaa2f82c7ec8d0/cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_zipp_sdist_a7a22e05": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "zipp-3.11.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "zipp==3.11.0",
+              "sha256": "a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766",
+              "urls": [
+                "https://files.pythonhosted.org/packages/8e/b3/8b16a007184714f71157b1a71bbe632c5d66dd43bc8152b3c799b13881e1/zipp-3.11.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_urllib3_sdist_076907bf": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "urllib3-1.26.14.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "urllib3==1.26.14",
+              "sha256": "076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c5/52/fe421fb7364aa738b3506a2d99e4f3a56e079c0a798e9f4fa5e14c60922f/urllib3-1.26.14.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_ppc64le_91fc98ad": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cffi==1.15.1",
+              "sha256": "91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
+              "urls": [
+                "https://files.pythonhosted.org/packages/5d/4e/4e0bb5579b01fdbfd4388bd1eb9394a989e1336203a4b7f700d887b233c1/cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_requests_py3_none_any_64299f49": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "requests-2.28.2-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "requests==2.28.2",
+              "sha256": "64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d2/f4/274d1dbe96b41cf4e0efb70cbced278ffd61b5c7bb70338b62af94ccb25b/requests-2.28.2-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_certifi_sdist_35824b4c": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "certifi-2022.12.7.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "certifi==2022.12.7",
+              "sha256": "35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
+              "urls": [
+                "https://files.pythonhosted.org/packages/37/f7/2b1b0ec44fdc30a3d31dfebe52226be9ddc40cd6c0f34ffc8923ba423b69/certifi-2022.12.7.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_readme_renderer_py3_none_any_f67a16ca": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "readme_renderer-37.3-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "readme-renderer==37.3",
+              "sha256": "f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343",
+              "urls": [
+                "https://files.pythonhosted.org/packages/97/52/fd8a77d6f0a9ddeb26ed8fb334e01ac546106bf0c5b8e40dc826c5bd160f/readme_renderer-37.3-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cffi_sdist_d400bfb9": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cffi-1.15.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cffi==1.15.1",
+              "sha256": "d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
+              "urls": [
+                "https://files.pythonhosted.org/packages/2b/a8/050ab4f0c3d4c1b8aaa805f70e26e84d0e27004907c5b8ecc1d31815f92a/cffi-1.15.1.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_idna_py3_none_any_82fee1fc": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "idna-3.7-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "idna==3.7",
+              "sha256": "82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0",
+              "urls": [
+                "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_certifi_py3_none_any_c198e21b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "certifi-2024.7.4-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "certifi==2024.7.4",
+              "sha256": "c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90",
+              "urls": [
+                "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_requests_toolbelt_py2_none_any_18565aa5": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "requests_toolbelt-0.10.1-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "requests-toolbelt==0.10.1",
+              "sha256": "18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7",
+              "urls": [
+                "https://files.pythonhosted.org/packages/05/d3/bf87a36bff1cb88fd30a509fd366c70ec30676517ee791b2f77e0e29817a/requests_toolbelt-0.10.1-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_universal2_802fe99c": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.3.2",
+              "sha256": "802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db",
+              "urls": [
+                "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_x86_64_94411f22": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cffi==1.15.1",
+              "sha256": "94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
+              "urls": [
+                "https://files.pythonhosted.org/packages/37/5a/c37631a86be838bdd84cc0259130942bf7e6e32f70f4cab95f479847fb91/cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_1_x86_64_6d0fbe73": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257",
+              "urls": [
+                "https://files.pythonhosted.org/packages/41/5d/33f17e40dbb7441ad51e8a6920e726f68443cdbfb388cb8eff53e4b6ffd4/cryptography-42.0.4-cp39-abi3-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pygments_py3_none_any_fa7bd7bd": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "Pygments-2.14.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pygments==2.14.0",
+              "sha256": "fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717",
+              "urls": [
+                "https://files.pythonhosted.org/packages/0b/42/d9d95cc461f098f204cd20c85642ae40fbff81f74c300341b8d0e0df14e0/Pygments-2.14.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_1_aarch64_3c6048f2": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-musllinux_1_1_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ea/a1/04733ecbe1e77a228c738f4ab321ca050e45284997f3e3a1539461cd4bca/cryptography-42.0.4-cp39-abi3-musllinux_1_1_aarch64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_bleach_py3_none_any_33c16e33": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "bleach-6.0.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "bleach==6.0.0",
+              "sha256": "33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ac/e2/dfcab68c9b2e7800c8f06b85c76e5f978d05b195a958daa9b1dda54a1db6/bleach-6.0.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_aarch64_a1327f28": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52",
+              "urls": [
+                "https://files.pythonhosted.org/packages/44/61/644e21048102cd72a13325fd6443db741746fbf0157e7c5d5c7628afc336/cryptography-42.0.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_keyring_py3_none_any_771ed2a9": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "keyring-23.13.1-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "keyring==23.13.1",
+              "sha256": "771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd",
+              "urls": [
+                "https://files.pythonhosted.org/packages/62/db/0e9a09b2b95986dcd73ac78be6ed2bd73ebe8bac65cba7add5b83eb9d899/keyring-23.13.1-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_jaraco_classes_sdist_89559fa5": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "jaraco.classes-3.2.3.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "jaraco-classes==3.2.3",
+              "sha256": "89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a",
+              "urls": [
+                "https://files.pythonhosted.org/packages/bf/02/a956c9bfd2dfe60b30c065ed8e28df7fcf72b292b861dca97e951c145ef6/jaraco.classes-3.2.3.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_rich_py3_none_any_7c963f0d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "rich-13.2.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "rich==13.2.0",
+              "sha256": "7c963f0d03819221e9ac561e1bc866e3f95a02248c1234daa48954e6d381c003",
+              "urls": [
+                "https://files.pythonhosted.org/packages/0e/cf/a6369a2aee266c2d7604230f083d4bd14b8f69bc69eb25b3da63b9f2f853/rich-13.2.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_x86_64_6ffb03d4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a",
+              "urls": [
+                "https://files.pythonhosted.org/packages/32/c2/4ff3cf950504aa6ccd3db3712f515151536eea0cf6125442015b0532a46d/cryptography-42.0.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_s390x_8c7fe7af": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317",
+              "urls": [
+                "https://files.pythonhosted.org/packages/df/c5/dd3a17a615775d0ffc3e12b0e47833d8b7e0a4871431dad87a3f92382a19/charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_secretstorage_sdist_2403533e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "SecretStorage-3.3.3.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "secretstorage==3.3.3",
+              "sha256": "2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77",
+              "urls": [
+                "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_ppc64le_5995f016": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc",
+              "urls": [
+                "https://files.pythonhosted.org/packages/86/eb/31c9025b4ed7eddd930c5f2ac269efb953de33140608c7539675d74a2081/charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_aarch64_887623fe": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-musllinux_1_2_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929",
+              "urls": [
+                "https://files.pythonhosted.org/packages/da/56/1b2c8aa8e62bfb568022b68d77ebd2bd9afddea37898350fbfe008dcefa7/cryptography-42.0.4-cp39-abi3-musllinux_1_2_aarch64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_more_itertools_sdist_5a6257e4": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "more-itertools-9.0.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "more-itertools==9.0.0",
+              "sha256": "5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab",
+              "urls": [
+                "https://files.pythonhosted.org/packages/13/b3/397aa9668da8b1f0c307bc474608653d46122ae0563d1d32f60e24fa0cbd/more-itertools-9.0.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_importlib_metadata_py3_none_any_7efb448e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "importlib_metadata-6.0.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "importlib-metadata==6.0.0",
+              "sha256": "7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad",
+              "urls": [
+                "https://files.pythonhosted.org/packages/26/a7/9da7d5b23fc98ab3d424ac2c65613d63c1f401efb84ad50f2fa27b2caab4/importlib_metadata-6.0.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_importlib_metadata_sdist_e354bede": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "importlib_metadata-6.0.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "importlib-metadata==6.0.0",
+              "sha256": "e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/90/07/6397ad02d31bddf1841c9ad3ec30a693a3ff208e09c2ef45c9a8a5f85156/importlib_metadata-6.0.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_x86_64_761e8904": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/82/49/ab81421d5aa25bc8535896a017c93204cb4051f2a4e72b1ad8f3b594e072/charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_certifi_py3_none_any_4ad3232f": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "certifi-2022.12.7-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "certifi==2022.12.7",
+              "sha256": "4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18",
+              "urls": [
+                "https://files.pythonhosted.org/packages/71/4c/3db2b8021bd6f2f0ceb0e088d6b2d49147671f25832fb17970e9b583d742/certifi-2022.12.7-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_jeepney_py3_none_any_c0a454ad": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "jeepney-0.8.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "jeepney==0.8.0",
+              "sha256": "c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ae/72/2a1e2290f1ab1e06f71f3d0f1646c9e4634e70e1d37491535e19266e8dc9/jeepney-0.8.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_secretstorage_py3_none_any_f356e662": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "SecretStorage-3.3.3-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "secretstorage==3.3.3",
+              "sha256": "f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99",
+              "urls": [
+                "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_idna_py3_none_any_90b77e79": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "idna-3.4-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "idna==3.4",
+              "sha256": "90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2",
+              "urls": [
+                "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_x86_64_44a64043": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-manylinux_2_28_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764",
+              "urls": [
+                "https://files.pythonhosted.org/packages/7e/45/81f378eb85aab14b229c1032ba3694eff85a3d75b35092c3e71abd2d34f6/cryptography-42.0.4-cp39-abi3-manylinux_2_28_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_urllib3_py2_none_any_75edcdc2": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "urllib3-1.26.14-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "urllib3==1.26.14",
+              "sha256": "75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1",
+              "urls": [
+                "https://files.pythonhosted.org/packages/fe/ca/466766e20b767ddb9b951202542310cba37ea5f2d792dae7589f1741af58/urllib3-1.26.14-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_urllib3_sdist_3e3d753a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "urllib3-1.26.19.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "urllib3==1.26.19",
+              "sha256": "3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c8/93/65e479b023bbc46dab3e092bda6b0005424ea3217d711964ccdede3f9b1b/urllib3-1.26.19.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_py3_none_any_7e189e2e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24",
+              "urls": [
+                "https://files.pythonhosted.org/packages/68/2b/02e9d6a98ddb73fa238d559a9edcc30b247b8dc4ee848b6184c936e99dc0/charset_normalizer-3.0.1-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_twine_sdist_9e102ef5": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "twine-4.0.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "twine==4.0.2",
+              "sha256": "9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b7/1a/a7884359429d801cd63c2c5512ad0a337a509994b0e42d9696d4778d71f6/twine-4.0.2.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pywin32_ctypes_py2_none_any_9dc2d991": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_windows_x86_64"
+              ],
+              "filename": "pywin32_ctypes-0.2.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pywin32-ctypes==0.2.0",
+              "sha256": "9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98",
+              "urls": [
+                "https://files.pythonhosted.org/packages/9e/4b/3ab2720f1fa4b4bc924ef1932b842edf10007e4547ea8157b0b9fc78599a/pywin32_ctypes-0.2.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pkginfo_py3_none_any_4b7a555a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "pkginfo-1.9.6-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pkginfo==1.9.6",
+              "sha256": "4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b3/f2/6e95c86a23a30fa205ea6303a524b20cbae27fbee69216377e3d95266406/pkginfo-1.9.6-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_x86_64_cc4d65ae": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cffi==1.15.1",
+              "sha256": "cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d3/56/3e94aa719ae96eeda8b68b3ec6e347e0a23168c6841dc276ccdcdadc9f32/cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_sdist_831a4b37": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb",
+              "urls": [
+                "https://files.pythonhosted.org/packages/81/d8/214d25515bf6034dce99aba22eeb47443b14c82160114e3d3f33067c6d3b/cryptography-42.0.4.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_sdist_f30c3cb3": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "charset-normalizer-3.3.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.3.2",
+              "sha256": "f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5",
+              "urls": [
+                "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_zipp_sdist_bf1dcf64": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "zipp-3.19.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "zipp==3.19.2",
+              "sha256": "bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d3/20/b48f58857d98dcb78f9e30ed2cfe533025e2e9827bbd36ea0a64cc00cbc1/zipp-3.19.2.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_mdurl_py3_none_any_84008a41": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "mdurl-0.1.2-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "mdurl==0.1.2",
+              "sha256": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_x86_64_ce8613be": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-musllinux_1_2_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0",
+              "urls": [
+                "https://files.pythonhosted.org/packages/a2/8e/dac70232d4231c53448e29aa4b768cf82d891fcfd6e0caa7ace242da8c9b/cryptography-42.0.4-cp39-abi3-musllinux_1_2_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_more_itertools_py3_none_any_250e83d7": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "more_itertools-9.0.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "more-itertools==9.0.0",
+              "sha256": "250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41",
+              "urls": [
+                "https://files.pythonhosted.org/packages/5d/87/1ec3fcc09d2c04b977eabf8a1083222f82eaa2f46d5a4f85f403bf8e7b30/more_itertools-9.0.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_mdurl_sdist_bb413d29": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "mdurl-0.1.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "mdurl==0.1.2",
+              "sha256": "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_keyring_sdist_ba2e15a9": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "keyring-23.13.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "keyring==23.13.1",
+              "sha256": "ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678",
+              "urls": [
+                "https://files.pythonhosted.org/packages/55/fe/282f4c205add8e8bb3a1635cbbac59d6def2e0891b145aa553a0e40dd2d0/keyring-23.13.1.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_rfc3986_sdist_97aacf9d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "rfc3986-2.0.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "rfc3986==2.0.0",
+              "sha256": "97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c",
+              "urls": [
+                "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_11_0_arm64_549a3a73": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.3.2",
+              "sha256": "549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e",
+              "urls": [
+                "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_six_py2_none_any_8abb2f1d": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "six-1.16.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "six==1.16.0",
+              "sha256": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_py3_none_any_3e4d1f65": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "charset_normalizer-3.3.2-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.3.2",
+              "sha256": "3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc",
+              "urls": [
+                "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_aarch64_1df6fcbf": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "cryptography-42.0.4-cp39-abi3-manylinux_2_28_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "cryptography==42.0.4",
+              "sha256": "1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9",
+              "urls": [
+                "https://files.pythonhosted.org/packages/4c/e1/18056b2c0e4ba031ea6b9d660bc2bdf491f7ef64ab7ef1a803a03a8b8d26/cryptography-42.0.4-cp39-abi3-manylinux_2_28_aarch64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_aarch64_14e76c0f": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c0/4d/6b82099e3f25a9ed87431e2f51156c14f3a9ce8fad73880a3856cd95f1d5/charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_readme_renderer_sdist_cd653186": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "readme_renderer-37.3.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "readme-renderer==37.3",
+              "sha256": "cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273",
+              "urls": [
+                "https://files.pythonhosted.org/packages/81/c3/d20152fcd1986117b898f66928938f329d0c91ddc47f081c58e64e0f51dc/readme_renderer-37.3.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_markdown_it_py_py3_none_any_93de681e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "markdown_it_py-2.1.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "markdown-it-py==2.1.0",
+              "sha256": "93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27",
+              "urls": [
+                "https://files.pythonhosted.org/packages/f9/3f/ecd1b708973b9a3e4574b43cffc1ce8eb98696da34f1a1c44a68c3c0d737/markdown_it_py-2.1.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_six_sdist_1e61c374": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "six-1.16.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "six==1.16.0",
+              "sha256": "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+              "urls": [
+                "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_certifi_sdist_5a1e7645": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "certifi-2024.7.4.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "certifi==2024.7.4",
+              "sha256": "5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/c2/02/a95f2b11e207f68bc64d7aae9666fed2e2b3f307748d5123dffb72a1bbea/certifi-2024.7.4.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_twine_py3_none_any_929bc3c2": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "twine-4.0.2-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "twine==4.0.2",
+              "sha256": "929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8",
+              "urls": [
+                "https://files.pythonhosted.org/packages/3a/38/a3f27a9e8ce45523d7d1e28c09e9085b61a98dab15d35ec086f36a44b37c/twine-4.0.2-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_webencodings_sdist_b36a1c24": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "webencodings-0.5.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "webencodings==0.5.1",
+              "sha256": "b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923",
+              "urls": [
+                "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_s390x_4a8fcf28": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b",
+              "urls": [
+                "https://files.pythonhosted.org/packages/80/54/183163f9910936e57a60ee618f4f5cc91c2f8333ee2d4ebc6c50f6c8684d/charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_markdown_it_py_sdist_cf7e59fe": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "markdown-it-py-2.1.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "markdown-it-py==2.1.0",
+              "sha256": "cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da",
+              "urls": [
+                "https://files.pythonhosted.org/packages/33/e9/ac8a93e9eda3891ecdfecf5e01c060bbd2c44d4e3e77efc83b9c7ce9db32/markdown-it-py-2.1.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_urllib3_py2_none_any_37a03444": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "urllib3-1.26.19-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "urllib3==1.26.19",
+              "sha256": "37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ae/6a/99eaaeae8becaa17a29aeb334a18e5d582d873b6f084c11f02581b8d7f7f/urllib3-1.26.19-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_x86_64_79909e27": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d9/7a/60d45c9453212b30eebbf8b5cddbdef330eebddfcf335bce7920c43fb72e/charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_idna_sdist_814f528e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "idna-3.4.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "idna==3.4",
+              "sha256": "814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
+              "urls": [
+                "https://files.pythonhosted.org/packages/8b/e1/43beb3d38dba6cb420cefa297822eac205a277ab43e5ba5d5c46faf96438/idna-3.4.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_jaraco_classes_py3_none_any_2353de32": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "jaraco.classes-3.2.3-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "jaraco-classes==3.2.3",
+              "sha256": "2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158",
+              "urls": [
+                "https://files.pythonhosted.org/packages/60/28/220d3ae0829171c11e50dded4355d17824d60895285631d7eb9dee0ab5e5/jaraco.classes-3.2.3-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pycparser_py2_none_any_8ee45429": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "pycparser-2.21-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pycparser==2.21",
+              "sha256": "8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
+              "urls": [
+                "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_rich_sdist_f1a00cdd": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "rich-13.2.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "rich==13.2.0",
+              "sha256": "f1a00cdd3eebf999a15d85ec498bfe0b1a77efe9b34f645768a54132ef444ac5",
+              "urls": [
+                "https://files.pythonhosted.org/packages/9e/5e/c3dc3ea32e2c14bfe46e48de954dd175bff76bcc549dd300acb9689521ae/rich-13.2.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pywin32_ctypes_sdist_24ffc3b3": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_windows_x86_64"
+              ],
+              "filename": "pywin32-ctypes-0.2.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pywin32-ctypes==0.2.0",
+              "sha256": "24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942",
+              "urls": [
+                "https://files.pythonhosted.org/packages/7a/7d/0dbc4c99379452a819b0fb075a0ffbb98611df6b6d59f54db67367af5bc0/pywin32-ctypes-0.2.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pkginfo_sdist_8fd5896e": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "pkginfo-1.9.6.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pkginfo==1.9.6",
+              "sha256": "8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046",
+              "urls": [
+                "https://files.pythonhosted.org/packages/b4/1c/89b38e431c20d6b2389ed8b3926c2ab72f58944733ba029354c6d9f69129/pkginfo-1.9.6.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pygments_sdist_b3ed06a9": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "Pygments-2.14.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pygments==2.14.0",
+              "sha256": "b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297",
+              "urls": [
+                "https://files.pythonhosted.org/packages/da/6a/c427c06913204e24de28de5300d3f0e809933f376e0b7df95194b2bb3f71/Pygments-2.14.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_zipp_py3_none_any_83a28fcb": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "zipp-3.11.0-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "zipp==3.11.0",
+              "sha256": "83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d8/20/256eb3f3f437c575fb1a2efdce5e801a5ce3162ea8117da96c43e6ee97d8/zipp-3.11.0-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_docutils_py3_none_any_5e1de4d8": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "docutils-0.19-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "docutils==0.19",
+              "sha256": "5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc",
+              "urls": [
+                "https://files.pythonhosted.org/packages/93/69/e391bd51bc08ed9141ecd899a0ddb61ab6465309f1eb470905c0c8868081/docutils-0.19-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_docutils_sdist_33995a67": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "docutils-0.19.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "docutils==0.19",
+              "sha256": "33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6",
+              "urls": [
+                "https://files.pythonhosted.org/packages/6b/5c/330ea8d383eb2ce973df34d1239b3b21e91cd8c865d21ff82902d952f91f/docutils-0.19.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_win_amd64_66394663": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.3.2",
+              "sha256": "663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77",
+              "urls": [
+                "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_idna_sdist_028ff3aa": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "idna-3.7.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "idna==3.7",
+              "sha256": "028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc",
+              "urls": [
+                "https://files.pythonhosted.org/packages/21/ed/f86a79a07470cb07819390452f178b3bef1d375f2ec021ecfc709fc7cf07/idna-3.7.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_jeepney_sdist_5efe48d2": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "jeepney-0.8.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "jeepney==0.8.0",
+              "sha256": "5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806",
+              "urls": [
+                "https://files.pythonhosted.org/packages/d6/f4/154cf374c2daf2020e05c3c6a03c91348d59b23c5366e968feb198306fdf/jeepney-0.8.0.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_zipp_py3_none_any_f091755f": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "zipp-3.19.2-py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "zipp==3.19.2",
+              "sha256": "f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c",
+              "urls": [
+                "https://files.pythonhosted.org/packages/20/38/f5c473fe9b90c8debdd29ea68d5add0289f1936d6f923b6b9cc0b931194c/zipp-3.19.2-py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_requests_toolbelt_sdist_62e09f7f": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "requests-toolbelt-0.10.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "requests-toolbelt==0.10.1",
+              "sha256": "62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d",
+              "urls": [
+                "https://files.pythonhosted.org/packages/0c/4c/07f01c6ac44f7784fa399137fbc8d0cdc1b5d35304e8c0f278ad82105b58/requests-toolbelt-0.10.1.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_ppc64le_0c0a5902": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1",
+              "urls": [
+                "https://files.pythonhosted.org/packages/12/e5/aa09a1c39c3e444dd223d63e2c816c18ed78d035cff954143b2a539bdc9e/charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_rfc3986_py2_none_any_50b1502b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "rfc3986-2.0.0-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "rfc3986==2.0.0",
+              "sha256": "50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd",
+              "urls": [
+                "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_requests_sdist_98b1b278": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "requests-2.28.2.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "requests==2.28.2",
+              "sha256": "98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf",
+              "urls": [
+                "https://files.pythonhosted.org/packages/9d/ee/391076f5937f0a8cdf5e53b701ffc91753e87b07d66bae4a09aa671897bf/requests-2.28.2.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps": {
+            "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl",
+            "ruleClassName": "hub_repository",
+            "attributes": {
+              "repo_name": "rules_python_publish_deps",
+              "whl_map": {
+                "six": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"six-1.16.0-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_six_py2_none_any_8abb2f1d\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"six-1.16.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_six_sdist_1e61c374\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "cffi": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_aarch64_3548db28\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl\",\"repo\":\"rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_ppc64le_91fc98ad\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_x86_64_94411f22\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_x86_64_cc4d65ae\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cffi-1.15.1.tar.gz\",\"repo\":\"rules_python_publish_deps_311_cffi_sdist_d400bfb9\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "idna": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"idna-3.4-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_idna_py3_none_any_90b77e79\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"idna-3.4.tar.gz\",\"repo\":\"rules_python_publish_deps_311_idna_sdist_814f528e\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"idna-3.7-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_idna_py3_none_any_82fee1fc\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"idna-3.7.tar.gz\",\"repo\":\"rules_python_publish_deps_311_idna_sdist_028ff3aa\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"}]",
+                "rich": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"rich-13.2.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_rich_py3_none_any_7c963f0d\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"rich-13.2.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_rich_sdist_f1a00cdd\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "zipp": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"zipp-3.11.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_zipp_py3_none_any_83a28fcb\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"zipp-3.11.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_zipp_sdist_a7a22e05\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"zipp-3.19.2-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_zipp_py3_none_any_f091755f\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"zipp-3.19.2.tar.gz\",\"repo\":\"rules_python_publish_deps_311_zipp_sdist_bf1dcf64\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"}]",
+                "mdurl": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"mdurl-0.1.2-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_mdurl_py3_none_any_84008a41\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"mdurl-0.1.2.tar.gz\",\"repo\":\"rules_python_publish_deps_311_mdurl_sdist_bb413d29\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "twine": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"twine-4.0.2-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_twine_py3_none_any_929bc3c2\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"twine-4.0.2.tar.gz\",\"repo\":\"rules_python_publish_deps_311_twine_sdist_9e102ef5\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "bleach": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"bleach-6.0.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_bleach_py3_none_any_33c16e33\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"bleach-6.0.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_bleach_sdist_1a1a85c1\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "certifi": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"certifi-2022.12.7-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_certifi_py3_none_any_4ad3232f\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"certifi-2022.12.7.tar.gz\",\"repo\":\"rules_python_publish_deps_311_certifi_sdist_35824b4c\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"certifi-2024.7.4-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_certifi_py3_none_any_c198e21b\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"certifi-2024.7.4.tar.gz\",\"repo\":\"rules_python_publish_deps_311_certifi_sdist_5a1e7645\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"}]",
+                "jeepney": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"jeepney-0.8.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_jeepney_py3_none_any_c0a454ad\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"jeepney-0.8.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_jeepney_sdist_5efe48d2\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "keyring": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"keyring-23.13.1-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_keyring_py3_none_any_771ed2a9\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"keyring-23.13.1.tar.gz\",\"repo\":\"rules_python_publish_deps_311_keyring_sdist_ba2e15a9\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "pkginfo": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"pkginfo-1.9.6-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_pkginfo_py3_none_any_4b7a555a\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"pkginfo-1.9.6.tar.gz\",\"repo\":\"rules_python_publish_deps_311_pkginfo_sdist_8fd5896e\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "rfc3986": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"rfc3986-2.0.0-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_rfc3986_py2_none_any_50b1502b\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"rfc3986-2.0.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_rfc3986_sdist_97aacf9d\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "urllib3": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"urllib3-1.26.14-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_urllib3_py2_none_any_75edcdc2\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"urllib3-1.26.14.tar.gz\",\"repo\":\"rules_python_publish_deps_311_urllib3_sdist_076907bf\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"urllib3-1.26.19-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_urllib3_py2_none_any_37a03444\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"urllib3-1.26.19.tar.gz\",\"repo\":\"rules_python_publish_deps_311_urllib3_sdist_3e3d753a\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"}]",
+                "docutils": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"docutils-0.19-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_docutils_py3_none_any_5e1de4d8\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"docutils-0.19.tar.gz\",\"repo\":\"rules_python_publish_deps_311_docutils_sdist_33995a67\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "pygments": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"Pygments-2.14.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_pygments_py3_none_any_fa7bd7bd\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"Pygments-2.14.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_pygments_sdist_b3ed06a9\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "requests": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"requests-2.28.2-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_requests_py3_none_any_64299f49\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"requests-2.28.2.tar.gz\",\"repo\":\"rules_python_publish_deps_311_requests_sdist_98b1b278\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "pycparser": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"pycparser-2.21-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_pycparser_py2_none_any_8ee45429\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"pycparser-2.21.tar.gz\",\"repo\":\"rules_python_publish_deps_311_pycparser_sdist_e644fdec\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "cryptography": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-musllinux_1_2_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_x86_64_ce8613be\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-manylinux_2_28_aarch64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_aarch64_1df6fcbf\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-musllinux_1_1_aarch64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_1_aarch64_3c6048f2\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-manylinux_2_28_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_x86_64_44a64043\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_x86_64_6ffb03d4\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-musllinux_1_2_aarch64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_aarch64_887623fe\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-musllinux_1_1_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_1_x86_64_6d0fbe73\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_aarch64_a1327f28\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"cryptography-42.0.4.tar.gz\",\"repo\":\"rules_python_publish_deps_311_cryptography_sdist_831a4b37\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "webencodings": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"webencodings-0.5.1-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_webencodings_py2_none_any_a0af1213\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"webencodings-0.5.1.tar.gz\",\"repo\":\"rules_python_publish_deps_311_webencodings_sdist_b36a1c24\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "secretstorage": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"SecretStorage-3.3.3-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_secretstorage_py3_none_any_f356e662\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"SecretStorage-3.3.3.tar.gz\",\"repo\":\"rules_python_publish_deps_311_secretstorage_sdist_2403533e\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "jaraco_classes": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"jaraco.classes-3.2.3-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_jaraco_classes_py3_none_any_2353de32\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"jaraco.classes-3.2.3.tar.gz\",\"repo\":\"rules_python_publish_deps_311_jaraco_classes_sdist_89559fa5\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "markdown_it_py": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"markdown_it_py-2.1.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_markdown_it_py_py3_none_any_93de681e\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"markdown-it-py-2.1.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_markdown_it_py_sdist_cf7e59fe\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "more_itertools": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"more_itertools-9.0.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_more_itertools_py3_none_any_250e83d7\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"more-itertools-9.0.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_more_itertools_sdist_5a6257e4\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "readme_renderer": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"readme_renderer-37.3-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_readme_renderer_py3_none_any_f67a16ca\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"readme_renderer-37.3.tar.gz\",\"repo\":\"rules_python_publish_deps_311_readme_renderer_sdist_cd653186\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "requests_toolbelt": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"requests_toolbelt-0.10.1-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_requests_toolbelt_py2_none_any_18565aa5\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"requests-toolbelt-0.10.1.tar.gz\",\"repo\":\"rules_python_publish_deps_311_requests_toolbelt_sdist_62e09f7f\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "charset_normalizer": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_ppc64le_0c0a5902\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_aarch64_14e76c0f\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_s390x_4a8fcf28\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_ppc64le_5995f016\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_aarch64_72966d1b\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_x86_64_761e8904\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_x86_64_79909e27\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_py3_none_any_7e189e2e\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_s390x_8c7fe7af\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset-normalizer-3.0.1.tar.gz\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_sdist_ebea339a\",\"target_platforms\":[\"cp311_linux_aarch64\",\"cp311_linux_arm\",\"cp311_linux_ppc\",\"cp311_linux_s390x\",\"cp311_linux_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.3.2-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_py3_none_any_3e4d1f65\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_11_0_arm64_549a3a73\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_x86_64_573f6eac\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_win_amd64_66394663\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_universal2_802fe99c\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"charset-normalizer-3.3.2.tar.gz\",\"repo\":\"rules_python_publish_deps_311_charset_normalizer_sdist_f30c3cb3\",\"target_platforms\":[\"cp311_osx_aarch64\",\"cp311_osx_x86_64\",\"cp311_windows_x86_64\"],\"version\":\"3.11\"}]",
+                "importlib_metadata": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"importlib_metadata-6.0.0-py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_importlib_metadata_py3_none_any_7efb448e\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"importlib_metadata-6.0.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_importlib_metadata_sdist_e354bede\",\"target_platforms\":null,\"version\":\"3.11\"}]",
+                "pywin32_ctypes": "[{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"pywin32_ctypes-0.2.0-py2.py3-none-any.whl\",\"repo\":\"rules_python_publish_deps_311_pywin32_ctypes_py2_none_any_9dc2d991\",\"target_platforms\":null,\"version\":\"3.11\"},{\"config_setting\":\"//_config:is_python_3.11\",\"filename\":\"pywin32-ctypes-0.2.0.tar.gz\",\"repo\":\"rules_python_publish_deps_311_pywin32_ctypes_sdist_24ffc3b3\",\"target_platforms\":null,\"version\":\"3.11\"}]"
+              },
+              "default_version": "3.9",
+              "packages": [
+                "bleach",
+                "certifi",
+                "charset_normalizer",
+                "docutils",
+                "idna",
+                "importlib_metadata",
+                "jaraco_classes",
+                "keyring",
+                "markdown_it_py",
+                "mdurl",
+                "more_itertools",
+                "pkginfo",
+                "pygments",
+                "readme_renderer",
+                "requests",
+                "requests_toolbelt",
+                "rfc3986",
+                "rich",
+                "six",
+                "twine",
+                "urllib3",
+                "webencodings",
+                "zipp"
+              ],
+              "groups": {}
+            }
+          },
+          "rules_python_publish_deps_311_webencodings_py2_none_any_a0af1213": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "webencodings-0.5.1-py2.py3-none-any.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "webencodings==0.5.1",
+              "sha256": "a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
+              "urls": [
+                "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_sdist_ebea339a": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset-normalizer-3.0.1.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f",
+              "urls": [
+                "https://files.pythonhosted.org/packages/96/d7/1675d9089a1f4677df5eb29c3f8b064aa1e70c1251a0a8a127803158942d/charset-normalizer-3.0.1.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_1_aarch64_72966d1b": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.0.1",
+              "sha256": "72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a",
+              "urls": [
+                "https://files.pythonhosted.org/packages/01/ff/9ee4a44e8c32fe96dfc12daa42f29294608a55eadc88f327939327fb20fb/charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_x86_64_573f6eac": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "charset-normalizer==3.3.2",
+              "sha256": "573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96",
+              "urls": [
+                "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_pycparser_sdist_e644fdec": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64"
+              ],
+              "filename": "pycparser-2.21.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "pycparser==2.21",
+              "sha256": "e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206",
+              "urls": [
+                "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz"
+              ]
+            }
+          },
+          "rules_python_publish_deps_311_bleach_sdist_1a1a85c1": {
+            "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl",
+            "ruleClassName": "whl_library",
+            "attributes": {
+              "dep_template": "@rules_python_publish_deps//{name}:{target}",
+              "experimental_target_platforms": [
+                "cp311_linux_aarch64",
+                "cp311_linux_arm",
+                "cp311_linux_ppc",
+                "cp311_linux_s390x",
+                "cp311_linux_x86_64",
+                "cp311_osx_aarch64",
+                "cp311_osx_x86_64",
+                "cp311_windows_x86_64"
+              ],
+              "filename": "bleach-6.0.0.tar.gz",
+              "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python",
+              "repo": "rules_python_publish_deps_311",
+              "requirement": "bleach==6.0.0",
+              "sha256": "1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414",
+              "urls": [
+                "https://files.pythonhosted.org/packages/7e/e6/d5f220ca638f6a25557a611860482cb6e54b2d97f0332966b1b005742e1f/bleach-6.0.0.tar.gz"
+              ]
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "bazel_features~",
+            "bazel_features_globals",
+            "bazel_features~~version_extension~bazel_features_globals"
+          ],
+          [
+            "bazel_features~",
+            "bazel_features_version",
+            "bazel_features~~version_extension~bazel_features_version"
+          ],
+          [
+            "rules_python~",
+            "bazel_features",
+            "bazel_features~"
+          ],
+          [
+            "rules_python~",
+            "bazel_skylib",
+            "bazel_skylib~"
+          ],
+          [
+            "rules_python~",
+            "bazel_tools",
+            "bazel_tools"
+          ],
+          [
+            "rules_python~",
+            "pypi__build",
+            "rules_python~~internal_deps~pypi__build"
+          ],
+          [
+            "rules_python~",
+            "pypi__click",
+            "rules_python~~internal_deps~pypi__click"
+          ],
+          [
+            "rules_python~",
+            "pypi__colorama",
+            "rules_python~~internal_deps~pypi__colorama"
+          ],
+          [
+            "rules_python~",
+            "pypi__importlib_metadata",
+            "rules_python~~internal_deps~pypi__importlib_metadata"
+          ],
+          [
+            "rules_python~",
+            "pypi__installer",
+            "rules_python~~internal_deps~pypi__installer"
+          ],
+          [
+            "rules_python~",
+            "pypi__more_itertools",
+            "rules_python~~internal_deps~pypi__more_itertools"
+          ],
+          [
+            "rules_python~",
+            "pypi__packaging",
+            "rules_python~~internal_deps~pypi__packaging"
+          ],
+          [
+            "rules_python~",
+            "pypi__pep517",
+            "rules_python~~internal_deps~pypi__pep517"
+          ],
+          [
+            "rules_python~",
+            "pypi__pip",
+            "rules_python~~internal_deps~pypi__pip"
+          ],
+          [
+            "rules_python~",
+            "pypi__pip_tools",
+            "rules_python~~internal_deps~pypi__pip_tools"
+          ],
+          [
+            "rules_python~",
+            "pypi__pyproject_hooks",
+            "rules_python~~internal_deps~pypi__pyproject_hooks"
+          ],
+          [
+            "rules_python~",
+            "pypi__setuptools",
+            "rules_python~~internal_deps~pypi__setuptools"
+          ],
+          [
+            "rules_python~",
+            "pypi__tomli",
+            "rules_python~~internal_deps~pypi__tomli"
+          ],
+          [
+            "rules_python~",
+            "pypi__wheel",
+            "rules_python~~internal_deps~pypi__wheel"
+          ],
+          [
+            "rules_python~",
+            "pypi__zipp",
+            "rules_python~~internal_deps~pypi__zipp"
+          ],
+          [
+            "rules_python~",
+            "pythons_hub",
+            "rules_python~~python~pythons_hub"
+          ],
+          [
+            "rules_python~~python~pythons_hub",
+            "python_3_10_host",
+            "rules_python~~python~python_3_10_host"
+          ],
+          [
+            "rules_python~~python~pythons_hub",
+            "python_3_11_host",
+            "rules_python~~python~python_3_11_host"
+          ],
+          [
+            "rules_python~~python~pythons_hub",
+            "python_3_9_host",
+            "rules_python~~python~python_3_9_host"
+          ]
+        ]
+      }
+    },
+    "@@rules_python~//python/uv:extensions.bzl%uv": {
+      "general": {
+        "bzlTransitiveDigest": "erdJbm7V7XAkG+eWOTPQdxCJy8aKLXh7jWB5ZQm63fo=",
+        "usagesDigest": "uLdEsM0i3cqtN+HXzxfaiFko1ilKeu6F8NWoY1IjdBw=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "uv_linux_aarch64": {
+            "bzlFile": "@@rules_python~//python/uv:repositories.bzl",
+            "ruleClassName": "uv_repository",
+            "attributes": {
+              "uv_version": "0.2.23",
+              "platform": "aarch64-unknown-linux-gnu"
+            }
+          },
+          "uv_darwin_aarch64": {
+            "bzlFile": "@@rules_python~//python/uv:repositories.bzl",
+            "ruleClassName": "uv_repository",
+            "attributes": {
+              "uv_version": "0.2.23",
+              "platform": "aarch64-apple-darwin"
+            }
+          },
+          "uv_linux_s390x": {
+            "bzlFile": "@@rules_python~//python/uv:repositories.bzl",
+            "ruleClassName": "uv_repository",
+            "attributes": {
+              "uv_version": "0.2.23",
+              "platform": "s390x-unknown-linux-gnu"
+            }
+          },
+          "uv_linux_ppc": {
+            "bzlFile": "@@rules_python~//python/uv:repositories.bzl",
+            "ruleClassName": "uv_repository",
+            "attributes": {
+              "uv_version": "0.2.23",
+              "platform": "powerpc64le-unknown-linux-gnu"
+            }
+          },
+          "uv_linux_x86_64": {
+            "bzlFile": "@@rules_python~//python/uv:repositories.bzl",
+            "ruleClassName": "uv_repository",
+            "attributes": {
+              "uv_version": "0.2.23",
+              "platform": "x86_64-unknown-linux-gnu"
+            }
+          },
+          "uv_toolchains": {
+            "bzlFile": "@@rules_python~//python/uv/private:toolchains_repo.bzl",
+            "ruleClassName": "uv_toolchains_repo",
+            "attributes": {
+              "toolchain_type": "'@@rules_python~//python/uv:uv_toolchain_type'",
+              "toolchain_names": [
+                "uv_darwin_aarch64_toolchain",
+                "uv_linux_aarch64_toolchain",
+                "uv_linux_ppc_toolchain",
+                "uv_linux_s390x_toolchain",
+                "uv_darwin_x86_64_toolchain",
+                "uv_windows_x86_64_toolchain",
+                "uv_linux_x86_64_toolchain"
+              ],
+              "toolchain_labels": {
+                "uv_darwin_aarch64_toolchain": "@uv_darwin_aarch64//:uv_toolchain",
+                "uv_linux_aarch64_toolchain": "@uv_linux_aarch64//:uv_toolchain",
+                "uv_linux_ppc_toolchain": "@uv_linux_ppc//:uv_toolchain",
+                "uv_linux_s390x_toolchain": "@uv_linux_s390x//:uv_toolchain",
+                "uv_darwin_x86_64_toolchain": "@uv_darwin_x86_64//:uv_toolchain",
+                "uv_windows_x86_64_toolchain": "@uv_windows_x86_64//:uv_toolchain",
+                "uv_linux_x86_64_toolchain": "@uv_linux_x86_64//:uv_toolchain"
+              },
+              "toolchain_compatible_with": {
+                "uv_darwin_aarch64_toolchain": [
+                  "@platforms//os:macos",
+                  "@platforms//cpu:aarch64"
+                ],
+                "uv_linux_aarch64_toolchain": [
+                  "@platforms//os:linux",
+                  "@platforms//cpu:aarch64"
+                ],
+                "uv_linux_ppc_toolchain": [
+                  "@platforms//os:linux",
+                  "@platforms//cpu:ppc"
+                ],
+                "uv_linux_s390x_toolchain": [
+                  "@platforms//os:linux",
+                  "@platforms//cpu:s390x"
+                ],
+                "uv_darwin_x86_64_toolchain": [
+                  "@platforms//os:macos",
+                  "@platforms//cpu:x86_64"
+                ],
+                "uv_windows_x86_64_toolchain": [
+                  "@platforms//os:windows",
+                  "@platforms//cpu:x86_64"
+                ],
+                "uv_linux_x86_64_toolchain": [
+                  "@platforms//os:linux",
+                  "@platforms//cpu:x86_64"
+                ]
+              }
+            }
+          },
+          "uv_darwin_x86_64": {
+            "bzlFile": "@@rules_python~//python/uv:repositories.bzl",
+            "ruleClassName": "uv_repository",
+            "attributes": {
+              "uv_version": "0.2.23",
+              "platform": "x86_64-apple-darwin"
+            }
+          },
+          "uv_windows_x86_64": {
+            "bzlFile": "@@rules_python~//python/uv:repositories.bzl",
+            "ruleClassName": "uv_repository",
+            "attributes": {
+              "uv_version": "0.2.23",
+              "platform": "x86_64-pc-windows-msvc"
+            }
+          }
+        },
+        "recordedRepoMappingEntries": []
+      }
+    },
+    "@@upb~//:non_module_deps.bzl%non_module_deps": {
+      "general": {
+        "bzlTransitiveDigest": "jsbfONl9OksDWiAs7KDFK5chH/tYI3DngdM30NKdk5Y=",
+        "usagesDigest": "IDJOf8yjMDcQ7vE4CsKItkDBrOU4C+76gC1pczv03FQ=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "utf8_range": {
+            "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
+            "ruleClassName": "http_archive",
+            "attributes": {
+              "urls": [
+                "https://github.com/protocolbuffers/utf8_range/archive/de0b4a8ff9b5d4c98108bdfe723291a33c52c54f.zip"
+              ],
+              "strip_prefix": "utf8_range-de0b4a8ff9b5d4c98108bdfe723291a33c52c54f",
+              "sha256": "5da960e5e5d92394c809629a03af3c7709d2d3d0ca731dacb3a9fb4bf28f7702"
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "upb~",
+            "bazel_tools",
+            "bazel_tools"
+          ]
+        ]
+      }
+    }
+  }
+}
diff --git a/examples/bzlmod/requirements_lock_3_9.txt b/examples/bzlmod/requirements_lock_3_9.txt
index e6aaa99..bfabfd5 100644
--- a/examples/bzlmod/requirements_lock_3_9.txt
+++ b/examples/bzlmod/requirements_lock_3_9.txt
@@ -1,9 +1,6 @@
-#
-# This file is autogenerated by pip-compile with Python 3.9
-# by the following command:
-#
-#    bazel run //:requirements_3_9.update
-#
+# This file was autogenerated by uv via the following command:
+#    bazel run //examples:bzlmod_requirements_3_9.update
+--index-url https://pypi.org/simple
 --extra-index-url https://pypi.org/simple/
 
 alabaster==0.7.13 \
@@ -29,7 +26,10 @@
 colorama==0.4.6 \
     --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
     --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
-    # via -r requirements.in
+    # via
+    #   -r examples/bzlmod/requirements.in
+    #   pylint
+    #   sphinx
 dill==0.3.6 \
     --hash=sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0 \
     --hash=sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373
@@ -46,9 +46,9 @@
     --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \
     --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a
     # via sphinx
-importlib-metadata==6.8.0 \
-    --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \
-    --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743
+importlib-metadata==8.4.0 ; python_version < '3.10' \
+    --hash=sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1 \
+    --hash=sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5
     # via sphinx
 isort==5.11.4 \
     --hash=sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6 \
@@ -183,17 +183,17 @@
     --hash=sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4 \
     --hash=sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb
     # via
-    #   -r requirements.in
+    #   -r examples/bzlmod/requirements.in
     #   pylint-print
 pylint-print==1.0.1 \
     --hash=sha256:30aa207e9718ebf4ceb47fb87012092e6d8743aab932aa07aa14a73e750ad3d0 \
     --hash=sha256:a2b2599e7887b93e551db2624c523c1e6e9e58c3be8416cd98d41e4427e2669b
-    # via -r requirements.in
+    # via -r examples/bzlmod/requirements.in
 python-dateutil==2.8.2 \
     --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
     --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
     # via
-    #   -r requirements.in
+    #   -r examples/bzlmod/requirements.in
     #   s3cmd
 python-magic==0.4.27 \
     --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \
@@ -256,12 +256,18 @@
     --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
     --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
     # via
-    #   -r requirements.in
+    #   -r examples/bzlmod/requirements.in
     #   sphinx
 s3cmd==2.1.0 \
     --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa \
     --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03
-    # via -r requirements.in
+    # via -r examples/bzlmod/requirements.in
+setuptools==65.6.3 \
+    --hash=sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54 \
+    --hash=sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75
+    # via
+    #   babel
+    #   yamllint
 six==1.16.0 \
     --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
     --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
@@ -274,7 +280,7 @@
     --hash=sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560 \
     --hash=sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5
     # via
-    #   -r requirements.in
+    #   -r examples/bzlmod/requirements.in
     #   sphinxcontrib-applehelp
     #   sphinxcontrib-devhelp
     #   sphinxcontrib-htmlhelp
@@ -304,13 +310,13 @@
     --hash=sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54 \
     --hash=sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1
     # via
-    #   -r requirements.in
+    #   -r examples/bzlmod/requirements.in
     #   sphinx
 tabulate==0.9.0 \
     --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \
     --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f
-    # via -r requirements.in
-tomli==2.0.1 \
+    # via -r examples/bzlmod/requirements.in
+tomli==2.0.1 ; python_version < '3.11' \
     --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
     --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
     # via pylint
@@ -318,9 +324,9 @@
     --hash=sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b \
     --hash=sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73
     # via pylint
-typing-extensions==4.4.0 \
-    --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \
-    --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e
+typing-extensions==4.12.2 ; python_version < '3.10' \
+    --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
+    --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
     # via
     #   astroid
     #   pylint
@@ -399,11 +405,11 @@
     --hash=sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74 \
     --hash=sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0 \
     --hash=sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564
-    # via -r requirements.in
+    # via -r examples/bzlmod/requirements.in
 wheel==0.40.0 \
     --hash=sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873 \
     --hash=sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247
-    # via -r requirements.in
+    # via -r examples/bzlmod/requirements.in
 wrapt==1.14.1 \
     --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \
     --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \
@@ -473,14 +479,8 @@
 yamllint==1.28.0 \
     --hash=sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2 \
     --hash=sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b
-    # via -r requirements.in
-zipp==3.17.0 \
-    --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \
-    --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0
+    # via -r examples/bzlmod/requirements.in
+zipp==3.20.0 ; python_version < '3.10' \
+    --hash=sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31 \
+    --hash=sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d
     # via importlib-metadata
-
-# The following packages are considered to be unsafe in a requirements file:
-setuptools==65.6.3 \
-    --hash=sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54 \
-    --hash=sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75
-    # via yamllint
diff --git a/examples/bzlmod/requirements_windows_3_9.txt b/examples/bzlmod/requirements_windows_3_9.txt
deleted file mode 100644
index 636b4df..0000000
--- a/examples/bzlmod/requirements_windows_3_9.txt
+++ /dev/null
@@ -1,489 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.9
-# by the following command:
-#
-#    bazel run //:requirements_3_9.update
-#
---extra-index-url https://pypi.org/simple/
-
-alabaster==0.7.13 \
-    --hash=sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3 \
-    --hash=sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2
-    # via sphinx
-astroid==2.12.13 \
-    --hash=sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907 \
-    --hash=sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7
-    # via pylint
-babel==2.13.1 \
-    --hash=sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900 \
-    --hash=sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed
-    # via sphinx
-certifi==2023.7.22 \
-    --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
-    --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9
-    # via requests
-chardet==4.0.0 \
-    --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
-    --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
-    # via requests
-colorama==0.4.6 \
-    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
-    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
-    # via
-    #   -r requirements.in
-    #   pylint
-    #   sphinx
-dill==0.3.6 \
-    --hash=sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0 \
-    --hash=sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373
-    # via pylint
-docutils==0.20.1 \
-    --hash=sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6 \
-    --hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b
-    # via sphinx
-idna==2.10 \
-    --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
-    --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
-    # via requests
-imagesize==1.4.1 \
-    --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \
-    --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a
-    # via sphinx
-importlib-metadata==6.8.0 \
-    --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \
-    --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743
-    # via sphinx
-isort==5.11.4 \
-    --hash=sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6 \
-    --hash=sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b
-    # via pylint
-jinja2==3.1.4 \
-    --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \
-    --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d
-    # via sphinx
-lazy-object-proxy==1.10.0 \
-    --hash=sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56 \
-    --hash=sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4 \
-    --hash=sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8 \
-    --hash=sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282 \
-    --hash=sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757 \
-    --hash=sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424 \
-    --hash=sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b \
-    --hash=sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255 \
-    --hash=sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70 \
-    --hash=sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94 \
-    --hash=sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074 \
-    --hash=sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c \
-    --hash=sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee \
-    --hash=sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9 \
-    --hash=sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9 \
-    --hash=sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69 \
-    --hash=sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f \
-    --hash=sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3 \
-    --hash=sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9 \
-    --hash=sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d \
-    --hash=sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977 \
-    --hash=sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b \
-    --hash=sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43 \
-    --hash=sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658 \
-    --hash=sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a \
-    --hash=sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd \
-    --hash=sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83 \
-    --hash=sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4 \
-    --hash=sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696 \
-    --hash=sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05 \
-    --hash=sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3 \
-    --hash=sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6 \
-    --hash=sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895 \
-    --hash=sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4 \
-    --hash=sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba \
-    --hash=sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03 \
-    --hash=sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c
-    # via astroid
-markupsafe==2.1.3 \
-    --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
-    --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
-    --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
-    --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
-    --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
-    --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
-    --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
-    --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
-    --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
-    --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
-    --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
-    --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
-    --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \
-    --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
-    --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
-    --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
-    --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
-    --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
-    --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
-    --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
-    --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \
-    --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \
-    --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
-    --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
-    --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
-    --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
-    --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
-    --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
-    --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
-    --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \
-    --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
-    --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
-    --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
-    --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
-    --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
-    --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
-    --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
-    --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
-    --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
-    --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
-    --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
-    --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
-    --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \
-    --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \
-    --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \
-    --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \
-    --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \
-    --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \
-    --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \
-    --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \
-    --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \
-    --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \
-    --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \
-    --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \
-    --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
-    --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
-    --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
-    --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
-    --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
-    --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
-    # via jinja2
-mccabe==0.7.0 \
-    --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \
-    --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e
-    # via pylint
-packaging==23.2 \
-    --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
-    --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
-    # via sphinx
-pathspec==0.10.3 \
-    --hash=sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6 \
-    --hash=sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6
-    # via yamllint
-platformdirs==2.6.0 \
-    --hash=sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca \
-    --hash=sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e
-    # via pylint
-pygments==2.16.1 \
-    --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \
-    --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29
-    # via sphinx
-pylint==2.15.9 \
-    --hash=sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4 \
-    --hash=sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb
-    # via
-    #   -r requirements.in
-    #   pylint-print
-pylint-print==1.0.1 \
-    --hash=sha256:30aa207e9718ebf4ceb47fb87012092e6d8743aab932aa07aa14a73e750ad3d0 \
-    --hash=sha256:a2b2599e7887b93e551db2624c523c1e6e9e58c3be8416cd98d41e4427e2669b
-    # via -r requirements.in
-python-dateutil==2.8.2 \
-    --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
-    --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
-    # via
-    #   -r requirements.in
-    #   s3cmd
-python-magic==0.4.27 \
-    --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \
-    --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3
-    # via s3cmd
-pyyaml==6.0.1 \
-    --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
-    --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
-    --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
-    --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
-    --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
-    --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
-    --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
-    --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
-    --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
-    --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
-    --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
-    --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
-    --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
-    --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
-    --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
-    --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
-    --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
-    --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
-    --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
-    --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
-    --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
-    --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
-    --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
-    --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
-    --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
-    --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
-    --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
-    --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
-    --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
-    --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
-    --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
-    --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
-    --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
-    --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
-    --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
-    --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
-    --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
-    --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
-    --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
-    --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
-    --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
-    --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
-    --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
-    --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
-    --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
-    --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
-    --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
-    --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
-    --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
-    --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
-    --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
-    # via yamllint
-requests==2.25.1 \
-    --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
-    --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
-    # via
-    #   -r requirements.in
-    #   sphinx
-s3cmd==2.1.0 \
-    --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa \
-    --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03
-    # via -r requirements.in
-six==1.16.0 \
-    --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
-    --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
-    # via python-dateutil
-snowballstemmer==2.2.0 \
-    --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
-    --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
-    # via sphinx
-sphinx==7.2.6 \
-    --hash=sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560 \
-    --hash=sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5
-    # via
-    #   -r requirements.in
-    #   sphinxcontrib-applehelp
-    #   sphinxcontrib-devhelp
-    #   sphinxcontrib-htmlhelp
-    #   sphinxcontrib-qthelp
-    #   sphinxcontrib-serializinghtml
-sphinxcontrib-applehelp==1.0.7 \
-    --hash=sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d \
-    --hash=sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa
-    # via sphinx
-sphinxcontrib-devhelp==1.0.5 \
-    --hash=sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212 \
-    --hash=sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f
-    # via sphinx
-sphinxcontrib-htmlhelp==2.0.4 \
-    --hash=sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a \
-    --hash=sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9
-    # via sphinx
-sphinxcontrib-jsmath==1.0.1 \
-    --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \
-    --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8
-    # via sphinx
-sphinxcontrib-qthelp==1.0.6 \
-    --hash=sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d \
-    --hash=sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4
-    # via sphinx
-sphinxcontrib-serializinghtml==1.1.9 \
-    --hash=sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54 \
-    --hash=sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1
-    # via
-    #   -r requirements.in
-    #   sphinx
-tabulate==0.9.0 \
-    --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \
-    --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f
-    # via -r requirements.in
-tomli==2.0.1 \
-    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
-    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
-    # via pylint
-tomlkit==0.11.6 \
-    --hash=sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b \
-    --hash=sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73
-    # via pylint
-typing-extensions==4.4.0 \
-    --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \
-    --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e
-    # via
-    #   astroid
-    #   pylint
-urllib3==1.26.18 \
-    --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \
-    --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0
-    # via requests
-websockets==11.0.3 \
-    --hash=sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd \
-    --hash=sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f \
-    --hash=sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998 \
-    --hash=sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82 \
-    --hash=sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788 \
-    --hash=sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa \
-    --hash=sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f \
-    --hash=sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4 \
-    --hash=sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7 \
-    --hash=sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f \
-    --hash=sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd \
-    --hash=sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69 \
-    --hash=sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb \
-    --hash=sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b \
-    --hash=sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016 \
-    --hash=sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac \
-    --hash=sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4 \
-    --hash=sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb \
-    --hash=sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99 \
-    --hash=sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e \
-    --hash=sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54 \
-    --hash=sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf \
-    --hash=sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007 \
-    --hash=sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3 \
-    --hash=sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6 \
-    --hash=sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86 \
-    --hash=sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1 \
-    --hash=sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61 \
-    --hash=sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11 \
-    --hash=sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8 \
-    --hash=sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f \
-    --hash=sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931 \
-    --hash=sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526 \
-    --hash=sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016 \
-    --hash=sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae \
-    --hash=sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd \
-    --hash=sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b \
-    --hash=sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311 \
-    --hash=sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af \
-    --hash=sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152 \
-    --hash=sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288 \
-    --hash=sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de \
-    --hash=sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97 \
-    --hash=sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d \
-    --hash=sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d \
-    --hash=sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca \
-    --hash=sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0 \
-    --hash=sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9 \
-    --hash=sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b \
-    --hash=sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e \
-    --hash=sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128 \
-    --hash=sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d \
-    --hash=sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c \
-    --hash=sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5 \
-    --hash=sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6 \
-    --hash=sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b \
-    --hash=sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b \
-    --hash=sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280 \
-    --hash=sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c \
-    --hash=sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c \
-    --hash=sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f \
-    --hash=sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20 \
-    --hash=sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8 \
-    --hash=sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb \
-    --hash=sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602 \
-    --hash=sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf \
-    --hash=sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0 \
-    --hash=sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74 \
-    --hash=sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0 \
-    --hash=sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564
-    # via -r requirements.in
-wheel==0.40.0 \
-    --hash=sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873 \
-    --hash=sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247
-    # via -r requirements.in
-wrapt==1.14.1 \
-    --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \
-    --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \
-    --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \
-    --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \
-    --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \
-    --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \
-    --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \
-    --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \
-    --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \
-    --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \
-    --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \
-    --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \
-    --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \
-    --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \
-    --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \
-    --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \
-    --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \
-    --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \
-    --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \
-    --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \
-    --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \
-    --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \
-    --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \
-    --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \
-    --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \
-    --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \
-    --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \
-    --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \
-    --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \
-    --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \
-    --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \
-    --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \
-    --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \
-    --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \
-    --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \
-    --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \
-    --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \
-    --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \
-    --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \
-    --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \
-    --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \
-    --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \
-    --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \
-    --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \
-    --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \
-    --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \
-    --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \
-    --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \
-    --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \
-    --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \
-    --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \
-    --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \
-    --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \
-    --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \
-    --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \
-    --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \
-    --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \
-    --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \
-    --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \
-    --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \
-    --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \
-    --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \
-    --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \
-    --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af
-    # via astroid
-yamllint==1.28.0 \
-    --hash=sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2 \
-    --hash=sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b
-    # via -r requirements.in
-zipp==3.17.0 \
-    --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \
-    --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0
-    # via importlib-metadata
-
-# The following packages are considered to be unsafe in a requirements file:
-setuptools==65.6.3 \
-    --hash=sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54 \
-    --hash=sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75
-    # via yamllint
diff --git a/examples/wheel/BUILD.bazel b/examples/wheel/BUILD.bazel
index aa063ce..1eaf035 100644
--- a/examples/wheel/BUILD.bazel
+++ b/examples/wheel/BUILD.bazel
@@ -333,6 +333,27 @@
     version = "0.0.1",
 )
 
+py_wheel(
+    name = "extra_requires",
+    distribution = "extra_requires",
+    extra_requires = {"example": [
+        "pyyaml>=6.0.0,!=6.0.1",
+        'toml; (python_version == "3.11" or python_version == "3.12") and python_version != "3.8"',
+        'wheel; python_version == "3.11" or python_version == "3.12" ',
+    ]},
+    python_tag = "py3",
+    # py_wheel can use text files to specify their requirements. This
+    # can be convenient for users of `compile_pip_requirements` who have
+    # granular `requirements.in` files per package.
+    requires = [
+        "tomli>=2.0.0",
+        "starlark",
+        'pytest; python_version != "3.8"',
+    ],
+    version = "0.0.1",
+    deps = [":example_pkg"],
+)
+
 py_test(
     name = "wheel_test",
     srcs = ["wheel_test.py"],
@@ -341,6 +362,7 @@
         ":custom_package_root_multi_prefix",
         ":custom_package_root_multi_prefix_reverse_order",
         ":customized",
+        ":extra_requires",
         ":filename_escaping",
         ":minimal_data_files",
         ":minimal_with_py_library",
diff --git a/examples/wheel/wheel_test.py b/examples/wheel/wheel_test.py
index 66ebd50..7212423 100644
--- a/examples/wheel/wheel_test.py
+++ b/examples/wheel/wheel_test.py
@@ -489,6 +489,36 @@
                 ],
             )
 
+    def test_extra_requires(self):
+        filename = self._get_path("extra_requires-0.0.1-py3-none-any.whl")
+
+        with zipfile.ZipFile(filename) as zf:
+            self.assertAllEntriesHasReproducibleMetadata(zf)
+            metadata_file = None
+            for f in zf.namelist():
+                if os.path.basename(f) == "METADATA":
+                    metadata_file = f
+            self.assertIsNotNone(metadata_file)
+
+            requires = []
+            with zf.open(metadata_file) as fp:
+                for line in fp:
+                    if line.startswith(b"Requires-Dist:"):
+                        requires.append(line.decode("utf-8").strip())
+
+            print(requires)
+            self.assertEqual(
+                [
+                    "Requires-Dist: tomli>=2.0.0",
+                    "Requires-Dist: starlark",
+                    'Requires-Dist: pytest; python_version != "3.8"',
+                    "Requires-Dist: pyyaml!=6.0.1,>=6.0.0; extra == 'example'",
+                    'Requires-Dist: toml; ((python_version == "3.11" or python_version == "3.12") and python_version != "3.8") and extra == \'example\'',
+                    'Requires-Dist: wheel; (python_version == "3.11" or python_version == "3.12") and extra == \'example\'',
+                ],
+                requires,
+            )
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/gazelle/manifest/test/test.go b/gazelle/manifest/test/test.go
index 506c7d2..a7647f3 100644
--- a/gazelle/manifest/test/test.go
+++ b/gazelle/manifest/test/test.go
@@ -33,12 +33,12 @@
 func TestGazelleManifestIsUpdated(t *testing.T) {
 	requirementsPath := os.Getenv("_TEST_REQUIREMENTS")
 	if requirementsPath == "" {
-		t.Fatalf("_TEST_REQUIREMENTS must be set")
+		t.Fatal("_TEST_REQUIREMENTS must be set")
 	}
 
 	manifestPath := os.Getenv("_TEST_MANIFEST")
 	if manifestPath == "" {
-		t.Fatalf("_TEST_MANIFEST must be set")
+		t.Fatal("_TEST_MANIFEST must be set")
 	}
 
 	manifestFile := new(manifest.File)
@@ -53,7 +53,7 @@
 	manifestGeneratorHashPath, err := runfiles.Rlocation(
 		os.Getenv("_TEST_MANIFEST_GENERATOR_HASH"))
 	if err != nil {
-		t.Fatal("failed to resolve runfiles path of manifest: %v", err)
+		t.Fatalf("failed to resolve runfiles path of manifest: %v", err)
 	}
 
 	manifestGeneratorHash, err := os.Open(manifestGeneratorHashPath)
diff --git a/gazelle/python/resolve.go b/gazelle/python/resolve.go
index ca306c3..a7b716a 100644
--- a/gazelle/python/resolve.go
+++ b/gazelle/python/resolve.go
@@ -206,11 +206,11 @@
 								continue MODULES_LOOP
 							} else if cfg.ValidateImportStatements() {
 								err := fmt.Errorf(
-									"%[1]q at line %[2]d from %[3]q is an invalid dependency: possible solutions:\n"+
+									"%[1]q, line %[2]d: %[3]q is an invalid dependency: possible solutions:\n"+
 										"\t1. Add it as a dependency in the requirements.txt file.\n"+
-										"\t2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive.\n"+
-										"\t3. Ignore it with a comment '# gazelle:ignore %[1]s' in the Python file.\n",
-									moduleName, mod.LineNumber, mod.Filepath,
+										"\t2. Use the '# gazelle:resolve py %[3]s TARGET_LABEL' BUILD file directive to resolve to a known dependency.\n"+
+										"\t3. Ignore it with a comment '# gazelle:ignore %[3]s' in the Python file.\n",
+									mod.Filepath, mod.LineNumber, moduleName,
 								)
 								errs = append(errs, err)
 								continue POSSIBLE_MODULE_LOOP
@@ -236,9 +236,10 @@
 							}
 							if len(sameRootMatches) != 1 {
 								err := fmt.Errorf(
-									"multiple targets (%s) may be imported with %q at line %d in %q "+
-										"- this must be fixed using the \"gazelle:resolve\" directive",
-									targetListFromResults(filteredMatches), moduleName, mod.LineNumber, mod.Filepath)
+									"%[1]q, line %[2]d: multiple targets (%[3]s) may be imported with %[4]q: possible solutions:\n"+
+										"\t1. Disambiguate the above multiple targets by removing duplicate srcs entries.\n"+
+										"\t2. Use the '# gazelle:resolve py %[4]s TARGET_LABEL' BUILD file directive to resolve to one of the above targets.\n",
+									mod.Filepath, mod.LineNumber, targetListFromResults(filteredMatches), moduleName)
 								errs = append(errs, err)
 								continue POSSIBLE_MODULE_LOOP
 							}
@@ -263,7 +264,7 @@
 				for _, err := range errs {
 					joinedErrs = fmt.Sprintf("%s%s\n", joinedErrs, err)
 				}
-				log.Printf("ERROR: failed to validate dependencies for target %q: %v\n", from.String(), joinedErrs)
+				log.Printf("ERROR: failed to validate dependencies for target %q:\n\n%v", from.String(), joinedErrs)
 				hasFatalError = true
 			}
 		}
diff --git a/gazelle/python/testdata/dependency_resolution_order/BUILD.in b/gazelle/python/testdata/dependency_resolution_order/BUILD.in
index 71a5c5a..aaf45f4 100644
--- a/gazelle/python/testdata/dependency_resolution_order/BUILD.in
+++ b/gazelle/python/testdata/dependency_resolution_order/BUILD.in
@@ -1 +1,2 @@
 # gazelle:resolve py bar //somewhere/bar
+# gazelle:resolve py third_party.foo //third_party/foo
diff --git a/gazelle/python/testdata/dependency_resolution_order/BUILD.out b/gazelle/python/testdata/dependency_resolution_order/BUILD.out
index eebe6c3..58fd266 100644
--- a/gazelle/python/testdata/dependency_resolution_order/BUILD.out
+++ b/gazelle/python/testdata/dependency_resolution_order/BUILD.out
@@ -1,6 +1,7 @@
 load("@rules_python//python:defs.bzl", "py_library")
 
 # gazelle:resolve py bar //somewhere/bar
+# gazelle:resolve py third_party.foo //third_party/foo
 
 py_library(
     name = "dependency_resolution_order",
@@ -9,6 +10,9 @@
     deps = [
         "//baz",
         "//somewhere/bar",
+        "//third_party",
+        "//third_party/foo",
+        "@gazelle_python_test//other_pip_dep",
         "@gazelle_python_test//some_foo",
     ],
 )
diff --git a/gazelle/python/testdata/dependency_resolution_order/__init__.py b/gazelle/python/testdata/dependency_resolution_order/__init__.py
index d9c6504..e2d0a8a 100644
--- a/gazelle/python/testdata/dependency_resolution_order/__init__.py
+++ b/gazelle/python/testdata/dependency_resolution_order/__init__.py
@@ -18,6 +18,14 @@
 import baz
 import foo
 
+# Ensure that even though @gazelle_python_test//other_pip_dep provides "third_party",
+# we can still override "third_party.foo.bar"
+import third_party.foo.bar
+
+from third_party import baz
+
+import third_party
+
 _ = sys
 _ = bar
 _ = baz
diff --git a/gazelle/python/testdata/dependency_resolution_order/gazelle_python.yaml b/gazelle/python/testdata/dependency_resolution_order/gazelle_python.yaml
index 8615181..e62ad33 100644
--- a/gazelle/python/testdata/dependency_resolution_order/gazelle_python.yaml
+++ b/gazelle/python/testdata/dependency_resolution_order/gazelle_python.yaml
@@ -15,4 +15,5 @@
 manifest:
   modules_mapping:
     foo: some_foo
+    third_party: other_pip_dep
   pip_deps_repository_name: gazelle_python_test
diff --git a/gazelle/python/testdata/dependency_resolution_order/third_party/BUILD.in b/gazelle/python/testdata/dependency_resolution_order/third_party/BUILD.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/dependency_resolution_order/third_party/BUILD.in
diff --git a/gazelle/python/testdata/dependency_resolution_order/third_party/BUILD.out b/gazelle/python/testdata/dependency_resolution_order/third_party/BUILD.out
new file mode 100644
index 0000000..2c130d7
--- /dev/null
+++ b/gazelle/python/testdata/dependency_resolution_order/third_party/BUILD.out
@@ -0,0 +1,7 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+py_library(
+    name = "third_party",
+    srcs = ["baz.py"],
+    visibility = ["//:__subpackages__"],
+)
diff --git a/gazelle/python/testdata/dependency_resolution_order/third_party/baz.py b/gazelle/python/testdata/dependency_resolution_order/third_party/baz.py
new file mode 100644
index 0000000..e01d49c
--- /dev/null
+++ b/gazelle/python/testdata/dependency_resolution_order/third_party/baz.py
@@ -0,0 +1,17 @@
+# Copyright 2024 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.
+
+import os
+
+_ = os
diff --git a/gazelle/python/testdata/invalid_imported_module/__init__.py b/gazelle/python/testdata/invalid_imported_module/__init__.py
index dc6fb85..40b5848 100644
--- a/gazelle/python/testdata/invalid_imported_module/__init__.py
+++ b/gazelle/python/testdata/invalid_imported_module/__init__.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import foo.bar
+
 try:
     import grpc
 
@@ -19,4 +21,4 @@
 except ImportError:
     grpc_available = False
 
-_ = grpc
+_ = bar(grpc)
diff --git a/gazelle/python/testdata/invalid_imported_module/foo/BUILD.in b/gazelle/python/testdata/invalid_imported_module/foo/BUILD.in
new file mode 100644
index 0000000..4f598e9
--- /dev/null
+++ b/gazelle/python/testdata/invalid_imported_module/foo/BUILD.in
@@ -0,0 +1,11 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+py_library(
+    name = "bar_1",
+    srcs = ["bar.py"],
+)
+
+py_library(
+    name = "bar_2",
+    srcs = ["bar.py"],
+)
diff --git a/gazelle/python/testdata/invalid_imported_module/foo/bar.py b/gazelle/python/testdata/invalid_imported_module/foo/bar.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/invalid_imported_module/foo/bar.py
diff --git a/gazelle/python/testdata/invalid_imported_module/test.yaml b/gazelle/python/testdata/invalid_imported_module/test.yaml
index 6bcea39..0085523 100644
--- a/gazelle/python/testdata/invalid_imported_module/test.yaml
+++ b/gazelle/python/testdata/invalid_imported_module/test.yaml
@@ -16,7 +16,20 @@
 expect:
   exit_code: 1
   stderr: |
-    gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module": "grpc" at line 16 from "__init__.py" is an invalid dependency: possible solutions:
+    gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module":
+
+    "__init__.py", line 15: multiple targets (//foo:bar_1, //foo:bar_2) may be imported with "foo.bar": possible solutions:
+    	1. Disambiguate the above multiple targets by removing duplicate srcs entries.
+    	2. Use the '# gazelle:resolve py foo.bar TARGET_LABEL' BUILD file directive to resolve to one of the above targets.
+
+    "__init__.py", line 15: "foo" is an invalid dependency: possible solutions:
     	1. Add it as a dependency in the requirements.txt file.
-    	2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive.
+    	2. Use the '# gazelle:resolve py foo TARGET_LABEL' BUILD file directive to resolve to a known dependency.
+    	3. Ignore it with a comment '# gazelle:ignore foo' in the Python file.
+
+    gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module":
+
+    "__init__.py", line 18: "grpc" is an invalid dependency: possible solutions:
+    	1. Add it as a dependency in the requirements.txt file.
+    	2. Use the '# gazelle:resolve py grpc TARGET_LABEL' BUILD file directive to resolve to a known dependency.
     	3. Ignore it with a comment '# gazelle:ignore grpc' in the Python file.
diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go
index 166b575..a24a90e 100644
--- a/gazelle/pythonconfig/pythonconfig.go
+++ b/gazelle/pythonconfig/pythonconfig.go
@@ -282,23 +282,16 @@
 	for currentCfg := c; currentCfg != nil; currentCfg = currentCfg.parent {
 		if currentCfg.gazelleManifest != nil {
 			gazelleManifest := currentCfg.gazelleManifest
-			for {
-				if distributionName, ok := gazelleManifest.ModulesMapping[modName]; ok {
-					var distributionRepositoryName string
-					if gazelleManifest.PipDepsRepositoryName != "" {
-						distributionRepositoryName = gazelleManifest.PipDepsRepositoryName
-					} else if gazelleManifest.PipRepository != nil {
-						distributionRepositoryName = gazelleManifest.PipRepository.Name
-					}
+			if distributionName, ok := gazelleManifest.ModulesMapping[modName]; ok {
+				var distributionRepositoryName string
+				if gazelleManifest.PipDepsRepositoryName != "" {
+					distributionRepositoryName = gazelleManifest.PipDepsRepositoryName
+				} else if gazelleManifest.PipRepository != nil {
+					distributionRepositoryName = gazelleManifest.PipRepository.Name
+				}
 
-					lbl := currentCfg.FormatThirdPartyDependency(distributionRepositoryName, distributionName)
-					return lbl.String(), true
-				}
-				i := strings.LastIndex(modName, ".")
-				if i == -1 {
-					break
-				}
-				modName = modName[:i]
+				lbl := currentCfg.FormatThirdPartyDependency(distributionRepositoryName, distributionName)
+				return lbl.String(), true
 			}
 		}
 	}
diff --git a/python/BUILD.bazel b/python/BUILD.bazel
index 878d20b..b7a2172 100644
--- a/python/BUILD.bazel
+++ b/python/BUILD.bazel
@@ -140,6 +140,24 @@
 )
 
 bzl_library(
+    name = "py_exec_tools_info_bzl",
+    srcs = ["py_exec_tools_info.bzl"],
+    deps = ["//python/private:py_exec_tools_info_bzl"],
+)
+
+bzl_library(
+    name = "py_exec_tools_toolchain_bzl",
+    srcs = ["py_exec_tools_toolchain.bzl"],
+    deps = ["//python/private:py_exec_tools_toolchain_bzl"],
+)
+
+bzl_library(
+    name = "py_executable_info_bzl",
+    srcs = ["py_executable_info.bzl"],
+    deps = ["//python/private:py_executable_info_bzl"],
+)
+
+bzl_library(
     name = "py_import_bzl",
     srcs = ["py_import.bzl"],
     deps = [":py_info_bzl"],
@@ -211,7 +229,11 @@
     name = "repositories_bzl",
     srcs = ["repositories.bzl"],
     deps = [
-        "//python/private:python_repositories_bzl",
+        "//python/private:is_standalone_interpreter_bzl",
+        "//python/private:py_repositories_bzl",
+        "//python/private:python_register_multi_toolchains_bzl",
+        "//python/private:python_register_toolchains_bzl",
+        "//python/private:python_repository_bzl",
     ],
 )
 
@@ -302,6 +324,12 @@
     visibility = ["//visibility:public"],
 )
 
+# Special target to indicate `None` for label attributes a default value.
+alias(
+    name = "none",
+    actual = "//python/private:sentinel",
+)
+
 # Definitions for a Python toolchain that, at execution time, attempts to detect
 # a platform runtime having the appropriate major Python version. Consider this
 # a toolchain of last resort.
diff --git a/python/cc/BUILD.bazel b/python/cc/BUILD.bazel
index d384d05..f4e4aeb 100644
--- a/python/cc/BUILD.bazel
+++ b/python/cc/BUILD.bazel
@@ -40,7 +40,7 @@
     name = "py_cc_toolchain_bzl",
     srcs = ["py_cc_toolchain.bzl"],
     visibility = ["//visibility:public"],
-    deps = ["//python/private:py_cc_toolchain_bzl"],
+    deps = ["//python/private:py_cc_toolchain_macro_bzl"],
 )
 
 bzl_library(
diff --git a/python/cc/py_cc_toolchain_info.bzl b/python/cc/py_cc_toolchain_info.bzl
index 9ea394a..3164f89 100644
--- a/python/cc/py_cc_toolchain_info.bzl
+++ b/python/cc/py_cc_toolchain_info.bzl
@@ -1,4 +1,4 @@
-# Copyright 2023 The Bazel Authors. All rights reserved.
+# Copyright 2024 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.
@@ -11,11 +11,12 @@
 # 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.
+"""Provider for C/C++ information from the toolchain.
 
-"""Provider for C/C++ information about the Python runtime.
-
-NOTE: This is a beta-quality feature. APIs subject to change until
-https://github.com/bazelbuild/rules_python/issues/824 is considered done.
+:::{seealso}
+* {any}`Custom toolchains` for how to define custom toolchains.
+* {obj}`py_cc_toolchain` rule for defining the toolchain.
+:::
 """
 
 load("//python/private:py_cc_toolchain_info.bzl", _PyCcToolchainInfo = "PyCcToolchainInfo")
diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel
index f2383d6..c31d69f 100644
--- a/python/config_settings/BUILD.bazel
+++ b/python/config_settings/BUILD.bazel
@@ -1,4 +1,5 @@
 load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
+load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
 load(
     "//python/private:flags.bzl",
     "BootstrapImplFlag",
@@ -27,6 +28,8 @@
 
 construct_config_settings(
     name = "construct_config_settings",
+    minor_mapping = MINOR_MAPPING,
+    versions = TOOL_VERSIONS.keys(),
 )
 
 string_flag(
@@ -58,7 +61,7 @@
 
 string_flag(
     name = "precompile_source_retention",
-    build_setting_default = PrecompileSourceRetentionFlag.KEEP_SOURCE,
+    build_setting_default = PrecompileSourceRetentionFlag.AUTO,
     values = sorted(PrecompileSourceRetentionFlag.__members__.values()),
     # NOTE: Only public because it's an implicit dependency
     visibility = ["//visibility:public"],
diff --git a/python/config_settings/config_settings.bzl b/python/config_settings/config_settings.bzl
index f1d2ff0..4410425 100644
--- a/python/config_settings/config_settings.bzl
+++ b/python/config_settings/config_settings.bzl
@@ -18,13 +18,7 @@
 load(
     "//python/private:config_settings.bzl",
     _construct_config_settings = "construct_config_settings",
-    _is_python_config_setting = "is_python_config_setting",
 )
 
-# This is exposed only for cases where the pip hub repo needs to use this rule
-# to define hub-repo scoped config_settings for platform specific wheel
-# support.
-is_python_config_setting = _is_python_config_setting
-
 # This is exposed for usage in rules_python only.
 construct_config_settings = _construct_config_settings
diff --git a/python/extensions/python.bzl b/python/extensions/python.bzl
index 4148d90..0f0da00 100644
--- a/python/extensions/python.bzl
+++ b/python/extensions/python.bzl
@@ -12,7 +12,38 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"Python toolchain module extensions for use with bzlmod"
+"""Python toolchain module extensions for use with bzlmod.
+
+:::{topic} Basic usage
+
+The simplest way to configure the toolchain with `rules_python` is as follows.
+
+```starlark
+python = use_extension("@rules_python//python/extensions:python.bzl", "python")
+python.toolchain(
+    is_default = True,
+    python_version = "3.11",
+)
+use_repo(python, "python_3_11")
+```
+
+::::{seealso}
+For more in-depth documentation see the {obj}`python.toolchain`.
+::::
+:::
+
+:::{topic} Overrides
+
+Overrides can be done at 3 different levels:
+* Overrides affecting all python toolchain versions on all platforms - {obj}`python.override`.
+* Overrides affecting a single toolchain versions on all platforms - {obj}`python.single_version_override`.
+* Overrides affecting a single toolchain versions on a single platforms - {obj}`python.single_version_platform_override`.
+
+::::{seealso}
+The main documentation page on registering [toolchains](/toolchains).
+::::
+:::
+"""
 
 load("//python/private:python.bzl", _python = "python")
 
diff --git a/python/packaging.bzl b/python/packaging.bzl
index a5ac25b..17f72a7 100644
--- a/python/packaging.bzl
+++ b/python/packaging.bzl
@@ -35,25 +35,28 @@
     attrs = py_package_lib.attrs,
 )
 
-# Based on https://github.com/aspect-build/bazel-lib/tree/main/lib/private/copy_to_directory.bzl
-# Avoiding a bazelbuild -> aspect-build dependency :(
 def _py_wheel_dist_impl(ctx):
-    dir = ctx.actions.declare_directory(ctx.attr.out)
+    out = ctx.actions.declare_directory(ctx.attr.out)
     name_file = ctx.attr.wheel[PyWheelInfo].name_file
-    cmds = [
-        "mkdir -p \"%s\"" % dir.path,
-        """cp "{}" "{}/$(cat "{}")" """.format(ctx.files.wheel[0].path, dir.path, name_file.path),
-    ]
-    ctx.actions.run_shell(
-        inputs = ctx.files.wheel + [name_file],
-        outputs = [dir],
-        command = "\n".join(cmds),
-        mnemonic = "CopyToDirectory",
-        progress_message = "Copying files to directory",
-        use_default_shell_env = True,
+    wheel = ctx.attr.wheel[PyWheelInfo].wheel
+
+    args = ctx.actions.args()
+    args.add("--wheel", wheel)
+    args.add("--name_file", name_file)
+    args.add("--output", out.path)
+
+    ctx.actions.run(
+        mnemonic = "PyWheelDistDir",
+        executable = ctx.executable._copier,
+        inputs = [wheel, name_file],
+        outputs = [out],
+        arguments = [args],
     )
     return [
-        DefaultInfo(files = depset([dir]), runfiles = ctx.runfiles([dir])),
+        DefaultInfo(
+            files = depset([out]),
+            runfiles = ctx.runfiles([out]),
+        ),
     ]
 
 py_wheel_dist = rule(
@@ -67,12 +70,28 @@
 """,
     implementation = _py_wheel_dist_impl,
     attrs = {
-        "out": attr.string(doc = "name of the resulting directory", mandatory = True),
-        "wheel": attr.label(doc = "a [py_wheel target](#py_wheel)", providers = [PyWheelInfo]),
+        "out": attr.string(
+            doc = "name of the resulting directory",
+            mandatory = True,
+        ),
+        "wheel": attr.label(
+            doc = "a [py_wheel target](#py_wheel)",
+            providers = [PyWheelInfo],
+        ),
+        "_copier": attr.label(
+            cfg = "exec",
+            executable = True,
+            default = Label("//python/private:py_wheel_dist"),
+        ),
     },
 )
 
-def py_wheel(name, twine = None, twine_binary = Label("//tools/publish:twine") if BZLMOD_ENABLED else None, publish_args = [], **kwargs):
+def py_wheel(
+        name,
+        twine = None,
+        twine_binary = Label("//tools/publish:twine") if BZLMOD_ENABLED else None,
+        publish_args = [],
+        **kwargs):
     """Builds a Python Wheel.
 
     Wheels are Python distribution format defined in https://www.python.org/dev/peps/pep-0427/.
@@ -153,24 +172,31 @@
             Note that you can also pass additional args to the bazel run command as in the example above.
         **kwargs: other named parameters passed to the underlying [py_wheel rule](#py_wheel_rule)
     """
-    _dist_target = "{}.dist".format(name)
+    tags = kwargs.pop("tags", [])
+    manual_tags = depset(tags + ["manual"]).to_list()
+
+    dist_target = "{}.dist".format(name)
     py_wheel_dist(
-        name = _dist_target,
+        name = dist_target,
         wheel = name,
         out = kwargs.pop("dist_folder", "{}_dist".format(name)),
+        tags = manual_tags,
         **copy_propagating_kwargs(kwargs)
     )
 
-    _py_wheel(name = name, **kwargs)
+    _py_wheel(
+        name = name,
+        tags = tags,
+        **kwargs
+    )
 
     twine_args = []
     if twine or twine_binary:
         twine_args = ["upload"]
         twine_args.extend(publish_args)
-        twine_args.append("$(rootpath :{})/*".format(_dist_target))
+        twine_args.append("$(rootpath :{})/*".format(dist_target))
 
     if twine_binary:
-        twine_kwargs = {"tags": ["manual"]}
         native_binary(
             name = "{}.publish".format(name),
             src = twine_binary,
@@ -179,9 +205,10 @@
                 "//conditions:default": "{}.publish_script".format(name),
             }),
             args = twine_args,
-            data = [_dist_target],
+            data = [dist_target],
+            tags = manual_tags,
             visibility = kwargs.get("visibility"),
-            **copy_propagating_kwargs(kwargs, twine_kwargs)
+            **copy_propagating_kwargs(kwargs)
         )
     elif twine:
         if not twine.endswith(":pkg"):
@@ -193,10 +220,11 @@
             name = "{}.publish".format(name),
             srcs = [twine_main],
             args = twine_args,
-            data = [_dist_target],
+            data = [dist_target],
             imports = ["."],
             main = twine_main,
             deps = [twine],
+            tags = manual_tags,
             visibility = kwargs.get("visibility"),
             **copy_propagating_kwargs(kwargs)
         )
diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel
index 146e934..bfe3764 100644
--- a/python/private/BUILD.bazel
+++ b/python/private/BUILD.bazel
@@ -18,6 +18,7 @@
 load("//python:py_library.bzl", "py_library")
 load("//python:versions.bzl", "print_toolchains_checksums")
 load(":py_exec_tools_toolchain.bzl", "current_interpreter_executable")
+load(":sentinel.bzl", "sentinel")
 load(":stamp.bzl", "stamp_build_setting")
 
 package(
@@ -77,8 +78,9 @@
     name = "config_settings_bzl",
     srcs = ["config_settings.bzl"],
     deps = [
-        "//python:versions_bzl",
+        ":semver_bzl",
         "@bazel_skylib//lib:selects",
+        "@bazel_skylib//rules:common_settings",
     ],
 )
 
@@ -113,7 +115,6 @@
 bzl_library(
     name = "full_version_bzl",
     srcs = ["full_version.bzl"],
-    deps = ["//python:versions_bzl"],
 )
 
 bzl_library(
@@ -123,6 +124,14 @@
 )
 
 bzl_library(
+    name = "is_standalone_interpreter_bzl",
+    srcs = ["is_standalone_interpreter.bzl"],
+    deps = [
+        ":repo_utils_bzl",
+    ],
+)
+
+bzl_library(
     name = "normalize_name_bzl",
     srcs = ["normalize_name.bzl"],
 )
@@ -131,52 +140,84 @@
     name = "python_bzl",
     srcs = ["python.bzl"],
     deps = [
+        ":full_version_bzl",
+        ":python_register_toolchains_bzl",
         ":pythons_hub_bzl",
         ":repo_utils_bzl",
+        ":semver_bzl",
         ":toolchains_repo_bzl",
         ":util_bzl",
-        "//python:repositories_bzl",
         "@bazel_features//:features",
     ],
 )
 
 bzl_library(
-    name = "python_repositories_bzl",
-    srcs = ["python_repositories.bzl"],
+    name = "py_repositories_bzl",
+    srcs = ["py_repositories.bzl"],
     deps = [
-        "//python:versions_bzl",
-        "//python/private:auth_bzl",
-        "//python/private:bazel_tools_bzl",
-        "//python/private:bzlmod_enabled_bzl",
-        "//python/private:coverage_deps_bzl",
-        "//python/private:full_version_bzl",
-        "//python/private:internal_config_repo_bzl",
-        "//python/private:repo_utils_bzl",
-        "//python/private:toolchains_repo_bzl",
+        ":bazel_tools_bzl",
+        ":internal_config_repo_bzl",
         "//python/private/pypi:deps_bzl",
     ],
 )
 
 bzl_library(
+    name = "python_register_toolchains_bzl",
+    srcs = ["python_register_toolchains.bzl"],
+    deps = [
+        ":auth_bzl",
+        ":bazel_tools_bzl",
+        ":bzlmod_enabled_bzl",
+        ":coverage_deps_bzl",
+        ":full_version_bzl",
+        ":internal_config_repo_bzl",
+        ":python_repository_bzl",
+        ":toolchains_repo_bzl",
+        "//python:versions_bzl",
+        "//python/private/pypi:deps_bzl",
+    ],
+)
+
+bzl_library(
+    name = "python_repository_bzl",
+    srcs = ["python_repository.bzl"],
+    deps = [
+        ":auth_bzl",
+        ":repo_utils_bzl",
+        ":text_util_bzl",
+        "//python:versions_bzl",
+    ],
+)
+
+bzl_library(
+    name = "python_register_multi_toolchains_bzl",
+    srcs = ["python_register_multi_toolchains.bzl"],
+    deps = [
+        ":python_register_toolchains_bzl",
+        ":toolchains_repo_bzl",
+        "//python:versions_bzl",
+    ],
+)
+
+bzl_library(
     name = "pythons_hub_bzl",
     srcs = ["pythons_hub.bzl"],
     deps = [
-        ":full_version_bzl",
         ":py_toolchain_suite_bzl",
-        "//python:versions_bzl",
     ],
 )
 
 bzl_library(
-    name = "py_cc_toolchain_bzl",
-    srcs = [
-        "py_cc_toolchain_macro.bzl",
-        "py_cc_toolchain_rule.bzl",
+    name = "py_cc_toolchain_macro_bzl",
+    srcs = ["py_cc_toolchain_macro.bzl"],
+    deps = [
+        ":py_cc_toolchain_rule_bzl",
     ],
-    visibility = [
-        "//docs:__subpackages__",
-        "//python/cc:__pkg__",
-    ],
+)
+
+bzl_library(
+    name = "py_cc_toolchain_rule_bzl",
+    srcs = ["py_cc_toolchain_rule.bzl"],
     deps = [
         ":py_cc_toolchain_info_bzl",
         ":rules_cc_srcs_bzl",
@@ -188,7 +229,6 @@
 bzl_library(
     name = "py_cc_toolchain_info_bzl",
     srcs = ["py_cc_toolchain_info.bzl"],
-    visibility = ["//python/cc:__pkg__"],
 )
 
 bzl_library(
@@ -204,16 +244,29 @@
 )
 
 bzl_library(
+    name = "py_exec_tools_info_bzl",
+    srcs = ["py_exec_tools_info.bzl"],
+)
+
+bzl_library(
     name = "py_exec_tools_toolchain_bzl",
     srcs = ["py_exec_tools_toolchain.bzl"],
     deps = [
+        ":py_exec_tools_info_bzl",
+        ":sentinel_bzl",
         ":toolchain_types_bzl",
         "//python/private/common:providers_bzl",
+        "@bazel_skylib//lib:paths",
         "@bazel_skylib//rules:common_settings",
     ],
 )
 
 bzl_library(
+    name = "py_executable_info_bzl",
+    srcs = ["py_executable_info.bzl"],
+)
+
+bzl_library(
     name = "py_interpreter_program_bzl",
     srcs = ["py_interpreter_program.bzl"],
     deps = ["@bazel_skylib//rules:common_settings"],
@@ -283,6 +336,16 @@
 )
 
 bzl_library(
+    name = "semver_bzl",
+    srcs = ["semver.bzl"],
+)
+
+bzl_library(
+    name = "sentinel_bzl",
+    srcs = ["sentinel.bzl"],
+)
+
+bzl_library(
     name = "stamp_bzl",
     srcs = ["stamp.bzl"],
     visibility = ["//:__subpackages__"],
@@ -298,6 +361,7 @@
     srcs = ["toolchains_repo.bzl"],
     deps = [
         ":repo_utils_bzl",
+        ":text_util_bzl",
         "//python:versions_bzl",
     ],
 )
@@ -346,7 +410,6 @@
     [
         "coverage.patch",
         "repack_whl.py",
-        "py_cc_toolchain_rule.bzl",
         "py_package.bzl",
         "py_wheel.bzl",
         "py_wheel_normalize_pep440.bzl",
@@ -433,6 +496,12 @@
     ],
 )
 
+py_binary(
+    name = "py_wheel_dist",
+    srcs = ["py_wheel_dist.py"],
+    visibility = ["//visibility:public"],
+)
+
 py_library(
     name = "py_console_script_gen_lib",
     srcs = ["py_console_script_gen.py"],
@@ -450,3 +519,7 @@
     # py_exec_tools_toolchain.
     visibility = ["//visibility:public"],
 )
+
+sentinel(
+    name = "sentinel",
+)
diff --git a/python/private/common/BUILD.bazel b/python/private/common/BUILD.bazel
index a415e05..805c002 100644
--- a/python/private/common/BUILD.bazel
+++ b/python/private/common/BUILD.bazel
@@ -132,9 +132,11 @@
         ":providers_bzl",
         ":py_internal_bzl",
         "//python/private:flags_bzl",
+        "//python/private:py_executable_info_bzl",
         "//python/private:rules_cc_srcs_bzl",
         "//python/private:toolchain_types_bzl",
         "@bazel_skylib//lib:dicts",
+        "@bazel_skylib//lib:structs",
         "@bazel_skylib//rules:common_settings",
     ],
 )
diff --git a/python/private/common/attributes.bzl b/python/private/common/attributes.bzl
index 503578b..90a5332 100644
--- a/python/private/common/attributes.bzl
+++ b/python/private/common/attributes.bzl
@@ -16,7 +16,7 @@
 load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load("@rules_cc//cc:defs.bzl", "CcInfo")
 load("//python/private:enum.bzl", "enum")
-load("//python/private:flags.bzl", "PrecompileFlag")
+load("//python/private:flags.bzl", "PrecompileFlag", "PrecompileSourceRetentionFlag")
 load("//python/private:reexports.bzl", "BuiltinPyInfo")
 load(":common.bzl", "union_attrs")
 load(":providers.bzl", "PyInfo")
@@ -85,7 +85,7 @@
 def _precompile_source_retention_get_effective_value(ctx):
     attr_value = ctx.attr.precompile_source_retention
     if attr_value == PrecompileSourceRetentionAttr.INHERIT:
-        attr_value = ctx.attr._precompile_source_retention_flag[BuildSettingInfo].value
+        attr_value = PrecompileSourceRetentionFlag.get_effective_value(ctx)
 
     if attr_value not in (
         PrecompileSourceRetentionAttr.KEEP_SOURCE,
@@ -278,19 +278,26 @@
         ),
         "precompile": attr.string(
             doc = """
-Whether py source files should be precompiled.
-
-See also: `--precompile` flag, which can override this attribute in some cases.
+Whether py source files **for this target** should be precompiled.
 
 Values:
 
-* `inherit`: Determine the value from the --precompile flag.
+* `inherit`: Determine the value from the {flag}`--precompile` flag.
 * `enabled`: Compile Python source files at build time. Note that
   --precompile_add_to_runfiles affects how the compiled files are included into
   a downstream binary.
 * `disabled`: Don't compile Python source files at build time.
 * `if_generated_source`: Compile Python source files, but only if they're a
   generated file.
+
+:::{seealso}
+
+* The {flag}`--precompile` flag, which can override this attribute in some cases
+  and will affect all targets when building.
+* The {obj}`pyc_collection` attribute for transitively enabling precompiling on
+  a per-target basis.
+* The [Precompiling](precompiling) docs for a guide about using precompiling.
+:::
 """,
             default = PrecompileAttr.INHERIT,
             values = sorted(PrecompileAttr.__members__.values()),
@@ -333,7 +340,7 @@
 Determines, when a source file is compiled, if the source file is kept
 in the resulting output or not. Valid values are:
 
-* `inherit`: Inherit the value from the `--precompile_source_retention` flag.
+* `inherit`: Inherit the value from the {flag}`--precompile_source_retention` flag.
 * `keep_source`: Include the original Python source.
 * `omit_source`: Don't include the original py source.
 * `omit_if_generated_source`: Keep the original source if it's a regular source
diff --git a/python/private/common/py_executable.bzl b/python/private/common/py_executable.bzl
index 2b4a939..80418ac 100644
--- a/python/private/common/py_executable.bzl
+++ b/python/private/common/py_executable.bzl
@@ -14,9 +14,11 @@
 """Common functionality between test/binary executables."""
 
 load("@bazel_skylib//lib:dicts.bzl", "dicts")
+load("@bazel_skylib//lib:structs.bzl", "structs")
 load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load("@rules_cc//cc:defs.bzl", "cc_common")
 load("//python/private:flags.bzl", "PrecompileAddToRunfilesFlag")
+load("//python/private:py_executable_info.bzl", "PyExecutableInfo")
 load("//python/private:reexports.bzl", "BuiltinPyRuntimeInfo")
 load(
     "//python/private:toolchain_types.bzl",
@@ -96,14 +98,15 @@
             doc = """
 Determines whether pyc files from dependencies should be manually included.
 
-NOTE: This setting is only useful with `--precompile_add_to_runfiles=decided_elsewhere`.
+NOTE: This setting is only useful with {flag}`--precompile_add_to_runfiles=decided_elsewhere`.
 
 Valid values are:
+* `inherit`: Inherit the value from {flag}`--pyc_collection`.
 * `include_pyc`: Add pyc files from dependencies in the binary (from
-  `PyInfo.transitive_pyc_files`.
+  {obj}`PyInfo.transitive_pyc_files`.
 * `disabled`: Don't explicitly add pyc files from dependencies. Note that
   pyc files may still come from dependencies if a target includes them as
-  part of their runfiles (such as when `--precompile_add_to_runfiles=always`
+  part of their runfiles (such as when {obj}`--precompile_add_to_runfiles=always`
   is used).
 """,
         ),
@@ -220,10 +223,14 @@
     extra_exec_runfiles = exec_result.extra_runfiles.merge(
         ctx.runfiles(transitive_files = exec_result.extra_files_to_build),
     )
-    runfiles_details = struct(
-        default_runfiles = runfiles_details.default_runfiles.merge(extra_exec_runfiles),
-        data_runfiles = runfiles_details.data_runfiles.merge(extra_exec_runfiles),
-    )
+
+    # Copy any existing fields in case of company patches.
+    runfiles_details = struct(**(
+        structs.to_dict(runfiles_details) | dict(
+            default_runfiles = runfiles_details.default_runfiles.merge(extra_exec_runfiles),
+            data_runfiles = runfiles_details.data_runfiles.merge(extra_exec_runfiles),
+        )
+    ))
 
     return _create_providers(
         ctx = ctx,
@@ -399,8 +406,8 @@
         semantics):
     """Returns the set of runfiles necessary prior to executable creation.
 
-    NOTE: The term "common runfiles" refers to the runfiles that both the
-    default and data runfiles have in common.
+    NOTE: The term "common runfiles" refers to the runfiles that are common to
+        runfiles_without_exe, default_runfiles, and data_runfiles.
 
     Args:
         ctx: The rule ctx.
@@ -417,6 +424,10 @@
         struct with attributes:
         * default_runfiles: The default runfiles
         * data_runfiles: The data runfiles
+        * runfiles_without_exe: The default runfiles, but without the executable
+          or files specific to the original program/executable.
+        * build_data_file: A file with build stamp information if stamping is enabled, otherwise
+          None.
     """
     common_runfiles_depsets = [main_py_files]
 
@@ -430,7 +441,6 @@
             common_runfiles_depsets.append(dep[PyInfo].transitive_pyc_files)
 
     common_runfiles = collect_runfiles(ctx, depset(
-        direct = [executable],
         transitive = common_runfiles_depsets,
     ))
     if extra_deps:
@@ -446,23 +456,31 @@
             runfiles = common_runfiles,
         )
 
+    # Don't include build_data.txt in the non-exe runfiles. The build data
+    # may contain program-specific content (e.g. target name).
+    runfiles_with_exe = common_runfiles.merge(ctx.runfiles([executable]))
+
     # Don't include build_data.txt in data runfiles. This allows binaries to
     # contain other binaries while still using the same fixed location symlink
     # for the build_data.txt file. Really, the fixed location symlink should be
     # removed and another way found to locate the underlying build data file.
-    data_runfiles = common_runfiles
+    data_runfiles = runfiles_with_exe
 
     if is_stamping_enabled(ctx, semantics) and semantics.should_include_build_data(ctx):
-        default_runfiles = common_runfiles.merge(_create_runfiles_with_build_data(
+        build_data_file, build_data_runfiles = _create_runfiles_with_build_data(
             ctx,
             semantics.get_central_uncachable_version_file(ctx),
             semantics.get_extra_write_build_data_env(ctx),
-        ))
+        )
+        default_runfiles = runfiles_with_exe.merge(build_data_runfiles)
     else:
-        default_runfiles = common_runfiles
+        build_data_file = None
+        default_runfiles = runfiles_with_exe
 
     return struct(
+        runfiles_without_exe = common_runfiles,
         default_runfiles = default_runfiles,
+        build_data_file = build_data_file,
         data_runfiles = data_runfiles,
     )
 
@@ -470,15 +488,15 @@
         ctx,
         central_uncachable_version_file,
         extra_write_build_data_env):
-    return ctx.runfiles(
-        symlinks = {
-            BUILD_DATA_SYMLINK_PATH: _write_build_data(
-                ctx,
-                central_uncachable_version_file,
-                extra_write_build_data_env,
-            ),
-        },
+    build_data_file = _write_build_data(
+        ctx,
+        central_uncachable_version_file,
+        extra_write_build_data_env,
     )
+    build_data_runfiles = ctx.runfiles(symlinks = {
+        BUILD_DATA_SYMLINK_PATH: build_data_file,
+    })
+    return build_data_file, build_data_runfiles
 
 def _write_build_data(ctx, central_uncachable_version_file, extra_write_build_data_env):
     # TODO: Remove this logic when a central file is always available
@@ -813,6 +831,12 @@
         ),
         create_instrumented_files_info(ctx),
         _create_run_environment_info(ctx, inherited_environment),
+        PyExecutableInfo(
+            main = main_py,
+            runfiles_without_exe = runfiles_details.runfiles_without_exe,
+            build_data_file = runfiles_details.build_data_file,
+            interpreter_path = runtime_details.executable_interpreter_path,
+        ),
     ]
 
     # TODO(b/265840007): Make this non-conditional once Google enables
@@ -903,6 +927,7 @@
     if "py" not in fragments:
         # The list might be frozen, so use concatentation
         fragments = fragments + ["py"]
+    kwargs.setdefault("provides", []).append(PyExecutableInfo)
     return rule(
         # TODO: add ability to remove attrs, i.e. for imports attr
         attrs = dicts.add(EXECUTABLE_ATTRS, attrs),
diff --git a/python/private/common/py_executable_bazel.bzl b/python/private/common/py_executable_bazel.bzl
index a0cfeba..dae1c4a 100644
--- a/python/private/common/py_executable_bazel.bzl
+++ b/python/private/common/py_executable_bazel.bzl
@@ -330,7 +330,9 @@
         imports,
         runtime_details):
     output = ctx.actions.declare_file(
-        "{}_stage2_bootstrap.py".format(output_prefix),
+        # Prepend with underscore to prevent pytest from trying to
+        # process the bootstrap for files starting with `test_`
+        "_{}_stage2_bootstrap.py".format(output_prefix),
         sibling = output_sibling,
     )
     runtime = runtime_details.effective_runtime
diff --git a/python/private/common/py_library.bzl b/python/private/common/py_library.bzl
index 673beed..fd53490 100644
--- a/python/private/common/py_library.bzl
+++ b/python/private/common/py_library.bzl
@@ -127,14 +127,16 @@
 
     # Within Google, the doc attribute is overridden
     kwargs.setdefault("doc", _DEFAULT_PY_LIBRARY_DOC)
+
+    # TODO: b/253818097 - fragments=py is only necessary so that
+    # RequiredConfigFragmentsTest passes
+    fragments = kwargs.pop("fragments", None) or []
     return rule(
         attrs = dicts.add(LIBRARY_ATTRS, attrs),
         toolchains = [
             config_common.toolchain_type(TOOLCHAIN_TYPE, mandatory = False),
             config_common.toolchain_type(EXEC_TOOLS_TOOLCHAIN_TYPE, mandatory = False),
         ],
-        # TODO(b/253818097): fragments=py is only necessary so that
-        # RequiredConfigFragmentsTest passes
-        fragments = ["py"],
+        fragments = fragments + ["py"],
         **kwargs
     )
diff --git a/python/private/common/py_runtime_rule.bzl b/python/private/common/py_runtime_rule.bzl
index e0b5fb2..b339425 100644
--- a/python/private/common/py_runtime_rule.bzl
+++ b/python/private/common/py_runtime_rule.bzl
@@ -15,6 +15,7 @@
 
 load("@bazel_skylib//lib:dicts.bzl", "dicts")
 load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load("//python/private:reexports.bzl", "BuiltinPyRuntimeInfo")
 load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")
 load(":attributes.bzl", "NATIVE_RULES_ALLOWLIST_ATTRS")
@@ -80,6 +81,10 @@
     python_version = ctx.attr.python_version
 
     interpreter_version_info = ctx.attr.interpreter_version_info
+    if not interpreter_version_info:
+        python_version_flag = ctx.attr._python_version_flag[BuildSettingInfo].value
+        if python_version_flag:
+            interpreter_version_info = _interpreter_version_info_from_version_str(python_version_flag)
 
     # TODO: Uncomment this after --incompatible_python_disable_py2 defaults to true
     # if ctx.fragments.py.disable_py2 and python_version == "PY2":
@@ -133,13 +138,6 @@
         ),
     ]
 
-def _is_singleton_depset(files):
-    # Bazel 6 doesn't have this helper to optimize detecting singleton depsets.
-    if _py_builtins:
-        return _py_builtins.is_singleton_depset(files)
-    else:
-        return len(files.to_list()) == 1
-
 # Bind to the name "py_runtime" to preserve the kind/rule_class it shows up
 # as elsewhere.
 py_runtime = rule(
@@ -204,8 +202,8 @@
         "coverage_tool": attr.label(
             allow_files = False,
             doc = """
-This is a target to use for collecting code coverage information from `py_binary`
-and `py_test` targets.
+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,
@@ -214,7 +212,7 @@
 
 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
+of [`coverage.py`](https://coverage.readthedocs.io), at least including
 the `run` and `lcov` subcommands.
 """,
         ),
@@ -260,15 +258,22 @@
 """),
         "interpreter_version_info": attr.string_dict(
             doc = """
-Version information about the interpreter this runtime provides. The
-supported keys match the names for `sys.version_info`. While the input
+Version information about the interpreter this runtime provides.
+
+If not specified, uses {obj}`--python_version`
+
+The supported keys match the names for `sys.version_info`. While the input
 values are strings, most are converted to ints. The supported keys are:
   * major: int, the major version number
   * minor: int, the minor version number
   * micro: optional int, the micro version number
   * releaselevel: optional str, the release level
-  * serial: optional int, the serial number of the release"
-            """,
+  * serial: optional int, the serial number of the release
+
+:::{versionchanged} 0.36.0
+{obj}`--python_version` determines the default value.
+:::
+""",
             mandatory = False,
         ),
         "pyc_tag": attr.string(
@@ -306,7 +311,7 @@
             default = DEFAULT_STUB_SHEBANG,
             doc = """
 "Shebang" expression prepended to the bootstrapping Python stub script
-used when executing `py_binary` targets.
+used when executing {rule}`py_binary` targets.
 
 See https://github.com/bazelbuild/bazel/issues/8685 for
 motivation.
@@ -327,5 +332,25 @@
 :::
 """,
         ),
+        "_python_version_flag": attr.label(
+            default = "//python/config_settings:python_version",
+        ),
     }),
 )
+
+def _is_singleton_depset(files):
+    # Bazel 6 doesn't have this helper to optimize detecting singleton depsets.
+    if _py_builtins:
+        return _py_builtins.is_singleton_depset(files)
+    else:
+        return len(files.to_list()) == 1
+
+def _interpreter_version_info_from_version_str(version_str):
+    parts = version_str.split(".")
+    version_info = {}
+    for key in ("major", "minor", "micro"):
+        if not parts:
+            break
+        version_info[key] = parts.pop(0)
+
+    return version_info
diff --git a/python/private/config_settings.bzl b/python/private/config_settings.bzl
index 0537655..b15d6a8 100644
--- a/python/private/config_settings.bzl
+++ b/python/private/config_settings.bzl
@@ -16,187 +16,130 @@
 """
 
 load("@bazel_skylib//lib:selects.bzl", "selects")
-load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
-load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
+load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
+load(":semver.bzl", "semver")
 
-_PYTHON_VERSION_FLAG = str(Label("//python/config_settings:python_version"))
+_PYTHON_VERSION_FLAG = Label("//python/config_settings:python_version")
+_PYTHON_VERSION_MAJOR_MINOR_FLAG = Label("//python/config_settings:_python_version_major_minor")
 
-def _ver_key(s):
-    major, _, s = s.partition(".")
-    minor, _, s = s.partition(".")
-    micro, _, s = s.partition(".")
-    return (int(major), int(minor), int(micro))
-
-def _flag_values(python_versions):
-    """Construct a map of python_version to a list of toolchain values.
-
-    This mapping maps the concept of a config setting to a list of compatible toolchain versions.
-    For using this in the code, the VERSION_FLAG_VALUES should be used instead.
-
-    Args:
-        python_versions: list of strings; all X.Y.Z python versions
-
-    Returns:
-        A `map[str, list[str]]`. Each key is a python_version flag value. Each value
-        is a list of the python_version flag values that should match when for the
-        `key`. For example:
-        ```
-         "3.8" -> ["3.8", "3.8.1", "3.8.2", ..., "3.8.19"]  # All 3.8 versions
-         "3.8.2" -> ["3.8.2"]  # Only 3.8.2
-         "3.8.19" -> ["3.8.19", "3.8"]  # The latest version should also match 3.8 so
-             as when the `3.8` toolchain is used we just use the latest `3.8` toolchain.
-             this makes the `select("is_python_3.8.19")` work no matter how the user
-             specifies the latest python version to use.
-        ```
-    """
-    ret = {}
-
-    for micro_version in sorted(python_versions, key = _ver_key):
-        minor_version, _, _ = micro_version.rpartition(".")
-
-        # This matches the raw flag value, e.g. --//python/config_settings:python_version=3.8
-        # It's private because matching the concept of e.g. "3.8" value is done
-        # using the `is_python_X.Y` config setting group, which is aware of the
-        # minor versions that could match instead.
-        ret.setdefault(minor_version, [minor_version]).append(micro_version)
-
-        # Ensure that is_python_3.9.8 is matched if python_version is set
-        # to 3.9 if MINOR_MAPPING points to 3.9.8
-        default_micro_version = MINOR_MAPPING[minor_version]
-        ret[micro_version] = [micro_version, minor_version] if default_micro_version == micro_version else [micro_version]
-
-    return ret
-
-VERSION_FLAG_VALUES = _flag_values(TOOL_VERSIONS.keys())
-
-def is_python_config_setting(name, *, python_version, reuse_conditions = None, **kwargs):
-    """Create a config setting for matching 'python_version' configuration flag.
-
-    This function is mainly intended for internal use within the `whl_library` and `pip_parse`
-    machinery.
-
-    The matching of the 'python_version' flag depends on the value passed in
-    `python_version` and here is the example for `3.8` (but the same applies
-    to other python versions present in @//python:versions.bzl#TOOL_VERSIONS):
-     * "3.8" -> ["3.8", "3.8.1", "3.8.2", ..., "3.8.19"]  # All 3.8 versions
-     * "3.8.2" -> ["3.8.2"]  # Only 3.8.2
-     * "3.8.19" -> ["3.8.19", "3.8"]  # The latest version should also match 3.8 so
-         as when the `3.8` toolchain is used we just use the latest `3.8` toolchain.
-         this makes the `select("is_python_3.8.19")` work no matter how the user
-         specifies the latest python version to use.
-
-    Args:
-        name: name for the target that will be created to be used in select statements.
-        python_version: The python_version to be passed in the `flag_values` in the
-            `config_setting`. Depending on the version, the matching python version list
-            can be as described above.
-        reuse_conditions: A dict of version to version label for which we should
-            reuse config_setting targets instead of creating them from scratch. This
-            is useful when using is_python_config_setting multiple times in the
-            same package with the same `major.minor` python versions.
-        **kwargs: extra kwargs passed to the `config_setting`.
-    """
-    if python_version not in name:
-        fail("The name '{}' must have the python version '{}' in it".format(name, python_version))
-
-    if python_version not in VERSION_FLAG_VALUES:
-        fail("The 'python_version' must be known to 'rules_python', choose from the values: {}".format(VERSION_FLAG_VALUES.keys()))
-
-    python_versions = VERSION_FLAG_VALUES[python_version]
-    extra_flag_values = kwargs.pop("flag_values", {})
-    if _PYTHON_VERSION_FLAG in extra_flag_values:
-        fail("Cannot set '{}' in the flag values".format(_PYTHON_VERSION_FLAG))
-
-    if len(python_versions) == 1:
-        native.config_setting(
-            name = name,
-            flag_values = {
-                _PYTHON_VERSION_FLAG: python_version,
-            } | extra_flag_values,
-            **kwargs
-        )
-        return
-
-    reuse_conditions = reuse_conditions or {}
-    create_config_settings = {
-        "_{}".format(name).replace(python_version, version): {_PYTHON_VERSION_FLAG: version}
-        for version in python_versions
-        if not reuse_conditions or version not in reuse_conditions
-    }
-    match_any = list(create_config_settings.keys())
-    for version, condition in reuse_conditions.items():
-        if len(VERSION_FLAG_VALUES[version]) == 1:
-            match_any.append(condition)
-            continue
-
-        # Convert the name to an internal label that this function would create,
-        # so that we are hitting the config_setting and not the config_setting_group.
-        condition = Label(condition)
-        if hasattr(condition, "same_package_label"):
-            condition = condition.same_package_label("_" + condition.name)
-        else:
-            condition = condition.relative("_" + condition.name)
-
-        match_any.append(condition)
-
-    for name_, flag_values_ in create_config_settings.items():
-        native.config_setting(
-            name = name_,
-            flag_values = flag_values_ | extra_flag_values,
-            **kwargs
-        )
-
-    # An alias pointing to an underscore-prefixed config_setting_group
-    # is used because config_setting_group creates
-    # `is_{version}_N` targets, which are easily confused with the
-    # `is_{minor}.{micro}` (dot) targets.
-    selects.config_setting_group(
-        name = "_{}_group".format(name),
-        match_any = match_any,
-        visibility = ["//visibility:private"],
-    )
-    native.alias(
-        name = name,
-        actual = "_{}_group".format(name),
-        visibility = kwargs.get("visibility", []),
-    )
-
-def construct_config_settings(name = None):  # buildifier: disable=function-docstring
+def construct_config_settings(*, name, versions, minor_mapping):  # buildifier: disable=function-docstring
     """Create a 'python_version' config flag and construct all config settings used in rules_python.
 
     This mainly includes the targets that are used in the toolchain and pip hub
     repositories that only match on the 'python_version' flag values.
 
     Args:
-        name(str): A dummy name value that is no-op for now.
+        name: {type}`str` A dummy name value that is no-op for now.
+        versions: {type}`list[str]` A list of versions to build constraint settings for.
+        minor_mapping: {type}`dict[str, str]` A mapping from `X.Y` to `X.Y.Z` python versions.
     """
-    string_flag(
-        name = "python_version",
+    _ = name  # @unused
+    _python_version_flag(
+        name = _PYTHON_VERSION_FLAG.name,
         # TODO: The default here should somehow match the MODULE config. Until
         # then, use the empty string to indicate an unknown version. This
         # also prevents version-unaware targets from inadvertently matching
         # a select condition when they shouldn't.
         build_setting_default = "",
-        values = [""] + VERSION_FLAG_VALUES.keys(),
+        visibility = ["//visibility:public"],
+    )
+
+    _python_version_major_minor_flag(
+        name = _PYTHON_VERSION_MAJOR_MINOR_FLAG.name,
+        build_setting_default = "",
         visibility = ["//visibility:public"],
     )
 
     native.config_setting(
         name = "is_python_version_unset",
-        flag_values = {
-            Label("//python/config_settings:python_version"): "",
-        },
+        flag_values = {_PYTHON_VERSION_FLAG: ""},
         visibility = ["//visibility:public"],
     )
 
-    for version, matching_versions in VERSION_FLAG_VALUES.items():
-        is_python_config_setting(
-            name = "is_python_{}".format(version),
-            python_version = version,
-            reuse_conditions = {
-                v: native.package_relative_label("is_python_{}".format(v))
-                for v in matching_versions
-                if v != version
-            },
+    _reverse_minor_mapping = {full: minor for minor, full in minor_mapping.items()}
+    for version in versions:
+        minor_version = _reverse_minor_mapping.get(version)
+        if not minor_version:
+            native.config_setting(
+                name = "is_python_{}".format(version),
+                flag_values = {":python_version": version},
+                visibility = ["//visibility:public"],
+            )
+            continue
+
+        # Also need to match the minor version when using
+        name = "is_python_{}".format(version)
+        native.config_setting(
+            name = "_" + name,
+            flag_values = {":python_version": version},
             visibility = ["//visibility:public"],
         )
+
+        # An alias pointing to an underscore-prefixed config_setting_group
+        # is used because config_setting_group creates
+        # `is_{version}_N` targets, which are easily confused with the
+        # `is_{minor}.{micro}` (dot) targets.
+        selects.config_setting_group(
+            name = "_{}_group".format(name),
+            match_any = [
+                ":_is_python_{}".format(version),
+                ":is_python_{}".format(minor_version),
+            ],
+            visibility = ["//visibility:private"],
+        )
+        native.alias(
+            name = name,
+            actual = "_{}_group".format(name),
+            visibility = ["//visibility:public"],
+        )
+
+    # This matches the raw flag value, e.g. --//python/config_settings:python_version=3.8
+    # It's private because matching the concept of e.g. "3.8" value is done
+    # using the `is_python_X.Y` config setting group, which is aware of the
+    # minor versions that could match instead.
+    for minor in minor_mapping.keys():
+        native.config_setting(
+            name = "is_python_{}".format(minor),
+            flag_values = {_PYTHON_VERSION_MAJOR_MINOR_FLAG: minor},
+            visibility = ["//visibility:public"],
+        )
+
+def _python_version_flag_impl(ctx):
+    value = ctx.build_setting_value
+    return [
+        # BuildSettingInfo is the original provider returned, so continue to
+        # return it for compatibility
+        BuildSettingInfo(value = value),
+        # FeatureFlagInfo is returned so that config_setting respects the value
+        # as returned by this rule instead of as originally seen on the command
+        # line.
+        # It is also for Google compatibility, which expects the FeatureFlagInfo
+        # provider.
+        config_common.FeatureFlagInfo(value = value),
+    ]
+
+_python_version_flag = rule(
+    implementation = _python_version_flag_impl,
+    build_setting = config.string(flag = True),
+    attrs = {},
+)
+
+def _python_version_major_minor_flag_impl(ctx):
+    input = ctx.attr._python_version_flag[config_common.FeatureFlagInfo].value
+    if input:
+        version = semver(input)
+        value = "{}.{}".format(version.major, version.minor)
+    else:
+        value = ""
+
+    return [config_common.FeatureFlagInfo(value = value)]
+
+_python_version_major_minor_flag = rule(
+    implementation = _python_version_major_minor_flag_impl,
+    build_setting = config.string(flag = False),
+    attrs = {
+        "_python_version_flag": attr.label(
+            default = _PYTHON_VERSION_FLAG,
+        ),
+    },
+)
diff --git a/python/private/flags.bzl b/python/private/flags.bzl
index fa31262..652e117 100644
--- a/python/private/flags.bzl
+++ b/python/private/flags.bzl
@@ -74,10 +74,18 @@
     get_effective_value = _precompile_flag_get_effective_value,
 )
 
+def _precompile_source_retention_flag_get_effective_value(ctx):
+    value = ctx.attr._precompile_source_retention_flag[BuildSettingInfo].value
+    if value == PrecompileSourceRetentionFlag.AUTO:
+        value = PrecompileSourceRetentionFlag.KEEP_SOURCE
+    return value
+
 # Determines if, when a source file is compiled, if the source file is kept
 # in the resulting output or not.
 # buildifier: disable=name-conventions
 PrecompileSourceRetentionFlag = enum(
+    # Automatically decide the effective value based on environment, etc.
+    AUTO = "auto",
     # Include the original py source in the output.
     KEEP_SOURCE = "keep_source",
     # Don't include the original py source.
@@ -85,6 +93,7 @@
     # Keep the original py source if it's a regular source file, but omit it
     # if it's a generated file.
     OMIT_IF_GENERATED_SOURCE = "omit_if_generated_source",
+    get_effective_value = _precompile_source_retention_flag_get_effective_value,
 )
 
 # Determines if a target adds its compiled files to its runfiles. When a target
diff --git a/python/private/full_version.bzl b/python/private/full_version.bzl
index 98eeee5..0292d6c 100644
--- a/python/private/full_version.bzl
+++ b/python/private/full_version.bzl
@@ -14,20 +14,19 @@
 
 """A small helper to ensure that we are working with full versions."""
 
-load("//python:versions.bzl", "MINOR_MAPPING")
-
-def full_version(version):
+def full_version(*, version, minor_mapping):
     """Return a full version.
 
     Args:
-        version: the version in `X.Y` or `X.Y.Z` format.
+        version: {type}`str` the version in `X.Y` or `X.Y.Z` format.
+        minor_mapping: {type}`dict[str, str]` mapping between `X.Y` to `X.Y.Z` format.
 
     Returns:
         a full version given the version string. If the string is already a
         major version then we return it as is.
     """
-    if version in MINOR_MAPPING:
-        return MINOR_MAPPING[version]
+    if version in minor_mapping:
+        return minor_mapping[version]
 
     parts = version.split(".")
     if len(parts) == 3:
@@ -36,7 +35,7 @@
         fail(
             "Unknown Python version '{}', available values are: {}".format(
                 version,
-                ",".join(MINOR_MAPPING.keys()),
+                ",".join(minor_mapping.keys()),
             ),
         )
     else:
diff --git a/python/private/hermetic_runtime_repo_setup.bzl b/python/private/hermetic_runtime_repo_setup.bzl
new file mode 100644
index 0000000..4b5a3c6
--- /dev/null
+++ b/python/private/hermetic_runtime_repo_setup.bzl
@@ -0,0 +1,161 @@
+# Copyright 2024 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.
+"""Setup a python-build-standalone based toolchain."""
+
+load("@rules_cc//cc:defs.bzl", "cc_import", "cc_library")
+load("//python:py_runtime.bzl", "py_runtime")
+load("//python:py_runtime_pair.bzl", "py_runtime_pair")
+load("//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain")
+load(":py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain")
+load(":semver.bzl", "semver")
+
+def define_hermetic_runtime_toolchain_impl(
+        *,
+        name,
+        extra_files_glob_include,
+        extra_files_glob_exclude,
+        python_version,
+        python_bin,
+        coverage_tool):
+    """Define a toolchain implementation for a python-build-standalone repo.
+
+    It expected this macro is called in the top-level package of an extracted
+    python-build-standalone repository. See
+    python/private/python_repositories.bzl for how it is invoked.
+
+    Args:
+        name: {type}`str` name used for tools to identify the invocation.
+        extra_files_glob_include: {type}`list[str]` additional glob include
+            patterns for the target runtime files (the one included in
+            binaries).
+        extra_files_glob_exclude: {type}`list[str]` additional glob exclude
+            patterns for the target runtime files.
+        python_version: {type}`str` The Python version, in `major.minor.micro`
+            format.
+        python_bin: {type}`str` The path to the Python binary within the
+            repositoroy.
+        coverage_tool: {type}`str` optional target to the coverage tool to
+            use.
+    """
+    _ = name  # @unused
+    version_info = semver(python_version)
+    version_dict = version_info.to_dict()
+    native.filegroup(
+        name = "files",
+        srcs = native.glob(
+            include = [
+                "bin/**",
+                "extensions/**",
+                "include/**",
+                "libs/**",
+                "share/**",
+            ] + extra_files_glob_include,
+            # Platform-agnostic filegroup can't match on all patterns.
+            allow_empty = True,
+            exclude = [
+                "**/* *",  # Bazel does not support spaces in file names.
+                # Unused shared libraries. `python` executable and the `:libpython` target
+                # depend on `libpython{python_version}.so.1.0`.
+                "lib/libpython{major}.{minor}.so".format(**version_dict),
+                # static libraries
+                "lib/**/*.a",
+                # tests for the standard libraries.
+                "lib/python{major}.{minor}/**/test/**".format(**version_dict),
+                "lib/python{major}.{minor}/**/tests/**".format(**version_dict),
+                "**/__pycache__/*.pyc.*",  # During pyc creation, temp files named *.pyc.NNN are created
+            ] + extra_files_glob_exclude,
+        ),
+    )
+    cc_import(
+        name = "interface",
+        interface_library = "libs/python{major}{minor}.lib".format(**version_dict),
+        system_provided = True,
+    )
+
+    native.filegroup(
+        name = "includes",
+        srcs = native.glob(["include/**/*.h"]),
+    )
+    cc_library(
+        name = "python_headers",
+        deps = select({
+            "@bazel_tools//src/conditions:windows": [":interface"],
+            "//conditions:default": None,
+        }),
+        hdrs = [":includes"],
+        includes = [
+            "include",
+            "include/python{major}.{minor}".format(**version_dict),
+            "include/python{major}.{minor}m".format(**version_dict),
+        ],
+    )
+    cc_library(
+        name = "libpython",
+        hdrs = [":includes"],
+        srcs = select({
+            "@platforms//os:linux": [
+                "lib/libpython{major}.{minor}.so".format(**version_dict),
+                "lib/libpython{major}.{minor}.so.1.0".format(**version_dict),
+            ],
+            "@platforms//os:macos": ["lib/libpython{major}.{minor}.dylib".format(**version_dict)],
+            "@platforms//os:windows": ["python3.dll", "libs/python{major}{minor}.lib".format(**version_dict)],
+        }),
+    )
+
+    native.exports_files(["python", python_bin])
+
+    # Used to only download coverage toolchain when the coverage is collected by
+    # bazel.
+    native.config_setting(
+        name = "coverage_enabled",
+        values = {"collect_code_coverage": "true"},
+        visibility = ["//visibility:private"],
+    )
+
+    py_runtime(
+        name = "py3_runtime",
+        files = [":files"],
+        interpreter = python_bin,
+        interpreter_version_info = {
+            "major": str(version_info.major),
+            "micro": str(version_info.patch),
+            "minor": str(version_info.minor),
+        },
+        # Convert empty string to None
+        coverage_tool = coverage_tool or None,
+        python_version = "PY3",
+        implementation_name = "cpython",
+        # See https://peps.python.org/pep-3147/ for pyc tag infix format
+        pyc_tag = "cpython-{major}{minor}".format(**version_dict),
+    )
+
+    py_runtime_pair(
+        name = "python_runtimes",
+        py2_runtime = None,
+        py3_runtime = ":py3_runtime",
+    )
+
+    py_cc_toolchain(
+        name = "py_cc_toolchain",
+        headers = ":python_headers",
+        libs = ":libpython",
+        python_version = python_version,
+    )
+
+    py_exec_tools_toolchain(
+        name = "py_exec_tools_toolchain",
+        # This macro is called in another repo: use Label() to ensure it
+        # resolves in the rules_python context.
+        precompiler = Label("//tools/precompiler:precompiler"),
+    )
diff --git a/python/private/is_standalone_interpreter.bzl b/python/private/is_standalone_interpreter.bzl
new file mode 100644
index 0000000..5da7389
--- /dev/null
+++ b/python/private/is_standalone_interpreter.bzl
@@ -0,0 +1,50 @@
+# Copyright 2024 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.
+
+"""This file contains repository rules and macros to support toolchain registration.
+"""
+
+load(":repo_utils.bzl", "repo_utils")
+
+STANDALONE_INTERPRETER_FILENAME = "STANDALONE_INTERPRETER"
+
+def is_standalone_interpreter(rctx, python_interpreter_path, *, logger = None):
+    """Query a python interpreter target for whether or not it's a rules_rust provided toolchain
+
+    Args:
+        rctx: {type}`repository_ctx` The repository rule's context object.
+        python_interpreter_path: {type}`path` A path representing the interpreter.
+        logger: Optional logger to use for operations.
+
+    Returns:
+        {type}`bool` Whether or not the target is from a rules_python generated toolchain.
+    """
+
+    # Only update the location when using a hermetic toolchain.
+    if not python_interpreter_path:
+        return False
+
+    # This is a rules_python provided toolchain.
+    return repo_utils.execute_unchecked(
+        rctx,
+        op = "IsStandaloneInterpreter",
+        arguments = [
+            "ls",
+            "{}/{}".format(
+                python_interpreter_path.dirname,
+                STANDALONE_INTERPRETER_FILENAME,
+            ),
+        ],
+        logger = logger,
+    ).return_code == 0
diff --git a/python/private/py_cc_toolchain_info.bzl b/python/private/py_cc_toolchain_info.bzl
index ae46bf4..c5cdbd9 100644
--- a/python/private/py_cc_toolchain_info.bzl
+++ b/python/private/py_cc_toolchain_info.bzl
@@ -41,9 +41,9 @@
     represents).
 """,
         "libs": """\
-:type: struct
+:type: struct | None
 
-Information about C libraries, struct with fields:
+If available, information about C libraries, struct with fields:
   * providers_map: A dict of string to provider instances. The key should be
     a fully qualified name (e.g. `@rules_foo//bar:baz.bzl#MyInfo`) of the
     provider to uniquely identify its type.
diff --git a/python/private/py_cc_toolchain_macro.bzl b/python/private/py_cc_toolchain_macro.bzl
index 35276f7..416caac 100644
--- a/python/private/py_cc_toolchain_macro.bzl
+++ b/python/private/py_cc_toolchain_macro.bzl
@@ -22,8 +22,10 @@
 def py_cc_toolchain(**kwargs):
     """Creates a py_cc_toolchain target.
 
+    This is a macro around the {rule}`py_cc_toolchain` rule.
+
     Args:
-        **kwargs: Keyword args to pass onto underlying rule.
+        **kwargs: Keyword args to pass onto underlying {rule}`py_cc_toolchain` rule.
     """
 
     #  This tag is added to easily identify usages through other macros.
diff --git a/python/private/py_cc_toolchain_rule.bzl b/python/private/py_cc_toolchain_rule.bzl
index 1599415..279f86c 100644
--- a/python/private/py_cc_toolchain_rule.bzl
+++ b/python/private/py_cc_toolchain_rule.bzl
@@ -23,6 +23,16 @@
 load(":py_cc_toolchain_info.bzl", "PyCcToolchainInfo")
 
 def _py_cc_toolchain_impl(ctx):
+    if ctx.attr.libs:
+        libs = struct(
+            providers_map = {
+                "CcInfo": ctx.attr.libs[CcInfo],
+                "DefaultInfo": ctx.attr.libs[DefaultInfo],
+            },
+        )
+    else:
+        libs = None
+
     py_cc_toolchain = PyCcToolchainInfo(
         headers = struct(
             providers_map = {
@@ -30,12 +40,7 @@
                 "DefaultInfo": ctx.attr.headers[DefaultInfo],
             },
         ),
-        libs = struct(
-            providers_map = {
-                "CcInfo": ctx.attr.libs[CcInfo],
-                "DefaultInfo": ctx.attr.libs[DefaultInfo],
-            },
-        ),
+        libs = libs,
         python_version = ctx.attr.python_version,
     )
     extra_kwargs = {}
@@ -59,7 +64,6 @@
             doc = ("Target that provides the Python runtime libraries for linking. " +
                    "Typically this is a cc_library target of `.so` files."),
             providers = [CcInfo],
-            mandatory = True,
         ),
         "python_version": attr.string(
             doc = "The Major.minor Python version, e.g. 3.11",
@@ -74,5 +78,11 @@
 
 This rule carries information about the C/C++ side of a Python runtime, e.g.
 headers, shared libraries, etc.
+
+This provides `ToolchainInfo` with the following attributes:
+* `py_cc_toolchain`: {type}`PyCcToolchainInfo`
+* `toolchain_label`: {type}`Label` _only present when `--visibile_for_testing=True`
+  for internal testing_. The rule's label; this allows identifying what toolchain
+  implmentation was selected for testing purposes.
 """,
 )
diff --git a/python/private/py_exec_tools_info.bzl b/python/private/py_exec_tools_info.bzl
index 2998543..b74f480 100644
--- a/python/private/py_exec_tools_info.bzl
+++ b/python/private/py_exec_tools_info.bzl
@@ -17,7 +17,9 @@
     doc = "Build tools used as part of building Python programs.",
     fields = {
         "exec_interpreter": """
-Optional Target; an interpreter valid for running in the exec configuration.
+:type: Target | None
+
+If available, an interpreter valid for running in the exec configuration.
 When running it in an action, use `DefaultInfo.files_to_run` to ensure all its
 files are appropriately available. An exec interpreter may not be available,
 e.g. if all the exec tools are prebuilt binaries.
@@ -33,7 +35,9 @@
 the toolchain.
 """,
         "precompiler": """
-Optional Target. The tool to use for generating pyc files. If not available,
+:type: Target | None
+
+If available, the tool to use for generating pyc files. If not available,
 precompiling will not be available.
 
 Must provide one of the following:
@@ -48,7 +52,7 @@
 (typically `exec_interpreter`). See the `PyInterpreterProgramInfo` provider docs
 for details on how to construct an invocation.
 
-If `testing.ExecutionInfo` is provided, it will be used to set execution
+If {obj}`testing.ExecutionInfo` is provided, it will be used to set execution
 requirements. This can be used to control persistent worker settings.
 
 The precompiler command line API is:
diff --git a/python/private/py_exec_tools_toolchain.bzl b/python/private/py_exec_tools_toolchain.bzl
index a4516d8..957448f 100644
--- a/python/private/py_exec_tools_toolchain.bzl
+++ b/python/private/py_exec_tools_toolchain.bzl
@@ -16,6 +16,7 @@
 
 load("@bazel_skylib//lib:paths.bzl", "paths")
 load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
+load("//python/private:sentinel.bzl", "SentinelInfo")
 load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE")
 load(":py_exec_tools_info.bzl", "PyExecToolsInfo")
 
@@ -24,9 +25,13 @@
     if ctx.attr._visible_for_testing[BuildSettingInfo].value:
         extra_kwargs["toolchain_label"] = ctx.label
 
+    exec_interpreter = ctx.attr.exec_interpreter
+    if SentinelInfo in ctx.attr.exec_interpreter:
+        exec_interpreter = None
+
     return [platform_common.ToolchainInfo(
         exec_tools = PyExecToolsInfo(
-            exec_interpreter = ctx.attr.exec_interpreter,
+            exec_interpreter = exec_interpreter,
             precompiler = ctx.attr.precompiler,
         ),
         **extra_kwargs
@@ -34,16 +39,42 @@
 
 py_exec_tools_toolchain = rule(
     implementation = _py_exec_tools_toolchain_impl,
+    doc = """
+Provides a toolchain for build time tools.
+
+This provides `ToolchainInfo` with the following attributes:
+* `exec_tools`: {type}`PyExecToolsInfo` 
+* `toolchain_label`: {type}`Label` _only present when `--visibile_for_testing=True`
+  for internal testing_. The rule's label; this allows identifying what toolchain
+  implmentation was selected for testing purposes.
+""",
     attrs = {
         "exec_interpreter": attr.label(
             default = "//python/private:current_interpreter_executable",
             cfg = "exec",
-            doc = "See PyexecToolsInfo.exec_interpreter.",
+            doc = """
+An interpreter that is directly usable in the exec configuration
+
+If not specified, the interpreter from {obj}`//python:toolchain_type` will
+be used.
+
+To disable, specify the special target {obj}`//python:none`; the raw value `None`
+will use the default.
+
+:::{note}
+This is only useful for `ctx.actions.run` calls that _directly_ invoke the
+interpreter, which is fairly uncommon and low level. It is better to use a
+`cfg="exec"` attribute that points to a `py_binary` rule instead, which will
+handle all the necessary transitions and runtime setup to invoke a program.
+:::
+
+See {obj}`PyExecToolsInfo.exec_interpreter` for further docs.
+""",
         ),
         "precompiler": attr.label(
             allow_files = True,
             cfg = "exec",
-            doc = "See PyExecToolsInfo.precompiler",
+            doc = "See {obj}`PyExecToolsInfo.precompiler`",
         ),
         "_visible_for_testing": attr.label(
             default = "//python/private:visible_for_testing",
diff --git a/python/private/py_executable_info.bzl b/python/private/py_executable_info.bzl
new file mode 100644
index 0000000..deb1194
--- /dev/null
+++ b/python/private/py_executable_info.bzl
@@ -0,0 +1,40 @@
+"""Implementation of PyExecutableInfo provider."""
+
+PyExecutableInfo = provider(
+    doc = """
+Information about an executable.
+
+This provider is for executable-specific information (e.g. tests and binaries).
+
+:::{versionadded} 0.36.0
+:::
+""",
+    fields = {
+        "build_data_file": """
+:type: None | File
+
+A symlink to build_data.txt if stamping is enabled, otherwise None.
+""",
+        "interpreter_path": """
+:type: None | str
+
+Path to the Python interpreter to use for running the executable itself (not the
+bootstrap script). Either an absolute path (which means it is
+platform-specific), or a runfiles-relative path (which means the interpreter
+should be within `runtime_files`)
+""",
+        "main": """
+:type: File
+
+The user-level entry point file. Usually a `.py` file, but may also be `.pyc`
+file if precompiling is enabled.
+""",
+        "runfiles_without_exe": """
+:type: runfiles
+
+The runfiles the program needs, but without the original executable,
+files only added to support the original executable, or files specific to the
+original program.
+""",
+    },
+)
diff --git a/python/private/py_repositories.bzl b/python/private/py_repositories.bzl
new file mode 100644
index 0000000..8ddcb5d
--- /dev/null
+++ b/python/private/py_repositories.bzl
@@ -0,0 +1,49 @@
+# Copyright 2024 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.
+
+"""This file contains macros to be called during WORKSPACE evaluation."""
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive")
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+load("//python/private/pypi:deps.bzl", "pypi_deps")
+load(":internal_config_repo.bzl", "internal_config_repo")
+
+def http_archive(**kwargs):
+    maybe(_http_archive, **kwargs)
+
+def py_repositories():
+    """Runtime dependencies that users must install.
+
+    This function should be loaded and called in the user's `WORKSPACE`.
+    With `bzlmod` enabled, this function is not needed since `MODULE.bazel` handles transitive deps.
+    """
+    maybe(
+        internal_config_repo,
+        name = "rules_python_internal",
+    )
+    http_archive(
+        name = "bazel_skylib",
+        sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
+        urls = [
+            "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
+            "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
+        ],
+    )
+    http_archive(
+        name = "rules_cc",
+        urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.0.9/rules_cc-0.0.9.tar.gz"],
+        sha256 = "2037875b9a4456dce4a79d112a8ae885bbc4aad968e6587dca6e64f3a0900cdf",
+        strip_prefix = "rules_cc-0.0.9",
+    )
+    pypi_deps()
diff --git a/python/private/py_wheel_dist.py b/python/private/py_wheel_dist.py
new file mode 100644
index 0000000..3af3345
--- /dev/null
+++ b/python/private/py_wheel_dist.py
@@ -0,0 +1,41 @@
+"""A utility for generating the output directory for `py_wheel_dist`."""
+
+import argparse
+import shutil
+from pathlib import Path
+
+
+def parse_args() -> argparse.Namespace:
+    """Parse command line arguments."""
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument(
+        "--wheel", type=Path, required=True, help="The path to a wheel."
+    )
+    parser.add_argument(
+        "--name_file",
+        type=Path,
+        required=True,
+        help="A file containing the sanitized name of the wheel.",
+    )
+    parser.add_argument(
+        "--output",
+        type=Path,
+        required=True,
+        help="The output location to copy the wheel to.",
+    )
+
+    return parser.parse_args()
+
+
+def main() -> None:
+    """The main entrypoint."""
+    args = parse_args()
+
+    wheel_name = args.name_file.read_text(encoding="utf-8").strip()
+    args.output.mkdir(exist_ok=True, parents=True)
+    shutil.copyfile(args.wheel, args.output / wheel_name)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel
index 3b11dbe..8cfd3d6 100644
--- a/python/private/pypi/BUILD.bazel
+++ b/python/private/pypi/BUILD.bazel
@@ -70,6 +70,7 @@
         "//python/private:full_version_bzl",
         "//python/private:normalize_name_bzl",
         "//python/private:version_label_bzl",
+        "//python/private:semver_bzl",
         "@bazel_features//:features",
     ] + [
         "@pythons_hub//:interpreters_bzl",
@@ -156,7 +157,10 @@
 bzl_library(
     name = "multi_pip_parse_bzl",
     srcs = ["multi_pip_parse.bzl"],
-    deps = ["pip_repository_bzl"],
+    deps = [
+        "pip_repository_bzl",
+        "//python/private:text_util_bzl",
+    ],
 )
 
 bzl_library(
@@ -302,9 +306,9 @@
         ":patch_whl_bzl",
         ":pypi_repo_utils_bzl",
         ":whl_target_platforms_bzl",
-        "//python:repositories_bzl",
         "//python/private:auth_bzl",
         "//python/private:envsubst_bzl",
+        "//python/private:is_standalone_interpreter_bzl",
         "//python/private:repo_utils_bzl",
     ],
 )
diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl
index 1bc8f15..77a4778 100644
--- a/python/private/pypi/extension.bzl
+++ b/python/private/pypi/extension.bzl
@@ -19,6 +19,7 @@
 load("//python/private:auth.bzl", "AUTH_ATTRS")
 load("//python/private:normalize_name.bzl", "normalize_name")
 load("//python/private:repo_utils.bzl", "repo_utils")
+load("//python/private:semver.bzl", "semver")
 load("//python/private:version_label.bzl", "version_label")
 load(":attrs.bzl", "use_isolated")
 load(":evaluate_markers.bzl", "evaluate_markers", EVALUATE_MARKERS_SRCS = "SRCS")
@@ -32,22 +33,8 @@
 load(":whl_library.bzl", "whl_library")
 load(":whl_repo_name.bzl", "whl_repo_name")
 
-def _parse_version(version):
-    major, _, version = version.partition(".")
-    minor, _, version = version.partition(".")
-    patch, _, version = version.partition(".")
-    build, _, version = version.partition(".")
-
-    return struct(
-        # use semver vocabulary here
-        major = major,
-        minor = minor,
-        patch = patch,  # this is called `micro` in the Python interpreter versioning scheme
-        build = build,
-    )
-
 def _major_minor_version(version):
-    version = _parse_version(version)
+    version = semver(version)
     return "{}.{}".format(version.major, version.minor)
 
 def _whl_mods_impl(mctx):
diff --git a/python/private/pypi/generate_whl_library_build_bazel.bzl b/python/private/pypi/generate_whl_library_build_bazel.bzl
index d25f73a..0be6f9c 100644
--- a/python/private/pypi/generate_whl_library_build_bazel.bzl
+++ b/python/private/pypi/generate_whl_library_build_bazel.bzl
@@ -157,14 +157,13 @@
         constraint_values_str = render.indent(render.list(constraint_values)).lstrip()
 
         if abi:
-            if not loads:
-                loads.append("""load("@rules_python//python/config_settings:config_settings.bzl", "is_python_config_setting")""")
-
             additional_content.append(
                 """\
-is_python_config_setting(
+config_setting(
     name = "is_{name}",
-    python_version = "3.{minor_version}",
+    flag_values = {{
+        "@rules_python//python/config_settings:_python_version_major_minor": "3.{minor_version}",
+    }},
     constraint_values = {constraint_values},
     visibility = ["//visibility:private"],
 )""".format(
diff --git a/python/private/pypi/multi_pip_parse.bzl b/python/private/pypi/multi_pip_parse.bzl
index fe9e2db..6e824f6 100644
--- a/python/private/pypi/multi_pip_parse.bzl
+++ b/python/private/pypi/multi_pip_parse.bzl
@@ -14,6 +14,7 @@
 
 """A pip_parse implementation for version aware toolchains in WORKSPACE."""
 
+load("//python/private:text_util.bzl", "render")
 load(":pip_repository.bzl", pip_parse = "pip_repository")
 
 def _multi_pip_parse_impl(rctx):
@@ -97,6 +98,7 @@
             name = "{name}_" + wheel_name,
             wheel_name = wheel_name,
             default_version = "{default_version}",
+            minor_mapping = {minor_mapping},
             version_map = _version_map[wheel_name],
         )
 """.format(
@@ -107,6 +109,7 @@
         process_requirements_calls = "\n".join(process_requirements_calls),
         rules_python = rules_python,
         default_version = rctx.attr.default_version,
+        minor_mapping = render.indent(render.dict(rctx.attr.minor_mapping)).lstrip(),
     )
     rctx.file("requirements.bzl", requirements_bzl)
     rctx.file("BUILD.bazel", "exports_files(['requirements.bzl'])")
@@ -115,12 +118,13 @@
     _multi_pip_parse_impl,
     attrs = {
         "default_version": attr.string(),
+        "minor_mapping": attr.string_dict(),
         "pip_parses": attr.string_dict(),
         "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
     },
 )
 
-def multi_pip_parse(name, default_version, python_versions, python_interpreter_target, requirements_lock, **kwargs):
+def multi_pip_parse(name, default_version, python_versions, python_interpreter_target, requirements_lock, minor_mapping, **kwargs):
     """NOT INTENDED FOR DIRECT USE!
 
     This is intended to be used by the multi_pip_parse implementation in the template of the
@@ -128,10 +132,11 @@
 
     Args:
         name: the name of the multi_pip_parse repository.
-        default_version: the default Python version.
-        python_versions: all Python toolchain versions currently registered.
-        python_interpreter_target: a dictionary which keys are Python versions and values are resolved host interpreters.
-        requirements_lock: a dictionary which keys are Python versions and values are locked requirements files.
+        default_version: {type}`str` the default Python version.
+        python_versions: {type}`list[str]` all Python toolchain versions currently registered.
+        python_interpreter_target: {type}`dict[str, Label]` a dictionary which keys are Python versions and values are resolved host interpreters.
+        requirements_lock: {type}`dict[str, Label]` a dictionary which keys are Python versions and values are locked requirements files.
+        minor_mapping: {type}`dict[str, str]` mapping between `X.Y` to `X.Y.Z` format.
         **kwargs: extra arguments passed to all wrapped pip_parse.
 
     Returns:
@@ -157,4 +162,5 @@
         name = name,
         default_version = default_version,
         pip_parses = pip_parses,
+        minor_mapping = minor_mapping,
     )
diff --git a/python/private/pypi/parse_simpleapi_html.bzl b/python/private/pypi/parse_simpleapi_html.bzl
index 81ee385..b4e7dd8 100644
--- a/python/private/pypi/parse_simpleapi_html.bzl
+++ b/python/private/pypi/parse_simpleapi_html.bzl
@@ -78,7 +78,7 @@
                 url = _absolute_url(url, dist_url),
                 sha256 = sha256,
                 metadata_sha256 = metadata_sha256,
-                metadata_url = _absolute_url(url, metadata_url),
+                metadata_url = _absolute_url(url, metadata_url) if metadata_url else "",
                 yanked = yanked,
             )
         else:
@@ -109,18 +109,33 @@
 
     return "{}://{}".format(scheme, host)
 
+def _is_downloadable(url):
+    """Checks if the URL would be accepted by the Bazel downloader.
+
+    This is based on Bazel's HttpUtils::isUrlSupportedByDownloader
+    """
+    return url.startswith("http://") or url.startswith("https://") or url.startswith("file://")
+
 def _absolute_url(index_url, candidate):
+    if candidate == "":
+        return candidate
+
+    if _is_downloadable(candidate):
+        return candidate
+
     if candidate.startswith("/"):
-        # absolute url
+        # absolute path
         root_directory = _get_root_directory(index_url)
         return "{}{}".format(root_directory, candidate)
 
-    if not candidate.startswith(".."):
-        return candidate
+    if candidate.startswith(".."):
+        # relative path with up references
+        candidate_parts = candidate.split("..")
+        last = candidate_parts[-1]
+        for _ in range(len(candidate_parts) - 1):
+            index_url, _, _ = index_url.rstrip("/").rpartition("/")
 
-    candidate_parts = candidate.split("..")
-    last = candidate_parts[-1]
-    for _ in range(len(candidate_parts) - 1):
-        index_url, _, _ = index_url.rstrip("/").rpartition("/")
+        return "{}/{}".format(index_url, last.strip("/"))
 
-    return "{}/{}".format(index_url, last.strip("/"))
+    # relative path without up-references
+    return "{}/{}".format(index_url, candidate)
diff --git a/python/private/pypi/pip_repository.bzl b/python/private/pypi/pip_repository.bzl
index 0c9e300..90cda77 100644
--- a/python/private/pypi/pip_repository.bzl
+++ b/python/private/pypi/pip_repository.bzl
@@ -326,7 +326,9 @@
 )
 ```
 
-### Vendoring the requirements.bzl file
+:::{rubric} Vendoring the requirements.bzl file
+:heading-level: 3
+:::
 
 In some cases you may not want to generate the requirements.bzl file as a repository rule
 while Bazel is fetching dependencies. For example, if you produce a reusable Bazel module
diff --git a/python/private/pypi/pypi_repo_utils.bzl b/python/private/pypi/pypi_repo_utils.bzl
index da449b4..1964316 100644
--- a/python/private/pypi/pypi_repo_utils.bzl
+++ b/python/private/pypi/pypi_repo_utils.bzl
@@ -51,7 +51,20 @@
     python_interpreter = _get_python_interpreter_attr(mrctx, python_interpreter = python_interpreter)
 
     if python_interpreter_target != None:
-        python_interpreter = mrctx.path(python_interpreter_target)
+        # The following line would make the MODULE.bazel.lock platform
+        # independent, because the lock file will then contain a hash of the
+        # file so that the lock file can be recalculated, hence the best way is
+        # to add this directory to PATH.
+        #
+        # hence we add the root BUILD.bazel file and get the directory of that
+        # and construct the path differently. At the end of the day we don't
+        # want the hash of the interpreter to end up in the lock file.
+        if hasattr(python_interpreter_target, "same_package_label"):
+            root_build_bazel = python_interpreter_target.same_package_label("BUILD.bazel")
+        else:
+            root_build_bazel = python_interpreter_target.relative(":BUILD.bazel")
+
+        python_interpreter = mrctx.path(root_build_bazel).dirname.get_child(python_interpreter_target.name)
 
         os = repo_utils.get_platforms_os_name(mrctx)
 
@@ -110,7 +123,7 @@
         # This will ensure that we will re-evaluate the bzlmod extension or
         # refetch the repository_rule when the srcs change. This should work on
         # Bazel versions without `mrctx.watch` as well.
-        repo_utils.watch(mrctx.path(src))
+        repo_utils.watch(mrctx, mrctx.path(src))
 
     env = kwargs.pop("environment", {})
     pythonpath = env.get("PYTHONPATH", "")
diff --git a/python/private/pypi/whl_library.bzl b/python/private/pypi/whl_library.bzl
index 2300eb3..309316b 100644
--- a/python/private/pypi/whl_library.bzl
+++ b/python/private/pypi/whl_library.bzl
@@ -16,7 +16,7 @@
 
 load("//python/private:auth.bzl", "AUTH_ATTRS", "get_auth")
 load("//python/private:envsubst.bzl", "envsubst")
-load("//python/private:python_repositories.bzl", "is_standalone_interpreter")
+load("//python/private:is_standalone_interpreter.bzl", "is_standalone_interpreter")
 load("//python/private:repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
 load(":attrs.bzl", "ATTRS", "use_isolated")
 load(":deps.bzl", "all_repo_names")
@@ -227,8 +227,10 @@
             whl_path = rctx.path(rctx.attr.filename)
         else:
             # It is an sdist and we need to tell PyPI to use a file in this directory
-            # and not use any indexes.
-            extra_pip_args.extend(["--no-index", "--find-links", "."])
+            # and, allow getting build dependencies from PYTHONPATH, which we
+            # setup in this repository rule, but still download any necessary
+            # build deps from PyPI (e.g. `flit_core`) if they are missing.
+            extra_pip_args.extend(["--no-build-isolation", "--find-links", "."])
 
     args = _parse_optional_attrs(rctx, args, extra_pip_args)
 
diff --git a/python/private/pypi/whl_library_alias.bzl b/python/private/pypi/whl_library_alias.bzl
index 263d7ec..d34b34a 100644
--- a/python/private/pypi/whl_library_alias.bzl
+++ b/python/private/pypi/whl_library_alias.bzl
@@ -29,6 +29,7 @@
         build_content.append(_whl_library_render_alias_target(
             alias_name = alias_name,
             default_repo_prefix = default_repo_prefix,
+            minor_mapping = rctx.attr.minor_mapping,
             rules_python = rules_python,
             version_map = version_map,
             wheel_name = rctx.attr.wheel_name,
@@ -36,8 +37,10 @@
     rctx.file("BUILD.bazel", "\n".join(build_content))
 
 def _whl_library_render_alias_target(
+        *,
         alias_name,
         default_repo_prefix,
+        minor_mapping,
         rules_python,
         version_map,
         wheel_name):
@@ -48,7 +51,7 @@
     for [python_version, repo_prefix] in version_map:
         alias.append("""\
         "@{rules_python}//python/config_settings:is_python_{full_python_version}": "{actual}",""".format(
-            full_python_version = full_version(python_version),
+            full_python_version = full_version(version = python_version, minor_mapping = minor_mapping),
             actual = "@{repo_prefix}{wheel_name}//:{alias_name}".format(
                 repo_prefix = repo_prefix,
                 wheel_name = wheel_name,
@@ -92,6 +95,7 @@
                   "not specified, then the default rules won't be able to " +
                   "resolve a wheel and an error will occur.",
         ),
+        "minor_mapping": attr.string_dict(mandatory = True),
         "version_map": attr.string_dict(mandatory = True),
         "wheel_name": attr.string(mandatory = True),
         "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
diff --git a/python/private/python.bzl b/python/private/python.bzl
index 6a265d1..cedf39a 100644
--- a/python/private/python.bzl
+++ b/python/private/python.bzl
@@ -12,13 +12,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"Python toolchain module extensions for use with bzlmod"
+"Python toolchain module extensions for use with bzlmod."
 
 load("@bazel_features//:features.bzl", "bazel_features")
-load("//python:repositories.bzl", "python_register_toolchains")
-load("//python:versions.bzl", "TOOL_VERSIONS")
-load("//python/private:repo_utils.bzl", "repo_utils")
+load("//python:versions.bzl", "DEFAULT_RELEASE_BASE_URL", "PLATFORMS", "TOOL_VERSIONS")
+load(":auth.bzl", "AUTH_ATTRS")
+load(":full_version.bzl", "full_version")
+load(":python_register_toolchains.bzl", "python_register_toolchains")
 load(":pythons_hub.bzl", "hub_repo")
+load(":repo_utils.bzl", "repo_utils")
+load(":semver.bzl", "semver")
 load(":text_util.bzl", "render")
 load(":toolchains_repo.bzl", "multi_toolchain_aliases")
 load(":util.bzl", "IS_BAZEL_6_4_OR_HIGHER")
@@ -28,22 +31,22 @@
 _MAX_NUM_TOOLCHAINS = 9999
 _TOOLCHAIN_INDEX_PAD_LENGTH = len(str(_MAX_NUM_TOOLCHAINS))
 
-def _python_register_toolchains(name, toolchain_attr, module, ignore_root_user_error):
-    """Calls python_register_toolchains and returns a struct used to collect the toolchains.
-    """
-    python_register_toolchains(
-        name = name,
-        python_version = toolchain_attr.python_version,
-        register_coverage_tool = toolchain_attr.configure_coverage_tool,
-        ignore_root_user_error = ignore_root_user_error,
-    )
-    return struct(
-        python_version = toolchain_attr.python_version,
-        name = name,
-        module = struct(name = module.name, is_root = module.is_root),
-    )
+def parse_modules(*, module_ctx, _fail = fail):
+    """Parse the modules and return a struct for registrations.
 
-def _python_impl(module_ctx):
+    Args:
+        module_ctx: {type}`module_ctx` module context.
+        _fail: {type}`function` the failure function, mainly for testing.
+
+    Returns:
+        A struct with the following attributes:
+            * `toolchains`: The list of toolchains to register. The last
+              element is special and is treated as the default toolchain.
+            * `defaults`: The default `kwargs` passed to
+              {bzl:obj}`python_register_toolchains`.
+            * `debug_info`: {type}`None | dict` extra information to be passed
+              to the debug repo.
+    """
     if module_ctx.os.environ.get("RULES_PYTHON_BZLMOD_DEBUG", "0") == "1":
         debug_info = {
             "toolchains_registered": [],
@@ -61,7 +64,7 @@
     # This is a toolchain_info struct.
     default_toolchain = None
 
-    # Map of string Major.Minor to the toolchain_info struct
+    # Map of string Major.Minor or Major.Minor.Patch to the toolchain_info struct
     global_toolchain_versions = {}
 
     ignore_root_user_error = None
@@ -73,10 +76,16 @@
     if not module_ctx.modules[0].tags.toolchain:
         ignore_root_user_error = False
 
+    config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)
+
+    seen_versions = {}
     for mod in module_ctx.modules:
         module_toolchain_versions = []
-
-        toolchain_attr_structs = _create_toolchain_attr_structs(mod)
+        toolchain_attr_structs = _create_toolchain_attr_structs(
+            mod = mod,
+            seen_versions = seen_versions,
+            config = config,
+        )
 
         for toolchain_attr in toolchain_attr_structs:
             toolchain_version = toolchain_attr.python_version
@@ -139,16 +148,17 @@
                     )
                 toolchain_info = None
             else:
-                toolchain_info = _python_register_toolchains(
-                    toolchain_name,
-                    toolchain_attr,
-                    module = mod,
-                    ignore_root_user_error = ignore_root_user_error,
+                toolchain_info = struct(
+                    python_version = toolchain_attr.python_version,
+                    name = toolchain_name,
+                    register_coverage_tool = toolchain_attr.configure_coverage_tool,
+                    module = struct(name = mod.name, is_root = mod.is_root),
                 )
                 global_toolchain_versions[toolchain_version] = toolchain_info
                 if debug_info:
                     debug_info["toolchains_registered"].append({
                         "ignore_root_user_error": ignore_root_user_error,
+                        "module": {"is_root": mod.is_root, "name": mod.name},
                         "name": toolchain_name,
                     })
 
@@ -166,6 +176,8 @@
             elif toolchain_info:
                 toolchains.append(toolchain_info)
 
+    config.default.setdefault("ignore_root_user_error", ignore_root_user_error)
+
     # A default toolchain is required so that the non-version-specific rules
     # are able to match a toolchain.
     if default_toolchain == None:
@@ -184,23 +196,61 @@
     if len(toolchains) > _MAX_NUM_TOOLCHAINS:
         fail("more than {} python versions are not supported".format(_MAX_NUM_TOOLCHAINS))
 
+    return struct(
+        config = config,
+        debug_info = debug_info,
+        default_python_version = toolchains[-1].python_version,
+        toolchains = [
+            struct(
+                python_version = t.python_version,
+                name = t.name,
+                register_coverage_tool = t.register_coverage_tool,
+            )
+            for t in toolchains
+        ],
+    )
+
+def _python_impl(module_ctx):
+    py = parse_modules(module_ctx = module_ctx)
+
+    for toolchain_info in py.toolchains:
+        # Ensure that we pass the full version here.
+        full_python_version = full_version(
+            version = toolchain_info.python_version,
+            minor_mapping = py.config.minor_mapping,
+        )
+        kwargs = {
+            "python_version": full_python_version,
+            "register_coverage_tool": toolchain_info.register_coverage_tool,
+        }
+
+        # Allow overrides per python version
+        kwargs.update(py.config.kwargs.get(toolchain_info.python_version, {}))
+        kwargs.update(py.config.kwargs.get(full_python_version, {}))
+        kwargs.update(py.config.default)
+        python_register_toolchains(name = toolchain_info.name, **kwargs)
+
     # Create the pythons_hub repo for the interpreter meta data and the
     # the various toolchains.
     hub_repo(
         name = "pythons_hub",
-        default_python_version = default_toolchain.python_version,
+        # Last toolchain is default
+        default_python_version = py.default_python_version,
         toolchain_prefixes = [
             render.toolchain_prefix(index, toolchain.name, _TOOLCHAIN_INDEX_PAD_LENGTH)
-            for index, toolchain in enumerate(toolchains)
+            for index, toolchain in enumerate(py.toolchains)
         ],
-        toolchain_python_versions = [t.python_version for t in toolchains],
+        toolchain_python_versions = [
+            full_version(version = t.python_version, minor_mapping = py.config.minor_mapping)
+            for t in py.toolchains
+        ],
         # The last toolchain is the default; it can't have version constraints
         # Despite the implication of the arg name, the values are strs, not bools
         toolchain_set_python_version_constraints = [
-            "True" if i != len(toolchains) - 1 else "False"
-            for i in range(len(toolchains))
+            "True" if i != len(py.toolchains) - 1 else "False"
+            for i in range(len(py.toolchains))
         ],
-        toolchain_user_repository_names = [t.name for t in toolchains],
+        toolchain_user_repository_names = [t.name for t in py.toolchains],
     )
 
     # This is require in order to support multiple version py_test
@@ -208,15 +258,15 @@
     multi_toolchain_aliases(
         name = "python_versions",
         python_versions = {
-            version: toolchain.name
-            for version, toolchain in global_toolchain_versions.items()
+            toolchain.python_version: toolchain.name
+            for toolchain in py.toolchains
         },
     )
 
-    if debug_info != None:
+    if py.debug_info != None:
         _debug_repo(
             name = "rules_python_bzlmod_debug",
-            debug_info = json.encode_indent(debug_info),
+            debug_info = json.encode_indent(py.debug_info),
         )
 
     if bazel_features.external_deps.extension_metadata_has_reproducible:
@@ -232,6 +282,9 @@
     ))
 
 def _warn_duplicate_global_toolchain_version(version, first, second_toolchain_name, second_module_name, logger):
+    if not logger:
+        return
+
     logger.info(lambda: (
         "Ignoring toolchain '{second_toolchain}' from module '{second_module}': " +
         "Toolchain '{first_toolchain}' from module '{first_module}' " +
@@ -252,25 +305,234 @@
         second = second,
     ))
 
-def _create_toolchain_attr_structs(mod):
+def _validate_version(*, version, _fail = fail):
+    parsed = semver(version)
+    if parsed.patch == None or parsed.build or parsed.pre_release:
+        _fail("The 'python_version' attribute needs to specify an 'X.Y.Z' semver-compatible version, got: '{}'".format(version))
+        return False
+
+    return True
+
+def _process_single_version_overrides(*, tag, _fail = fail, default):
+    if not _validate_version(version = tag.python_version, _fail = _fail):
+        return
+
+    available_versions = default["tool_versions"]
+    kwargs = default.setdefault("kwargs", {})
+
+    if tag.sha256 or tag.urls:
+        if not (tag.sha256 and tag.urls):
+            _fail("Both `sha256` and `urls` overrides need to be provided together")
+            return
+
+        for platform in (tag.sha256 or []):
+            if platform not in PLATFORMS:
+                _fail("The platform must be one of {allowed} but got '{got}'".format(
+                    allowed = sorted(PLATFORMS),
+                    got = platform,
+                ))
+                return
+
+    sha256 = dict(tag.sha256) or available_versions[tag.python_version]["sha256"]
+    override = {
+        "sha256": sha256,
+        "strip_prefix": {
+            platform: tag.strip_prefix
+            for platform in sha256
+        },
+        "url": {
+            platform: list(tag.urls)
+            for platform in tag.sha256
+        } or available_versions[tag.python_version]["url"],
+    }
+
+    if tag.patches:
+        override["patch_strip"] = {
+            platform: tag.patch_strip
+            for platform in sha256
+        }
+        override["patches"] = {
+            platform: list(tag.patches)
+            for platform in sha256
+        }
+
+    available_versions[tag.python_version] = {k: v for k, v in override.items() if v}
+
+    if tag.distutils_content:
+        kwargs.setdefault(tag.python_version, {})["distutils_content"] = tag.distutils_content
+    if tag.distutils:
+        kwargs.setdefault(tag.python_version, {})["distutils"] = tag.distutils
+
+def _process_single_version_platform_overrides(*, tag, _fail = fail, default):
+    if not _validate_version(version = tag.python_version, _fail = _fail):
+        return
+
+    available_versions = default["tool_versions"]
+
+    if tag.python_version not in available_versions:
+        if not tag.urls or not tag.sha256 or not tag.strip_prefix:
+            _fail("When introducing a new python_version '{}', 'sha256', 'strip_prefix' and 'urls' must be specified".format(tag.python_version))
+            return
+        available_versions[tag.python_version] = {}
+
+    if tag.coverage_tool:
+        available_versions[tag.python_version].setdefault("coverage_tool", {})[tag.platform] = tag.coverage_tool
+    if tag.patch_strip:
+        available_versions[tag.python_version].setdefault("patch_strip", {})[tag.platform] = tag.patch_strip
+    if tag.patches:
+        available_versions[tag.python_version].setdefault("patches", {})[tag.platform] = list(tag.patches)
+    if tag.sha256:
+        available_versions[tag.python_version].setdefault("sha256", {})[tag.platform] = tag.sha256
+    if tag.strip_prefix:
+        available_versions[tag.python_version].setdefault("strip_prefix", {})[tag.platform] = tag.strip_prefix
+    if tag.urls:
+        available_versions[tag.python_version].setdefault("url", {})[tag.platform] = tag.urls
+
+def _process_global_overrides(*, tag, default, _fail = fail):
+    if tag.available_python_versions:
+        available_versions = default["tool_versions"]
+        all_versions = dict(available_versions)
+        available_versions.clear()
+        for v in tag.available_python_versions:
+            if v not in all_versions:
+                _fail("unknown version '{}', known versions are: {}".format(
+                    v,
+                    sorted(all_versions),
+                ))
+                return
+
+            available_versions[v] = all_versions[v]
+
+    if tag.minor_mapping:
+        for minor_version, full_version in tag.minor_mapping.items():
+            parsed = semver(minor_version)
+            if parsed.patch != None or parsed.build or parsed.pre_release:
+                fail("Expected the key to be of `X.Y` format but got `{}`".format(minor_version))
+            parsed = semver(full_version)
+            if parsed.patch == None:
+                fail("Expected the value to at least be of `X.Y.Z` format but got `{}`".format(minor_version))
+
+        default["minor_mapping"] = tag.minor_mapping
+
+    forwarded_attrs = sorted(AUTH_ATTRS) + [
+        "ignore_root_user_error",
+        "base_url",
+        "register_all_versions",
+    ]
+    for key in forwarded_attrs:
+        if getattr(tag, key, None):
+            default[key] = getattr(tag, key)
+
+def _override_defaults(*overrides, modules, _fail = fail, default):
+    mod = modules[0] if modules else None
+    if not mod or not mod.is_root:
+        return
+
+    overriden_keys = []
+
+    for override in overrides:
+        for tag in getattr(mod.tags, override.name):
+            key = override.key(tag)
+            if key not in overriden_keys:
+                overriden_keys.append(key)
+            elif key:
+                _fail("Only a single 'python.{}' can be present for '{}'".format(override.name, key))
+                return
+            else:
+                _fail("Only a single 'python.{}' can be present".format(override.name))
+                return
+
+            override.fn(tag = tag, _fail = _fail, default = default)
+
+def _get_toolchain_config(*, modules, _fail = fail):
+    # Items that can be overridden
+    available_versions = {
+        version: {
+            # Use a dicts straight away so that we could do URL overrides for a
+            # single version.
+            "sha256": dict(item["sha256"]),
+            "strip_prefix": {
+                platform: item["strip_prefix"]
+                for platform in item["sha256"]
+            },
+            "url": {
+                platform: [item["url"]]
+                for platform in item["sha256"]
+            },
+        }
+        for version, item in TOOL_VERSIONS.items()
+    }
+    default = {
+        "base_url": DEFAULT_RELEASE_BASE_URL,
+        "tool_versions": available_versions,
+    }
+
+    _override_defaults(
+        # First override by single version, because the sha256 will replace
+        # anything that has been there before.
+        struct(
+            name = "single_version_override",
+            key = lambda t: t.python_version,
+            fn = _process_single_version_overrides,
+        ),
+        # Then override particular platform entries if they need to be overridden.
+        struct(
+            name = "single_version_platform_override",
+            key = lambda t: (t.python_version, t.platform),
+            fn = _process_single_version_platform_overrides,
+        ),
+        # Then finally add global args and remove the unnecessary toolchains.
+        # This ensures that we can do further validations when removing.
+        struct(
+            name = "override",
+            key = lambda t: None,
+            fn = _process_global_overrides,
+        ),
+        modules = modules,
+        default = default,
+        _fail = _fail,
+    )
+
+    minor_mapping = default.pop("minor_mapping", {})
+    register_all_versions = default.pop("register_all_versions", False)
+    kwargs = default.pop("kwargs", {})
+
+    if not minor_mapping:
+        versions = {}
+        for version_string in available_versions:
+            v = semver(version_string)
+            versions.setdefault("{}.{}".format(v.major, v.minor), []).append((int(v.patch), version_string))
+
+        minor_mapping = {
+            major_minor: max(subset)[1]
+            for major_minor, subset in versions.items()
+        }
+
+    return struct(
+        kwargs = kwargs,
+        minor_mapping = minor_mapping,
+        default = default,
+        register_all_versions = register_all_versions,
+    )
+
+def _create_toolchain_attr_structs(*, mod, config, seen_versions):
     arg_structs = []
-    seen_versions = {}
+
     for tag in mod.tags.toolchain:
-        arg_structs.append(_create_toolchain_attrs_struct(tag = tag, toolchain_tag_count = len(mod.tags.toolchain)))
+        arg_structs.append(_create_toolchain_attrs_struct(
+            tag = tag,
+            toolchain_tag_count = len(mod.tags.toolchain),
+        ))
+
         seen_versions[tag.python_version] = True
 
-    if mod.is_root:
-        register_all = False
-        for tag in mod.tags.rules_python_private_testing:
-            if tag.register_all_versions:
-                register_all = True
-                break
-        if register_all:
-            arg_structs.extend([
-                _create_toolchain_attrs_struct(python_version = v)
-                for v in TOOL_VERSIONS.keys()
-                if v not in seen_versions
-            ])
+    if config.register_all_versions:
+        arg_structs.extend([
+            _create_toolchain_attrs_struct(python_version = v)
+            for v in config.default["tool_versions"].keys() + config.minor_mapping.keys()
+            if v not in seen_versions
+        ])
+
     return arg_structs
 
 def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolchain_tag_count = None):
@@ -297,78 +559,295 @@
 
     return kwargs
 
-python = module_extension(
-    doc = """Bzlmod extension that is used to register Python toolchains.
-""",
-    implementation = _python_impl,
-    tag_classes = {
-        "rules_python_private_testing": tag_class(
-            attrs = {
-                "register_all_versions": attr.bool(default = False),
-            },
-        ),
-        "toolchain": tag_class(
-            doc = """Tag class used to register Python toolchains.
+_toolchain = tag_class(
+    doc = """Tag class used to register Python toolchains.
 Use this tag class to register one or more Python toolchains. This class
 is also potentially called by sub modules. The following covers different
 business rules and use cases.
 
-Toolchains in the Root Module
+:::{topic} Toolchains in the Root Module
 
 This class registers all toolchains in the root module.
+:::
 
-Toolchains in Sub Modules
+:::{topic} Toolchains in Sub Modules
 
 It will create a toolchain that is in a sub module, if the toolchain
 of the same name does not exist in the root module.  The extension stops name
 clashing between toolchains in the root module and toolchains in sub modules.
 You cannot configure more than one toolchain as the default toolchain.
+:::
 
-Toolchain set as the default version
+:::{topic} Toolchain set as the default version
 
 This extension will not create a toolchain that exists in a sub module,
 if the sub module toolchain is marked as the default version. If you have
 more than one toolchain in your root module, you need to set one of the
 toolchains as the default version.  If there is only one toolchain it
 is set as the default toolchain.
+:::
 
-Toolchain repository name
+:::{topic} Toolchain repository name
 
 A toolchain's repository name uses the format `python_{major}_{minor}`, e.g.
 `python_3_10`. The `major` and `minor` components are
 `major` and `minor` are the Python version from the `python_version` attribute.
-""",
-            attrs = {
-                "configure_coverage_tool": attr.bool(
-                    mandatory = False,
-                    doc = "Whether or not to configure the default coverage tool for the toolchains.",
-                ),
-                "ignore_root_user_error": attr.bool(
-                    default = False,
-                    doc = """\
-If False, the Python runtime installation will be made read only. This improves
-the ability for Bazel to cache it, but prevents the interpreter from creating
-pyc files for the standard library dynamically at runtime as they are loaded.
 
-If True, the Python runtime installation is read-write. This allows the
-interpreter to create pyc files for the standard library, but, because they are
+If a toolchain is registered in `X.Y.Z`, then similarly the toolchain name will
+be `python_{major}_{minor}_{patch}`, e.g. `python_3_10_19`.
+:::
+
+:::{topic} Toolchain detection
+The definition of the first toolchain wins, which means that the root module
+can override settings for any python toolchain available. This relies on the
+documented module traversal from the {obj}`module_ctx.modules`.
+:::
+
+:::{tip}
+In order to use a different name than the above, you can use the following `MODULE.bazel`
+syntax:
+```starlark
+python = use_extension("@rules_python//python/extensions:python.bzl", "python")
+python.toolchain(
+    is_default = True,
+    python_version = "3.11",
+)
+
+use_repo(python, my_python_name = "python_3_11")
+```
+
+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.",
+        ),
+        "ignore_root_user_error": attr.bool(
+            default = False,
+            doc = """\
+If `False`, the Python runtime installation will be made read only. This improves
+the ability for Bazel to cache it, but prevents the interpreter from creating
+`.pyc` files for the standard library dynamically at runtime as they are loaded.
+
+If `True`, the Python runtime installation is read-write. This allows the
+interpreter to create `.pyc` files for the standard library, but, because they are
 created as needed, it adversely affects Bazel's ability to cache the runtime and
 can result in spurious build failures.
 """,
-                    mandatory = False,
-                ),
-                "is_default": attr.bool(
-                    mandatory = False,
-                    doc = "Whether the toolchain is the default version",
-                ),
-                "python_version": attr.string(
-                    mandatory = True,
-                    doc = "The Python version, in `major.minor` format, e.g " +
-                          "'3.12', to create a toolchain for. Patch level " +
-                          "granularity (e.g. '3.12.1') is not supported.",
-                ),
-            },
+            mandatory = False,
         ),
+        "is_default": attr.bool(
+            mandatory = False,
+            doc = "Whether the toolchain is the default version",
+        ),
+        "python_version": attr.string(
+            mandatory = True,
+            doc = """\
+The Python version, in `major.minor` or `major.minor.patch` format, e.g
+`3.12` (or `3.12.3`), to create a toolchain for.
+""",
+        ),
+    },
+)
+
+_override = tag_class(
+    doc = """Tag class used to override defaults and behaviour of the module extension.
+
+:::{versionadded} 0.36.0
+:::
+""",
+    attrs = {
+        "available_python_versions": attr.string_list(
+            mandatory = False,
+            doc = """\
+The list of available python tool versions to use. Must be in `X.Y.Z` format.
+If the unknown version given the processing of the extension will fail - all of
+the versions in the list have to be defined with
+{obj}`python.single_version_override` or
+{obj}`python.single_version_platform_override` before they are used in this
+list.
+
+This attribute is usually used in order to ensure that no unexpected transitive
+dependencies are introduced.
+""",
+        ),
+        "base_url": attr.string(
+            mandatory = False,
+            doc = "The base URL to be used when downloading toolchains.",
+            default = DEFAULT_RELEASE_BASE_URL,
+        ),
+        "ignore_root_user_error": attr.bool(
+            default = False,
+            doc = """\
+If `False`, the Python runtime installation will be made read only. This improves
+the ability for Bazel to cache it, but prevents the interpreter from creating
+`.pyc` files for the standard library dynamically at runtime as they are loaded.
+
+If `True`, the Python runtime installation is read-write. This allows the
+interpreter to create `.pyc` files for the standard library, but, because they are
+created as needed, it adversely affects Bazel's ability to cache the runtime and
+can result in spurious build failures.
+""",
+            mandatory = False,
+        ),
+        "minor_mapping": attr.string_dict(
+            mandatory = False,
+            doc = """\
+The mapping between `X.Y` to `X.Y.Z` versions to be used when setting up
+toolchains. It defaults to the interpreter with the highest available patch
+version for each minor version. For example if one registers `3.10.3`, `3.10.4`
+and `3.11.4` then the default for the `minor_mapping` dict will be:
+```starlark
+{
+"3.10": "3.10.4",
+"3.11": "3.11.4",
+}
+```
+""",
+            default = {},
+        ),
+        "register_all_versions": attr.bool(default = False, doc = "Add all versions"),
+    } | AUTH_ATTRS,
+)
+
+_single_version_override = tag_class(
+    doc = """Override single python version URLs and patches for all platforms.
+
+:::{note}
+This will replace any existing configuration for the given python version.
+:::
+
+:::{tip}
+If you would like to modify the configuration for a specific `(version,
+platform)`, please use the {obj}`single_version_platform_override` tag
+class.
+:::
+
+:::{versionadded} 0.36.0
+:::
+""",
+    attrs = {
+        # NOTE @aignas 2024-09-01: all of the attributes except for `version`
+        # can be part of the `python.toolchain` call. That would make it more
+        # ergonomic to define new toolchains and to override values for old
+        # toolchains. The same semantics of the `first one wins` would apply,
+        # so technically there is no need for any overrides?
+        #
+        # Although these attributes would override the code that is used by the
+        # code in non-root modules, so technically this could be thought as
+        # being overridden.
+        #
+        # rules_go has a single download call:
+        # https://github.com/bazelbuild/rules_go/blob/master/go/private/extensions.bzl#L38
+        #
+        # However, we need to understand how to accommodate the fact that
+        # {attr}`single_version_override.version` only allows patch versions.
+        "distutils": attr.label(
+            allow_single_file = True,
+            doc = "A distutils.cfg file to be included in the Python installation. " +
+                  "Either {attr}`distutils` or {attr}`distutils_content` can be specified, but not both.",
+            mandatory = False,
+        ),
+        "distutils_content": attr.string(
+            doc = "A distutils.cfg file content to be included in the Python installation. " +
+                  "Either {attr}`distutils` or {attr}`distutils_content` can be specified, but not both.",
+            mandatory = False,
+        ),
+        "patch_strip": attr.int(
+            mandatory = False,
+            doc = "Same as the --strip argument of Unix patch.",
+            default = 0,
+        ),
+        "patches": attr.label_list(
+            mandatory = False,
+            doc = "A list of labels pointing to patch files to apply for the interpreter repository. They are applied in the list order and are applied before any platform-specific patches are applied.",
+        ),
+        "python_version": attr.string(
+            mandatory = True,
+            doc = "The python version to override URLs for. Must be in `X.Y.Z` format.",
+        ),
+        "sha256": attr.string_dict(
+            mandatory = False,
+            doc = "The python platform to sha256 dict. See {attr}`python.single_version_platform_override.platform` for allowed key values.",
+        ),
+        "strip_prefix": attr.string(
+            mandatory = False,
+            doc = "The 'strip_prefix' for the archive, defaults to 'python'.",
+            default = "python",
+        ),
+        "urls": attr.string_list(
+            mandatory = False,
+            doc = "The URL template to fetch releases for this Python version. See {attr}`python.single_version_platform_override.urls` for documentation.",
+        ),
+    },
+)
+
+_single_version_platform_override = tag_class(
+    doc = """Override single python version for a single existing platform.
+
+If the `(version, platform)` is new, we will add it to the existing versions and will
+use the same `url` template.
+
+:::{tip}
+If you would like to add or remove platforms to a single python version toolchain
+configuration, please use {obj}`single_version_override`.
+:::
+
+:::{versionadded} 0.36.0
+:::
+""",
+    attrs = {
+        "coverage_tool": attr.label(
+            doc = """\
+The coverage tool to be used for a particular Python interpreter. This can override
+`rules_python` defaults.
+""",
+        ),
+        "patch_strip": attr.int(
+            mandatory = False,
+            doc = "Same as the --strip argument of Unix patch.",
+            default = 0,
+        ),
+        "patches": attr.label_list(
+            mandatory = False,
+            doc = "A list of labels pointing to patch files to apply for the interpreter repository. They are applied in the list order and are applied after the common patches are applied.",
+        ),
+        "platform": attr.string(
+            mandatory = True,
+            values = PLATFORMS.keys(),
+            doc = "The platform to override the values for, must be one of:\n{}.".format("\n".join(sorted(["* `{}`".format(p) for p in PLATFORMS]))),
+        ),
+        "python_version": attr.string(
+            mandatory = True,
+            doc = "The python version to override URLs for. Must be in `X.Y.Z` format.",
+        ),
+        "sha256": attr.string(
+            mandatory = False,
+            doc = "The sha256 for the archive",
+        ),
+        "strip_prefix": attr.string(
+            mandatory = False,
+            doc = "The 'strip_prefix' for the archive, defaults to 'python'.",
+            default = "python",
+        ),
+        "urls": attr.string_list(
+            mandatory = False,
+            doc = "The URL template to fetch releases for this Python version. If the URL template results in a relative fragment, default base URL is going to be used. Occurrences of `{python_version}`, `{platform}` and `{build}` will be interpolated based on the contents in the override and the known {attr}`platform` values.",
+        ),
+    },
+)
+
+python = module_extension(
+    doc = """Bzlmod extension that is used to register Python toolchains.
+""",
+    implementation = _python_impl,
+    tag_classes = {
+        "override": _override,
+        "single_version_override": _single_version_override,
+        "single_version_platform_override": _single_version_platform_override,
+        "toolchain": _toolchain,
     },
     **_get_bazel_version_specific_kwargs()
 )
diff --git a/python/private/python_register_multi_toolchains.bzl b/python/private/python_register_multi_toolchains.bzl
new file mode 100644
index 0000000..68f5249
--- /dev/null
+++ b/python/private/python_register_multi_toolchains.bzl
@@ -0,0 +1,75 @@
+# Copyright 2024 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.
+
+"""This file contains repository rules and macros to support toolchain registration.
+"""
+
+load("//python:versions.bzl", "MINOR_MAPPING")
+load(":python_register_toolchains.bzl", "python_register_toolchains")
+load(":toolchains_repo.bzl", "multi_toolchain_aliases")
+
+def python_register_multi_toolchains(
+        name,
+        python_versions,
+        default_version = None,
+        minor_mapping = None,
+        **kwargs):
+    """Convenience macro for registering multiple Python toolchains.
+
+    Args:
+        name: {type}`str` base name for each name in {obj}`python_register_toolchains` call.
+        python_versions: {type}`list[str]` the Python versions.
+        default_version: {type}`str` the default Python version. If not set,
+            the first version in python_versions is used.
+        minor_mapping: {type}`dict[str, str]` mapping between `X.Y` to `X.Y.Z`
+            format. Defaults to the value in `//python:versions.bzl`.
+        **kwargs: passed to each {obj}`python_register_toolchains` call.
+    """
+    if len(python_versions) == 0:
+        fail("python_versions must not be empty")
+
+    minor_mapping = minor_mapping or MINOR_MAPPING
+
+    if not default_version:
+        default_version = python_versions.pop(0)
+    for python_version in python_versions:
+        if python_version == default_version:
+            # We register the default version lastly so that it's not picked first when --platforms
+            # is set with a constraint during toolchain resolution. This is due to the fact that
+            # Bazel will match the unconstrained toolchain if we register it before the constrained
+            # ones.
+            continue
+        python_register_toolchains(
+            name = name + "_" + python_version.replace(".", "_"),
+            python_version = python_version,
+            set_python_version_constraint = True,
+            minor_mapping = minor_mapping,
+            **kwargs
+        )
+    python_register_toolchains(
+        name = name + "_" + default_version.replace(".", "_"),
+        python_version = default_version,
+        set_python_version_constraint = False,
+        minor_mapping = minor_mapping,
+        **kwargs
+    )
+
+    multi_toolchain_aliases(
+        name = name,
+        python_versions = {
+            python_version: name + "_" + python_version.replace(".", "_")
+            for python_version in (python_versions + [default_version])
+        },
+        minor_mapping = minor_mapping,
+    )
diff --git a/python/private/python_register_toolchains.bzl b/python/private/python_register_toolchains.bzl
new file mode 100644
index 0000000..d20e049
--- /dev/null
+++ b/python/private/python_register_toolchains.bzl
@@ -0,0 +1,180 @@
+# Copyright 2024 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.
+
+"""This file contains repository rules and macros to support toolchain registration.
+"""
+
+load(
+    "//python:versions.bzl",
+    "DEFAULT_RELEASE_BASE_URL",
+    "MINOR_MAPPING",
+    "PLATFORMS",
+    "TOOL_VERSIONS",
+    "get_release_info",
+)
+load(":bzlmod_enabled.bzl", "BZLMOD_ENABLED")
+load(":coverage_deps.bzl", "coverage_dep")
+load(":full_version.bzl", "full_version")
+load(":python_repository.bzl", "python_repository")
+load(
+    ":toolchains_repo.bzl",
+    "host_toolchain",
+    "toolchain_aliases",
+    "toolchains_repo",
+)
+
+# Wrapper macro around everything above, this is the primary API.
+def python_register_toolchains(
+        name,
+        python_version,
+        register_toolchains = True,
+        register_coverage_tool = False,
+        set_python_version_constraint = False,
+        tool_versions = None,
+        minor_mapping = None,
+        **kwargs):
+    """Convenience macro for users which does typical setup.
+
+    With `bzlmod` enabled, this function is not needed since `rules_python` is
+    handling everything. In order to override the default behaviour from the
+    root module one can see the docs for the {rule}`python` extension.
+
+    - Create a repository for each built-in platform like "python_3_8_linux_amd64" -
+      this repository is lazily fetched when Python is needed for that platform.
+    - Create a repository exposing toolchains for each platform like
+      "python_platforms".
+    - Register a toolchain pointing at each platform.
+
+    Users can avoid this macro and do these steps themselves, if they want more
+    control.
+
+    Args:
+        name: {type}`str` base name for all created repos, e.g. "python_3_8".
+        python_version: {type}`str` the Python version.
+        register_toolchains: {type}`bool` Whether or not to register the downloaded toolchains.
+        register_coverage_tool: {type}`bool` Whether or not to register the
+            downloaded coverage tool to the toolchains.
+        set_python_version_constraint: {type}`bool` When set to `True`,
+            `target_compatible_with` for the toolchains will include a version
+            constraint.
+        tool_versions: {type}`dict` contains a mapping of version with SHASUM
+            and platform info. If not supplied, the defaults in
+            python/versions.bzl will be used.
+        minor_mapping: {type}`dict[str, str]` contains a mapping from `X.Y` to `X.Y.Z`
+            version.
+        **kwargs: passed to each {obj}`python_repository` call.
+    """
+
+    if BZLMOD_ENABLED:
+        # you cannot used native.register_toolchains when using bzlmod.
+        register_toolchains = False
+
+    base_url = kwargs.pop("base_url", DEFAULT_RELEASE_BASE_URL)
+    tool_versions = tool_versions or TOOL_VERSIONS
+    minor_mapping = minor_mapping or MINOR_MAPPING
+
+    python_version = full_version(version = python_version, minor_mapping = minor_mapping)
+
+    toolchain_repo_name = "{name}_toolchains".format(name = name)
+
+    # When using unreleased Bazel versions, the version is an empty string
+    if native.bazel_version:
+        bazel_major = int(native.bazel_version.split(".")[0])
+        if bazel_major < 6:
+            if register_coverage_tool:
+                # buildifier: disable=print
+                print((
+                    "WARNING: ignoring register_coverage_tool=True when " +
+                    "registering @{name}: Bazel 6+ required, got {version}"
+                ).format(
+                    name = name,
+                    version = native.bazel_version,
+                ))
+            register_coverage_tool = False
+
+    loaded_platforms = []
+    for platform in PLATFORMS.keys():
+        sha256 = tool_versions[python_version]["sha256"].get(platform, None)
+        if not sha256:
+            continue
+
+        loaded_platforms.append(platform)
+        (release_filename, urls, strip_prefix, patches, patch_strip) = get_release_info(platform, python_version, base_url, tool_versions)
+
+        # allow passing in a tool version
+        coverage_tool = None
+        coverage_tool = tool_versions[python_version].get("coverage_tool", {}).get(platform, None)
+        if 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 = platform,
+                visibility = ["@{name}_{platform}//:__subpackages__".format(
+                    name = name,
+                    platform = platform,
+                )],
+            )
+
+        python_repository(
+            name = "{name}_{platform}".format(
+                name = name,
+                platform = platform,
+            ),
+            sha256 = sha256,
+            patches = patches,
+            patch_strip = patch_strip,
+            platform = platform,
+            python_version = python_version,
+            release_filename = release_filename,
+            urls = urls,
+            strip_prefix = strip_prefix,
+            coverage_tool = coverage_tool,
+            **kwargs
+        )
+        if register_toolchains:
+            native.register_toolchains("@{toolchain_repo_name}//:{platform}_toolchain".format(
+                toolchain_repo_name = toolchain_repo_name,
+                platform = platform,
+            ))
+            native.register_toolchains("@{toolchain_repo_name}//:{platform}_py_cc_toolchain".format(
+                toolchain_repo_name = toolchain_repo_name,
+                platform = platform,
+            ))
+            native.register_toolchains("@{toolchain_repo_name}//:{platform}_py_exec_tools_toolchain".format(
+                toolchain_repo_name = toolchain_repo_name,
+                platform = platform,
+            ))
+
+    host_toolchain(name = name + "_host")
+
+    toolchain_aliases(
+        name = name,
+        python_version = python_version,
+        user_repository_name = name,
+        platforms = loaded_platforms,
+    )
+
+    # in bzlmod we write out our own toolchain repos
+    if BZLMOD_ENABLED:
+        return
+
+    toolchains_repo(
+        name = toolchain_repo_name,
+        python_version = python_version,
+        set_python_version_constraint = set_python_version_constraint,
+        user_repository_name = name,
+    )
diff --git a/python/private/python_repositories.bzl b/python/private/python_repositories.bzl
deleted file mode 100644
index 25d8a96..0000000
--- a/python/private/python_repositories.bzl
+++ /dev/null
@@ -1,749 +0,0 @@
-# Copyright 2022 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.
-
-"""This file contains macros to be called during WORKSPACE evaluation.
-
-For historic reasons, pip_repositories() is defined in //python:pip.bzl.
-"""
-
-load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive")
-load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
-load(
-    "//python:versions.bzl",
-    "DEFAULT_RELEASE_BASE_URL",
-    "PLATFORMS",
-    "TOOL_VERSIONS",
-    "get_release_info",
-)
-load("//python/private/pypi:deps.bzl", "pypi_deps")
-load(":auth.bzl", "get_auth")
-load(":bzlmod_enabled.bzl", "BZLMOD_ENABLED")
-load(":coverage_deps.bzl", "coverage_dep")
-load(":full_version.bzl", "full_version")
-load(":internal_config_repo.bzl", "internal_config_repo")
-load(":repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
-load(
-    ":toolchains_repo.bzl",
-    "host_toolchain",
-    "multi_toolchain_aliases",
-    "toolchain_aliases",
-    "toolchains_repo",
-)
-
-def http_archive(**kwargs):
-    maybe(_http_archive, **kwargs)
-
-def py_repositories():
-    """Runtime dependencies that users must install.
-
-    This function should be loaded and called in the user's WORKSPACE.
-    With bzlmod enabled, this function is not needed since MODULE.bazel handles transitive deps.
-    """
-    maybe(
-        internal_config_repo,
-        name = "rules_python_internal",
-    )
-    http_archive(
-        name = "bazel_skylib",
-        sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
-        urls = [
-            "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
-            "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
-        ],
-    )
-    http_archive(
-        name = "rules_cc",
-        urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.0.9/rules_cc-0.0.9.tar.gz"],
-        sha256 = "2037875b9a4456dce4a79d112a8ae885bbc4aad968e6587dca6e64f3a0900cdf",
-        strip_prefix = "rules_cc-0.0.9",
-    )
-    pypi_deps()
-
-########
-# Remaining content of the file is only used to support toolchains.
-########
-
-STANDALONE_INTERPRETER_FILENAME = "STANDALONE_INTERPRETER"
-
-def is_standalone_interpreter(rctx, python_interpreter_path, *, logger = None):
-    """Query a python interpreter target for whether or not it's a rules_rust provided toolchain
-
-    Args:
-        rctx (repository_ctx): The repository rule's context object.
-        python_interpreter_path (path): A path representing the interpreter.
-        logger: Optional logger to use for operations.
-
-    Returns:
-        bool: Whether or not the target is from a rules_python generated toolchain.
-    """
-
-    # Only update the location when using a hermetic toolchain.
-    if not python_interpreter_path:
-        return False
-
-    # This is a rules_python provided toolchain.
-    return repo_utils.execute_unchecked(
-        rctx,
-        op = "IsStandaloneInterpreter",
-        arguments = [
-            "ls",
-            "{}/{}".format(
-                python_interpreter_path.dirname,
-                STANDALONE_INTERPRETER_FILENAME,
-            ),
-        ],
-        logger = logger,
-    ).return_code == 0
-
-def _python_repository_impl(rctx):
-    if rctx.attr.distutils and rctx.attr.distutils_content:
-        fail("Only one of (distutils, distutils_content) should be set.")
-    if bool(rctx.attr.url) == bool(rctx.attr.urls):
-        fail("Exactly one of (url, urls) must be set.")
-
-    logger = repo_utils.logger(rctx)
-
-    platform = rctx.attr.platform
-    python_version = rctx.attr.python_version
-    python_version_info = python_version.split(".")
-    python_short_version = "{0}.{1}".format(*python_version_info)
-    release_filename = rctx.attr.release_filename
-    urls = rctx.attr.urls or [rctx.attr.url]
-    auth = get_auth(rctx, urls)
-
-    if release_filename.endswith(".zst"):
-        rctx.download(
-            url = urls,
-            sha256 = rctx.attr.sha256,
-            output = release_filename,
-            auth = auth,
-        )
-        unzstd = rctx.which("unzstd")
-        if not unzstd:
-            url = rctx.attr.zstd_url.format(version = rctx.attr.zstd_version)
-            rctx.download_and_extract(
-                url = url,
-                sha256 = rctx.attr.zstd_sha256,
-                auth = auth,
-            )
-            working_directory = "zstd-{version}".format(version = rctx.attr.zstd_version)
-
-            repo_utils.execute_checked(
-                rctx,
-                op = "python_repository.MakeZstd",
-                arguments = [
-                    repo_utils.which_checked(rctx, "make"),
-                    "--jobs=4",
-                ],
-                timeout = 600,
-                quiet = True,
-                working_directory = working_directory,
-                logger = logger,
-            )
-            zstd = "{working_directory}/zstd".format(working_directory = working_directory)
-            unzstd = "./unzstd"
-            rctx.symlink(zstd, unzstd)
-
-        repo_utils.execute_checked(
-            rctx,
-            op = "python_repository.ExtractRuntime",
-            arguments = [
-                repo_utils.which_checked(rctx, "tar"),
-                "--extract",
-                "--strip-components=2",
-                "--use-compress-program={unzstd}".format(unzstd = unzstd),
-                "--file={}".format(release_filename),
-            ],
-            logger = logger,
-        )
-    else:
-        rctx.download_and_extract(
-            url = urls,
-            sha256 = rctx.attr.sha256,
-            stripPrefix = rctx.attr.strip_prefix,
-            auth = auth,
-        )
-
-    patches = rctx.attr.patches
-    if patches:
-        for patch in patches:
-            # Should take the strip as an attr, but this is fine for the moment
-            rctx.patch(patch, strip = 1)
-
-    # Write distutils.cfg to the Python installation.
-    if "windows" in platform:
-        distutils_path = "Lib/distutils/distutils.cfg"
-    else:
-        distutils_path = "lib/python{}/distutils/distutils.cfg".format(python_short_version)
-    if rctx.attr.distutils:
-        rctx.file(distutils_path, rctx.read(rctx.attr.distutils))
-    elif rctx.attr.distutils_content:
-        rctx.file(distutils_path, rctx.attr.distutils_content)
-
-    if "darwin" in platform and "osx" == repo_utils.get_platforms_os_name(rctx):
-        # Fix up the Python distribution's LC_ID_DYLIB field.
-        # It points to a build directory local to the GitHub Actions
-        # host machine used in the Python standalone build, which causes
-        # dyld lookup errors. To fix, set the full path to the dylib as
-        # it appears in the Bazel workspace as its LC_ID_DYLIB using
-        # the `install_name_tool` bundled with macOS.
-        dylib = "lib/libpython{}.dylib".format(python_short_version)
-        full_dylib_path = rctx.path(dylib)
-        repo_utils.execute_checked(
-            rctx,
-            op = "python_repository.FixUpDyldIdPath",
-            arguments = [repo_utils.which_checked(rctx, "install_name_tool"), "-id", full_dylib_path, dylib],
-            logger = logger,
-        )
-
-    # Make the Python installation read-only. This is to prevent issues due to
-    # pycs being generated at runtime:
-    # * The pycs are not deterministic (they contain timestamps)
-    # * Multiple processes trying to write the same pycs can result in errors.
-    if not rctx.attr.ignore_root_user_error:
-        if "windows" not in platform:
-            lib_dir = "lib" if "windows" not in platform else "Lib"
-
-            repo_utils.execute_checked(
-                rctx,
-                op = "python_repository.MakeReadOnly",
-                arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
-                logger = logger,
-            )
-            exec_result = repo_utils.execute_unchecked(
-                rctx,
-                op = "python_repository.TestReadOnly",
-                arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
-                logger = logger,
-            )
-
-            # The issue with running as root is the installation is no longer
-            # read-only, so the problems due to pyc can resurface.
-            if exec_result.return_code == 0:
-                stdout = repo_utils.execute_checked_stdout(
-                    rctx,
-                    op = "python_repository.GetUserId",
-                    arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
-                    logger = logger,
-                )
-                uid = int(stdout.strip())
-                if uid == 0:
-                    fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
-                else:
-                    fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
-
-    python_bin = "python.exe" if ("windows" in platform) else "bin/python3"
-
-    glob_include = []
-    glob_exclude = [
-        "**/* *",  # Bazel does not support spaces in file names.
-        # Unused shared libraries. `python` executable and the `:libpython` target
-        # depend on `libpython{python_version}.so.1.0`.
-        "lib/libpython{python_version}.so".format(python_version = python_short_version),
-        # static libraries
-        "lib/**/*.a",
-        # tests for the standard libraries.
-        "lib/python{python_version}/**/test/**".format(python_version = python_short_version),
-        "lib/python{python_version}/**/tests/**".format(python_version = python_short_version),
-        "**/__pycache__/*.pyc.*",  # During pyc creation, temp files named *.pyc.NNN are created
-    ]
-
-    if "linux" in platform:
-        # Workaround around https://github.com/indygreg/python-build-standalone/issues/231
-        for url in urls:
-            head_and_release, _, _ = url.rpartition("/")
-            _, _, release = head_and_release.rpartition("/")
-            if not release.isdigit():
-                # Maybe this is some custom toolchain, so skip this
-                break
-
-            if int(release) >= 20240224:
-                # Starting with this release the Linux toolchains have infinite symlink loop
-                # on host platforms that are not Linux. Delete the files no
-                # matter the host platform so that the cross-built artifacts
-                # are the same irrespective of the host platform we are
-                # building on.
-                #
-                # Link to the first affected release:
-                # https://github.com/indygreg/python-build-standalone/releases/tag/20240224
-                rctx.delete("share/terminfo")
-                break
-
-    if rctx.attr.ignore_root_user_error or "windows" in platform:
-        glob_exclude += [
-            # These pycache files are created on first use of the associated python files.
-            # Exclude them from the glob because otherwise between the first time and second time a python toolchain is used,"
-            # the definition of this filegroup will change, and depending rules will get invalidated."
-            # See https://github.com/bazelbuild/rules_python/issues/1008 for unconditionally adding these to toolchains so we can stop ignoring them."
-            "**/__pycache__/*.pyc",
-            "**/__pycache__/*.pyo",
-        ]
-
-    if "windows" in platform:
-        glob_include += [
-            "*.exe",
-            "*.dll",
-            "bin/**",
-            "DLLs/**",
-            "extensions/**",
-            "include/**",
-            "Lib/**",
-            "libs/**",
-            "Scripts/**",
-            "share/**",
-            "tcl/**",
-        ]
-    else:
-        glob_include += [
-            "bin/**",
-            "extensions/**",
-            "include/**",
-            "lib/**",
-            "libs/**",
-            "share/**",
-        ]
-
-    if rctx.attr.coverage_tool:
-        if "windows" in platform:
-            coverage_tool = None
-        else:
-            coverage_tool = '"{}"'.format(rctx.attr.coverage_tool)
-
-        coverage_attr_text = """\
-    coverage_tool = select({{
-        ":coverage_enabled": {coverage_tool},
-        "//conditions:default": None
-    }}),
-""".format(coverage_tool = coverage_tool)
-    else:
-        coverage_attr_text = "    # coverage_tool attribute not supported by this Bazel version"
-
-    build_content = """\
-# Generated by python/repositories.bzl
-
-load("@rules_python//python:py_runtime.bzl", "py_runtime")
-load("@rules_python//python:py_runtime_pair.bzl", "py_runtime_pair")
-load("@rules_python//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain")
-load("@rules_python//python/private:py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain")
-
-package(default_visibility = ["//visibility:public"])
-
-filegroup(
-    name = "files",
-    srcs = glob(
-        include = {glob_include},
-        # Platform-agnostic filegroup can't match on all patterns.
-        allow_empty = True,
-        exclude = {glob_exclude},
-    ),
-)
-
-cc_import(
-    name = "interface",
-    interface_library = "libs/python{python_version_nodot}.lib",
-    system_provided = True,
-)
-
-filegroup(
-    name = "includes",
-    srcs = glob(["include/**/*.h"]),
-)
-
-cc_library(
-    name = "python_headers",
-    deps = select({{
-        "@bazel_tools//src/conditions:windows": [":interface"],
-        "//conditions:default": None,
-    }}),
-    hdrs = [":includes"],
-    includes = [
-        "include",
-        "include/python{python_version}",
-        "include/python{python_version}m",
-    ],
-)
-
-cc_library(
-    name = "libpython",
-    hdrs = [":includes"],
-    srcs = select({{
-        "@platforms//os:windows": ["python3.dll", "libs/python{python_version_nodot}.lib"],
-        "@platforms//os:macos": ["lib/libpython{python_version}.dylib"],
-        "@platforms//os:linux": ["lib/libpython{python_version}.so", "lib/libpython{python_version}.so.1.0"],
-    }}),
-)
-
-exports_files(["python", "{python_path}"])
-
-# Used to only download coverage toolchain when the coverage is collected by
-# bazel.
-config_setting(
-    name = "coverage_enabled",
-    values = {{"collect_code_coverage": "true"}},
-    visibility = ["//visibility:private"],
-)
-
-py_runtime(
-    name = "py3_runtime",
-    files = [":files"],
-{coverage_attr}
-    interpreter = "{python_path}",
-    interpreter_version_info = {{
-        "major": "{interpreter_version_info_major}",
-        "minor": "{interpreter_version_info_minor}",
-        "micro": "{interpreter_version_info_micro}",
-    }},
-    python_version = "PY3",
-    implementation_name = 'cpython',
-    pyc_tag = "cpython-{interpreter_version_info_major}{interpreter_version_info_minor}",
-)
-
-py_runtime_pair(
-    name = "python_runtimes",
-    py2_runtime = None,
-    py3_runtime = ":py3_runtime",
-)
-
-py_cc_toolchain(
-    name = "py_cc_toolchain",
-    headers = ":python_headers",
-    libs = ":libpython",
-    python_version = "{python_version}",
-)
-
-py_exec_tools_toolchain(
-    name = "py_exec_tools_toolchain",
-    precompiler = "@rules_python//tools/precompiler:precompiler",
-)
-""".format(
-        glob_exclude = repr(glob_exclude),
-        glob_include = repr(glob_include),
-        python_path = python_bin,
-        python_version = python_short_version,
-        python_version_nodot = python_short_version.replace(".", ""),
-        coverage_attr = coverage_attr_text,
-        interpreter_version_info_major = python_version_info[0],
-        interpreter_version_info_minor = python_version_info[1],
-        interpreter_version_info_micro = python_version_info[2],
-    )
-    rctx.delete("python")
-    rctx.symlink(python_bin, "python")
-    rctx.file(STANDALONE_INTERPRETER_FILENAME, "# File intentionally left blank. Indicates that this is an interpreter repo created by rules_python.")
-    rctx.file("BUILD.bazel", build_content)
-
-    attrs = {
-        "auth_patterns": rctx.attr.auth_patterns,
-        "coverage_tool": rctx.attr.coverage_tool,
-        "distutils": rctx.attr.distutils,
-        "distutils_content": rctx.attr.distutils_content,
-        "ignore_root_user_error": rctx.attr.ignore_root_user_error,
-        "name": rctx.attr.name,
-        "netrc": rctx.attr.netrc,
-        "patches": rctx.attr.patches,
-        "platform": platform,
-        "python_version": python_version,
-        "release_filename": release_filename,
-        "sha256": rctx.attr.sha256,
-        "strip_prefix": rctx.attr.strip_prefix,
-    }
-
-    if rctx.attr.url:
-        attrs["url"] = rctx.attr.url
-    else:
-        attrs["urls"] = urls
-
-    return attrs
-
-python_repository = repository_rule(
-    _python_repository_impl,
-    doc = "Fetches the external tools needed for the Python toolchain.",
-    attrs = {
-        "auth_patterns": attr.string_dict(
-            doc = "Override mapping of hostnames to authorization patterns; mirrors the eponymous attribute from http_archive",
-        ),
-        "coverage_tool": attr.string(
-            # Mirrors the definition at
-            # https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/python/py_runtime_rule.bzl
-            doc = """
-This is a target to use for collecting code coverage information from `py_binary`
-and `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.
-
-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.
-
-The target is accepted as a string by the python_repository and evaluated within
-the context of the toolchain repository.
-
-For more information see the official bazel docs
-(https://bazel.build/reference/be/python#py_runtime.coverage_tool).
-""",
-        ),
-        "distutils": attr.label(
-            allow_single_file = True,
-            doc = "A distutils.cfg file to be included in the Python installation. " +
-                  "Either distutils or distutils_content can be specified, but not both.",
-            mandatory = False,
-        ),
-        "distutils_content": attr.string(
-            doc = "A distutils.cfg file content to be included in the Python installation. " +
-                  "Either distutils or distutils_content can be specified, but not both.",
-            mandatory = False,
-        ),
-        "ignore_root_user_error": attr.bool(
-            default = False,
-            doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
-            mandatory = False,
-        ),
-        "netrc": attr.string(
-            doc = ".netrc file to use for authentication; mirrors the eponymous attribute from http_archive",
-        ),
-        "patches": attr.label_list(
-            doc = "A list of patch files to apply to the unpacked interpreter",
-            mandatory = False,
-        ),
-        "platform": attr.string(
-            doc = "The platform name for the Python interpreter tarball.",
-            mandatory = True,
-            values = PLATFORMS.keys(),
-        ),
-        "python_version": attr.string(
-            doc = "The Python version.",
-            mandatory = True,
-        ),
-        "release_filename": attr.string(
-            doc = "The filename of the interpreter to be downloaded",
-            mandatory = True,
-        ),
-        "sha256": attr.string(
-            doc = "The SHA256 integrity hash for the Python interpreter tarball.",
-            mandatory = True,
-        ),
-        "strip_prefix": attr.string(
-            doc = "A directory prefix to strip from the extracted files.",
-        ),
-        "url": attr.string(
-            doc = "The URL of the interpreter to download. Exactly one of url and urls must be set.",
-        ),
-        "urls": attr.string_list(
-            doc = "The URL of the interpreter to download. Exactly one of url and urls must be set.",
-        ),
-        "zstd_sha256": attr.string(
-            default = "7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0",
-        ),
-        "zstd_url": attr.string(
-            default = "https://github.com/facebook/zstd/releases/download/v{version}/zstd-{version}.tar.gz",
-        ),
-        "zstd_version": attr.string(
-            default = "1.5.2",
-        ),
-        "_rule_name": attr.string(default = "python_repository"),
-    },
-    environ = [REPO_DEBUG_ENV_VAR],
-)
-
-# Wrapper macro around everything above, this is the primary API.
-def python_register_toolchains(
-        name,
-        python_version,
-        distutils = None,
-        distutils_content = None,
-        register_toolchains = True,
-        register_coverage_tool = False,
-        set_python_version_constraint = False,
-        tool_versions = TOOL_VERSIONS,
-        **kwargs):
-    """Convenience macro for users which does typical setup.
-
-    - Create a repository for each built-in platform like "python_linux_amd64" -
-      this repository is lazily fetched when Python is needed for that platform.
-    - Create a repository exposing toolchains for each platform like
-      "python_platforms".
-    - Register a toolchain pointing at each platform.
-    Users can avoid this macro and do these steps themselves, if they want more
-    control.
-    Args:
-        name: base name for all created repos, like "python38".
-        python_version: the Python version.
-        distutils: see the distutils attribute in the python_repository repository rule.
-        distutils_content: see the distutils_content attribute in the python_repository repository rule.
-        register_toolchains: Whether or not to register the downloaded toolchains.
-        register_coverage_tool: Whether or not to register the downloaded coverage tool to the toolchains.
-            NOTE: Coverage support using the toolchain is only supported in Bazel 6 and higher.
-
-        set_python_version_constraint: When set to true, target_compatible_with for the toolchains will include a version constraint.
-        tool_versions: a dict containing a mapping of version with SHASUM and platform info. If not supplied, the defaults
-            in python/versions.bzl will be used.
-        **kwargs: passed to each python_repositories call.
-    """
-
-    if BZLMOD_ENABLED:
-        # you cannot used native.register_toolchains when using bzlmod.
-        register_toolchains = False
-
-    base_url = kwargs.pop("base_url", DEFAULT_RELEASE_BASE_URL)
-
-    python_version = full_version(python_version)
-
-    toolchain_repo_name = "{name}_toolchains".format(name = name)
-
-    # When using unreleased Bazel versions, the version is an empty string
-    if native.bazel_version:
-        bazel_major = int(native.bazel_version.split(".")[0])
-        if bazel_major < 6:
-            if register_coverage_tool:
-                # buildifier: disable=print
-                print((
-                    "WARNING: ignoring register_coverage_tool=True when " +
-                    "registering @{name}: Bazel 6+ required, got {version}"
-                ).format(
-                    name = name,
-                    version = native.bazel_version,
-                ))
-            register_coverage_tool = False
-
-    loaded_platforms = []
-    for platform in PLATFORMS.keys():
-        sha256 = tool_versions[python_version]["sha256"].get(platform, None)
-        if not sha256:
-            continue
-
-        loaded_platforms.append(platform)
-        (release_filename, urls, strip_prefix, patches) = get_release_info(platform, python_version, base_url, tool_versions)
-
-        # allow passing in a tool version
-        coverage_tool = None
-        coverage_tool = tool_versions[python_version].get("coverage_tool", {}).get(platform, None)
-        if 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 = platform,
-                visibility = ["@{name}_{platform}//:__subpackages__".format(
-                    name = name,
-                    platform = platform,
-                )],
-            )
-
-        python_repository(
-            name = "{name}_{platform}".format(
-                name = name,
-                platform = platform,
-            ),
-            sha256 = sha256,
-            patches = patches,
-            platform = platform,
-            python_version = python_version,
-            release_filename = release_filename,
-            urls = urls,
-            distutils = distutils,
-            distutils_content = distutils_content,
-            strip_prefix = strip_prefix,
-            coverage_tool = coverage_tool,
-            **kwargs
-        )
-        if register_toolchains:
-            native.register_toolchains("@{toolchain_repo_name}//:{platform}_toolchain".format(
-                toolchain_repo_name = toolchain_repo_name,
-                platform = platform,
-            ))
-            native.register_toolchains("@{toolchain_repo_name}//:{platform}_py_cc_toolchain".format(
-                toolchain_repo_name = toolchain_repo_name,
-                platform = platform,
-            ))
-            native.register_toolchains("@{toolchain_repo_name}//:{platform}_py_exec_tools_toolchain".format(
-                toolchain_repo_name = toolchain_repo_name,
-                platform = platform,
-            ))
-
-    host_toolchain(
-        name = name + "_host",
-        python_version = python_version,
-        user_repository_name = name,
-        platforms = loaded_platforms,
-    )
-
-    toolchain_aliases(
-        name = name,
-        python_version = python_version,
-        user_repository_name = name,
-        platforms = loaded_platforms,
-    )
-
-    # in bzlmod we write out our own toolchain repos
-    if BZLMOD_ENABLED:
-        return
-
-    toolchains_repo(
-        name = toolchain_repo_name,
-        python_version = python_version,
-        set_python_version_constraint = set_python_version_constraint,
-        user_repository_name = name,
-    )
-
-def python_register_multi_toolchains(
-        name,
-        python_versions,
-        default_version = None,
-        **kwargs):
-    """Convenience macro for registering multiple Python toolchains.
-
-    Args:
-        name: base name for each name in python_register_toolchains call.
-        python_versions: the Python version.
-        default_version: the default Python version. If not set, the first version in
-            python_versions is used.
-        **kwargs: passed to each python_register_toolchains call.
-    """
-    if len(python_versions) == 0:
-        fail("python_versions must not be empty")
-
-    if not default_version:
-        default_version = python_versions.pop(0)
-    for python_version in python_versions:
-        if python_version == default_version:
-            # We register the default version lastly so that it's not picked first when --platforms
-            # is set with a constraint during toolchain resolution. This is due to the fact that
-            # Bazel will match the unconstrained toolchain if we register it before the constrained
-            # ones.
-            continue
-        python_register_toolchains(
-            name = name + "_" + python_version.replace(".", "_"),
-            python_version = python_version,
-            set_python_version_constraint = True,
-            **kwargs
-        )
-    python_register_toolchains(
-        name = name + "_" + default_version.replace(".", "_"),
-        python_version = default_version,
-        set_python_version_constraint = False,
-        **kwargs
-    )
-
-    multi_toolchain_aliases(
-        name = name,
-        python_versions = {
-            python_version: name + "_" + python_version.replace(".", "_")
-            for python_version in (python_versions + [default_version])
-        },
-    )
diff --git a/python/private/python_repository.bzl b/python/private/python_repository.bzl
new file mode 100644
index 0000000..2710299
--- /dev/null
+++ b/python/private/python_repository.bzl
@@ -0,0 +1,390 @@
+# Copyright 2024 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.
+
+"""This file contains repository rules and macros to support toolchain registration.
+"""
+
+load("//python:versions.bzl", "PLATFORMS")
+load(":auth.bzl", "get_auth")
+load(":repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
+load(":text_util.bzl", "render")
+
+STANDALONE_INTERPRETER_FILENAME = "STANDALONE_INTERPRETER"
+
+def is_standalone_interpreter(rctx, python_interpreter_path, *, logger = None):
+    """Query a python interpreter target for whether or not it's a rules_rust provided toolchain
+
+    Args:
+        rctx: {type}`repository_ctx` The repository rule's context object.
+        python_interpreter_path: {type}`path` A path representing the interpreter.
+        logger: Optional logger to use for operations.
+
+    Returns:
+        {type}`bool` Whether or not the target is from a rules_python generated toolchain.
+    """
+
+    # Only update the location when using a hermetic toolchain.
+    if not python_interpreter_path:
+        return False
+
+    # This is a rules_python provided toolchain.
+    return repo_utils.execute_unchecked(
+        rctx,
+        op = "IsStandaloneInterpreter",
+        arguments = [
+            "ls",
+            "{}/{}".format(
+                python_interpreter_path.dirname,
+                STANDALONE_INTERPRETER_FILENAME,
+            ),
+        ],
+        logger = logger,
+    ).return_code == 0
+
+def _python_repository_impl(rctx):
+    if rctx.attr.distutils and rctx.attr.distutils_content:
+        fail("Only one of (distutils, distutils_content) should be set.")
+    if bool(rctx.attr.url) == bool(rctx.attr.urls):
+        fail("Exactly one of (url, urls) must be set.")
+
+    logger = repo_utils.logger(rctx)
+
+    platform = rctx.attr.platform
+    python_version = rctx.attr.python_version
+    python_version_info = python_version.split(".")
+    python_short_version = "{0}.{1}".format(*python_version_info)
+    release_filename = rctx.attr.release_filename
+    urls = rctx.attr.urls or [rctx.attr.url]
+    auth = get_auth(rctx, urls)
+
+    if release_filename.endswith(".zst"):
+        rctx.download(
+            url = urls,
+            sha256 = rctx.attr.sha256,
+            output = release_filename,
+            auth = auth,
+        )
+        unzstd = rctx.which("unzstd")
+        if not unzstd:
+            url = rctx.attr.zstd_url.format(version = rctx.attr.zstd_version)
+            rctx.download_and_extract(
+                url = url,
+                sha256 = rctx.attr.zstd_sha256,
+                auth = auth,
+            )
+            working_directory = "zstd-{version}".format(version = rctx.attr.zstd_version)
+
+            repo_utils.execute_checked(
+                rctx,
+                op = "python_repository.MakeZstd",
+                arguments = [
+                    repo_utils.which_checked(rctx, "make"),
+                    "--jobs=4",
+                ],
+                timeout = 600,
+                quiet = True,
+                working_directory = working_directory,
+                logger = logger,
+            )
+            zstd = "{working_directory}/zstd".format(working_directory = working_directory)
+            unzstd = "./unzstd"
+            rctx.symlink(zstd, unzstd)
+
+        repo_utils.execute_checked(
+            rctx,
+            op = "python_repository.ExtractRuntime",
+            arguments = [
+                repo_utils.which_checked(rctx, "tar"),
+                "--extract",
+                "--strip-components=2",
+                "--use-compress-program={unzstd}".format(unzstd = unzstd),
+                "--file={}".format(release_filename),
+            ],
+            logger = logger,
+        )
+    else:
+        rctx.download_and_extract(
+            url = urls,
+            sha256 = rctx.attr.sha256,
+            stripPrefix = rctx.attr.strip_prefix,
+            auth = auth,
+        )
+
+    patches = rctx.attr.patches
+    if patches:
+        for patch in patches:
+            rctx.patch(patch, strip = rctx.attr.patch_strip)
+
+    # Write distutils.cfg to the Python installation.
+    if "windows" in platform:
+        distutils_path = "Lib/distutils/distutils.cfg"
+    else:
+        distutils_path = "lib/python{}/distutils/distutils.cfg".format(python_short_version)
+    if rctx.attr.distutils:
+        rctx.file(distutils_path, rctx.read(rctx.attr.distutils))
+    elif rctx.attr.distutils_content:
+        rctx.file(distutils_path, rctx.attr.distutils_content)
+
+    if "darwin" in platform and "osx" == repo_utils.get_platforms_os_name(rctx):
+        # Fix up the Python distribution's LC_ID_DYLIB field.
+        # It points to a build directory local to the GitHub Actions
+        # host machine used in the Python standalone build, which causes
+        # dyld lookup errors. To fix, set the full path to the dylib as
+        # it appears in the Bazel workspace as its LC_ID_DYLIB using
+        # the `install_name_tool` bundled with macOS.
+        dylib = "lib/libpython{}.dylib".format(python_short_version)
+        full_dylib_path = rctx.path(dylib)
+        repo_utils.execute_checked(
+            rctx,
+            op = "python_repository.FixUpDyldIdPath",
+            arguments = [repo_utils.which_checked(rctx, "install_name_tool"), "-id", full_dylib_path, dylib],
+            logger = logger,
+        )
+
+    # Make the Python installation read-only. This is to prevent issues due to
+    # pycs being generated at runtime:
+    # * The pycs are not deterministic (they contain timestamps)
+    # * Multiple processes trying to write the same pycs can result in errors.
+    if not rctx.attr.ignore_root_user_error:
+        if "windows" not in platform:
+            lib_dir = "lib" if "windows" not in platform else "Lib"
+
+            repo_utils.execute_checked(
+                rctx,
+                op = "python_repository.MakeReadOnly",
+                arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
+                logger = logger,
+            )
+            exec_result = repo_utils.execute_unchecked(
+                rctx,
+                op = "python_repository.TestReadOnly",
+                arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
+                logger = logger,
+            )
+
+            # The issue with running as root is the installation is no longer
+            # read-only, so the problems due to pyc can resurface.
+            if exec_result.return_code == 0:
+                stdout = repo_utils.execute_checked_stdout(
+                    rctx,
+                    op = "python_repository.GetUserId",
+                    arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
+                    logger = logger,
+                )
+                uid = int(stdout.strip())
+                if uid == 0:
+                    fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
+                else:
+                    fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
+
+    python_bin = "python.exe" if ("windows" in platform) else "bin/python3"
+
+    if "linux" in platform:
+        # Workaround around https://github.com/indygreg/python-build-standalone/issues/231
+        for url in urls:
+            head_and_release, _, _ = url.rpartition("/")
+            _, _, release = head_and_release.rpartition("/")
+            if not release.isdigit():
+                # Maybe this is some custom toolchain, so skip this
+                break
+
+            if int(release) >= 20240224:
+                # Starting with this release the Linux toolchains have infinite symlink loop
+                # on host platforms that are not Linux. Delete the files no
+                # matter the host platform so that the cross-built artifacts
+                # are the same irrespective of the host platform we are
+                # building on.
+                #
+                # Link to the first affected release:
+                # https://github.com/indygreg/python-build-standalone/releases/tag/20240224
+                rctx.delete("share/terminfo")
+                break
+
+    glob_include = []
+    glob_exclude = []
+    if rctx.attr.ignore_root_user_error or "windows" in platform:
+        glob_exclude += [
+            # These pycache files are created on first use of the associated python files.
+            # Exclude them from the glob because otherwise between the first time and second time a python toolchain is used,"
+            # the definition of this filegroup will change, and depending rules will get invalidated."
+            # See https://github.com/bazelbuild/rules_python/issues/1008 for unconditionally adding these to toolchains so we can stop ignoring them."
+            "**/__pycache__/*.pyc",
+            "**/__pycache__/*.pyo",
+        ]
+
+    if "windows" in platform:
+        glob_include += [
+            "*.exe",
+            "*.dll",
+            "DLLs/**",
+            "Lib/**",
+            "Scripts/**",
+            "tcl/**",
+        ]
+    else:
+        glob_include.append(
+            "lib/**",
+        )
+
+    if "windows" in platform:
+        coverage_tool = None
+    else:
+        coverage_tool = rctx.attr.coverage_tool
+
+    build_content = """\
+# Generated by python/private/python_repositories.bzl
+
+load("@rules_python//python/private:hermetic_runtime_repo_setup.bzl", "define_hermetic_runtime_toolchain_impl")
+
+package(default_visibility = ["//visibility:public"])
+
+define_hermetic_runtime_toolchain_impl(
+  name = "define_runtime",
+  extra_files_glob_include = {extra_files_glob_include},
+  extra_files_glob_exclude = {extra_files_glob_exclude},
+  python_version = {python_version},
+  python_bin = {python_bin},
+  coverage_tool = {coverage_tool},
+)
+""".format(
+        extra_files_glob_exclude = render.list(glob_exclude),
+        extra_files_glob_include = render.list(glob_include),
+        python_bin = render.str(python_bin),
+        python_version = render.str(rctx.attr.python_version),
+        coverage_tool = render.str(coverage_tool),
+    )
+    rctx.delete("python")
+    rctx.symlink(python_bin, "python")
+    rctx.file(STANDALONE_INTERPRETER_FILENAME, "# File intentionally left blank. Indicates that this is an interpreter repo created by rules_python.")
+    rctx.file("BUILD.bazel", build_content)
+
+    attrs = {
+        "auth_patterns": rctx.attr.auth_patterns,
+        "coverage_tool": rctx.attr.coverage_tool,
+        "distutils": rctx.attr.distutils,
+        "distutils_content": rctx.attr.distutils_content,
+        "ignore_root_user_error": rctx.attr.ignore_root_user_error,
+        "name": rctx.attr.name,
+        "netrc": rctx.attr.netrc,
+        "patch_strip": rctx.attr.patch_strip,
+        "patches": rctx.attr.patches,
+        "platform": platform,
+        "python_version": python_version,
+        "release_filename": release_filename,
+        "sha256": rctx.attr.sha256,
+        "strip_prefix": rctx.attr.strip_prefix,
+    }
+
+    if rctx.attr.url:
+        attrs["url"] = rctx.attr.url
+    else:
+        attrs["urls"] = urls
+
+    return attrs
+
+python_repository = repository_rule(
+    _python_repository_impl,
+    doc = "Fetches the external tools needed for the Python toolchain.",
+    attrs = {
+        "auth_patterns": attr.string_dict(
+            doc = "Override mapping of hostnames to authorization patterns; mirrors the eponymous attribute from http_archive",
+        ),
+        "coverage_tool": attr.string(
+            doc = """
+This is a target to use for collecting code coverage information from {rule}`py_binary`
+and {rule}`py_test` targets.
+
+The target is accepted as a string by the python_repository and evaluated within
+the context of the toolchain repository.
+
+For more information see {attr}`py_runtime.coverage_tool`.
+""",
+        ),
+        "distutils": attr.label(
+            allow_single_file = True,
+            doc = "A distutils.cfg file to be included in the Python installation. " +
+                  "Either distutils or distutils_content can be specified, but not both.",
+            mandatory = False,
+        ),
+        "distutils_content": attr.string(
+            doc = "A distutils.cfg file content to be included in the Python installation. " +
+                  "Either distutils or distutils_content can be specified, but not both.",
+            mandatory = False,
+        ),
+        "ignore_root_user_error": attr.bool(
+            default = False,
+            doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
+            mandatory = False,
+        ),
+        "netrc": attr.string(
+            doc = ".netrc file to use for authentication; mirrors the eponymous attribute from http_archive",
+        ),
+        "patch_strip": attr.int(
+            doc = """
+Same as the --strip argument of Unix patch.
+
+:::{note}
+In the future the default value will be set to `0`, to mimic the well known
+function defaults (e.g. `single_version_override` for `MODULE.bazel` files.
+:::
+
+:::{versionadded} 0.36.0
+:::
+""",
+            default = 1,
+            mandatory = False,
+        ),
+        "patches": attr.label_list(
+            doc = "A list of patch files to apply to the unpacked interpreter",
+            mandatory = False,
+        ),
+        "platform": attr.string(
+            doc = "The platform name for the Python interpreter tarball.",
+            mandatory = True,
+            values = PLATFORMS.keys(),
+        ),
+        "python_version": attr.string(
+            doc = "The Python version.",
+            mandatory = True,
+        ),
+        "release_filename": attr.string(
+            doc = "The filename of the interpreter to be downloaded",
+            mandatory = True,
+        ),
+        "sha256": attr.string(
+            doc = "The SHA256 integrity hash for the Python interpreter tarball.",
+            mandatory = True,
+        ),
+        "strip_prefix": attr.string(
+            doc = "A directory prefix to strip from the extracted files.",
+        ),
+        "url": attr.string(
+            doc = "The URL of the interpreter to download. Exactly one of url and urls must be set.",
+        ),
+        "urls": attr.string_list(
+            doc = "The URL of the interpreter to download. Exactly one of url and urls must be set.",
+        ),
+        "zstd_sha256": attr.string(
+            default = "7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0",
+        ),
+        "zstd_url": attr.string(
+            default = "https://github.com/facebook/zstd/releases/download/v{version}/zstd-{version}.tar.gz",
+        ),
+        "zstd_version": attr.string(
+            default = "1.5.2",
+        ),
+        "_rule_name": attr.string(default = "python_repository"),
+    },
+    environ = [REPO_DEBUG_ENV_VAR],
+)
diff --git a/python/private/pythons_hub.bzl b/python/private/pythons_hub.bzl
index 7a8c874..da6c80d 100644
--- a/python/private/pythons_hub.bzl
+++ b/python/private/pythons_hub.bzl
@@ -14,7 +14,6 @@
 
 "Repo rule used by bzlmod extension to create a repo that has a map of Python interpreters and their labels"
 
-load("//python/private:full_version.bzl", "full_version")
 load(
     "//python/private:toolchains_repo.bzl",
     "python_toolchain_build_file_content",
@@ -59,7 +58,7 @@
         [
             python_toolchain_build_file_content(
                 prefix = prefixes[i],
-                python_version = full_version(python_versions[i]),
+                python_version = python_versions[i],
                 set_python_version_constraint = set_python_version_constraints[i],
                 user_repository_name = user_repository_names[i],
             )
@@ -123,7 +122,7 @@
     implementation = _hub_repo_impl,
     attrs = {
         "default_python_version": attr.string(
-            doc = "Default Python version for the build.",
+            doc = "Default Python version for the build in `X.Y` or `X.Y.Z` format.",
             mandatory = True,
         ),
         "toolchain_prefixes": attr.string_list(
@@ -131,7 +130,7 @@
             mandatory = True,
         ),
         "toolchain_python_versions": attr.string_list(
-            doc = "List of Python versions for the toolchains",
+            doc = "List of Python versions for the toolchains. In `X.Y.Z` format.",
             mandatory = True,
         ),
         "toolchain_set_python_version_constraints": attr.string_list(
diff --git a/python/private/repo_utils.bzl b/python/private/repo_utils.bzl
index aab0325..e0bf69a 100644
--- a/python/private/repo_utils.bzl
+++ b/python/private/repo_utils.bzl
@@ -404,12 +404,18 @@
 # TODO: Remove after Bazel 6 support dropped
 def _watch(mrctx, *args, **kwargs):
     """Calls mrctx.watch, if available."""
+    if not args and not kwargs:
+        fail("'watch' needs at least a single argument.")
+
     if hasattr(mrctx, "watch"):
         mrctx.watch(*args, **kwargs)
 
 # TODO: Remove after Bazel 6 support dropped
 def _watch_tree(mrctx, *args, **kwargs):
     """Calls mrctx.watch_tree, if available."""
+    if not args and not kwargs:
+        fail("'watch_tree' needs at least a single argument.")
+
     if hasattr(mrctx, "watch_tree"):
         mrctx.watch_tree(*args, **kwargs)
 
diff --git a/python/private/semver.bzl b/python/private/semver.bzl
new file mode 100644
index 0000000..73d6b13
--- /dev/null
+++ b/python/private/semver.bzl
@@ -0,0 +1,75 @@
+# Copyright 2024 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 semver version parser"
+
+def _key(version):
+    return (
+        version.major,
+        version.minor or 0,
+        version.patch or 0,
+        # non pre-release versions are higher
+        version.pre_release == "",
+        # then we compare each element of the pre_release tag separately
+        tuple([
+            (
+                i if not i.isdigit() else "",
+                # digit values take precedence
+                int(i) if i.isdigit() else 0,
+            )
+            for i in version.pre_release.split(".")
+        ]) if version.pre_release else None,
+        # And build info is just alphabetic
+        version.build,
+    )
+
+def _to_dict(self):
+    return {
+        "build": self.build,
+        "major": self.major,
+        "minor": self.minor,
+        "patch": self.patch,
+        "pre_release": self.pre_release,
+    }
+
+def semver(version):
+    """Parse the semver version and return the values as a struct.
+
+    Args:
+        version: {type}`str` the version string.
+
+    Returns:
+        A {type}`struct` with `major`, `minor`, `patch` and `build` attributes.
+    """
+
+    # Implement the https://semver.org/ spec
+    major, _, tail = version.partition(".")
+    minor, _, tail = tail.partition(".")
+    patch, _, build = tail.partition("+")
+    patch, _, pre_release = patch.partition("-")
+
+    # buildifier: disable=uninitialized
+    self = struct(
+        major = int(major),
+        minor = int(minor) if minor.isdigit() else None,
+        # NOTE: this is called `micro` in the Python interpreter versioning scheme
+        patch = int(patch) if patch.isdigit() else None,
+        pre_release = pre_release,
+        build = build,
+        # buildifier: disable=uninitialized
+        key = lambda: _key(self),
+        str = lambda: version,
+        to_dict = lambda: _to_dict(self),
+    )
+    return self
diff --git a/python/private/sentinel.bzl b/python/private/sentinel.bzl
new file mode 100644
index 0000000..6d753e1
--- /dev/null
+++ b/python/private/sentinel.bzl
@@ -0,0 +1,30 @@
+# Copyright 2024 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 rule to define a target to act as a singleton for label attributes.
+
+Label attributes with defaults cannot accept None, otherwise they fall
+back to using the default. A sentinel allows detecting an intended None value.
+"""
+
+SentinelInfo = provider(
+    doc = "Indicates this was the sentinel target.",
+    fields = [],
+)
+
+def _sentinel_impl(ctx):
+    _ = ctx  # @unused
+    return [SentinelInfo()]
+
+sentinel = rule(implementation = _sentinel_impl)
diff --git a/python/private/stage1_bootstrap_template.sh b/python/private/stage1_bootstrap_template.sh
index 959e7ba..e7e418c 100644
--- a/python/private/stage1_bootstrap_template.sh
+++ b/python/private/stage1_bootstrap_template.sh
@@ -44,10 +44,10 @@
       echo "$RUNFILES_DIR"
       return 0
     elif [[ "${RUNFILES_MANIFEST_FILE:-}" = *".runfiles_manifest" ]]; then
-      echo "${RUNFILES_MANIFEST_FILE%%.runfiles_manifest}"
+      echo "${RUNFILES_MANIFEST_FILE%%.runfiles_manifest}.runfiles"
       return 0
     elif [[ "${RUNFILES_MANIFEST_FILE:-}" = *".runfiles/MANIFEST" ]]; then
-      echo "${RUNFILES_MANIFEST_FILE%%.runfiles/MANIFEST}"
+      echo "${RUNFILES_MANIFEST_FILE%%.runfiles/MANIFEST}.runfiles"
       return 0
     fi
 
@@ -57,7 +57,6 @@
     if [[ "$stub_filename" != /* ]]; then
       stub_filename="$PWD/$stub_filename"
     fi
-
     while true; do
       module_space="${stub_filename}.runfiles"
       if [[ -d "$module_space" ]]; then
diff --git a/python/private/stage2_bootstrap_template.py b/python/private/stage2_bootstrap_template.py
index 29f59d2..f66c28b 100644
--- a/python/private/stage2_bootstrap_template.py
+++ b/python/private/stage2_bootstrap_template.py
@@ -364,6 +364,14 @@
                 # Pipes can't be read back later, which can cause coverage to
                 # throw an error when trying to get its source code.
                 "/dev/fd/*",
+                # The mechanism for finding third-party packages in coverage-py
+                # only works for installed packages, not for runfiles. e.g:
+                #'$HOME/.local/lib/python3.10/site-packages',
+                # '/usr/lib/python',
+                # '/usr/lib/python3.10/site-packages',
+                # '/usr/local/lib/python3.10/dist-packages'
+                # see https://github.com/nedbat/coveragepy/blob/bfb0c708fdd8182b2a9f0fc403596693ef65e475/coverage/inorout.py#L153-L164
+                "*/external/*",
             ],
         )
         cov.start()
diff --git a/python/private/toolchains_repo.bzl b/python/private/toolchains_repo.bzl
index df16fb8..4fae987 100644
--- a/python/private/toolchains_repo.bzl
+++ b/python/private/toolchains_repo.bzl
@@ -56,9 +56,6 @@
         build_content: Text containing toolchain definitions
     """
 
-    # We create a list of toolchain content from iterating over
-    # the enumeration of PLATFORMS.  We enumerate PLATFORMS in
-    # order to get us an index to increment the increment.
     return "\n\n".join([
         """\
 py_toolchain_suite(
@@ -314,14 +311,6 @@
 this repo causes an eager fetch of the toolchain for the host platform.
     """,
     attrs = {
-        "platforms": attr.string_list(
-            doc = "List of platforms for which aliases shall be created",
-        ),
-        "python_version": attr.string(doc = "The Python version."),
-        "user_repository_name": attr.string(
-            mandatory = True,
-            doc = "The base name for all created repositories, like 'python38'.",
-        ),
         "_rule_name": attr.string(default = "host_toolchain"),
         "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
     },
@@ -366,11 +355,13 @@
         name = name,
         python_versions = {python_versions},
         requirements_lock = requirements_lock,
+        minor_mapping = {minor_mapping},
         **kwargs
     )
 
 """.format(
         python_versions = rctx.attr.python_versions.keys(),
+        minor_mapping = render.indent(render.dict(rctx.attr.minor_mapping), indent = " " * 8).lstrip(),
         rules_python = rules_python,
     )
     rctx.file("pip.bzl", content = pip_bzl)
@@ -379,6 +370,7 @@
 multi_toolchain_aliases = repository_rule(
     _multi_toolchain_aliases_impl,
     attrs = {
+        "minor_mapping": attr.string_dict(doc = "The mapping between `X.Y` and `X.Y.Z` python version values"),
         "python_versions": attr.string_dict(doc = "The Python versions."),
         "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),
     },
diff --git a/python/py_exec_tools_info.bzl b/python/py_exec_tools_info.bzl
new file mode 100644
index 0000000..4384123
--- /dev/null
+++ b/python/py_exec_tools_info.bzl
@@ -0,0 +1,24 @@
+# Copyright 2024 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.
+"""Provider for the exec tools toolchain.
+
+:::{seealso}
+* {any}`Custom toolchains` for how to define custom toolchains.
+* {obj}`py_cc_toolchain` rule for defining the toolchain.
+:::
+"""
+
+load("//python/private:py_exec_tools_info.bzl", _PyExecToolsInfo = "PyExecToolsInfo")
+
+PyExecToolsInfo = _PyExecToolsInfo
diff --git a/python/py_exec_tools_toolchain.bzl b/python/py_exec_tools_toolchain.bzl
new file mode 100644
index 0000000..6e0a663
--- /dev/null
+++ b/python/py_exec_tools_toolchain.bzl
@@ -0,0 +1,18 @@
+# Copyright 2024 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.
+"""Toolchain for build-time tools."""
+
+load("//python/private:py_exec_tools_toolchain.bzl", _py_exec_tools_toolchain = "py_exec_tools_toolchain")
+
+py_exec_tools_toolchain = _py_exec_tools_toolchain
diff --git a/python/py_executable_info.bzl b/python/py_executable_info.bzl
new file mode 100644
index 0000000..59c0bb2
--- /dev/null
+++ b/python/py_executable_info.bzl
@@ -0,0 +1,12 @@
+"""Provider for executable-specific information.
+
+The `PyExecutableInfo` provider contains information about an executable that
+isn't otherwise available from its public attributes or other providers.
+
+It exposes information primarily useful for consumers to package the executable,
+or derive a new executable from the base binary.
+"""
+
+load("//python/private:py_executable_info.bzl", _PyExecutableInfo = "PyExecutableInfo")
+
+PyExecutableInfo = _PyExecutableInfo
diff --git a/python/py_runtime_pair.bzl b/python/py_runtime_pair.bzl
index 1728dcd..b1e9041 100644
--- a/python/py_runtime_pair.bzl
+++ b/python/py_runtime_pair.bzl
@@ -25,6 +25,8 @@
 def py_runtime_pair(name, py2_runtime = None, py3_runtime = None, **attrs):
     """A toolchain rule for Python.
 
+    This is a macro around the underlying {rule}`py_runtime_pair` rule.
+
     This used to wrap up to two Python runtimes, one for Python 2 and one for Python 3.
     However, Python 2 is no longer supported, so it now only wraps a single Python 3
     runtime.
diff --git a/python/repositories.bzl b/python/repositories.bzl
index cf87234..768b587 100644
--- a/python/repositories.bzl
+++ b/python/repositories.bzl
@@ -16,23 +16,24 @@
 """
 
 load(
-    "//python/private:python_repositories.bzl",
+    "//python/private:is_standalone_interpreter.bzl",
     _STANDALONE_INTERPRETER_FILENAME = "STANDALONE_INTERPRETER_FILENAME",
-    _http_archive = "http_archive",
     _is_standalone_interpreter = "is_standalone_interpreter",
-    _py_repositories = "py_repositories",
-    _python_register_multi_toolchains = "python_register_multi_toolchains",
-    _python_register_toolchains = "python_register_toolchains",
-    _python_repository = "python_repository",
 )
+load("//python/private:py_repositories.bzl", _py_repositories = "py_repositories")
+load("//python/private:python_register_multi_toolchains.bzl", _python_register_multi_toolchains = "python_register_multi_toolchains")
+load("//python/private:python_register_toolchains.bzl", _python_register_toolchains = "python_register_toolchains")
+load("//python/private:python_repository.bzl", _python_repository = "python_repository")
 
 py_repositories = _py_repositories
 python_register_multi_toolchains = _python_register_multi_toolchains
 python_register_toolchains = _python_register_toolchains
 
+# Useful for documentation, but is not intended for public use - the python
+# module extension will be the main interface in the future.
+python_repository = _python_repository
+
 # These symbols are of questionable public visibility. They were probably
 # not intended to be actually public.
 STANDALONE_INTERPRETER_FILENAME = _STANDALONE_INTERPRETER_FILENAME
-http_archive = _http_archive
 is_standalone_interpreter = _is_standalone_interpreter
-python_repository = _python_repository
diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl
new file mode 100644
index 0000000..f0a66a1
--- /dev/null
+++ b/python/uv/private/lock.bzl
@@ -0,0 +1,121 @@
+# Copyright 2024 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 simple macro to lock the requirements.
+"""
+
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+load("//python:py_binary.bzl", "py_binary")
+load("//python/config_settings:transition.bzl", transition_py_binary = "py_binary")
+load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")  # buildifier: disable=bzl-visibility
+
+visibility(["//..."])
+
+_REQUIREMENTS_TARGET_COMPATIBLE_WITH = [] if BZLMOD_ENABLED else ["@platforms//:incompatible"]
+
+def lock(*, name, srcs, out, upgrade = False, universal = True, python_version = None):
+    """Pin the requirements based on the src files.
+
+    Args:
+        name: The name of the target to run for updating the requirements.
+        srcs: The srcs to use as inputs.
+        out: The output file.
+        upgrade: Tell `uv` to always upgrade the dependencies instead of
+            keeping them as they are.
+        universal: Tell `uv` to generate a universal lock file.
+        python_version: Tell `rules_python` to use a particular version.
+            Defaults to the default py toolchain.
+
+    Differences with the current pip-compile rule:
+    - This is implemented in shell and uv.
+    - This does not error out if the output file does not exist yet.
+    - Supports transitions out of the box.
+    """
+    pkg = native.package_name()
+    update_target = name + ".update"
+
+    args = [
+        "--custom-compile-command='bazel run //{}:{}'".format(pkg, update_target),
+        "--generate-hashes",
+        "--emit-index-url",
+        "--no-strip-extras",
+        "--python=$(PYTHON3)",
+    ] + [
+        "$(location {})".format(src)
+        for src in srcs
+    ]
+    if upgrade:
+        args.append("--upgrade")
+    if universal:
+        args.append("--universal")
+    args.append("--output-file=$@")
+    cmd = "$(UV_BIN) pip compile " + " ".join(args)
+
+    # Check if the output file already exists, if yes, first copy it to the
+    # output file location in order to make `uv` not change the requirements if
+    # we are just running the command.
+    if native.glob([out]):
+        cmd = "cp -v $(location {}) $@; {}".format(out, cmd)
+        srcs.append(out)
+
+    native.genrule(
+        name = name,
+        srcs = srcs,
+        outs = [out + ".new"],
+        cmd_bash = cmd,
+        tags = [
+            "local",
+            "manual",
+            "no-cache",
+        ],
+        target_compatible_with = _REQUIREMENTS_TARGET_COMPATIBLE_WITH,
+        toolchains = [
+            Label("//python/uv:current_toolchain"),
+            Label("//python:current_py_toolchain"),
+        ],
+    )
+    if python_version:
+        py_binary_rule = lambda *args, **kwargs: transition_py_binary(python_version = python_version, *args, **kwargs)
+    else:
+        py_binary_rule = py_binary
+
+    # Write a script that can be used for updating the in-tree version of the
+    # requirements file
+    write_file(
+        name = name + ".update_gen",
+        out = update_target + ".py",
+        content = [
+            "from os import environ",
+            "from pathlib import Path",
+            "from sys import stderr",
+            "",
+            'src = Path(environ["REQUIREMENTS_FILE"])',
+            'assert src.exists(), f"the {src} file does not exist"',
+            'dst = Path(environ["BUILD_WORKSPACE_DIRECTORY"]) / "{}" / "{}"'.format(pkg, out),
+            'print(f"Writing requirements contents\\n  from {src.absolute()}\\n  to {dst.absolute()}", file=stderr)',
+            "dst.write_text(src.read_text())",
+            'print("Success!", file=stderr)',
+        ],
+    )
+
+    py_binary_rule(
+        name = update_target,
+        srcs = [update_target + ".py"],
+        main = update_target + ".py",
+        data = [name],
+        env = {
+            "REQUIREMENTS_FILE": "$(rootpath {})".format(name),
+        },
+        tags = ["manual"],
+    )
diff --git a/python/versions.bzl b/python/versions.bzl
index 2cf9b39..c97c1cc 100644
--- a/python/versions.bzl
+++ b/python/versions.bzl
@@ -41,7 +41,7 @@
 #       "strip_prefix": "python",
 #   },
 #
-# It is possible to provide lists in "url".
+# It is possible to provide lists in "url". It is also possible to provide patches or patch_strip.
 #
 # buildifier: disable=unsorted-dict-items
 TOOL_VERSIONS = {
@@ -636,7 +636,7 @@
         tool_versions: A dict listing the interpreter versions, their SHAs and URL
 
     Returns:
-        A tuple of (filename, url, and archive strip prefix)
+        A tuple of (filename, url, archive strip prefix, patches, patch_strip)
     """
 
     url = tool_versions[python_version]["url"]
@@ -673,8 +673,14 @@
             patches = patches[platform]
         else:
             patches = []
+    patch_strip = tool_versions[python_version].get("patch_strip", None)
+    if type(patch_strip) == type({}):
+        if platform in patch_strip.keys():
+            patch_strip = patch_strip[platform]
+        else:
+            patch_strip = None
 
-    return (release_filename, rendered_urls, strip_prefix, patches)
+    return (release_filename, rendered_urls, strip_prefix, patches, patch_strip)
 
 def print_toolchains_checksums(name):
     native.genrule(
@@ -716,5 +722,6 @@
     for platform in PLATFORMS.keys():
         native.config_setting(
             name = "{name}{platform}".format(name = name, platform = platform),
+            flag_values = PLATFORMS[platform].flag_values,
             constraint_values = PLATFORMS[platform].compatible_with,
         )
diff --git a/sphinxdocs/BUILD.bazel b/sphinxdocs/BUILD.bazel
index 6cb69ba..9ad1e1e 100644
--- a/sphinxdocs/BUILD.bazel
+++ b/sphinxdocs/BUILD.bazel
@@ -48,6 +48,12 @@
 )
 
 bzl_library(
+    name = "sphinx_docs_library_bzl",
+    srcs = ["sphinx_docs_library.bzl"],
+    deps = ["//sphinxdocs/private:sphinx_docs_library_macro_bzl"],
+)
+
+bzl_library(
     name = "sphinx_stardoc_bzl",
     srcs = ["sphinx_stardoc.bzl"],
     deps = ["//sphinxdocs/private:sphinx_stardoc_bzl"],
diff --git a/sphinxdocs/docs/BUILD.bazel b/sphinxdocs/docs/BUILD.bazel
new file mode 100644
index 0000000..6af908d
--- /dev/null
+++ b/sphinxdocs/docs/BUILD.bazel
@@ -0,0 +1,64 @@
+load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
+load("//sphinxdocs:sphinx_docs_library.bzl", "sphinx_docs_library")
+load("//sphinxdocs:sphinx_stardoc.bzl", "sphinx_stardocs")
+
+package(default_visibility = ["//:__subpackages__"])
+
+# We only build for Linux and Mac because:
+# 1. The actual doc process only runs on Linux
+# 2. Mac is a common development platform, and is close enough to Linux
+#    it's feasible to make work.
+# Making CI happy under Windows is too much of a headache, though, so we don't
+# bother with that.
+_TARGET_COMPATIBLE_WITH = select({
+    "@platforms//os:linux": [],
+    "@platforms//os:macos": [],
+    "//conditions:default": ["@platforms//:incompatible"],
+}) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"]
+
+sphinx_docs_library(
+    name = "docs_lib",
+    deps = [
+        ":artisian_api_docs",
+        ":bzl_docs",
+        ":py_api_srcs",
+        ":regular_docs",
+    ],
+)
+
+sphinx_docs_library(
+    name = "regular_docs",
+    srcs = glob(
+        ["**/*.md"],
+        exclude = ["api/**"],
+    ),
+    prefix = "sphinxdocs/",
+)
+
+sphinx_docs_library(
+    name = "artisian_api_docs",
+    srcs = glob(
+        ["api/**/*.md"],
+    ),
+    prefix = "api/sphinxdocs/",
+    strip_prefix = "sphinxdocs/docs/api/",
+)
+
+sphinx_stardocs(
+    name = "bzl_docs",
+    srcs = [
+        "//sphinxdocs:readthedocs_bzl",
+        "//sphinxdocs:sphinx_bzl",
+        "//sphinxdocs:sphinx_docs_library_bzl",
+        "//sphinxdocs:sphinx_stardoc_bzl",
+        "//sphinxdocs/private:sphinx_docs_library_bzl",
+    ],
+    prefix = "api/sphinxdocs/",
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+)
+
+sphinx_docs_library(
+    name = "py_api_srcs",
+    srcs = ["//sphinxdocs/src/sphinx_bzl"],
+    strip_prefix = "sphinxdocs/src/",
+)
diff --git a/sphinxdocs/docs/api/index.md b/sphinxdocs/docs/api/index.md
new file mode 100644
index 0000000..3420b91
--- /dev/null
+++ b/sphinxdocs/docs/api/index.md
@@ -0,0 +1,8 @@
+# sphinxdocs Bazel APIs
+
+API documentation for sphinxdocs Bazel objects.
+
+```{toctree}
+:glob:
+**
+```
diff --git a/sphinxdocs/docs/api/sphinxdocs/index.md b/sphinxdocs/docs/api/sphinxdocs/index.md
new file mode 100644
index 0000000..bd4e9b6
--- /dev/null
+++ b/sphinxdocs/docs/api/sphinxdocs/index.md
@@ -0,0 +1,29 @@
+:::{bzl:currentfile} //sphinxdocs:BUILD.bazel
+:::
+
+# //sphinxdocs
+
+:::{bzl:flag} extra_defines
+Additional `-D` values to add to every Sphinx build.
+
+This is a list flag. Multiple uses are accumulated.
+
+This is most useful for overriding e.g. the version when performing
+release builds.
+:::
+
+:::{bzl:flag} extra_env
+Additional environment variables to for every Sphinx build.
+
+This is a list flag. Multiple uses are accumulated. Values are `key=value`
+format.
+:::
+
+:::{bzl:flag} quiet
+Whether to add the `-q` arg to Sphinx invocations.
+
+This is a boolean flag.
+
+This is useful for debugging invocations or developing extensions. The Sphinx
+`-q` flag causes sphinx to produce additional output on stdout.
+:::
diff --git a/sphinxdocs/docs/api/sphinxdocs/inventories/index.md b/sphinxdocs/docs/api/sphinxdocs/inventories/index.md
new file mode 100644
index 0000000..a03645e
--- /dev/null
+++ b/sphinxdocs/docs/api/sphinxdocs/inventories/index.md
@@ -0,0 +1,11 @@
+:::{bzl:currentfile} //sphinxdocs/inventories:BUILD.bazel
+:::
+
+# //sphinxdocs/inventories
+
+:::{bzl:target} bazel_inventory
+A Sphinx inventory of Bazel objects.
+
+By including this target in your Sphinx build and enabling intersphinx, cross
+references to builtin Bazel objects can be written.
+:::
diff --git a/sphinxdocs/docs/index.md b/sphinxdocs/docs/index.md
new file mode 100644
index 0000000..ac857d6
--- /dev/null
+++ b/sphinxdocs/docs/index.md
@@ -0,0 +1,20 @@
+# Docgen using Sphinx with Bazel
+
+The `sphinxdocs` project allows using Bazel to run Sphinx to generate
+documentation. It comes with:
+
+* Rules for running Sphinx
+* Rules for generating documentation for Starlark code.
+* A Sphinx plugin for documenting Starlark and Bazel objects.
+* Rules for readthedocs build integration.
+
+While it is primarily oriented towards docgen for Starlark code, the core of it
+is agnostic as to what is being documented.
+
+
+```{toctree}
+:hidden:
+
+starlark-docgen
+sphinx-bzl
+```
diff --git a/sphinxdocs/docs/sphinx-bzl.md b/sphinxdocs/docs/sphinx-bzl.md
new file mode 100644
index 0000000..73ae138
--- /dev/null
+++ b/sphinxdocs/docs/sphinx-bzl.md
@@ -0,0 +1,257 @@
+# Bazel plugin for Sphinx
+
+The `sphinx_bzl` Python package is a Sphinx plugin that defines a custom domain
+("bzl") in the Sphinx system. This provides first-class integration with Sphinx
+and allows code comments to provide rich information and allows manually writing
+docs for objects that aren't directly representable in bzl source code. For
+example, the fields of a provider can use `:type:` to indicate the type of a
+field, or manually written docs can use the `{bzl:target}` directive to document
+a well known target.
+
+## Configuring Sphinx
+
+To enable the plugin in Sphinx, depend on
+`@rules_python//sphinxdocs/src/sphinx_bzl` and enable it in `conf.py`:
+
+```
+extensions = [
+    "sphinx_bzl.bzl",
+]
+```
+
+## Brief introduction to Sphinx terminology
+
+To aid understanding how to write docs, lets define a few common terms:
+
+* **Role**: A role is the "bzl:obj" part when writing ``{bzl:obj}`ref` ``.
+  Roles mark inline text as needing special processing. There's generally
+  two types of processing: creating cross references, or role-specific custom
+  rendering. For example `{bzl:obj}` will create a cross references, while
+  `{bzl:default-value}` indicates the default value of an argument.
+* **Directive**: A directive is indicated with `:::` and allows defining an
+  entire object and its parts. For example, to describe a function and its
+  arguments, the `:::{bzl:function}` directive is used.
+* **Directive Option**: A directive option is the "type" part when writing
+  `:type:` within a directive. Directive options are how directives are told
+  the meaning of certain values, such as the type of a provider field. Depending
+  on the object being documented, a directive option may be used instead of
+  special role to indicate semantic values.
+
+Most often, you'll be using roles to refer other objects or indicate special
+values in doc strings. For directives, you're likely to only use them when
+manually writing docs to document flags, targets, or other objects that
+`sphinx_stardoc` generates for you.
+
+## MyST vs RST
+
+By default, Sphinx uses ReStructured Text (RST) syntax for its documents.
+Unfortunately, RST syntax is very different than the popular Markdown syntax. To
+bridge the gap, MyST translates Markdown-style syntax into the RST equivalents.
+This allows easily using Markdown in bzl files.
+
+While MyST isn't required for the core `sphinx_bzl` plugin to work, this
+document uses MyST syntax because `sphinx_stardoc` bzl doc gen rule requires
+MyST.
+
+The main difference in syntax is:
+* MyST directives use `:::{name}` with closing `:::` instead of `.. name::` with
+  indented content.
+* MyST roles use `{role:name}` instead of `:role:name:`
+
+## Type expressions
+
+Several roles or fields accept type expressions. Type expressions use
+Python-style annotation syntax to describe data types. For example `None | list[str]`
+describes a type of "None or a list of strings". Each component of the
+expression is parsed and cross reference to its associated type definition.
+
+## Cross references
+
+In brief, to reference bzl objects, use the `bzl:obj` role and use the
+Bazel label string you would use to refer to the object in Bazel (using `%` to
+denote names within a file). For example, to unambiguously refer to `py_binary`:
+
+```
+{bzl:obj}`@rules_python//python:py_binary.bzl%py_binary`
+```
+
+The above is pretty long, so shorter names are also supported, and `sphinx_bzl`
+will try to find something that matches. Additionally, in `.bzl` code, the
+`bzl:` prefix is set as the default. The above can then be shortened to:
+
+```
+{obj}`py_binary`
+```
+
+The text that is displayed can be customized by putting the reference string in
+chevrons (`<>`):
+
+```
+{obj}`the binary rule <py_binary>`
+```
+
+Specific types of objects (rules, functions, providers, etc) can be
+specified to help disambiguate short names:
+
+```
+{function}`py_binary`  # Refers to the wrapping macro
+{rule}`py_binary`  # Refers to the underlying rule
+```
+
+Finally, objects built into Bazel can be explicitly referenced by forcing
+a lookup outside the local project using `{external}`. For example, the symbol
+`toolchain` is a builtin Bazel function, but it could also be the name of a tag
+class in the local project. To force looking up the builtin Bazel `toolchain` rule,
+`{external:bzl:rule}` can be used, e.g.:
+
+```
+{external:bzl:obj}`toolchain`
+```
+
+Those are the basics of cross referencing. Sphinx has several additional
+syntaxes for finding and referencing objects; see
+[the MyST docs for supported
+syntaxes](https://myst-parser.readthedocs.io/en/latest/syntax/cross-referencing.html#reference-roles)
+
+### Cross reference roles
+
+A cross reference role is the `obj` portion of `{bzl:obj}`. It affects what is
+searched and matched.
+
+:::{note}
+The documentation renders using RST notation (`:foo:role:`), not
+MyST notation (`{foo:role}`.
+:::
+
+:::{rst:role} bzl:arg
+Refer to a function argument.
+:::
+
+:::{rst:role} bzl:attr
+Refer to a rule attribute.
+:::
+
+:::{rst:role} bzl:flag
+Refer to a flag.
+:::
+
+:::{rst:role} bzl:obj
+Refer to any type of Bazel object
+:::
+
+:::{rst:role} bzl:rule
+Refer to a rule.
+:::
+
+:::{rst:role} bzl:target
+Refer to a target.
+:::
+
+:::{rst:role} bzl:type
+Refer to a type or type expression; can also be used in argument documentation.
+
+```
+def func(arg):
+    """Do stuff
+
+    Args:
+      arg: {type}`int | str` the arg
+    """
+    print(arg + 1)
+```
+:::
+
+## Special roles
+
+There are several special roles that can be used to annotate parts of objects,
+such as the type of arguments or their default values.
+
+:::{note}
+The documentation renders using RST notation (`:foo:role:`), not
+MyST notation (`{foo:role}`.
+:::
+
+:::{rst:role} bzl:default-value
+
+Indicate the default value for a function argument or rule attribute. Use it in
+the Args doc of a function or the doc text of an attribute.
+
+```
+def func(arg=1):
+   """Do stuff
+
+   Args:
+     foo: {default-value}`1` the arg
+
+my_rule = rule(attrs = {
+    "foo": attr.string(doc="{default-value}`bar`)
+})
+
+```
+:::
+
+:::{rst:role} bzl:return-type
+
+Indicates the return type for a function. Use it in the Returns doc of a
+function.
+
+```
+def func():
+    """Do stuff
+
+    Returns:
+      {return-type}`int`
+    """
+    return 1
+```
+:::
+
+## Directives
+
+Most directives are automatically generated by `sphinx_stardoc`. Here, we only
+document ones that must be manually written.
+
+To write a directive, a line starts with 3 to 6 colons (`:`), followed by the
+directive name in braces (`{}`), and eventually ended by the same number of
+colons on their own line. For example:
+
+```
+:::{bzl:target} //my:target
+
+Doc about target
+:::
+```
+
+:::{note}
+The documentation renders using RST notation (`.. directive::`), not
+MyST notation.
+:::
+
+:::{rst:directive} .. bzl:currentfile:: file
+
+This directive indicates the Bazel file that objects defined in the current
+documentation file are in. This is required for any page that defines Bazel
+objects. The format of `file` is Bazel label syntax, e.g. `//foo:bar.bzl` for bzl
+files, and `//foo:BUILD.bazel` for things in BUILD files.
+
+:::
+
+
+:::{rst:directive} .. bzl:target:: target
+
+Documents a target. It takes no directive options. The format of `target`
+can either be a fully qualified label (`//foo:bar`), or the base target name
+relative to `{bzl:currentfile}`.
+
+```
+:::{bzl:target} //foo:target
+
+My docs
+:::
+```
+
+:::{rst:directive} .. bzl:flag:: target
+
+Documents a flag. It has the same format as `{bzl:target}`
+:::
+
diff --git a/sphinxdocs/docs/starlark-docgen.md b/sphinxdocs/docs/starlark-docgen.md
new file mode 100644
index 0000000..d131607
--- /dev/null
+++ b/sphinxdocs/docs/starlark-docgen.md
@@ -0,0 +1,75 @@
+# Starlark docgen
+
+Using the `sphinx_stardoc` rule, API documentation can be generated from bzl
+source code. This rule requires both MyST-based markdown and the `sphinx_bzl`
+Sphinx extension are enabled. This allows source code to use Markdown and
+Sphinx syntax to create rich documentation with cross references, types, and
+more.
+
+
+## Configuring Sphinx
+
+While the `sphinx_stardoc` rule doesn't require Sphinx itself, the source
+it generates requires some additional Sphinx plugins and config settings.
+
+When defining the `sphinx_build_binary` target, also depend on:
+* `@rules_python//sphinxdocs/src/sphinx_bzl:sphinx_bzl`
+* `myst_parser` (e.g. `@pypi//myst_parser`)
+* `typing_extensions` (e.g. `@pypi//myst_parser`)
+
+```
+sphinx_build_binary(
+    name = "sphinx-build",
+    deps = [
+        "@rules_python//sphinxdocs/src/sphinx_bzl",
+        "@pypi//myst_parser",
+        "@pypi//typing_extensions",
+        ...
+    ]
+)
+```
+
+In `conf.py`, enable the `sphinx_bzl` extension, `myst_parser` extension,
+and the `colon_fence` MyST extension.
+
+```
+extensions = [
+    "myst_parser",
+    "sphinx_bzl.bzl",
+]
+
+myst_enable_extensions = [
+    "colon_fence",
+]
+```
+
+## Generating docs from bzl files
+
+To convert the bzl code to Sphinx doc sources, `sphinx_stardocs` is the primary
+rule to do so. It takes a list of `bzl_library` targets or files and generates docs for
+each. When a `bzl_library` target is passed, the `bzl_library.srcs` value can only
+have a single file.
+
+Example:
+
+```
+sphinx_stardocs(
+    name = "my_docs",
+    srcs = [
+      ":binary_bzl",
+      ":library_bzl",
+    ]
+)
+
+bzl_library(
+   name = "binary_bzl",
+   srcs = ["binary.bzl"],
+   deps = ...
+)
+
+bzl_library(
+   name = "library_bzl",
+   srcs = ["library.bzl"],
+   deps = ...
+)
+```
diff --git a/sphinxdocs/inventories/bazel_inventory.txt b/sphinxdocs/inventories/bazel_inventory.txt
index a7f0222..969c772 100644
--- a/sphinxdocs/inventories/bazel_inventory.txt
+++ b/sphinxdocs/inventories/bazel_inventory.txt
@@ -1,27 +1,145 @@
 # Sphinx inventory version 2
 # Project: Bazel
-# Version: 7.0.0
+# Version: 7.3.0
 # The remainder of this file is compressed using zlib
 Action bzl:type 1 rules/lib/Action -
+CcInfo bzl:provider 1 rules/lib/providers/CcInfo -
+CcInfo.linking_context bzl:provider-field 1 rules/lib/providers/CcInfo#linking_context -
+ExecutionInfo bzl:type 1 rules/lib/providers/ExecutionInfo -
 File bzl:type 1 rules/lib/File -
 Label bzl:type 1 rules/lib/Label -
+Name bzl:type 1 concepts/labels#target-names -
+RBE bzl:obj 1 remote/rbe -
+RunEnvironmentInfo bzl:type 1 rules/lib/providers/RunEnvironmentInfo -
 Target bzl:type 1 rules/lib/builtins/Target -
-bool bzl:type 1 rules/lib/bool -
-int bzl:type 1 rules/lib/int -
-depset bzl:type 1 rules/lib/depset -
-dict bzl:type 1 rules/lib/dict -
-label bzl:doc 1 concepts/labels -
+ToolchainInfo bzl:type 1 rules/lib/providers/ToolchainInfo.html -
 attr.bool bzl:type 1 rules/lib/toplevel/attr#bool -
 attr.int bzl:type 1 rules/lib/toplevel/attr#int -
 attr.label bzl:type 1 rules/lib/toplevel/attr#label -
 attr.label_list bzl:type 1 rules/lib/toplevel/attr#label_list -
 attr.string bzl:type 1 rules/lib/toplevel/attr#string -
 attr.string_list bzl:type 1 rules/lib/toplevel/attr#string_list -
+bool bzl:type 1 rules/lib/bool -
+callable bzl:type 1 rules/lib/core/function -
+config_common.FeatureFlagInfo bzl:type 1 rules/lib/toplevel/config_common#FeatureFlagInfo -
+config_common.toolchain_type bzl:function 1 rules/lib/toplevel/config_common#toolchain_type -
+ctx.actions bzl:obj 1 rules/lib/builtins/ctx#actions -
+ctx.aspect_ids bzl:obj 1 rules/lib/builtins/ctx#aspect_ids -
+ctx.attr bzl:obj 1 rules/lib/builtins/ctx#attr -
+ctx.bin_dir bzl:obj 1 rules/lib/builtins/ctx#bin_dir -
+ctx.build_file_path bzl:obj 1 rules/lib/builtins/ctx#build_file_path -
+ctx.build_setting_value bzl:obj 1 rules/lib/builtins/ctx#build_setting_value -
+ctx.configuration bzl:obj 1 rules/lib/builtins/ctx#configuration -
+ctx.coverage_instrumented bzl:function 1 rules/lib/builtins/ctx#coverage_instrumented -
+ctx.created_actions bzl:function 1 rules/lib/builtins/ctx#created_actions -
+ctx.disabled_features bzl:obj 1 rules/lib/builtins/ctx#disabled_features -
+ctx.exec_groups bzl:obj 1 rules/lib/builtins/ctx#exec_groups -
+ctx.executable bzl:obj 1 rules/lib/builtins/ctx#executable -
+ctx.expand_location bzl:function 1 rules/lib/builtins/ctx#expand_location -
+ctx.expand_location bzl:function 1 rules/lib/builtins/ctx#expand_location - -
+ctx.expand_make_variables bzl:function 1 rules/lib/builtins/ctx#expand_make_variables -
+ctx.features bzl:obj 1 rules/lib/builtins/ctx#features -
+ctx.file bzl:obj 1 rules/lib/builtins/ctx#file -
+ctx.files bzl:obj 1 rules/lib/builtins/ctx#files -
+ctx.fragments bzl:obj 1 rules/lib/builtins/ctx#fragments -
+ctx.genfiles_dir bzl:obj 1 rules/lib/builtins/ctx#genfiles_dir -
+ctx.info_file bzl:obj 1 rules/lib/builtins/ctx#info_file -
+ctx.label bzl:obj 1 rules/lib/builtins/ctx#label -
+ctx.outputs bzl:obj 1 rules/lib/builtins/ctx#outputs -
+ctx.resolve_command bzl:function 1 rules/lib/builtins/ctx#resolve_command -
+ctx.resolve_tools bzl:function 1 rules/lib/builtins/ctx#resolve_tools -
+ctx.rule bzl:obj 1 rules/lib/builtins/ctx#rule -
+ctx.runfiles bzl:function 1 rules/lib/builtins/ctx#runfiles -
+ctx.split_attr bzl:obj 1 rules/lib/builtins/ctx#split_attr -
+ctx.super bzl:obj 1 rules/lib/builtins/ctx#super -
+ctx.target_platform_has_constraint bzl:function 1 rules/lib/builtins/ctx#target_platform_has_constraint -
+ctx.toolchains bzl:obj 1 rules/lib/builtins/ctx#toolchains -
+ctx.var bzl:obj 1 rules/lib/builtins/ctx#var -
+ctx.version_file bzl:obj 1 rules/lib/builtins/ctx#version_file -
+ctx.workspace_name bzl:obj 1 rules/lib/builtins/ctx#workspace_name -
+depset bzl:type 1 rules/lib/depset -
+dict bzl:type 1 rules/lib/dict -
+exec_compatible_with bzl:attr 1 reference/be/common-definitions#common.exec_compatible_with -
+int bzl:type 1 rules/lib/int -
+label bzl:type 1 concepts/labels -
 list bzl:type 1 rules/lib/list -
-python bzl:doc 1 reference/be/python -
+module_ctx bzl:type 1 rules/lib/builtins/module_ctx -
+module_ctx.download bzl:function 1 rules/lib/builtins/module_ctx#download -
+module_ctx.download_and_extract bzl:function 1 rules/lib/builtins/module_ctx#download_and_extract -
+module_ctx.execute bzl:function 1 rules/lib/builtins/module_ctx#execute -
+module_ctx.extension_metadata bzl:function 1 rules/lib/builtins/module_ctx#extension_metadata -
+module_ctx.extract bzl:function 1 rules/lib/builtins/module_ctx#extract -
+module_ctx.file bzl:function 1 rules/lib/builtins/module_ctx#file -
+module_ctx.getenv bzl:function 1 rules/lib/builtins/module_ctx#getenv -
+module_ctx.is_dev_dependency bzl:obj 1 rules/lib/builtins/module_ctx#is_dev_dependency -
+module_ctx.modules bzl:obj 1 rules/lib/builtins/module_ctx#modules -
+module_ctx.os bzl:obj 1 rules/lib/builtins/module_ctx#os -
+module_ctx.path bzl:function 1 rules/lib/builtins/module_ctx#path -
+module_ctx.read bzl:function 1 rules/lib/builtins/module_ctx#read -
+module_ctx.report_progress bzl:function 1 rules/lib/builtins/module_ctx#report_progress -
+module_ctx.root_module_has_non_dev_dependency bzl:function 1 rules/lib/builtins/module_ctx#root_module_has_non_dev_dependency -
+module_ctx.watch bzl:function 1 rules/lib/builtins/module_ctx#watch -
+module_ctx.which bzl:function 1 rules/lib/builtins/module_ctx#which -
+native.existing_rule bzl:function 1 rules/lib/toplevel/native#existing_rule -
+native.existing_rules bzl:function 1 rules/lib/toplevel/native#existing_rules -
+native.exports_files bzl:function 1 rules/lib/toplevel/native#exports_files -
+native.glob bzl:function 1 rules/lib/toplevel/native#glob -
+native.module_name bzl:function 1 rules/lib/toplevel/native#module_name -
+native.module_version bzl:function 1 rules/lib/toplevel/native#module_version -
+native.package_group bzl:function 1 rules/lib/toplevel/native#package_group -
+native.package_name bzl:function 1 rules/lib/toplevel/native#package_name -
+native.package_relative_label bzl:function 1 rules/lib/toplevel/native#package_relative_label -
+native.repo_name bzl:function 1 rules/lib/toplevel/native#repo_name -
+native.repository_name bzl:function 1 rules/lib/toplevel/native#repository_name -
+path bzl:type 1 rules/lib/builtins/path -
+path.basename bzl:obj 1 rules/lib/builtins/path#basename
+path.dirname bzl:obj 1 rules/lib/builtins/path#dirname
+path.exists bzl:obj 1 rules/lib/builtins/path#exists
+path.get_child bzl:function 1 rules/lib/builtins/path#get_child
+path.is_dir bzl:obj 1 rules/lib/builtins/path#is_dir
+path.readdir bzl:function 1 rules/lib/builtins/path#readdir
+path.realpath bzl:obj 1 rules/lib/builtins/path#realpath
+repository_ctx bzl:type 1 rules/lib/builtins/repository_ctx -
+repository_ctx.attr bzl:obj 1 rules/lib/builtins/repository_ctx#attr
+repository_ctx.delete bzl:function 1 rules/lib/builtins/repository_ctx#delete
+repository_ctx.download bzl:function 1 rules/lib/builtins/repository_ctx#download
+repository_ctx.download_and_extract bzl:function 1 rules/lib/builtins/repository_ctx#download_and_extract
+repository_ctx.execute bzl:function 1 rules/lib/builtins/repository_ctx#execute
+repository_ctx.extract bzl:function 1 rules/lib/builtins/repository_ctx#extract
+repository_ctx.file bzl:function 1 rules/lib/builtins/repository_ctx#file
+repository_ctx.getenv bzl:function 1 rules/lib/builtins/repository_ctx#getenv
+repository_ctx.name bzl:obj 1 rules/lib/builtins/repository_ctx#name
+repository_ctx.os bzl:obj 1 rules/lib/builtins/repository_ctx#os
+repository_ctx.patch bzl:function 1 rules/lib/builtins/repository_ctx#patch
+repository_ctx.path bzl:obj 1 rules/lib/builtins/repository_ctx#path
+repository_ctx.read bzl:function 1 rules/lib/builtins/repository_ctx#read
+repository_ctx.report_progress bzl:function 1 rules/lib/builtins/repository_ctx#report_progress
+repository_ctx.symlink bzl:function 1 rules/lib/builtins/repository_ctx#symlink
+repository_ctx.template bzl:function 1 rules/lib/builtins/repository_ctx#template
+repository_ctx.watch bzl:function 1 rules/lib/builtins/repository_ctx#watch
+repository_ctx.watch_tree bzl:function 1 rules/lib/builtins/repository_ctx#watch_tree
+repository_ctx.which bzl:function 1 rules/lib/builtins/repository_ctx#which
+repository_ctx.workspace_root bzl:obj 1 rules/lib/builtins/repository_ctx#workspace_root
+repository_os bzl:type 1 rules/lib/builtins/repository_os -
+repository_os.arch bzl:obj 1 rules/lib/builtins/repository_os#arch
+repository_os.environ bzl:obj 1 rules/lib/builtins/repository_os#environ
+repository_os.name bzl:obj 1 rules/lib/builtins/repository_os#name
+runfiles bzl:type 1 rules/lib/builtins/runfiles -
+runfiles.empty_filenames bzl:type 1 rules/lib/builtins/runfiles#empty_filenames -
+runfiles.files bzl:type 1 rules/lib/builtins/runfiles#files -
+runfiles.merge bzl:type 1 rules/lib/builtins/runfiles#merge -
+runfiles.merge_all bzl:type 1 rules/lib/builtins/runfiles#merge_all -
+runfiles.root_symlinks bzl:type 1 rules/lib/builtins/runfiles#root_symlinks -
+runfiles.symlinks bzl:type 1 rules/lib/builtins/runfiles#symlinks -
 str bzl:type 1 rules/lib/string -
 struct bzl:type 1 rules/lib/builtins/struct -
-Name bzl:type 1 concepts/labels#target-names -
-CcInfo bzl:provider 1 rules/lib/providers/CcInfo -
-CcInfo.linking_context bzl:provider-field 1 rules/lib/providers/CcInfo#linking_context -
-ToolchainInfo bzl:type 1 rules/lib/providers/ToolchainInfo.html -
+target_compatible_with bzl:attr 1 reference/be/common-definitions#common.target_compatible_with -
+testing bzl:obj 1 rules/lib/toplevel/testing -
+testing.ExecutionInfo bzl:function 1 rules/lib/toplevel/testing#ExecutionInfo -
+testing.TestEnvironment bzl:function 1 rules/lib/toplevel/testing#TestEnvironment -
+testing.analysis_test bzl:rule 1 rules/lib/toplevel/testing#analysis_test -
+toolchain bzl:rule 1 reference/be/platforms-and-toolchains#toolchain -
+toolchain.exec_compatible_with bzl:rule 1 reference/be/platforms-and-toolchains#toolchain.exec_compatible_with -
+toolchain.target_settings bzl:attr 1 reference/be/platforms-and-toolchains#toolchain.target_settings -
+toolchain.target_compatible_with bzl:attr 1 reference/be/platforms-and-toolchains#toolchain.target_compatible_with -
+toolchain_type bzl:type 1 rules/lib/builtins/toolchain_type.html -
diff --git a/sphinxdocs/private/BUILD.bazel b/sphinxdocs/private/BUILD.bazel
index ec6a945..c4246ed 100644
--- a/sphinxdocs/private/BUILD.bazel
+++ b/sphinxdocs/private/BUILD.bazel
@@ -26,25 +26,45 @@
 # referenced by the //sphinxdocs macros.
 exports_files(
     [
-        "func_template.vm",
-        "header_template.vm",
-        "provider_template.vm",
         "readthedocs_install.py",
-        "rule_template.vm",
         "sphinx_build.py",
         "sphinx_server.py",
+        "sphinx_run_template.sh",
     ],
     visibility = ["//visibility:public"],
 )
 
 bzl_library(
+    name = "sphinx_docs_library_macro_bzl",
+    srcs = ["sphinx_docs_library_macro.bzl"],
+    deps = [
+        ":sphinx_docs_library_bzl",
+        "//python/private:util_bzl",
+    ],
+)
+
+bzl_library(
+    name = "sphinx_docs_library_bzl",
+    srcs = ["sphinx_docs_library.bzl"],
+    deps = [":sphinx_docs_library_info_bzl"],
+)
+
+bzl_library(
+    name = "sphinx_docs_library_info_bzl",
+    srcs = ["sphinx_docs_library_info.bzl"],
+)
+
+bzl_library(
     name = "sphinx_bzl",
     srcs = ["sphinx.bzl"],
     deps = [
+        ":sphinx_docs_library_info_bzl",
         "//python:py_binary_bzl",
+        "@bazel_skylib//:bzl_library",
         "@bazel_skylib//lib:paths",
         "@bazel_skylib//lib:types",
         "@bazel_skylib//rules:build_test",
+        "@bazel_skylib//rules:common_settings",
         "@io_bazel_stardoc//stardoc:stardoc_lib",
     ],
 )
@@ -53,7 +73,11 @@
     name = "sphinx_stardoc_bzl",
     srcs = ["sphinx_stardoc.bzl"],
     deps = [
+        ":sphinx_docs_library_macro_bzl",
         "//python/private:util_bzl",
+        "//sphinxdocs:sphinx_bzl",
+        "@bazel_skylib//:bzl_library",
+        "@bazel_skylib//lib:paths",
         "@bazel_skylib//lib:types",
         "@bazel_skylib//rules:build_test",
         "@io_bazel_stardoc//stardoc:stardoc_lib",
diff --git a/sphinxdocs/private/readthedocs.bzl b/sphinxdocs/private/readthedocs.bzl
index ee8e7aa..a62c51b 100644
--- a/sphinxdocs/private/readthedocs.bzl
+++ b/sphinxdocs/private/readthedocs.bzl
@@ -27,11 +27,11 @@
     for more information.
 
     Args:
-        name: (str) name of the installer
-        docs: (label list) list of targets that generate directories to copy
+        name: {type}`Name` name of the installer
+        docs: {type}`list[label]` list of targets that generate directories to copy
             into the directories readthedocs expects final output in. This
-            is typically a single `sphinx_stardocs` target.
-        **kwargs: (dict) additional kwargs to pass onto the installer
+            is typically a single {obj}`sphinx_stardocs` target.
+        **kwargs: {type}`dict` additional kwargs to pass onto the installer
     """
     add_tag(kwargs, "@rules_python//sphinxdocs:readthedocs_install")
     py_binary(
diff --git a/sphinxdocs/private/sphinx.bzl b/sphinxdocs/private/sphinx.bzl
index a5ac831..2ee6cfc 100644
--- a/sphinxdocs/private/sphinx.bzl
+++ b/sphinxdocs/private/sphinx.bzl
@@ -4,7 +4,7 @@
 # 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
+#    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,
@@ -15,13 +15,59 @@
 """Implementation of sphinx rules."""
 
 load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
 load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load("//python:py_binary.bzl", "py_binary")
 load("//python/private:util.bzl", "add_tag", "copy_propagating_kwargs")  # buildifier: disable=bzl-visibility
+load(":sphinx_docs_library_info.bzl", "SphinxDocsLibraryInfo")
 
 _SPHINX_BUILD_MAIN_SRC = Label("//sphinxdocs/private:sphinx_build.py")
 _SPHINX_SERVE_MAIN_SRC = Label("//sphinxdocs/private:sphinx_server.py")
 
+_SphinxSourceTreeInfo = provider(
+    doc = "Information about source tree for Sphinx to build.",
+    fields = {
+        "source_dir_runfiles_path": """
+:type: str
+
+Runfiles-root relative path of the root directory for the source files.
+""",
+        "source_root": """
+:type: str
+
+Exec-root relative path of the root directory for the source files (which are in DefaultInfo.files)
+""",
+    },
+)
+
+_SphinxRunInfo = provider(
+    doc = "Information for running the underlying Sphinx command directly",
+    fields = {
+        "per_format_args": """
+:type: dict[str, struct]
+
+A dict keyed by output format name. The values are a struct with attributes:
+* args: a `list[str]` of args to run this format's build
+* env: a `dict[str, str]` of environment variables to set for this format's build
+""",
+        "source_tree": """
+:type: Target
+
+Target with the source tree files
+""",
+        "sphinx": """
+:type: Target
+
+The sphinx-build binary to run.
+""",
+        "tools": """
+:type: list[Target]
+
+Additional tools Sphinx needs
+""",
+    },
+)
+
 def sphinx_build_binary(name, py_binary_rule = py_binary, **kwargs):
     """Create an executable with the sphinx-build command line interface.
 
@@ -29,13 +75,13 @@
     needs at runtime.
 
     Args:
-        name: (str) name of the target. The name "sphinx-build" is the
+        name: {type}`str` name of the target. The name "sphinx-build" is the
             conventional name to match what Sphinx itself uses.
-        py_binary_rule: (optional callable) A `py_binary` compatible callable
+        py_binary_rule: {type}`callable` A `py_binary` compatible callable
             for creating the target. If not set, the regular `py_binary`
             rule is used. This allows using the version-aware rules, or
             other alternative implementations.
-        **kwargs: Additional kwargs to pass onto `py_binary`. The `srcs` and
+        **kwargs: {type}`dict` Additional kwargs to pass onto `py_binary`. The `srcs` and
             `main` attributes must not be specified.
     """
     add_tag(kwargs, "@rules_python//sphinxdocs:sphinx_build_binary")
@@ -50,6 +96,7 @@
         name,
         *,
         srcs = [],
+        deps = [],
         renamed_srcs = {},
         sphinx,
         config,
@@ -60,60 +107,69 @@
         **kwargs):
     """Generate docs using Sphinx.
 
-    This generates three public targets:
-        * `<name>`: The output of this target is a directory for each
-          format Sphinx creates. This target also has a separate output
-          group for each format. e.g. `--output_group=html` will only build
-          the "html" format files.
-        * `<name>_define`: A multi-string flag to add additional `-D`
-          arguments to the Sphinx invocation. This is useful for overriding
-          the version information in the config file for builds.
-        * `<name>.serve`: A binary that locally serves the HTML output. This
-          allows previewing docs during development.
+    Generates targets:
+    * `<name>`: The output of this target is a directory for each
+      format Sphinx creates. This target also has a separate output
+      group for each format. e.g. `--output_group=html` will only build
+      the "html" format files.
+    * `<name>.serve`: A binary that locally serves the HTML output. This
+      allows previewing docs during development.
+    * `<name>.run`: A binary that directly runs the underlying Sphinx command
+      to build the docs. This is a debugging aid.
 
     Args:
-        name: (str) name of the docs rule.
-        srcs: (label list) The source files for Sphinx to process.
-        renamed_srcs: (label_keyed_string_dict) Doc source files for Sphinx that
+        name: {type}`Name` name of the docs rule.
+        srcs: {type}`list[label]` The source files for Sphinx to process.
+        deps: {type}`list[label]` of {obj}`sphinx_docs_library` targets.
+        renamed_srcs: {type}`dict[label, dict]` Doc source files for Sphinx that
             are renamed. This is typically used for files elsewhere, such as top
             level files in the repo.
-        sphinx: (label) the Sphinx tool to use for building
+        sphinx: {type}`label` the Sphinx tool to use for building
             documentation. Because Sphinx supports various plugins, you must
             construct your own binary with the necessary dependencies. The
-            `sphinx_build_binary` rule can be used to define such a binary, but
+            {obj}`sphinx_build_binary` rule can be used to define such a binary, but
             any executable supporting the `sphinx-build` command line interface
             can be used (typically some `py_binary` program).
-        config: (label) the Sphinx config file (`conf.py`) to use.
+        config: {type}`label` the Sphinx config file (`conf.py`) to use.
         formats: (list of str) the formats (`-b` flag) to generate documentation
             in. Each format will become an output group.
-        strip_prefix: (str) A prefix to remove from the file paths of the
-            source files. e.g., given `//docs:foo.md`, stripping `docs/`
-            makes Sphinx see `foo.md` in its generated source directory.
-        extra_opts: (list[str]) Additional options to pass onto Sphinx building.
+        strip_prefix: {type}`str` A prefix to remove from the file paths of the
+            source files. e.g., given `//docs:foo.md`, stripping `docs/` makes
+            Sphinx see `foo.md` in its generated source directory. If not
+            specified, then {any}`native.package_name` is used.
+        extra_opts: {type}`list[str]` Additional options to pass onto Sphinx building.
             On each provided option, a location expansion is performed.
-            See `ctx.expand_location()`.
-        tools: (list[label]) Additional tools that are used by Sphinx and its plugins.
+            See {any}`ctx.expand_location`.
+        tools: {type}`list[label]` Additional tools that are used by Sphinx and its plugins.
             This just makes the tools available during Sphinx execution. To locate
-            them, use `extra_opts` and `$(location)`.
-        **kwargs: (dict) Common attributes to pass onto rules.
+            them, use {obj}`extra_opts` and `$(location)`.
+        **kwargs: {type}`dict` Common attributes to pass onto rules.
     """
     add_tag(kwargs, "@rules_python//sphinxdocs:sphinx_docs")
     common_kwargs = copy_propagating_kwargs(kwargs)
 
+    internal_name = "_{}".format(name.lstrip("_"))
+
+    _sphinx_source_tree(
+        name = internal_name + "/_sources",
+        srcs = srcs,
+        deps = deps,
+        renamed_srcs = renamed_srcs,
+        config = config,
+        strip_prefix = strip_prefix,
+        **common_kwargs
+    )
     _sphinx_docs(
         name = name,
-        srcs = srcs,
-        renamed_srcs = renamed_srcs,
         sphinx = sphinx,
-        config = config,
         formats = formats,
-        strip_prefix = strip_prefix,
+        source_tree = internal_name + "/_sources",
         extra_opts = extra_opts,
         tools = tools,
         **kwargs
     )
 
-    html_name = "_{}_html".format(name.lstrip("_"))
+    html_name = internal_name + "_html"
     native.filegroup(
         name = html_name,
         srcs = [name],
@@ -131,13 +187,26 @@
         ],
         **common_kwargs
     )
+    sphinx_run(
+        name = name + ".run",
+        docs = name,
+    )
+
+    build_test(
+        name = name + "_build_test",
+        targets = [name],
+        **kwargs  # kwargs used to pick up target_compatible_with
+    )
 
 def _sphinx_docs_impl(ctx):
-    source_dir_path, _, inputs = _create_sphinx_source_tree(ctx)
+    source_tree_info = ctx.attr.source_tree[_SphinxSourceTreeInfo]
+    source_dir_path = source_tree_info.source_root
+    inputs = ctx.attr.source_tree[DefaultInfo].files
 
+    per_format_args = {}
     outputs = {}
     for format in ctx.attr.formats:
-        output_dir = _run_sphinx(
+        output_dir, args_env = _run_sphinx(
             ctx = ctx,
             format = format,
             source_path = source_dir_path,
@@ -145,32 +214,32 @@
             inputs = inputs,
         )
         outputs[format] = output_dir
+        per_format_args[format] = args_env
     return [
         DefaultInfo(files = depset(outputs.values())),
         OutputGroupInfo(**{
             format: depset([output])
             for format, output in outputs.items()
         }),
+        _SphinxRunInfo(
+            sphinx = ctx.attr.sphinx,
+            source_tree = ctx.attr.source_tree,
+            tools = ctx.attr.tools,
+            per_format_args = per_format_args,
+        ),
     ]
 
 _sphinx_docs = rule(
     implementation = _sphinx_docs_impl,
     attrs = {
-        "config": attr.label(
-            allow_single_file = True,
-            mandatory = True,
-            doc = "Config file for Sphinx",
-        ),
         "extra_opts": attr.string_list(
             doc = "Additional options to pass onto Sphinx. These are added after " +
                   "other options, but before the source/output args.",
         ),
         "formats": attr.string_list(doc = "Output formats for Sphinx to create."),
-        "renamed_srcs": attr.label_keyed_string_dict(
-            allow_files = True,
-            doc = "Doc source files for Sphinx that are renamed. This is " +
-                  "typically used for files elsewhere, such as top level " +
-                  "files in the repo.",
+        "source_tree": attr.label(
+            doc = "Directory of files for Sphinx to process.",
+            providers = [_SphinxSourceTreeInfo],
         ),
         "sphinx": attr.label(
             executable = True,
@@ -178,11 +247,6 @@
             mandatory = True,
             doc = "Sphinx binary to generate documentation.",
         ),
-        "srcs": attr.label_list(
-            allow_files = True,
-            doc = "Doc source files for Sphinx.",
-        ),
-        "strip_prefix": attr.string(doc = "Prefix to remove from input file paths."),
         "tools": attr.label_list(
             cfg = "exec",
             doc = "Additional tools that are used by Sphinx and its plugins.",
@@ -193,70 +257,39 @@
     },
 )
 
-def _create_sphinx_source_tree(ctx):
-    # Sphinx only accepts a single directory to read its doc sources from.
-    # Because plain files and generated files are in different directories,
-    # we need to merge the two into a single directory.
-    source_prefix = paths.join(ctx.label.name, "_sources")
-    sphinx_source_files = []
-
-    def _symlink_source(orig):
-        source_rel_path = orig.short_path
-        if source_rel_path.startswith(ctx.attr.strip_prefix):
-            source_rel_path = source_rel_path[len(ctx.attr.strip_prefix):]
-
-        sphinx_source = ctx.actions.declare_file(paths.join(source_prefix, source_rel_path))
-        ctx.actions.symlink(
-            output = sphinx_source,
-            target_file = orig,
-            progress_message = "Symlinking Sphinx source %{input} to %{output}",
-        )
-        sphinx_source_files.append(sphinx_source)
-        return sphinx_source
-
-    # Though Sphinx has a -c flag, we move the config file into the sources
-    # directory to make the config more intuitive because some configuration
-    # options are relative to the config location, not the sources directory.
-    source_conf_file = _symlink_source(ctx.file.config)
-    sphinx_source_dir_path = paths.dirname(source_conf_file.path)
-
-    for orig_file in ctx.files.srcs:
-        _symlink_source(orig_file)
-
-    for src_target, dest in ctx.attr.renamed_srcs.items():
-        src_files = src_target.files.to_list()
-        if len(src_files) != 1:
-            fail("A single file must be specified to be renamed. Target {} " +
-                 "generate {} files: {}".format(
-                     src_target,
-                     len(src_files),
-                     src_files,
-                 ))
-        sphinx_src = ctx.actions.declare_file(paths.join(source_prefix, dest))
-        ctx.actions.symlink(
-            output = sphinx_src,
-            target_file = src_files[0],
-            progress_message = "Symlinking (renamed) Sphinx source %{input} to %{output}",
-        )
-        sphinx_source_files.append(sphinx_src)
-
-    return sphinx_source_dir_path, source_conf_file, sphinx_source_files
-
 def _run_sphinx(ctx, format, source_path, inputs, output_prefix):
     output_dir = ctx.actions.declare_directory(paths.join(output_prefix, format))
 
-    args = ctx.actions.args()
-    args.add("-T")  # Full tracebacks on error
-    args.add("-b", format)
+    run_args = []  # Copy of the args to forward along to debug runner
+    args = ctx.actions.args()  # Args passed to the action
+
+    args.add("--show-traceback")  # Full tracebacks on error
+    run_args.append("--show-traceback")
+    args.add("--builder", format)
+    run_args.extend(("--builder", format))
 
     if ctx.attr._quiet_flag[BuildSettingInfo].value:
-        args.add("-q")  # Suppress stdout informational text
-    args.add("-j", "auto")  # Build in parallel, if possible
-    args.add("-E")  # Don't try to use cache files. Bazel can't make use of them.
-    args.add("-a")  # Write all files; don't try to detect "changed" files
+        # Not added to run_args because run_args is for debugging
+        args.add("--quiet")  # Suppress stdout informational text
+
+    # Build in parallel, if possible
+    # Don't add to run_args: parallel building breaks interactive debugging
+    args.add("--jobs", "auto")
+    args.add("--fresh-env")  # Don't try to use cache files. Bazel can't make use of them.
+    run_args.append("--fresh-env")
+    args.add("--write-all")  # Write all files; don't try to detect "changed" files
+    run_args.append("--write-all")
+
     for opt in ctx.attr.extra_opts:
-        args.add(ctx.expand_location(opt))
-    args.add_all(ctx.attr._extra_defines_flag[_FlagInfo].value, before_each = "-D")
+        expanded = ctx.expand_location(opt)
+        args.add(expanded)
+        run_args.append(expanded)
+
+    extra_defines = ctx.attr._extra_defines_flag[_FlagInfo].value
+    args.add_all(extra_defines, before_each = "--define")
+    for define in extra_defines:
+        run_args.extend(("--define", define))
+
     args.add(source_path)
     args.add(output_dir.path)
 
@@ -279,8 +312,96 @@
         progress_message = "Sphinx building {} for %{{label}}".format(format),
         env = env,
     )
-    return output_dir
+    return output_dir, struct(args = run_args, env = env)
 
+def _sphinx_source_tree_impl(ctx):
+    # Sphinx only accepts a single directory to read its doc sources from.
+    # Because plain files and generated files are in different directories,
+    # we need to merge the two into a single directory.
+    source_prefix = ctx.label.name
+    sphinx_source_files = []
+
+    # Materialize a file under the `_sources` dir
+    def _relocate(source_file, dest_path = None):
+        if not dest_path:
+            dest_path = source_file.short_path.removeprefix(ctx.attr.strip_prefix)
+        dest_file = ctx.actions.declare_file(paths.join(source_prefix, dest_path))
+        ctx.actions.symlink(
+            output = dest_file,
+            target_file = source_file,
+            progress_message = "Symlinking Sphinx source %{input} to %{output}",
+        )
+        sphinx_source_files.append(dest_file)
+        return dest_file
+
+    # Though Sphinx has a -c flag, we move the config file into the sources
+    # directory to make the config more intuitive because some configuration
+    # options are relative to the config location, not the sources directory.
+    source_conf_file = _relocate(ctx.file.config)
+    sphinx_source_dir_path = paths.dirname(source_conf_file.path)
+
+    for src in ctx.attr.srcs:
+        if SphinxDocsLibraryInfo in src:
+            fail((
+                "In attribute srcs: target {src} is misplaced here: " +
+                "sphinx_docs_library targets belong in the deps attribute."
+            ).format(src = src))
+
+    for orig_file in ctx.files.srcs:
+        _relocate(orig_file)
+
+    for src_target, dest in ctx.attr.renamed_srcs.items():
+        src_files = src_target.files.to_list()
+        if len(src_files) != 1:
+            fail("A single file must be specified to be renamed. Target {} " +
+                 "generate {} files: {}".format(
+                     src_target,
+                     len(src_files),
+                     src_files,
+                 ))
+        _relocate(src_files[0], dest)
+
+    for t in ctx.attr.deps:
+        info = t[SphinxDocsLibraryInfo]
+        for entry in info.transitive.to_list():
+            for original in entry.files:
+                new_path = entry.prefix + original.short_path.removeprefix(entry.strip_prefix)
+                _relocate(original, new_path)
+
+    return [
+        DefaultInfo(
+            files = depset(sphinx_source_files),
+        ),
+        _SphinxSourceTreeInfo(
+            source_root = sphinx_source_dir_path,
+            source_dir_runfiles_path = paths.dirname(source_conf_file.short_path),
+        ),
+    ]
+
+_sphinx_source_tree = rule(
+    implementation = _sphinx_source_tree_impl,
+    attrs = {
+        "config": attr.label(
+            allow_single_file = True,
+            mandatory = True,
+            doc = "Config file for Sphinx",
+        ),
+        "deps": attr.label_list(
+            providers = [SphinxDocsLibraryInfo],
+        ),
+        "renamed_srcs": attr.label_keyed_string_dict(
+            allow_files = True,
+            doc = "Doc source files for Sphinx that are renamed. This is " +
+                  "typically used for files elsewhere, such as top level " +
+                  "files in the repo.",
+        ),
+        "srcs": attr.label_list(
+            allow_files = True,
+            doc = "Doc source files for Sphinx.",
+        ),
+        "strip_prefix": attr.string(doc = "Prefix to remove from input file paths."),
+    },
+)
 _FlagInfo = provider(
     doc = "Provider for a flag value",
     fields = ["value"],
@@ -294,7 +415,7 @@
     build_setting = config.string_list(flag = True, repeatable = True),
 )
 
-def sphinx_inventory(name, src, **kwargs):
+def sphinx_inventory(*, name, src, **kwargs):
     """Creates a compressed inventory file from an uncompressed on.
 
     The Sphinx inventory format isn't formally documented, but is understood
@@ -324,11 +445,14 @@
       * `display name` is a string. It can contain spaces, or simply be
         the value `-` to indicate it is the same as `name`
 
+    :::{seealso}
+    {bzl:obj}`//sphinxdocs/inventories` for inventories of Bazel objects.
+    :::
 
     Args:
-        name: [`target-name`] name of the target.
-        src: [`label`] Uncompressed inventory text file.
-        **kwargs: additional kwargs of common attributes.
+        name: {type}`Name` name of the target.
+        src: {type}`label` Uncompressed inventory text file.
+        **kwargs: {type}`dict` additional kwargs of common attributes.
     """
     _sphinx_inventory(name = name, src = src, **kwargs)
 
@@ -356,3 +480,85 @@
         ),
     },
 )
+
+def _sphinx_run_impl(ctx):
+    run_info = ctx.attr.docs[_SphinxRunInfo]
+
+    builder = ctx.attr.builder
+
+    if builder not in run_info.per_format_args:
+        builder = run_info.per_format_args.keys()[0]
+
+    args_info = run_info.per_format_args.get(builder)
+    if not args_info:
+        fail("Format {} not built by {}".format(
+            builder,
+            ctx.attr.docs.label,
+        ))
+
+    args_str = []
+    args_str.extend(args_info.args)
+    args_str = "\n".join(["args+=('{}')".format(value) for value in args_info.args])
+    if not args_str:
+        args_str = "# empty custom args"
+
+    env_str = "\n".join([
+        "sphinx_env+=({}='{}')".format(*item)
+        for item in args_info.env.items()
+    ])
+    if not env_str:
+        env_str = "# empty custom env"
+
+    executable = ctx.actions.declare_file(ctx.label.name)
+    sphinx = run_info.sphinx
+    ctx.actions.expand_template(
+        template = ctx.file._template,
+        output = executable,
+        substitutions = {
+            "%SETUP_ARGS%": args_str,
+            "%SETUP_ENV%": env_str,
+            "%SOURCE_DIR_EXEC_PATH%": run_info.source_tree[_SphinxSourceTreeInfo].source_root,
+            "%SOURCE_DIR_RUNFILES_PATH%": run_info.source_tree[_SphinxSourceTreeInfo].source_dir_runfiles_path,
+            "%SPHINX_EXEC_PATH%": sphinx[DefaultInfo].files_to_run.executable.path,
+            "%SPHINX_RUNFILES_PATH%": sphinx[DefaultInfo].files_to_run.executable.short_path,
+        },
+        is_executable = True,
+    )
+    runfiles = ctx.runfiles(
+        transitive_files = run_info.source_tree[DefaultInfo].files,
+    ).merge(sphinx[DefaultInfo].default_runfiles).merge_all([
+        tool[DefaultInfo].default_runfiles
+        for tool in run_info.tools
+    ])
+    return [
+        DefaultInfo(
+            executable = executable,
+            runfiles = runfiles,
+        ),
+    ]
+
+sphinx_run = rule(
+    implementation = _sphinx_run_impl,
+    doc = """
+Directly run the underlying Sphinx command `sphinx_docs` uses.
+
+This is primarily a debugging tool. It's useful for directly running the
+Sphinx command so that debuggers can be attached or output more directly
+inspected without Bazel interference.
+""",
+    attrs = {
+        "builder": attr.string(
+            doc = "The output format to make runnable.",
+            default = "html",
+        ),
+        "docs": attr.label(
+            doc = "The {obj}`sphinx_docs` target to make directly runnable.",
+            providers = [_SphinxRunInfo],
+        ),
+        "_template": attr.label(
+            allow_single_file = True,
+            default = "//sphinxdocs/private:sphinx_run_template.sh",
+        ),
+    },
+    executable = True,
+)
diff --git a/sphinxdocs/private/sphinx_docs_library.bzl b/sphinxdocs/private/sphinx_docs_library.bzl
new file mode 100644
index 0000000..076ed72
--- /dev/null
+++ b/sphinxdocs/private/sphinx_docs_library.bzl
@@ -0,0 +1,51 @@
+"""Implementation of sphinx_docs_library."""
+
+load(":sphinx_docs_library_info.bzl", "SphinxDocsLibraryInfo")
+
+def _sphinx_docs_library_impl(ctx):
+    strip_prefix = ctx.attr.strip_prefix or (ctx.label.package + "/")
+    direct_entries = []
+    if ctx.files.srcs:
+        entry = struct(
+            strip_prefix = strip_prefix,
+            prefix = ctx.attr.prefix,
+            files = ctx.files.srcs,
+        )
+        direct_entries.append(entry)
+
+    return [
+        SphinxDocsLibraryInfo(
+            strip_prefix = strip_prefix,
+            prefix = ctx.attr.prefix,
+            files = ctx.files.srcs,
+            transitive = depset(
+                direct = direct_entries,
+                transitive = [t[SphinxDocsLibraryInfo].transitive for t in ctx.attr.deps],
+            ),
+        ),
+        DefaultInfo(
+            files = depset(ctx.files.srcs),
+        ),
+    ]
+
+sphinx_docs_library = rule(
+    implementation = _sphinx_docs_library_impl,
+    attrs = {
+        "deps": attr.label_list(
+            doc = """
+Additional `sphinx_docs_library` targets to include. They do not have the
+`prefix` and `strip_prefix` attributes applied to them.""",
+            providers = [SphinxDocsLibraryInfo],
+        ),
+        "prefix": attr.string(
+            doc = "Prefix to prepend to file paths. Added after `strip_prefix` is removed.",
+        ),
+        "srcs": attr.label_list(
+            allow_files = True,
+            doc = "Files that are part of the library.",
+        ),
+        "strip_prefix": attr.string(
+            doc = "Prefix to remove from file paths. Removed before `prefix` is prepended.",
+        ),
+    },
+)
diff --git a/sphinxdocs/private/sphinx_docs_library_info.bzl b/sphinxdocs/private/sphinx_docs_library_info.bzl
new file mode 100644
index 0000000..de40d8d
--- /dev/null
+++ b/sphinxdocs/private/sphinx_docs_library_info.bzl
@@ -0,0 +1,30 @@
+"""Provider for collecting doc files as libraries."""
+
+SphinxDocsLibraryInfo = provider(
+    doc = "Information about a collection of doc files.",
+    fields = {
+        "files": """
+:type: depset[File]
+
+The documentation files for the library.
+""",
+        "prefix": """
+:type: str
+
+Prefix to prepend to file paths in `files`. It is added after `strip_prefix`
+is removed.
+""",
+        "strip_prefix": """
+:type: str
+
+Prefix to remove from file paths in `files`. It is removed before `prefix`
+is prepended.
+""",
+        "transitive": """
+:type: depset[struct]
+
+Depset of transitive library information. Each entry in the depset is a struct
+with fields matching the fields of this provider.
+""",
+    },
+)
diff --git a/sphinxdocs/private/sphinx_docs_library_macro.bzl b/sphinxdocs/private/sphinx_docs_library_macro.bzl
new file mode 100644
index 0000000..095b376
--- /dev/null
+++ b/sphinxdocs/private/sphinx_docs_library_macro.bzl
@@ -0,0 +1,13 @@
+"""Implementation of sphinx_docs_library macro."""
+
+load("//python/private:util.bzl", "add_tag")  # buildifier: disable=bzl-visibility
+load(":sphinx_docs_library.bzl", _sphinx_docs_library = "sphinx_docs_library")
+
+def sphinx_docs_library(**kwargs):
+    """Collection of doc files for use by `sphinx_docs`.
+
+    Args:
+        **kwargs: Args passed onto underlying {bzl:rule}`sphinx_docs_library` rule
+    """
+    add_tag(kwargs, "@rules_python//sphinxdocs:sphinx_docs_library")
+    _sphinx_docs_library(**kwargs)
diff --git a/sphinxdocs/private/sphinx_run_template.sh b/sphinxdocs/private/sphinx_run_template.sh
new file mode 100644
index 0000000..4a1f1e4
--- /dev/null
+++ b/sphinxdocs/private/sphinx_run_template.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+declare -a args
+%SETUP_ARGS%
+
+declare -a sphinx_env
+%SETUP_ENV%
+
+for path in "%SOURCE_DIR_RUNFILES_PATH%" "%SOURCE_DIR_EXEC_PATH%"; do
+  if [[ -e $path ]]; then
+    source_dir=$path
+    break
+  fi
+done
+
+if [[ -z "$source_dir" ]]; then
+    echo "Could not find source dir"
+    exit 1
+fi
+
+for path in "%SPHINX_RUNFILES_PATH%" "%SPHINX_EXEC_PATH%"; do
+  if [[ -e $path ]]; then
+    sphinx=$path
+    break
+  fi
+done
+
+if [[ -z $sphinx ]]; then
+  echo "Could not find sphinx"
+  exit 1
+fi
+
+output_dir=${SPHINX_OUT:-/tmp/sphinx-out}
+
+set -x
+exec env "${sphinx_env[@]}" -- "$sphinx" "${args[@]}" "$@" "$source_dir" "$output_dir"
diff --git a/sphinxdocs/private/sphinx_stardoc.bzl b/sphinxdocs/private/sphinx_stardoc.bzl
index e2b1756..d5869b0 100644
--- a/sphinxdocs/private/sphinx_stardoc.bzl
+++ b/sphinxdocs/private/sphinx_stardoc.bzl
@@ -14,12 +14,34 @@
 
 """Rules to generate Sphinx-compatible documentation for bzl files."""
 
+load("@bazel_skylib//:bzl_library.bzl", "StarlarkLibraryInfo")
+load("@bazel_skylib//lib:paths.bzl", "paths")
 load("@bazel_skylib//lib:types.bzl", "types")
 load("@bazel_skylib//rules:build_test.bzl", "build_test")
 load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc")
 load("//python/private:util.bzl", "add_tag", "copy_propagating_kwargs")  # buildifier: disable=bzl-visibility
+load("//sphinxdocs/private:sphinx_docs_library_macro.bzl", "sphinx_docs_library")
 
-def sphinx_stardocs(name, docs, **kwargs):
+_StardocInputHelperInfo = provider(
+    doc = "Extracts the single source file from a bzl library.",
+    fields = {
+        "file": """
+:type: File
+
+The sole output file from the wrapped target.
+""",
+    },
+)
+
+def sphinx_stardocs(
+        *,
+        name,
+        srcs = [],
+        deps = [],
+        docs = {},
+        prefix = None,
+        strip_prefix = None,
+        **kwargs):
     """Generate Sphinx-friendly Markdown docs using Stardoc for bzl libraries.
 
     A `build_test` for the docs is also generated to ensure Stardoc is able
@@ -28,8 +50,12 @@
     NOTE: This generates MyST-flavored Markdown.
 
     Args:
-        name: `str`, the name of the resulting file group with the generated docs.
-        docs: `dict[str output, source]` of the bzl files to generate documentation
+        name: {type}`Name`, the name of the resulting file group with the generated docs.
+        srcs: {type}`list[label]` Each source is either the bzl file to process
+            or a `bzl_library` target with one source file of the bzl file to
+            process.
+        deps: {type}`list[label]` Targets that provide files loaded by `src`
+        docs: {type}`dict[str, str|dict]` of the bzl files to generate documentation
             for. The `output` key is the path of the output filename, e.g.,
             `foo/bar.md`. The `source` values can be either of:
             * A `str` label that points to a `bzl_library` target. The target
@@ -39,10 +65,17 @@
             * A `dict` with keys `input` and `dep`. The `input` key is a string
               label to the bzl file to generate docs for. The `dep` key is a
               string label to a `bzl_library` providing the necessary dependencies.
+        prefix: {type}`str` Prefix to add to the output file path. It is prepended
+            after `strip_prefix` is removed.
+        strip_prefix: {type}`str | None` Prefix to remove from the input file path;
+            it is removed before `prefix` is prepended. If not specified, then
+            {any}`native.package_name` is used.
         **kwargs: Additional kwargs to pass onto each `sphinx_stardoc` target
     """
+    internal_name = "_{}".format(name)
     add_tag(kwargs, "@rules_python//sphinxdocs:sphinx_stardocs")
     common_kwargs = copy_propagating_kwargs(kwargs)
+    common_kwargs["target_compatible_with"] = kwargs.get("target_compatible_with")
 
     stardocs = []
     for out_name, entry in docs.items():
@@ -51,50 +84,165 @@
 
         if types.is_string(entry):
             stardoc_kwargs["deps"] = [entry]
-            stardoc_kwargs["input"] = entry.replace("_bzl", ".bzl")
+            stardoc_kwargs["src"] = entry.replace("_bzl", ".bzl")
         else:
             stardoc_kwargs.update(entry)
+
+            # input is accepted for backwards compatiblity. Remove when ready.
+            if "src" not in stardoc_kwargs and "input" in stardoc_kwargs:
+                stardoc_kwargs["src"] = stardoc_kwargs.pop("input")
             stardoc_kwargs["deps"] = [stardoc_kwargs.pop("dep")]
 
-        doc_name = "_{}_{}".format(name.lstrip("_"), out_name.replace("/", "_"))
-        _sphinx_stardoc(
+        doc_name = "{}_{}".format(internal_name, _name_from_label(out_name))
+        sphinx_stardoc(
             name = doc_name,
-            out = out_name,
+            output = out_name,
+            create_test = False,
             **stardoc_kwargs
         )
         stardocs.append(doc_name)
 
-    native.filegroup(
+    for label in srcs:
+        doc_name = "{}_{}".format(internal_name, _name_from_label(label))
+        sphinx_stardoc(
+            name = doc_name,
+            src = label,
+            # NOTE: We set prefix/strip_prefix here instead of
+            # on the sphinx_docs_library so that building the
+            # target produces markdown files in the expected location, which
+            # is convenient.
+            prefix = prefix,
+            strip_prefix = strip_prefix,
+            deps = deps,
+            create_test = False,
+            **common_kwargs
+        )
+        stardocs.append(doc_name)
+
+    sphinx_docs_library(
         name = name,
-        srcs = stardocs,
+        deps = stardocs,
         **common_kwargs
     )
-    build_test(
-        name = name + "_build_test",
-        targets = stardocs,
+    if stardocs:
+        build_test(
+            name = name + "_build_test",
+            targets = stardocs,
+            **common_kwargs
+        )
+
+def sphinx_stardoc(
+        name,
+        src,
+        deps = [],
+        public_load_path = None,
+        prefix = None,
+        strip_prefix = None,
+        create_test = True,
+        output = None,
+        **kwargs):
+    """Generate Sphinx-friendly Markdown for a single bzl file.
+
+    Args:
+        name: {type}`Name` name for the target.
+        src: {type}`label` The bzl file to process, or a `bzl_library`
+            target with one source file of the bzl file to process.
+        deps: {type}`list[label]` Targets that provide files loaded by `src`
+        public_load_path: {type}`str | None` override the file name that
+            is reported as the file being.
+        prefix: {type}`str | None` prefix to add to the output file path
+        strip_prefix: {type}`str | None` Prefix to remove from the input file path.
+            If not specified, then {any}`native.package_name` is used.
+        create_test: {type}`bool` True if a test should be defined to verify the
+            docs are buildable, False if not.
+        output: {type}`str | None` Optional explicit output file to use. If
+            not set, the output name will be derived from `src`.
+        **kwargs: {type}`dict` common args passed onto rules.
+    """
+    internal_name = "_{}".format(name.lstrip("_"))
+    add_tag(kwargs, "@rules_python//sphinxdocs:sphinx_stardoc")
+    common_kwargs = copy_propagating_kwargs(kwargs)
+    common_kwargs["target_compatible_with"] = kwargs.get("target_compatible_with")
+
+    input_helper_name = internal_name + ".primary_bzl_src"
+    _stardoc_input_helper(
+        name = input_helper_name,
+        target = src,
         **common_kwargs
     )
 
-def _sphinx_stardoc(*, name, out, public_load_path = None, **kwargs):
-    stardoc_name = "_{}_stardoc".format(name.lstrip("_"))
+    stardoc_name = internal_name + "_stardoc"
+
+    # NOTE: The .binaryproto suffix is an optimization. It makes the stardoc()
+    # call avoid performing a copy of the output to the desired name.
     stardoc_pb = stardoc_name + ".binaryproto"
 
-    if not public_load_path:
-        public_load_path = str(kwargs["input"])
-
     stardoc(
         name = stardoc_name,
+        input = input_helper_name,
         out = stardoc_pb,
         format = "proto",
-        **kwargs
+        deps = [src] + deps,
+        **common_kwargs
     )
 
+    pb2md_name = internal_name + "_pb2md"
     _stardoc_proto_to_markdown(
-        name = name,
+        name = pb2md_name,
         src = stardoc_pb,
-        output = out,
+        output = output,
+        output_name_from = input_helper_name if not output else None,
         public_load_path = public_load_path,
+        strip_prefix = strip_prefix,
+        prefix = prefix,
+        **common_kwargs
     )
+    sphinx_docs_library(
+        name = name,
+        srcs = [pb2md_name],
+        **common_kwargs
+    )
+    if create_test:
+        build_test(
+            name = name + "_build_test",
+            targets = [name],
+            **common_kwargs
+        )
+
+def _stardoc_input_helper_impl(ctx):
+    target = ctx.attr.target
+    if StarlarkLibraryInfo in target:
+        files = ctx.attr.target[StarlarkLibraryInfo].srcs
+    else:
+        files = target[DefaultInfo].files.to_list()
+
+    if len(files) == 0:
+        fail("Target {} produces no files, but must produce exactly 1 file".format(
+            ctx.attr.target.label,
+        ))
+    elif len(files) == 1:
+        primary = files[0]
+    else:
+        fail("Target {} produces {} files, but must produce exactly 1 file.".format(
+            ctx.attr.target.label,
+            len(files),
+        ))
+
+    return [
+        DefaultInfo(
+            files = depset([primary]),
+        ),
+        _StardocInputHelperInfo(
+            file = primary,
+        ),
+    ]
+
+_stardoc_input_helper = rule(
+    implementation = _stardoc_input_helper_impl,
+    attrs = {
+        "target": attr.label(allow_files = True),
+    },
+)
 
 def _stardoc_proto_to_markdown_impl(ctx):
     args = ctx.actions.args()
@@ -103,7 +251,16 @@
 
     inputs = [ctx.file.src]
     args.add("--proto", ctx.file.src)
-    args.add("--output", ctx.outputs.output)
+
+    if not ctx.outputs.output:
+        output_name = ctx.attr.output_name_from[_StardocInputHelperInfo].file.short_path
+        output_name = paths.replace_extension(output_name, ".md")
+        output_name = ctx.attr.prefix + output_name.removeprefix(ctx.attr.strip_prefix)
+        output = ctx.actions.declare_file(output_name)
+    else:
+        output = ctx.outputs.output
+
+    args.add("--output", output)
 
     if ctx.attr.public_load_path:
         args.add("--public-load-path={}".format(ctx.attr.public_load_path))
@@ -112,17 +269,23 @@
         executable = ctx.executable._proto_to_markdown,
         arguments = [args],
         inputs = inputs,
-        outputs = [ctx.outputs.output],
+        outputs = [output],
         mnemonic = "SphinxStardocProtoToMd",
         progress_message = "SphinxStardoc: converting proto to markdown: %{input} -> %{output}",
     )
+    return [DefaultInfo(
+        files = depset([output]),
+    )]
 
 _stardoc_proto_to_markdown = rule(
     implementation = _stardoc_proto_to_markdown_impl,
     attrs = {
-        "output": attr.output(mandatory = True),
+        "output": attr.output(mandatory = False),
+        "output_name_from": attr.label(),
+        "prefix": attr.string(),
         "public_load_path": attr.string(),
         "src": attr.label(allow_single_file = True, mandatory = True),
+        "strip_prefix": attr.string(),
         "_proto_to_markdown": attr.label(
             default = "//sphinxdocs/private:proto_to_markdown",
             executable = True,
@@ -130,3 +293,7 @@
         ),
     },
 )
+
+def _name_from_label(label):
+    label = label.lstrip("/").lstrip(":").replace(":", "/")
+    return label
diff --git a/sphinxdocs/sphinx.bzl b/sphinxdocs/sphinx.bzl
index d9385bd..6cae80e 100644
--- a/sphinxdocs/sphinx.bzl
+++ b/sphinxdocs/sphinx.bzl
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""# Rules to generate Sphinx documentation.
+"""Rules to generate Sphinx documentation.
 
 The general usage of the Sphinx rules requires two pieces:
 
@@ -32,8 +32,10 @@
     _sphinx_build_binary = "sphinx_build_binary",
     _sphinx_docs = "sphinx_docs",
     _sphinx_inventory = "sphinx_inventory",
+    _sphinx_run = "sphinx_run",
 )
 
 sphinx_build_binary = _sphinx_build_binary
 sphinx_docs = _sphinx_docs
 sphinx_inventory = _sphinx_inventory
+sphinx_run = _sphinx_run
diff --git a/sphinxdocs/sphinx_docs_library.bzl b/sphinxdocs/sphinx_docs_library.bzl
new file mode 100644
index 0000000..e864329
--- /dev/null
+++ b/sphinxdocs/sphinx_docs_library.bzl
@@ -0,0 +1,5 @@
+"""Library-like rule to collect docs."""
+
+load("//sphinxdocs/private:sphinx_docs_library_macro.bzl", _sphinx_docs_library = "sphinx_docs_library")
+
+sphinx_docs_library = _sphinx_docs_library
diff --git a/sphinxdocs/sphinx_stardoc.bzl b/sphinxdocs/sphinx_stardoc.bzl
index 623bc64..9913964 100644
--- a/sphinxdocs/sphinx_stardoc.bzl
+++ b/sphinxdocs/sphinx_stardoc.bzl
@@ -14,6 +14,7 @@
 
 """Rules to generate Sphinx-compatible documentation for bzl files."""
 
-load("//sphinxdocs/private:sphinx_stardoc.bzl", _sphinx_stardocs = "sphinx_stardocs")
+load("//sphinxdocs/private:sphinx_stardoc.bzl", _sphinx_stardoc = "sphinx_stardoc", _sphinx_stardocs = "sphinx_stardocs")
 
 sphinx_stardocs = _sphinx_stardocs
+sphinx_stardoc = _sphinx_stardoc
diff --git a/sphinxdocs/src/sphinx_bzl/bzl.py b/sphinxdocs/src/sphinx_bzl/bzl.py
index be38d8a..54b1285 100644
--- a/sphinxdocs/src/sphinx_bzl/bzl.py
+++ b/sphinxdocs/src/sphinx_bzl/bzl.py
@@ -1,3 +1,16 @@
+# Copyright 2024 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.
 """Sphinx extension for documenting Bazel/Starlark objects."""
 
 import ast
@@ -21,7 +34,7 @@
 from sphinx.util import inspect, logging
 from sphinx.util import nodes as sphinx_nodes
 from sphinx.util import typing as sphinx_typing
-from typing_extensions import override
+from typing_extensions import TypeAlias, override
 
 _logger = logging.getLogger(__name__)
 _LOG_PREFIX = f"[{_logger.name}] "
@@ -33,10 +46,10 @@
 _T = TypeVar("_T")
 
 # See https://www.sphinx-doc.org/en/master/extdev/domainapi.html#sphinx.domains.Domain.get_objects
-_GetObjectsTuple: typing.TypeAlias = tuple[str, str, str, str, str, int]
+_GetObjectsTuple: TypeAlias = tuple[str, str, str, str, str, int]
 
 # See SphinxRole.run definition; the docs for role classes are pretty sparse.
-_RoleRunResult: typing.TypeAlias = tuple[
+_RoleRunResult: TypeAlias = tuple[
     list[docutils_nodes.Node], list[docutils_nodes.system_message]
 ]
 
@@ -54,35 +67,6 @@
         yield i == 0, i == last_i, value
 
 
-# TODO: Remove this. Use @repo//pkg:file.bzl%symbol to identify things instead
-# of dots. This more directly reflects the bzl concept and avoids issues with
-# e.g. repos, directories, or files containing dots themselves.
-def _label_to_dotted_name(label: str) -> str:
-    """Convert an absolute label to a dotted name.
-
-    Args:
-        label: Absolute label with optional repo prefix, e.g. `@a//b:c.bzl`
-            or `//b:c.bzl`
-
-    Returns:
-        Label converted to a dotted notation for easier writing of object
-        references.
-    """
-    if label.endswith(".bzl"):
-        label = label[: -len(".bzl")]
-    elif ":BUILD" in label:
-        label = label[: label.find(":BUILD")]
-    else:
-        raise InvalidValueError(
-            f"Malformed label: Label must end with .bzl or :BUILD*, got {label}"
-        )
-
-    # Make a //foo:bar.bzl convert to foo.bar, not .foo.bar
-    if label.startswith("//"):
-        label = label.lstrip("/")
-    return label.replace("@", "").replace("//", "/").replace(":", "/").replace("/", ".")
-
-
 class InvalidValueError(Exception):
     """Generic error for an invalid value instead of ValueError.
 
@@ -142,9 +126,9 @@
     entry_type: str,
     entry_name: str,
     target: str,
-    main: str | None = None,
-    category_key: str | None = None,
-) -> tuple[str, str, str, str | None, str | None]:
+    main: typing.Union[str, None] = None,
+    category_key: typing.Union[str, None] = None,
+) -> tuple[str, str, str, typing.Union[str, None], typing.Union[str, None]]:
     # For this tuple definition, see:
     # https://www.sphinx-doc.org/en/master/extdev/nodes.html#sphinx.addnodes.index
     # For the definition of entry_type, see:
@@ -153,14 +137,21 @@
 
 
 class _BzlObjectId:
+    """Identifies an object defined by a directive.
+
+    This object is returned by `handle_signature()` and passed onto
+    `add_target_and_index()`. It contains information to identify the object
+    that is being described so that it can be indexed and tracked by the
+    domain.
+    """
+
     def __init__(
         self,
         *,
         repo: str,
-        bzl_file: str = None,
+        label: str,
         namespace: str = None,
         symbol: str = None,
-        target: str = None,
     ):
         """Creates an instance.
 
@@ -172,33 +163,79 @@
         """
         if not repo:
             raise InvalidValueError("repo cannot be empty")
-        if not bzl_file:
-            raise InvalidValueError("bzl_file cannot be empty")
-        if not symbol:
-            raise InvalidvalueError("symbol cannot be empty")
+        if not repo.startswith("@"):
+            raise InvalidValueError("repo must start with @")
+        if not label:
+            raise InvalidValueError("label cannot be empty")
+        if not label.startswith("//"):
+            raise InvalidValueError("label must start with //")
+
+        if not label.endswith(".bzl") and (symbol or namespace):
+            raise InvalidValueError(
+                "Symbol and namespace can only be specified for .bzl labels"
+            )
 
         self.repo = repo
-        self.bzl_file = bzl_file
+        self.label = label
+        self.package, self.target_name = self.label.split(":")
         self.namespace = namespace
         self.symbol = symbol  # Relative to namespace
+        # doc-relative identifier for this object
+        self.doc_id = symbol or self.target_name
 
-        clean_repo = repo.replace("@", "")
-        package = _label_to_dotted_name(bzl_file)
-        self.full_id = ".".join(filter(None, [clean_repo, package, namespace, symbol]))
+        if not self.doc_id:
+            raise InvalidValueError("doc_id is empty")
+
+        self.full_id = _full_id_from_parts(repo, label, [namespace, symbol])
 
     @classmethod
     def from_env(
-        cls, env: environment.BuildEnvironment, symbol: str = None, target: str = None
+        cls, env: environment.BuildEnvironment, *, symbol: str = None, label: str = None
     ) -> "_BzlObjectId":
-        if target:
-            symbol = target.lstrip("/:").replace(":", ".")
+        label = label or env.ref_context["bzl:file"]
+        if symbol:
+            namespace = ".".join(env.ref_context["bzl:doc_id_stack"])
+        else:
+            namespace = None
+
         return cls(
             repo=env.ref_context["bzl:repo"],
-            bzl_file=env.ref_context["bzl:file"],
-            namespace=".".join(env.ref_context["bzl:doc_id_stack"]),
+            label=label,
+            namespace=namespace,
             symbol=symbol,
         )
 
+    def __repr__(self):
+        return f"_BzlObjectId({self.full_id=})"
+
+
+def _full_id_from_env(env, object_ids=None):
+    return _full_id_from_parts(
+        env.ref_context["bzl:repo"],
+        env.ref_context["bzl:file"],
+        env.ref_context["bzl:object_id_stack"] + (object_ids or []),
+    )
+
+
+def _full_id_from_parts(repo, bzl_file, symbol_names=None):
+    parts = [repo, bzl_file]
+
+    symbol_names = symbol_names or []
+    symbol_names = list(filter(None, symbol_names))  # Filter out empty values
+    if symbol_names:
+        parts.append("%")
+        parts.append(".".join(symbol_names))
+
+    full_id = "".join(parts)
+    return full_id
+
+
+def _parse_full_id(full_id):
+    repo, slashes, label = full_id.partition("//")
+    label = slashes + label
+    label, _, symbol = label.partition("%")
+    return (repo, label, symbol)
+
 
 class _TypeExprParser(ast.NodeVisitor):
     """Parsers a string description of types to doc nodes."""
@@ -302,10 +339,10 @@
         domain: str,
         target: str,
         innernode: type[sphinx_typing.TextlikeNode] = addnodes.literal_emphasis,
-        contnode: docutils_nodes.Node | None = None,
-        env: environment.BuildEnvironment | None = None,
-        inliner: states.Inliner | None = None,
-        location: docutils_nodes.Element | None = None,
+        contnode: typing.Union[docutils_nodes.Node, None] = None,
+        env: typing.Union[environment.BuildEnvironment, None] = None,
+        inliner: typing.Union[states.Inliner, None] = None,
+        location: typing.Union[docutils_nodes.Element, None] = None,
     ) -> list[docutils_nodes.Node]:
         if rolename in ("arg", "attr"):
             return self._make_xrefs_for_arg_attr(
@@ -322,10 +359,10 @@
         domain: str,
         arg_name: str,
         innernode: type[sphinx_typing.TextlikeNode] = addnodes.literal_emphasis,
-        contnode: docutils_nodes.Node | None = None,
-        env: environment.BuildEnvironment | None = None,
-        inliner: states.Inliner | None = None,
-        location: docutils_nodes.Element | None = None,
+        contnode: typing.Union[docutils_nodes.Node, None] = None,
+        env: typing.Union[environment.BuildEnvironment, None] = None,
+        inliner: typing.Union[states.Inliner, None] = None,
+        location: typing.Union[docutils_nodes.Element, None] = None,
     ) -> list[docutils_nodes.Node]:
         bzl_file = env.ref_context["bzl:file"]
         anchor_prefix = ".".join(env.ref_context["bzl:doc_id_stack"])
@@ -335,7 +372,7 @@
             )
         index_description = f"{arg_name} ({self.name} in {bzl_file}%{anchor_prefix})"
         anchor_id = f"{anchor_prefix}.{arg_name}"
-        full_id = ".".join(env.ref_context["bzl:object_id_stack"] + [arg_name])
+        full_id = _full_id_from_env(env, [arg_name])
 
         env.get_domain(domain).add_object(
             _ObjectEntry(
@@ -408,8 +445,8 @@
         domain: str,
         item: tuple,
         env: environment.BuildEnvironment = None,
-        inliner: states.Inliner | None = None,
-        location: docutils_nodes.Element | None = None,
+        inliner: typing.Union[states.Inliner, None] = None,
+        location: typing.Union[docutils_nodes.Element, None] = None,
     ) -> docutils_nodes.field:
         field_text = item[1][0].astext()
         parts = [p.strip() for p in field_text.split(",")]
@@ -459,9 +496,7 @@
             repo = self.env.config.bzl_default_repository_name
         self.env.ref_context["bzl:repo"] = repo
         self.env.ref_context["bzl:file"] = file_label
-        self.env.ref_context["bzl:object_id_stack"] = [
-            _label_to_dotted_name(repo + file_label)
-        ]
+        self.env.ref_context["bzl:object_id_stack"] = []
         self.env.ref_context["bzl:doc_id_stack"] = []
         return []
 
@@ -511,12 +546,15 @@
     @override
     def before_content(self) -> None:
         symbol_name = self.names[-1].symbol
-        self.env.ref_context["bzl:object_id_stack"].append(symbol_name)
-        self.env.ref_context["bzl:doc_id_stack"].append(symbol_name)
+        if symbol_name:
+            self.env.ref_context["bzl:object_id_stack"].append(symbol_name)
+            self.env.ref_context["bzl:doc_id_stack"].append(symbol_name)
 
     @override
     def transform_content(self, content_node: addnodes.desc_content) -> None:
-        def first_child_with_class_name(root, class_name) -> "None | Element":
+        def first_child_with_class_name(
+            root, class_name
+        ) -> typing.Union[None, docutils_nodes.Element]:
             matches = root.findall(
                 lambda node: isinstance(node, docutils_nodes.Element)
                 and class_name in node["classes"]
@@ -566,8 +604,9 @@
 
     @override
     def after_content(self) -> None:
-        self.env.ref_context["bzl:object_id_stack"].pop()
-        self.env.ref_context["bzl:doc_id_stack"].pop()
+        if self.names[-1].symbol:
+            self.env.ref_context["bzl:object_id_stack"].pop()
+            self.env.ref_context["bzl:doc_id_stack"].pop()
 
     # docs on how to build signatures:
     # https://www.sphinx-doc.org/en/master/extdev/nodes.html#sphinx.addnodes.desc_signature
@@ -668,7 +707,7 @@
                 if signature.return_annotation is not signature.empty:
                     sig_node += addnodes.desc_returns("", signature.return_annotation)
 
-        obj_id = _BzlObjectId.from_env(self.env, relative_name)
+        obj_id = _BzlObjectId.from_env(self.env, symbol=relative_name)
 
         sig_node["bzl:object_id"] = obj_id.full_id
         return obj_id
@@ -683,24 +722,25 @@
         self, obj_desc: _BzlObjectId, sig: str, sig_node: addnodes.desc_signature
     ) -> None:
         super().add_target_and_index(obj_desc, sig, sig_node)
-        symbol_name = obj_desc.symbol
-        display_name = sig_node.get("bzl:index_display_name", symbol_name)
+        if obj_desc.symbol:
+            display_name = obj_desc.symbol
+            location = obj_desc.label
+            if obj_desc.namespace:
+                location += f"%{obj_desc.namespace}"
+        else:
+            display_name = obj_desc.target_name
+            location = obj_desc.package
 
         anchor_prefix = ".".join(self.env.ref_context["bzl:doc_id_stack"])
         if anchor_prefix:
-            anchor_id = f"{anchor_prefix}.{symbol_name}"
-            file_location = "%" + anchor_prefix
+            anchor_id = f"{anchor_prefix}.{obj_desc.doc_id}"
         else:
-            anchor_id = symbol_name
-            file_location = ""
+            anchor_id = obj_desc.doc_id
 
         sig_node["ids"].append(anchor_id)
 
         object_type_display = self._get_object_type_display_name()
-        index_description = (
-            f"{display_name} ({object_type_display} in "
-            f"{obj_desc.bzl_file}{file_location})"
-        )
+        index_description = f"{display_name} ({object_type_display} in {location})"
         self.indexnode["entries"].extend(
             _index_node_tuple("single", f"{index_type}; {index_description}", anchor_id)
             for index_type in [object_type_display] + self._get_additional_index_types()
@@ -715,7 +755,7 @@
             object_type=self.objtype,
             search_priority=1,
             index_entry=domains.IndexEntry(
-                name=symbol_name,
+                name=display_name,
                 subtype=_INDEX_SUBTYPE_NORMAL,
                 docname=self.env.docname,
                 anchor=anchor_id,
@@ -732,13 +772,9 @@
                 # Options require \@ for leading @, but don't
                 # remove the escaping slash, so we have to do it manually
                 .lstrip("\\")
-                .lstrip("@")
-                .replace("//", "/")
-                .replace(".bzl%", ".")
-                .replace("/", ".")
-                .replace(":", ".")
             )
-        alt_names.extend(self._get_alt_names(object_entry))
+        extra_alt_names = self._get_alt_names(object_entry)
+        alt_names.extend(extra_alt_names)
 
         self.env.get_domain(self.domain).add_object(object_entry, alt_names=alt_names)
 
@@ -749,7 +785,7 @@
     def _object_hierarchy_parts(
         self, sig_node: addnodes.desc_signature
     ) -> tuple[str, ...]:
-        return tuple(sig_node["bzl:object_id"].split("."))
+        return _parse_full_id(sig_node["bzl:object_id"])
 
     @override
     def _toc_entry_name(self, sig_node: addnodes.desc_signature) -> str:
@@ -762,16 +798,26 @@
         return self._get_object_type_display_name()
 
     def _get_alt_names(self, object_entry):
-        return [object_entry.full_id.split(".")[-1]]
+        alt_names = []
+        full_id = object_entry.full_id
+        label, _, symbol = full_id.partition("%")
+        if symbol:
+            # Allow referring to the file-relative fully qualified symbol name
+            alt_names.append(symbol)
+            if "." in symbol:
+                # Allow referring to the last component of the symbol
+                alt_names.append(symbol.split(".")[-1])
+        else:
+            # Otherwise, it's a target. Allow referring to just the target name
+            _, _, target_name = label.partition(":")
+            alt_names.append(target_name)
+
+        return alt_names
 
 
 class _BzlCallable(_BzlObject):
     """Abstract base class for objects that are callable."""
 
-    @override
-    def _get_alt_names(self, object_entry):
-        return [object_entry.full_id.split(".")[-1]]
-
 
 class _BzlProvider(_BzlObject):
     """Documents a provider type.
@@ -790,10 +836,6 @@
     ```
     """
 
-    @override
-    def _get_alt_names(self, object_entry):
-        return [object_entry.full_id.split(".")[-1]]
-
 
 class _BzlProviderField(_BzlObject):
     """Documents a field of a provider.
@@ -822,7 +864,12 @@
 
     @override
     def _get_alt_names(self, object_entry):
-        return [".".join(object_entry.full_id.split(".")[-2:])]
+        alt_names = super()._get_alt_names(object_entry)
+        _, _, symbol = object_entry.full_id.partition("%")
+        # Allow refering to `mod_ext_name.tag_name`, even if the extension
+        # is nested within another object
+        alt_names.append(".".join(symbol.split(".")[-2:]))
+        return alt_names
 
 
 class _BzlRepositoryRule(_BzlCallable):
@@ -1094,6 +1141,15 @@
     def _get_signature_object_type(self) -> str:
         return ""
 
+    @override
+    def _get_alt_names(self, object_entry):
+        alt_names = super()._get_alt_names(object_entry)
+        _, _, symbol = object_entry.full_id.partition("%")
+        # Allow refering to `ProviderName.field`, even if the provider
+        # is nested within another object
+        alt_names.append(".".join(symbol.split(".")[-2:]))
+        return alt_names
+
 
 class _TargetType(enum.Enum):
     TARGET = "target"
@@ -1120,9 +1176,8 @@
         sig_node += addnodes.desc_addname(package, package)
         sig_node += addnodes.desc_name(target_name, target_name)
 
-        obj_id = _BzlObjectId.from_env(self.env, target=sig_text)
+        obj_id = _BzlObjectId.from_env(self.env, label=package + target_name)
         sig_node["bzl:object_id"] = obj_id.full_id
-        sig_node["bzl:index_display_name"] = f"{package}{target_name}"
         return obj_id
 
     @override
@@ -1384,7 +1439,7 @@
     object_types = {
         "arg": domains.ObjType("arg", "arg", "obj"),  # macro/function arg
         "aspect": domains.ObjType("aspect", "aspect", "obj"),
-        "attribute": domains.ObjType("attribute", "attribute", "obj"),  # rule attribute
+        "attr": domains.ObjType("attr", "attr", "obj"),  # rule attribute
         "function": domains.ObjType("function", "func", "obj"),
         "method": domains.ObjType("method", "method", "obj"),
         "module-extension": domains.ObjType(
@@ -1403,6 +1458,7 @@
         # types are objects that have a constructor and methods/attrs
         "type": domains.ObjType("type", "type", "obj"),
     }
+
     # This controls:
     # * What is recognized when parsing, e.g. ":bzl:ref:`foo`" requires
     # "ref" to be in the role dict below.
@@ -1410,9 +1466,11 @@
         "arg": roles.XRefRole(),
         "attr": roles.XRefRole(),
         "default-value": _DefaultValueRole(),
+        "flag": roles.XRefRole(),
         "obj": roles.XRefRole(),
         "required-providers": _RequiredProvidersRole(),
         "return-type": _ReturnTypeRole(),
+        "rule": roles.XRefRole(),
         "target": roles.XRefRole(),
         "type": _TypeRole(),
     }
@@ -1449,12 +1507,14 @@
         # dict[str, dict[str, _ObjectEntry]]
         "doc_names": {},
         # Objects by a shorter or alternative name
-        # dict[str, _ObjectEntry]
+        # dict[str, dict[str id, _ObjectEntry]]
         "alt_names": {},
     }
 
     @override
-    def get_full_qualified_name(self, node: docutils_nodes.Element) -> str | None:
+    def get_full_qualified_name(
+        self, node: docutils_nodes.Element
+    ) -> typing.Union[str, None]:
         bzl_file = node.get("bzl:file")
         symbol_name = node.get("bzl:symbol")
         ref_target = node.get("reftarget")
@@ -1498,7 +1558,7 @@
         target: str,
         node: addnodes.pending_xref,
         contnode: docutils_nodes.Element,
-    ) -> docutils_nodes.Element | None:
+    ) -> typing.Union[docutils_nodes.Element, None]:
         _log_debug(
             "resolve_xref: fromdocname=%s, typ=%s, target=%s", fromdocname, typ, target
         )
@@ -1515,24 +1575,26 @@
 
     def _find_entry_for_xref(
         self, fromdocname: str, object_type: str, target: str
-    ) -> _ObjectEntry | None:
-        # Normalize a variety of formats to the dotted format used internally.
-        # --@foo//:bar flags
-        # --@foo//:bar=value labels
-        # //foo:bar.bzl labels
-        target = (
-            target.lstrip("@/:-")
-            .replace("//", "/")
-            .replace(".bzl%", ".")
-            .replace("/", ".")
-            .replace(":", ".")
-        )
+    ) -> typing.Union[_ObjectEntry, None]:
+        if target.startswith("--"):
+            target = target.strip("-")
+            object_type = "flag"
+
+        # Allow using parentheses, e.g. `foo()` or `foo(x=...)`
+        target, _, _ = target.partition("(")
+
         # Elide the value part of --foo=bar flags
         # Note that the flag value could contain `=`
         if "=" in target:
             target = target[: target.find("=")]
+
         if target in self.data["doc_names"].get(fromdocname, {}):
-            return self.data["doc_names"][fromdocname][target]
+            entry = self.data["doc_names"][fromdocname][target]
+            # Prevent a local doc name masking a global alt name when its of
+            # a different type. e.g. when the macro `foo` refers to the
+            # rule `foo` in another doc.
+            if object_type in self.object_types[entry.object_type].roles:
+                return entry
 
         if object_type == "obj":
             search_space = self.data["objects"]
@@ -1543,7 +1605,15 @@
 
         _log_debug("find_entry: alt_names=%s", sorted(self.data["alt_names"].keys()))
         if target in self.data["alt_names"]:
-            return self.data["alt_names"][target]
+            # Give preference to shorter object ids. This is a work around
+            # to allow e.g. `FooInfo` to refer to the FooInfo type rather than
+            # the `FooInfo` constructor.
+            entries = sorted(
+                self.data["alt_names"][target].items(), key=lambda item: len(item[0])
+            )
+            for _, entry in entries:
+                if object_type in self.object_types[entry.object_type].roles:
+                    return entry
 
         return None
 
@@ -1564,26 +1634,20 @@
         self.data["objects_by_type"].setdefault(entry.object_type, {})
         self.data["objects_by_type"][entry.object_type][entry.full_id] = entry
 
-        base_name = entry.full_id.split(".")[-1]
-
-        without_repo = entry.full_id.split(".", 1)[1]
+        repo, label, symbol = _parse_full_id(entry.full_id)
+        if symbol:
+            base_name = symbol.split(".")[-1]
+        else:
+            base_name = label.split(":")[-1]
 
         if alt_names is not None:
             alt_names = list(alt_names)
-        alt_names.append(without_repo)
+        # Add the repo-less version as an alias
+        alt_names.append(label + (f"%{symbol}" if symbol else ""))
 
-        for alt_name in alt_names:
-            if alt_name in self.data["alt_names"]:
-                existing = self.data["alt_names"][alt_name]
-                # This situation usually occurs for the constructor function
-                # of a provider, but could occur for e.g. an exported struct
-                # with an attribute the same name as the struct. For lack
-                # of a better option, take the shorter entry, on the assumption
-                # it refers to some container of the longer entry.
-                if len(entry.full_id) < len(existing.full_id):
-                    self.data["alt_names"][alt_name] = entry
-            else:
-                self.data["alt_names"][alt_name] = entry
+        for alt_name in sorted(set(alt_names)):
+            self.data["alt_names"].setdefault(alt_name, {})
+            self.data["alt_names"][alt_name][entry.full_id] = entry
 
         docname = entry.index_entry.docname
         self.data["doc_names"].setdefault(docname, {})
@@ -1593,11 +1657,11 @@
         self, docnames: list[str], otherdata: dict[str, typing.Any]
     ) -> None:
         # Merge in simple dict[key, value] data
-        for top_key in ("objects", "alt_names"):
+        for top_key in ("objects",):
             self.data[top_key].update(otherdata.get(top_key, {}))
 
         # Merge in two-level dict[top_key, dict[sub_key, value]] data
-        for top_key in ("objects_by_type", "doc_names"):
+        for top_key in ("objects_by_type", "doc_names", "alt_names"):
             existing_top_map = self.data[top_key]
             for sub_key, sub_values in otherdata.get(top_key, {}).items():
                 if sub_key not in existing_top_map:
diff --git a/sphinxdocs/tests/sphinx_stardoc/BUILD.bazel b/sphinxdocs/tests/sphinx_stardoc/BUILD.bazel
index b141e5f..3741e41 100644
--- a/sphinxdocs/tests/sphinx_stardoc/BUILD.bazel
+++ b/sphinxdocs/tests/sphinx_stardoc/BUILD.bazel
@@ -1,7 +1,20 @@
 load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("//python:py_test.bzl", "py_test")
 load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
 load("//sphinxdocs:sphinx.bzl", "sphinx_build_binary", "sphinx_docs")
-load("//sphinxdocs:sphinx_stardoc.bzl", "sphinx_stardocs")
+load("//sphinxdocs:sphinx_stardoc.bzl", "sphinx_stardoc", "sphinx_stardocs")
+
+# We only build for Linux and Mac because:
+# 1. The actual doc process only runs on Linux
+# 2. Mac is a common development platform, and is close enough to Linux
+#    it's feasible to make work.
+# Making CI happy under Windows is too much of a headache, though, so we don't
+# bother with that.
+_TARGET_COMPATIBLE_WITH = select({
+    "@platforms//os:linux": [],
+    "@platforms//os:macos": [],
+    "//conditions:default": ["@platforms//:incompatible"],
+}) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"]
 
 sphinx_docs(
     name = "docs",
@@ -9,7 +22,7 @@
         include = [
             "*.md",
         ],
-    ) + [":bzl_docs"],
+    ),
     config = "conf.py",
     formats = [
         "html",
@@ -19,37 +32,48 @@
     },
     sphinx = ":sphinx-build",
     strip_prefix = package_name() + "/",
-    # We only develop the docs using Linux/Mac, and there are deps that
-    # don't work for Windows, so just skip Windows.
-    target_compatible_with = select({
-        "@platforms//os:linux": [],
-        "@platforms//os:macos": [],
-        "//conditions:default": ["@platforms//:incompatible"],
-    }) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"],
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+    deps = [
+        ":bzl_function",
+        ":bzl_providers",
+        ":simple_bzl_docs",
+    ],
 )
 
 sphinx_stardocs(
-    name = "bzl_docs",
-    docs = {
-        "bzl_function.md": dict(
-            dep = ":all_bzl",
-            input = "//sphinxdocs/tests/sphinx_stardoc:bzl_function.bzl",
-        ),
-        "bzl_providers.md": dict(
-            dep = ":all_bzl",
-            input = "//sphinxdocs/tests/sphinx_stardoc:bzl_providers.bzl",
-        ),
-        "bzl_rule.md": dict(
-            dep = ":all_bzl",
-            input = "//sphinxdocs/tests/sphinx_stardoc:bzl_rule.bzl",
-        ),
-    },
-    target_compatible_with = [] if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"],
+    name = "simple_bzl_docs",
+    srcs = [":bzl_rule_bzl"],
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+)
+
+sphinx_stardoc(
+    name = "bzl_function",
+    src = ":bzl_function.bzl",
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+    deps = [":func_and_providers_bzl"],
+)
+
+sphinx_stardoc(
+    name = "bzl_providers",
+    src = ":bzl_providers.bzl",
+    prefix = "addprefix_",
+    target_compatible_with = _TARGET_COMPATIBLE_WITH,
+    deps = [":func_and_providers_bzl"],
+)
+
+# A bzl_library with multiple sources
+bzl_library(
+    name = "func_and_providers_bzl",
+    srcs = [
+        "bzl_function.bzl",
+        "bzl_providers.bzl",
+    ],
 )
 
 bzl_library(
-    name = "all_bzl",
-    srcs = glob(["*.bzl"]),
+    name = "bzl_rule_bzl",
+    srcs = ["bzl_rule.bzl"],
+    deps = [":func_and_providers_bzl"],
 )
 
 sphinx_build_binary(
@@ -62,3 +86,10 @@
         "@dev_pip//typing_extensions",  # Needed by sphinx_stardoc
     ],
 )
+
+py_test(
+    name = "sphinx_output_test",
+    srcs = ["sphinx_output_test.py"],
+    data = [":docs"],
+    deps = ["@dev_pip//absl_py"],
+)
diff --git a/sphinxdocs/tests/sphinx_stardoc/bzl_rule.bzl b/sphinxdocs/tests/sphinx_stardoc/bzl_rule.bzl
index d17c8bc..366e372 100644
--- a/sphinxdocs/tests/sphinx_stardoc/bzl_rule.bzl
+++ b/sphinxdocs/tests/sphinx_stardoc/bzl_rule.bzl
@@ -14,7 +14,7 @@
 def _impl(ctx):
     _ = ctx  # @unused
 
-my_rule = rule(
+bzl_rule = rule(
     implementation = _impl,
     attrs = {
         "srcs": attr.label(
diff --git a/sphinxdocs/tests/sphinx_stardoc/index.md b/sphinxdocs/tests/sphinx_stardoc/index.md
index 4f70482..43ef14f 100644
--- a/sphinxdocs/tests/sphinx_stardoc/index.md
+++ b/sphinxdocs/tests/sphinx_stardoc/index.md
@@ -21,6 +21,6 @@
 :hidden:
 :glob:
 
-*
+**
 genindex
 :::
diff --git a/sphinxdocs/tests/sphinx_stardoc/sphinx_output_test.py b/sphinxdocs/tests/sphinx_stardoc/sphinx_output_test.py
new file mode 100644
index 0000000..6d65c92
--- /dev/null
+++ b/sphinxdocs/tests/sphinx_stardoc/sphinx_output_test.py
@@ -0,0 +1,73 @@
+import importlib.resources
+from xml.etree import ElementTree
+
+from absl.testing import absltest, parameterized
+
+from sphinxdocs.tests import sphinx_stardoc
+
+
+class SphinxOutputTest(parameterized.TestCase):
+    def setUp(self):
+        super().setUp()
+        self._docs = {}
+        self._xmls = {}
+
+    def assert_xref(self, doc, *, text, href):
+        match = self._doc_element(doc).find(f".//*[.='{text}']")
+        if not match:
+            self.fail(f"No element found with {text=}")
+        actual = match.attrib.get("href", "<UNSET>")
+        self.assertEqual(
+            href,
+            actual,
+            msg=f"Unexpected href for {text=}: "
+            + ElementTree.tostring(match).decode("utf8"),
+        )
+
+    def _read_doc(self, doc):
+        doc += ".html"
+        if doc not in self._docs:
+            self._docs[doc] = (
+                importlib.resources.files(sphinx_stardoc)
+                .joinpath("docs/_build/html")
+                .joinpath(doc)
+                .read_text()
+            )
+        return self._docs[doc]
+
+    def _doc_element(self, doc):
+        xml = self._read_doc(doc)
+        if doc not in self._xmls:
+            self._xmls[doc] = ElementTree.fromstring(xml)
+        return self._xmls[doc]
+
+    @parameterized.named_parameters(
+        # fmt: off
+        ("short_func", "myfunc", "function.html#myfunc"),
+        ("short_func_arg", "myfunc.arg1", "function.html#myfunc.arg1"),
+        ("short_rule", "my_rule", "rule.html#my_rule"),
+        ("short_rule_attr", "my_rule.ra1", "rule.html#my_rule.ra1"),
+        ("short_provider", "LangInfo", "provider.html#LangInfo"),
+        ("short_tag_class", "myext.mytag", "module_extension.html#myext.mytag"),
+        ("full_norepo_func", "//lang:function.bzl%myfunc", "function.html#myfunc"),
+        ("full_norepo_func_arg", "//lang:function.bzl%myfunc.arg1", "function.html#myfunc.arg1"),
+        ("full_norepo_rule", "//lang:rule.bzl%my_rule", "rule.html#my_rule"),
+        ("full_norepo_rule_attr", "//lang:rule.bzl%my_rule.ra1", "rule.html#my_rule.ra1"),
+        ("full_norepo_provider", "//lang:provider.bzl%LangInfo", "provider.html#LangInfo"),
+        ("full_norepo_aspect", "//lang:aspect.bzl%myaspect", "aspect.html#myaspect"),
+        ("full_norepo_target", "//lang:relativetarget", "target.html#relativetarget"),
+        ("full_repo_func", "@testrepo//lang:function.bzl%myfunc", "function.html#myfunc"),
+        ("full_repo_func_arg", "@testrepo//lang:function.bzl%myfunc.arg1", "function.html#myfunc.arg1"),
+        ("full_repo_rule", "@testrepo//lang:rule.bzl%my_rule", "rule.html#my_rule"),
+        ("full_repo_rule_attr", "@testrepo//lang:rule.bzl%my_rule.ra1", "rule.html#my_rule.ra1"),
+        ("full_repo_provider", "@testrepo//lang:provider.bzl%LangInfo", "provider.html#LangInfo"),
+        ("full_repo_aspect", "@testrepo//lang:aspect.bzl%myaspect", "aspect.html#myaspect"),
+        ("full_repo_target", "@testrepo//lang:relativetarget", "target.html#relativetarget"),
+        # fmt: on
+    )
+    def test_xrefs(self, text, href):
+        self.assert_xref("xrefs", text=text, href=href)
+
+
+if __name__ == "__main__":
+    absltest.main()
diff --git a/sphinxdocs/tests/sphinx_stardoc/xrefs.md b/sphinxdocs/tests/sphinx_stardoc/xrefs.md
index 9eb7b81..83f6869 100644
--- a/sphinxdocs/tests/sphinx_stardoc/xrefs.md
+++ b/sphinxdocs/tests/sphinx_stardoc/xrefs.md
@@ -12,13 +12,14 @@
 * rule: {obj}`my_rule`
 * rule attr: {obj}`my_rule.ra1`
 * provider: {obj}`LangInfo`
+* tag class: {obj}`myext.mytag`
 
 ## Fully qualified label without repo
 
 * function: {obj}`//lang:function.bzl%myfunc`
 * function arg: {obj}`//lang:function.bzl%myfunc.arg1`
 * rule: {obj}`//lang:rule.bzl%my_rule`
-* function: {obj}`//lang:rule.bzl%my_rule.ra1`
+* rule attr: {obj}`//lang:rule.bzl%my_rule.ra1`
 * provider: {obj}`//lang:provider.bzl%LangInfo`
 * aspect: {obj}`//lang:aspect.bzl%myaspect`
 * target: {obj}`//lang:relativetarget`
@@ -33,22 +34,6 @@
 * aspect: {obj}`@testrepo//lang:aspect.bzl%myaspect`
 * target: {obj}`@testrepo//lang:relativetarget`
 
-## Fully qualified dotted name with repo
-
-* function: {obj}`testrepo.lang.function.myfunc`
-* function arg: {obj}`testrepo.lang.function.myfunc.arg1`
-* rule: {obj}`testrepo.lang.rule.my_rule`
-* function: {obj}`testrepo.lang.rule.my_rule.ra1`
-* provider: {obj}`testrepo.lang.provider.LangInfo`
-
-## Fully qualified dotted name without repo
-
-* function: {obj}`lang.function.myfunc`
-* function arg: {obj}`lang.function.myfunc.arg1`
-* rule: {obj}`lang.rule.my_rule`
-* rule attr: {obj}`lang.rule.my_rule.ra1`
-* provider: {obj}`lang.provider.LangInfo`
-
 ## Using origin keys
 
 * provider using `{type}`: {type}`"@rules_python//sphinxdocs/tests/sphinx_stardoc:bzl_rule.bzl%GenericInfo"`
diff --git a/tests/base_rules/BUILD.bazel b/tests/base_rules/BUILD.bazel
index cd57715..aa21042 100644
--- a/tests/base_rules/BUILD.bazel
+++ b/tests/base_rules/BUILD.bazel
@@ -11,70 +11,3 @@
 # 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("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test", "sh_py_run_test")
-
-_SUPPORTS_BOOTSTRAP_SCRIPT = select({
-    "@platforms//os:windows": ["@platforms//:incompatible"],
-    "//conditions:default": [],
-}) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"]
-
-sh_py_run_test(
-    name = "run_binary_zip_no_test",
-    build_python_zip = "no",
-    py_src = "bin.py",
-    sh_src = "run_binary_zip_no_test.sh",
-)
-
-sh_py_run_test(
-    name = "run_binary_zip_yes_test",
-    build_python_zip = "yes",
-    py_src = "bin.py",
-    sh_src = "run_binary_zip_yes_test.sh",
-)
-
-sh_py_run_test(
-    name = "run_binary_bootstrap_script_zip_yes_test",
-    bootstrap_impl = "script",
-    build_python_zip = "yes",
-    py_src = "bin.py",
-    sh_src = "run_binary_zip_yes_test.sh",
-    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
-)
-
-sh_py_run_test(
-    name = "run_binary_bootstrap_script_zip_no_test",
-    bootstrap_impl = "script",
-    build_python_zip = "no",
-    py_src = "bin.py",
-    sh_src = "run_binary_zip_no_test.sh",
-    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
-)
-
-py_reconfig_test(
-    name = "sys_path_order_bootstrap_script_test",
-    srcs = ["sys_path_order_test.py"],
-    bootstrap_impl = "script",
-    env = {"BOOTSTRAP": "script"},
-    imports = ["./site-packages"],
-    main = "sys_path_order_test.py",
-    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
-)
-
-py_reconfig_test(
-    name = "sys_path_order_bootstrap_system_python_test",
-    srcs = ["sys_path_order_test.py"],
-    bootstrap_impl = "system_python",
-    env = {"BOOTSTRAP": "system_python"},
-    imports = ["./site-packages"],
-    main = "sys_path_order_test.py",
-)
-
-sh_py_run_test(
-    name = "inherit_pythonsafepath_env_test",
-    bootstrap_impl = "script",
-    py_src = "bin.py",
-    sh_src = "inherit_pythonsafepath_env_test.sh",
-    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
-)
diff --git a/tests/base_rules/base_tests.bzl b/tests/base_rules/base_tests.bzl
index fb95c15..ae298ed 100644
--- a/tests/base_rules/base_tests.bzl
+++ b/tests/base_rules/base_tests.bzl
@@ -18,8 +18,8 @@
 load("@rules_testing//lib:util.bzl", "PREVENT_IMPLICIT_BUILDING_TAGS", rt_util = "util")
 load("//python:defs.bzl", "PyInfo")
 load("//python/private:reexports.bzl", "BuiltinPyInfo")  # buildifier: disable=bzl-visibility
-load("//tests/base_rules:py_info_subject.bzl", "py_info_subject")
 load("//tests/base_rules:util.bzl", pt_util = "util")
+load("//tests/support:py_info_subject.bzl", "py_info_subject")
 
 _tests = []
 
diff --git a/tests/base_rules/precompile/precompile_tests.bzl b/tests/base_rules/precompile/precompile_tests.bzl
index 5599f61..4c0f936 100644
--- a/tests/base_rules/precompile/precompile_tests.bzl
+++ b/tests/base_rules/precompile/precompile_tests.bzl
@@ -23,18 +23,24 @@
 load("//python:py_info.bzl", "PyInfo")
 load("//python:py_library.bzl", "py_library")
 load("//python:py_test.bzl", "py_test")
-load("//tests/base_rules:py_info_subject.bzl", "py_info_subject")
+load("//tests/support:py_info_subject.bzl", "py_info_subject")
 load(
     "//tests/support:support.bzl",
     "CC_TOOLCHAIN",
     "EXEC_TOOLS_TOOLCHAIN",
-    "PLATFORM_TOOLCHAIN",
     "PRECOMPILE",
     "PRECOMPILE_ADD_TO_RUNFILES",
     "PRECOMPILE_SOURCE_RETENTION",
+    "PY_TOOLCHAINS",
 )
 
-_TEST_TOOLCHAINS = [PLATFORM_TOOLCHAIN, CC_TOOLCHAIN]
+_COMMON_CONFIG_SETTINGS = {
+    # This isn't enabled in all environments the tests run in, so disable
+    # it for conformity.
+    "//command_line_option:allow_unresolved_symlinks": True,
+    "//command_line_option:extra_toolchains": [PY_TOOLCHAINS, CC_TOOLCHAIN],
+    EXEC_TOOLS_TOOLCHAIN: "enabled",
+}
 
 _tests = []
 
@@ -60,10 +66,7 @@
         name = name,
         impl = _test_precompile_enabled_impl,
         target = name + "_subject",
-        config_settings = {
-            "//command_line_option:extra_toolchains": _TEST_TOOLCHAINS,
-            EXEC_TOOLS_TOOLCHAIN: "enabled",
-        },
+        config_settings = _COMMON_CONFIG_SETTINGS,
     )
 
 def _test_precompile_enabled_impl(env, target):
@@ -118,10 +121,8 @@
     analysis_test(
         name = name,
         impl = _test_pyc_only_impl,
-        config_settings = {
-            "//command_line_option:extra_toolchains": _TEST_TOOLCHAINS,
+        config_settings = _COMMON_CONFIG_SETTINGS | {
             ##PRECOMPILE_SOURCE_RETENTION: "omit_source",
-            EXEC_TOOLS_TOOLCHAIN: "enabled",
             PRECOMPILE: "enabled",
         },
         target = name + "_subject",
@@ -163,10 +164,7 @@
         name = name,
         impl = _test_precompile_if_generated_impl,
         target = name + "_subject",
-        config_settings = {
-            "//command_line_option:extra_toolchains": _TEST_TOOLCHAINS,
-            EXEC_TOOLS_TOOLCHAIN: "enabled",
-        },
+        config_settings = _COMMON_CONFIG_SETTINGS,
     )
 
 _tests.append(_test_precompile_if_generated)
@@ -205,10 +203,8 @@
         name = name,
         impl = _test_omit_source_if_generated_source_impl,
         target = name + "_subject",
-        config_settings = {
-            "//command_line_option:extra_toolchains": _TEST_TOOLCHAINS,
+        config_settings = _COMMON_CONFIG_SETTINGS | {
             PRECOMPILE_SOURCE_RETENTION: "omit_if_generated_source",
-            EXEC_TOOLS_TOOLCHAIN: "enabled",
         },
     )
 
@@ -254,11 +250,9 @@
             "binary": name + "_binary",
             "library": name + "_lib",
         },
-        config_settings = {
-            "//command_line_option:extra_toolchains": _TEST_TOOLCHAINS,
+        config_settings = _COMMON_CONFIG_SETTINGS | {
             PRECOMPILE_ADD_TO_RUNFILES: "decided_elsewhere",
             PRECOMPILE: "enabled",
-            EXEC_TOOLS_TOOLCHAIN: "enabled",
         },
     )
 
@@ -266,14 +260,14 @@
 
 def _test_precompile_add_to_runfiles_decided_elsewhere_impl(env, targets):
     env.expect.that_target(targets.binary).runfiles().contains_at_least([
-        "{workspace}/tests/base_rules/precompile/__pycache__/bin.fakepy-45.pyc",
-        "{workspace}/tests/base_rules/precompile/__pycache__/lib.fakepy-45.pyc",
-        "{workspace}/tests/base_rules/precompile/bin.py",
-        "{workspace}/tests/base_rules/precompile/lib.py",
+        "{workspace}/{package}/__pycache__/bin.fakepy-45.pyc",
+        "{workspace}/{package}/__pycache__/lib.fakepy-45.pyc",
+        "{workspace}/{package}/bin.py",
+        "{workspace}/{package}/lib.py",
     ])
 
     env.expect.that_target(targets.library).runfiles().contains_exactly([
-        "{workspace}/tests/base_rules/precompile/lib.py",
+        "{workspace}/{package}/lib.py",
     ])
 
 def _test_precompiler_action(name):
@@ -293,16 +287,12 @@
         name = name,
         impl = _test_precompiler_action_impl,
         target = name + "_subject",
-        config_settings = {
-            "//command_line_option:extra_toolchains": _TEST_TOOLCHAINS,
-            EXEC_TOOLS_TOOLCHAIN: "enabled",
-        },
+        config_settings = _COMMON_CONFIG_SETTINGS,
     )
 
 _tests.append(_test_precompiler_action)
 
 def _test_precompiler_action_impl(env, target):
-    #env.expect.that_target(target).runfiles().contains_exactly([])
     action = env.expect.that_target(target).action_named("PyCompile")
     action.contains_flag_values([
         ("--optimize", "2"),
diff --git a/tests/base_rules/py_executable_base_tests.bzl b/tests/base_rules/py_executable_base_tests.bzl
index eb1a1b6..873349f 100644
--- a/tests/base_rules/py_executable_base_tests.bzl
+++ b/tests/base_rules/py_executable_base_tests.bzl
@@ -18,10 +18,12 @@
 load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
 load("@rules_testing//lib:truth.bzl", "matching")
 load("@rules_testing//lib:util.bzl", rt_util = "util")
+load("//python:py_executable_info.bzl", "PyExecutableInfo")
 load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
 load("//tests/base_rules:base_tests.bzl", "create_base_tests")
 load("//tests/base_rules:util.bzl", "WINDOWS_ATTR", pt_util = "util")
-load("//tests/support:support.bzl", "LINUX_X86_64", "WINDOWS_X86_64")
+load("//tests/support:py_executable_info_subject.bzl", "PyExecutableInfoSubject")
+load("//tests/support:support.bzl", "CC_TOOLCHAIN", "CROSSTOOL_TOP", "LINUX_X86_64", "WINDOWS_X86_64")
 
 _BuiltinPyRuntimeInfo = PyRuntimeInfo
 
@@ -49,8 +51,8 @@
             # platforms.
             "//command_line_option:build_python_zip": "true",
             "//command_line_option:cpu": "windows_x86_64",
-            "//command_line_option:crosstool_top": Label("//tests/cc:cc_toolchain_suite"),
-            "//command_line_option:extra_toolchains": [str(Label("//tests/cc:all"))],
+            "//command_line_option:crosstool_top": CROSSTOOL_TOP,
+            "//command_line_option:extra_toolchains": [CC_TOOLCHAIN],
             "//command_line_option:platforms": [WINDOWS_X86_64],
         },
         attr_values = {"target_compatible_with": target_compatible_with},
@@ -94,8 +96,8 @@
             # platforms.
             "//command_line_option:build_python_zip": "true",
             "//command_line_option:cpu": "linux_x86_64",
-            "//command_line_option:crosstool_top": Label("//tests/cc:cc_toolchain_suite"),
-            "//command_line_option:extra_toolchains": [str(Label("//tests/cc:all"))],
+            "//command_line_option:crosstool_top": CROSSTOOL_TOP,
+            "//command_line_option:extra_toolchains": [CC_TOOLCHAIN],
             "//command_line_option:platforms": [LINUX_X86_64],
         },
         attr_values = {"target_compatible_with": target_compatible_with},
@@ -132,11 +134,19 @@
         exe = ".exe"
     else:
         exe = ""
-
     env.expect.that_target(target).runfiles().contains_at_least([
         "{workspace}/{package}/{test_name}_subject" + exe,
     ])
 
+    if rp_config.enable_pystar:
+        py_exec_info = env.expect.that_target(target).provider(PyExecutableInfo, factory = PyExecutableInfoSubject.new)
+        py_exec_info.main().path().contains("_subject.py")
+        py_exec_info.interpreter_path().contains("python")
+        py_exec_info.runfiles_without_exe().contains_none_of([
+            "{workspace}/{package}/{test_name}_subject" + exe,
+            "{workspace}/{package}/{test_name}_subject",
+        ])
+
 def _test_default_main_can_be_generated(name, config):
     rt_util.helper_target(
         config.rule,
diff --git a/tests/base_rules/py_test/py_test_tests.bzl b/tests/base_rules/py_test/py_test_tests.bzl
index c77bd7e..6bd31ed 100644
--- a/tests/base_rules/py_test/py_test_tests.bzl
+++ b/tests/base_rules/py_test/py_test_tests.bzl
@@ -21,12 +21,7 @@
     "create_executable_tests",
 )
 load("//tests/base_rules:util.bzl", pt_util = "util")
-load("//tests/support:support.bzl", "LINUX_X86_64", "MAC_X86_64")
-
-# Explicit Label() calls are required so that it resolves in @rules_python
-# context instead of @rules_testing context.
-_FAKE_CC_TOOLCHAIN = Label("//tests/cc:cc_toolchain_suite")
-_FAKE_CC_TOOLCHAINS = [str(Label("//tests/cc:all"))]
+load("//tests/support:support.bzl", "CC_TOOLCHAIN", "CROSSTOOL_TOP", "LINUX_X86_64", "MAC_X86_64")
 
 # The Windows CI currently runs as root, which breaks when
 # the analysis tests try to install (but not use, because
@@ -63,8 +58,8 @@
         target = name + "_subject",
         config_settings = {
             "//command_line_option:cpu": "darwin_x86_64",
-            "//command_line_option:crosstool_top": _FAKE_CC_TOOLCHAIN,
-            "//command_line_option:extra_toolchains": _FAKE_CC_TOOLCHAINS,
+            "//command_line_option:crosstool_top": CROSSTOOL_TOP,
+            "//command_line_option:extra_toolchains": CC_TOOLCHAIN,
             "//command_line_option:platforms": [MAC_X86_64],
         },
         attr_values = _SKIP_WINDOWS,
@@ -96,8 +91,8 @@
         target = name + "_subject",
         config_settings = {
             "//command_line_option:cpu": "k8",
-            "//command_line_option:crosstool_top": _FAKE_CC_TOOLCHAIN,
-            "//command_line_option:extra_toolchains": _FAKE_CC_TOOLCHAINS,
+            "//command_line_option:crosstool_top": CROSSTOOL_TOP,
+            "//command_line_option:extra_toolchains": CC_TOOLCHAIN,
             "//command_line_option:platforms": [LINUX_X86_64],
         },
         attr_values = _SKIP_WINDOWS,
diff --git a/tests/base_rules/run_binary_zip_no_test.sh b/tests/base_rules/run_binary_zip_no_test.sh
deleted file mode 100755
index 2ee69f3..0000000
--- a/tests/base_rules/run_binary_zip_no_test.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2024 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.
-
-# --- begin runfiles.bash initialization v3 ---
-# Copy-pasted from the Bazel Bash runfiles library v3.
-set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
-source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
-  source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
-  source "$0.runfiles/$f" 2>/dev/null || \
-  source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
-  source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
-  { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
-# --- end runfiles.bash initialization v3 ---
-set +e
-
-bin=$(rlocation $BIN_RLOCATION)
-if [[ -z "$bin" ]]; then
-  echo "Unable to locate test binary: $BIN_RLOCATION"
-  exit 1
-fi
-actual=$($bin 2>&1)
-
-# How we detect if a zip file was executed from depends on which bootstrap
-# is used.
-# bootstrap_impl=script outputs RULES_PYTHON_ZIP_DIR=<somepath>
-# bootstrap_impl=system_python outputs file:.*Bazel.runfiles
-expected_pattern="Hello"
-if ! (echo "$actual" | grep "$expected_pattern" ) >/dev/null; then
-  echo "expected output to match: $expected_pattern"
-  echo "but got:\n$actual"
-  exit 1
-fi
diff --git a/tests/bootstrap_impls/BUILD.bazel b/tests/bootstrap_impls/BUILD.bazel
new file mode 100644
index 0000000..cd57715
--- /dev/null
+++ b/tests/bootstrap_impls/BUILD.bazel
@@ -0,0 +1,80 @@
+# 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.
+
+load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
+load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test", "sh_py_run_test")
+
+_SUPPORTS_BOOTSTRAP_SCRIPT = select({
+    "@platforms//os:windows": ["@platforms//:incompatible"],
+    "//conditions:default": [],
+}) if IS_BAZEL_7_OR_HIGHER else ["@platforms//:incompatible"]
+
+sh_py_run_test(
+    name = "run_binary_zip_no_test",
+    build_python_zip = "no",
+    py_src = "bin.py",
+    sh_src = "run_binary_zip_no_test.sh",
+)
+
+sh_py_run_test(
+    name = "run_binary_zip_yes_test",
+    build_python_zip = "yes",
+    py_src = "bin.py",
+    sh_src = "run_binary_zip_yes_test.sh",
+)
+
+sh_py_run_test(
+    name = "run_binary_bootstrap_script_zip_yes_test",
+    bootstrap_impl = "script",
+    build_python_zip = "yes",
+    py_src = "bin.py",
+    sh_src = "run_binary_zip_yes_test.sh",
+    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
+)
+
+sh_py_run_test(
+    name = "run_binary_bootstrap_script_zip_no_test",
+    bootstrap_impl = "script",
+    build_python_zip = "no",
+    py_src = "bin.py",
+    sh_src = "run_binary_zip_no_test.sh",
+    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
+)
+
+py_reconfig_test(
+    name = "sys_path_order_bootstrap_script_test",
+    srcs = ["sys_path_order_test.py"],
+    bootstrap_impl = "script",
+    env = {"BOOTSTRAP": "script"},
+    imports = ["./site-packages"],
+    main = "sys_path_order_test.py",
+    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
+)
+
+py_reconfig_test(
+    name = "sys_path_order_bootstrap_system_python_test",
+    srcs = ["sys_path_order_test.py"],
+    bootstrap_impl = "system_python",
+    env = {"BOOTSTRAP": "system_python"},
+    imports = ["./site-packages"],
+    main = "sys_path_order_test.py",
+)
+
+sh_py_run_test(
+    name = "inherit_pythonsafepath_env_test",
+    bootstrap_impl = "script",
+    py_src = "bin.py",
+    sh_src = "inherit_pythonsafepath_env_test.sh",
+    target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
+)
diff --git a/tests/base_rules/bin.py b/tests/bootstrap_impls/bin.py
similarity index 100%
rename from tests/base_rules/bin.py
rename to tests/bootstrap_impls/bin.py
diff --git a/tests/base_rules/inherit_pythonsafepath_env_test.sh b/tests/bootstrap_impls/inherit_pythonsafepath_env_test.sh
similarity index 100%
rename from tests/base_rules/inherit_pythonsafepath_env_test.sh
rename to tests/bootstrap_impls/inherit_pythonsafepath_env_test.sh
diff --git a/tests/bootstrap_impls/run_binary_zip_no_test.sh b/tests/bootstrap_impls/run_binary_zip_no_test.sh
new file mode 100755
index 0000000..c45cae5
--- /dev/null
+++ b/tests/bootstrap_impls/run_binary_zip_no_test.sh
@@ -0,0 +1,74 @@
+# Copyright 2024 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.
+
+# --- begin runfiles.bash initialization v3 ---
+# Copy-pasted from the Bazel Bash runfiles library v3.
+set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
+source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
+  source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
+  source "$0.runfiles/$f" 2>/dev/null || \
+  source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
+  source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
+  { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
+# --- end runfiles.bash initialization v3 ---
+set +e
+
+bin=$(rlocation $BIN_RLOCATION)
+if [[ -z "$bin" ]]; then
+  echo "Unable to locate test binary: $BIN_RLOCATION"
+  exit 1
+fi
+
+function test_invocation() {
+  actual=$($bin)
+  # How we detect if a zip file was executed from depends on which bootstrap
+  # is used.
+  # bootstrap_impl=script outputs RULES_PYTHON_ZIP_DIR=<somepath>
+  # bootstrap_impl=system_python outputs file:.*Bazel.runfiles
+  expected_pattern="Hello"
+  if ! (echo "$actual" | grep "$expected_pattern" ) >/dev/null; then
+    echo "Test case failed: $1"
+    echo "expected output to match: $expected_pattern"
+    echo "but got:\n$actual"
+    exit 1
+  fi
+}
+
+# Test invocation with RUNFILES_DIR set
+unset RUNFILES_MANIFEST_FILE
+if [[ ! -e "$RUNFILES_DIR" ]]; then
+  echo "Runfiles doesn't exist: $RUNFILES_DIR"
+  exit 1
+fi
+test_invocation "using RUNFILES_DIR"
+
+
+orig_runfiles_dir="$RUNFILES_DIR"
+unset RUNFILES_DIR
+
+# Test invocation using manifest within runfiles directory (output manifest)
+# NOTE: this file may not actually exist in our test, but that's OK; the
+# bootstrap just uses the path to find the runfiles directory.
+export RUNFILES_MANIFEST_FILE="$orig_runfiles_dir/MANIFEST"
+test_invocation "using RUNFILES_MANIFEST_FILE with output manifest"
+
+# Test invocation using manifest outside runfiles (input manifest)
+# NOTE: this file may not actually exist in our test, but that's OK; the
+# bootstrap just uses the path to find the runfiles directory.
+export RUNFILES_MANIFEST_FILE="${orig_runfiles_dir%%.runfiles}.runfiles_manifest"
+test_invocation "using RUNFILES_MANIFEST_FILE with input manifest"
+
+# Test invocation without any runfiles env vars set
+unset RUNFILES_MANIFEST_FILE
+test_invocation "using no runfiles env vars"
diff --git a/tests/base_rules/run_binary_zip_yes_test.sh b/tests/bootstrap_impls/run_binary_zip_yes_test.sh
similarity index 100%
rename from tests/base_rules/run_binary_zip_yes_test.sh
rename to tests/bootstrap_impls/run_binary_zip_yes_test.sh
diff --git a/tests/base_rules/run_zip_test.sh b/tests/bootstrap_impls/run_zip_test.sh
similarity index 100%
rename from tests/base_rules/run_zip_test.sh
rename to tests/bootstrap_impls/run_zip_test.sh
diff --git a/tests/base_rules/sys_path_order_test.py b/tests/bootstrap_impls/sys_path_order_test.py
similarity index 100%
rename from tests/base_rules/sys_path_order_test.py
rename to tests/bootstrap_impls/sys_path_order_test.py
diff --git a/tests/cc/BUILD.bazel b/tests/cc/BUILD.bazel
index 889f9e0..aa21042 100644
--- a/tests/cc/BUILD.bazel
+++ b/tests/cc/BUILD.bazel
@@ -11,140 +11,3 @@
 # 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("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite")
-load("@rules_testing//lib:util.bzl", "PREVENT_IMPLICIT_BUILDING_TAGS")
-load("//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain")
-load(":fake_cc_toolchain_config.bzl", "fake_cc_toolchain_config")
-
-package(default_visibility = ["//:__subpackages__"])
-
-exports_files(["fake_header.h"])
-
-filegroup(
-    name = "libpython",
-    srcs = ["libpython-fake.so"],
-    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
-)
-
-toolchain(
-    name = "fake_py_cc_toolchain",
-    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
-    toolchain = ":fake_py_cc_toolchain_impl",
-    toolchain_type = "@rules_python//python/cc:toolchain_type",
-)
-
-py_cc_toolchain(
-    name = "fake_py_cc_toolchain_impl",
-    headers = ":fake_headers",
-    libs = ":fake_libs",
-    python_version = "3.999",
-    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
-)
-
-# buildifier: disable=native-cc
-cc_library(
-    name = "fake_headers",
-    hdrs = ["fake_header.h"],
-    data = ["data.txt"],
-    includes = ["fake_include"],
-    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
-)
-
-# buildifier: disable=native-cc
-cc_library(
-    name = "fake_libs",
-    srcs = ["libpython3.so"],
-    data = ["libdata.txt"],
-    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
-)
-
-cc_toolchain_suite(
-    name = "cc_toolchain_suite",
-    tags = ["manual"],
-    toolchains = {
-        "darwin_x86_64": ":mac_toolchain",
-        "k8": ":linux_toolchain",
-        "windows_x86_64": ":windows_toolchain",
-    },
-)
-
-filegroup(name = "empty")
-
-cc_toolchain(
-    name = "mac_toolchain",
-    all_files = ":empty",
-    compiler_files = ":empty",
-    dwp_files = ":empty",
-    linker_files = ":empty",
-    objcopy_files = ":empty",
-    strip_files = ":empty",
-    supports_param_files = 0,
-    toolchain_config = ":mac_toolchain_config",
-    toolchain_identifier = "mac-toolchain",
-)
-
-toolchain(
-    name = "mac_toolchain_definition",
-    target_compatible_with = ["@platforms//os:macos"],
-    toolchain = ":mac_toolchain",
-    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
-)
-
-fake_cc_toolchain_config(
-    name = "mac_toolchain_config",
-    target_cpu = "darwin_x86_64",
-    toolchain_identifier = "mac-toolchain",
-)
-
-cc_toolchain(
-    name = "linux_toolchain",
-    all_files = ":empty",
-    compiler_files = ":empty",
-    dwp_files = ":empty",
-    linker_files = ":empty",
-    objcopy_files = ":empty",
-    strip_files = ":empty",
-    supports_param_files = 0,
-    toolchain_config = ":linux_toolchain_config",
-    toolchain_identifier = "linux-toolchain",
-)
-
-toolchain(
-    name = "linux_toolchain_definition",
-    target_compatible_with = ["@platforms//os:linux"],
-    toolchain = ":linux_toolchain",
-    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
-)
-
-fake_cc_toolchain_config(
-    name = "linux_toolchain_config",
-    target_cpu = "k8",
-    toolchain_identifier = "linux-toolchain",
-)
-
-cc_toolchain(
-    name = "windows_toolchain",
-    all_files = ":empty",
-    compiler_files = ":empty",
-    dwp_files = ":empty",
-    linker_files = ":empty",
-    objcopy_files = ":empty",
-    strip_files = ":empty",
-    supports_param_files = 0,
-    toolchain_config = ":windows_toolchain_config",
-    toolchain_identifier = "windows-toolchain",
-)
-
-toolchain(
-    name = "windows_toolchain_definition",
-    target_compatible_with = ["@platforms//os:windows"],
-    toolchain = ":windows_toolchain",
-    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
-)
-
-fake_cc_toolchain_config(
-    name = "windows_toolchain_config",
-    target_cpu = "windows_x86_64",
-    toolchain_identifier = "windows-toolchain",
-)
diff --git a/tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl b/tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl
index 9aeec38..8bbdace 100644
--- a/tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl
+++ b/tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl
@@ -17,7 +17,8 @@
 load("@rules_cc//cc:defs.bzl", "CcInfo")
 load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
 load("@rules_testing//lib:truth.bzl", "matching")
-load("//tests:cc_info_subject.bzl", "cc_info_subject")
+load("//tests/support:cc_info_subject.bzl", "cc_info_subject")
+load("//tests/support:support.bzl", "CC_TOOLCHAIN")
 
 _tests = []
 
@@ -27,11 +28,11 @@
         impl = _test_current_toolchain_headers_impl,
         target = "//python/cc:current_py_cc_headers",
         config_settings = {
-            "//command_line_option:extra_toolchains": [str(Label("//tests/cc:all"))],
+            "//command_line_option:extra_toolchains": [CC_TOOLCHAIN],
         },
         attrs = {
             "header": attr.label(
-                default = "//tests/cc:fake_header.h",
+                default = "//tests/support/cc_toolchains:fake_header.h",
                 allow_single_file = True,
             ),
         },
@@ -58,7 +59,7 @@
 
     # Check that the forward DefaultInfo looks correct
     env.expect.that_target(target).runfiles().contains_predicate(
-        matching.str_matches("*/cc/data.txt"),
+        matching.str_matches("*/cc_toolchains/data.txt"),
     )
 
 _tests.append(_test_current_toolchain_headers)
diff --git a/tests/cc/current_py_cc_libs/current_py_cc_libs_tests.bzl b/tests/cc/current_py_cc_libs/current_py_cc_libs_tests.bzl
index 44615ee..4a08ce7 100644
--- a/tests/cc/current_py_cc_libs/current_py_cc_libs_tests.bzl
+++ b/tests/cc/current_py_cc_libs/current_py_cc_libs_tests.bzl
@@ -17,7 +17,7 @@
 load("@rules_cc//cc:defs.bzl", "CcInfo")
 load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
 load("@rules_testing//lib:truth.bzl", "matching")
-load("//tests:cc_info_subject.bzl", "cc_info_subject")
+load("//tests/support:cc_info_subject.bzl", "cc_info_subject")
 
 _tests = []
 
@@ -27,11 +27,11 @@
         impl = _test_current_toolchain_libs_impl,
         target = "//python/cc:current_py_cc_libs",
         config_settings = {
-            "//command_line_option:extra_toolchains": [str(Label("//tests/cc:all"))],
+            "//command_line_option:extra_toolchains": [str(Label("//tests/support/cc_toolchains:all"))],
         },
         attrs = {
             "lib": attr.label(
-                default = "//tests/cc:libpython",
+                default = "//tests/support/cc_toolchains:libpython",
                 allow_single_file = True,
             ),
         },
diff --git a/tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl b/tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl
index fe83bf2..0419a04 100644
--- a/tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl
+++ b/tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl
@@ -16,26 +16,26 @@
 
 load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
 load("@rules_testing//lib:truth.bzl", "matching", "subjects")
-load("//tests:cc_info_subject.bzl", "cc_info_subject")
-load("//tests:default_info_subject.bzl", "default_info_subject")
-load("//tests:py_cc_toolchain_info_subject.bzl", "PyCcToolchainInfoSubject")
+load("//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain")
+load("//tests/support:cc_info_subject.bzl", "cc_info_subject")
+load("//tests/support:py_cc_toolchain_info_subject.bzl", "PyCcToolchainInfoSubject")
 
 _tests = []
 
-def _py_cc_toolchain_test(name):
+def _test_py_cc_toolchain(name):
     analysis_test(
         name = name,
-        impl = _py_cc_toolchain_test_impl,
-        target = "//tests/cc:fake_py_cc_toolchain_impl",
+        impl = _test_py_cc_toolchain_impl,
+        target = "//tests/support/cc_toolchains:fake_py_cc_toolchain_impl",
         attrs = {
             "header": attr.label(
-                default = "//tests/cc:fake_header.h",
+                default = "//tests/support/cc_toolchains:fake_header.h",
                 allow_single_file = True,
             ),
         },
     )
 
-def _py_cc_toolchain_test_impl(env, target):
+def _test_py_cc_toolchain_impl(env, target):
     env.expect.that_target(target).has_provider(platform_common.ToolchainInfo)
 
     toolchain = PyCcToolchainInfoSubject.new(
@@ -63,15 +63,9 @@
         matching.str_matches("*/fake_include"),
     ])
 
-    # TODO: Once subjects.default_info is available, do
-    # default_info = headers_providers.get("DefaultInfo", factory=subjects.default_info)
-    # https://github.com/bazelbuild/rules_python/issues/1297
-    default_info = default_info_subject(
-        headers_providers.get("DefaultInfo", factory = lambda v, meta: v),
-        meta = env.expect.meta.derive(expr = "default_info"),
-    )
+    default_info = headers_providers.get("DefaultInfo", factory = subjects.default_info)
     default_info.runfiles().contains_predicate(
-        matching.str_matches("*/cc/data.txt"),
+        matching.str_matches("*/cc_toolchains/data.txt"),
     )
 
     libs_providers = toolchain.libs().providers_map()
@@ -82,12 +76,31 @@
     cc_info.linking_context().linker_inputs().has_size(2)
 
     default_info = libs_providers.get("DefaultInfo", factory = subjects.default_info)
-    default_info.runfiles().contains("{workspace}/tests/cc/libdata.txt")
+    default_info.runfiles().contains("{workspace}/tests/support/cc_toolchains/libdata.txt")
     default_info.runfiles().contains_predicate(
         matching.str_matches("/libpython3."),
     )
 
-_tests.append(_py_cc_toolchain_test)
+_tests.append(_test_py_cc_toolchain)
+
+def _test_libs_optional(name):
+    py_cc_toolchain(
+        name = name + "_subject",
+        libs = None,
+        headers = "//tests/support/cc_toolchains:fake_headers",
+        python_version = "4.5",
+    )
+    analysis_test(
+        name = name,
+        target = name + "_subject",
+        impl = _test_libs_optional_impl,
+    )
+
+def _test_libs_optional_impl(env, target):
+    libs = target[platform_common.ToolchainInfo].py_cc_toolchain.libs
+    env.expect.that_bool(libs == None).equals(True)
+
+_tests.append(_test_libs_optional)
 
 def py_cc_toolchain_test_suite(name):
     test_suite(
diff --git a/tests/config_settings/construct_config_settings_tests.bzl b/tests/config_settings/construct_config_settings_tests.bzl
index b1b2e06..9e6b6e1 100644
--- a/tests/config_settings/construct_config_settings_tests.bzl
+++ b/tests/config_settings/construct_config_settings_tests.bzl
@@ -18,7 +18,6 @@
 load("@rules_testing//lib:test_suite.bzl", "test_suite")
 load("@rules_testing//lib:truth.bzl", "subjects")
 load("@rules_testing//lib:util.bzl", rt_util = "util")
-load("//python/config_settings:config_settings.bzl", "is_python_config_setting")
 
 _tests = []
 
@@ -162,21 +161,25 @@
     # We have CI runners running on a great deal of the platforms from the list below,
     # hence use all of them within tests.
     for os in ["linux", "osx", "windows"]:
-        is_python_config_setting(
+        native.config_setting(
             name = "is_python_3.11_" + os,
             constraint_values = [
                 "@platforms//os:" + os,
             ],
-            python_version = "3.11",
+            flag_values = {
+                "//python/config_settings:_python_version_major_minor": "3.11",
+            },
         )
 
     for cpu in ["s390x", "ppc", "x86_64", "aarch64"]:
-        is_python_config_setting(
+        native.config_setting(
             name = "is_python_3.11_" + cpu,
             constraint_values = [
                 "@platforms//cpu:" + cpu,
             ],
-            python_version = "3.11",
+            flag_values = {
+                "//python/config_settings:_python_version_major_minor": "3.11",
+            },
         )
 
     for (os, cpu) in [
@@ -188,13 +191,15 @@
         ("osx", "x86_64"),
         ("windows", "x86_64"),
     ]:
-        is_python_config_setting(
+        native.config_setting(
             name = "is_python_3.11_{}_{}".format(os, cpu),
             constraint_values = [
                 "@platforms//cpu:" + cpu,
                 "@platforms//os:" + os,
             ],
-            python_version = "3.11",
+            flag_values = {
+                "//python/config_settings:_python_version_major_minor": "3.11",
+            },
         )
 
     test_suite(
diff --git a/tests/config_settings/transition/multi_version_tests.bzl b/tests/config_settings/transition/multi_version_tests.bzl
index 6659da5..367837b 100644
--- a/tests/config_settings/transition/multi_version_tests.bzl
+++ b/tests/config_settings/transition/multi_version_tests.bzl
@@ -20,6 +20,7 @@
 load("//python/config_settings:transition.bzl", py_binary_transitioned = "py_binary", py_test_transitioned = "py_test")
 load("//python/private:reexports.bzl", "BuiltinPyInfo")  # buildifier: disable=bzl-visibility
 load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
+load("//tests/support:support.bzl", "CC_TOOLCHAIN")
 
 # NOTE @aignas 2024-06-04: we are using here something that is registered in the MODULE.Bazel
 # and if you find tests failing, it could be because of the toolchain resolution issues here.
@@ -87,7 +88,7 @@
         impl = impl,
         config_settings = {
             "//command_line_option:build_python_zip": build_python_zip,
-            "//command_line_option:extra_toolchains": "//tests/cc:all",
+            "//command_line_option:extra_toolchains": CC_TOOLCHAIN,
             "//command_line_option:platforms": str(Label("//tests/support:windows_x86_64")),
         },
     )
diff --git a/tests/integration/BUILD.bazel b/tests/integration/BUILD.bazel
index 8724b25..289c85d 100644
--- a/tests/integration/BUILD.bazel
+++ b/tests/integration/BUILD.bazel
@@ -19,10 +19,14 @@
 
 licenses(["notice"])
 
-_WORKSPACE_FLAGS = [
+_LEGACY_WORKSPACE_FLAGS = [
     "--noenable_bzlmod",
 ]
 
+_WORKSPACE_FLAGS = _LEGACY_WORKSPACE_FLAGS + [
+    "--enable_workspace",
+]
+
 _WORKSPACE_GAZELLE_PLUGIN_FLAGS = [
     "--override_repository=rules_python_gazelle_plugin=../../../rules_python_gazelle_plugin",
 ]
@@ -32,6 +36,24 @@
 ]
 
 default_test_runner(
+    name = "bazel_6_4_workspace_test_runner",
+    bazel_cmds = [
+        "info {}".format(" ".join(_LEGACY_WORKSPACE_FLAGS)),
+        "test {} //...".format(" ".join(_LEGACY_WORKSPACE_FLAGS)),
+    ],
+    visibility = ["//visibility:public"],
+)
+
+default_test_runner(
+    name = "bazel_6_4_workspace_test_runner_gazelle_plugin",
+    bazel_cmds = [
+        "info {}".format(" ".join(_LEGACY_WORKSPACE_FLAGS + _WORKSPACE_GAZELLE_PLUGIN_FLAGS)),
+        "test {} //...".format(" ".join(_LEGACY_WORKSPACE_FLAGS + _WORKSPACE_GAZELLE_PLUGIN_FLAGS)),
+    ],
+    visibility = ["//visibility:public"],
+)
+
+default_test_runner(
     name = "workspace_test_runner",
     bazel_cmds = [
         "info {}".format(" ".join(_WORKSPACE_FLAGS)),
diff --git a/tests/integration/ignore_root_user_error/bzlmod_test.py b/tests/integration/ignore_root_user_error/bzlmod_test.py
index 98715b3..1283415 100644
--- a/tests/integration/ignore_root_user_error/bzlmod_test.py
+++ b/tests/integration/ignore_root_user_error/bzlmod_test.py
@@ -28,8 +28,16 @@
         debug_info = json.loads(debug_path.read_bytes())
 
         expected = [
-            {"ignore_root_user_error": True, "name": "python_3_11"},
-            {"ignore_root_user_error": True, "name": "python_3_10"},
+            {
+                "ignore_root_user_error": True,
+                "module": {"is_root": False, "name": "submodule"},
+                "name": "python_3_10",
+            },
+            {
+                "ignore_root_user_error": True,
+                "module": {"is_root": True, "name": "ignore_root_user_error"},
+                "name": "python_3_11",
+            },
         ]
         self.assertCountEqual(debug_info["toolchains_registered"], expected)
 
diff --git a/tests/integration/integration_test.bzl b/tests/integration/integration_test.bzl
index 8606f66..c437953 100644
--- a/tests/integration/integration_test.bzl
+++ b/tests/integration/integration_test.bzl
@@ -16,11 +16,40 @@
 load("@bazel_binaries//:defs.bzl", "bazel_binaries")
 load(
     "@rules_bazel_integration_test//bazel_integration_test:defs.bzl",
-    "bazel_integration_tests",
+    "bazel_integration_test",
     "integration_test_utils",
 )
 load("//python:py_test.bzl", "py_test")
 
+def _test_runner(*, name, bazel_version, py_main, bzlmod, gazelle_plugin):
+    if py_main:
+        test_runner = "{}_bazel_{}_py_runner".format(name, bazel_version)
+        py_test(
+            name = test_runner,
+            srcs = [py_main],
+            main = py_main,
+            deps = [":runner_lib"],
+            # Hide from ... patterns; should only be run as part
+            # of the bazel integration test
+            tags = ["manual"],
+        )
+        return test_runner
+
+    if bazel_version.startswith("6") and not bzlmod:
+        if gazelle_plugin:
+            return "//tests/integration:bazel_6_4_workspace_test_runner_gazelle_plugin"
+        else:
+            return "//tests/integration:bazel_6_4_workspace_test_runner"
+
+    if bzlmod and gazelle_plugin:
+        return "//tests/integration:test_runner_gazelle_plugin"
+    elif bzlmod:
+        return "//tests/integration:test_runner"
+    elif gazelle_plugin:
+        return "//tests/integration:workspace_test_runner_gazelle_plugin"
+    else:
+        return "//tests/integration:workspace_test_runner"
+
 def rules_python_integration_test(
         name,
         workspace_path = None,
@@ -48,26 +77,6 @@
         **kwargs: Passed to the upstream `bazel_integration_tests` rule.
     """
     workspace_path = workspace_path or name.removesuffix("_test")
-    if py_main:
-        test_runner = name + "_py_runner"
-        py_test(
-            name = test_runner,
-            srcs = [py_main],
-            main = py_main,
-            deps = [":runner_lib"],
-            # Hide from ... patterns; should only be run as part
-            # of the bazel integration test
-            tags = ["manual"],
-        )
-    elif bzlmod:
-        if gazelle_plugin:
-            test_runner = "//tests/integration:test_runner_gazelle_plugin"
-        else:
-            test_runner = "//tests/integration:test_runner"
-    elif gazelle_plugin:
-        test_runner = "//tests/integration:workspace_test_runner_gazelle_plugin"
-    else:
-        test_runner = "//tests/integration:workspace_test_runner"
 
     # Because glob expansion happens at loading time, the bazel-* symlinks
     # in the workspaces can recursively expand to tens-of-thousands of entries,
@@ -89,27 +98,35 @@
         ],
     )
     kwargs.setdefault("size", "enormous")
-    bazel_integration_tests(
-        name = name,
-        workspace_path = workspace_path,
-        test_runner = test_runner,
-        bazel_versions = bazel_versions or bazel_binaries.versions.all,
-        workspace_files = [name + "_workspace_files"],
-        # Override the tags so that the `manual` tag isn't applied.
-        tags = (tags or []) + [
-            # These tests are very heavy weight, so much so that only a couple
-            # can be run in parallel without harming their reliability,
-            # overall runtime, and the system's stability. Unfortunately,
-            # there doesn't appear to be a way to tell Bazel to limit their
-            # concurrency, only disable it entirely with exclusive.
-            "exclusive",
-            # The default_test_runner() assumes it can write to the user's home
-            # directory for caching purposes. Give it access.
-            "no-sandbox",
-            # The CI RBE setup can't successfully run these tests remotely.
-            "no-remote-exec",
-            # A special tag is used so CI can run them as a separate job.
-            "integration-test",
-        ],
-        **kwargs
-    )
+    for bazel_version in bazel_versions or bazel_binaries.versions.all:
+        test_runner = _test_runner(
+            name = name,
+            bazel_version = bazel_version,
+            py_main = py_main,
+            bzlmod = bzlmod,
+            gazelle_plugin = gazelle_plugin,
+        )
+        bazel_integration_test(
+            name = "{}_bazel_{}".format(name, bazel_version),
+            workspace_path = workspace_path,
+            test_runner = test_runner,
+            bazel_version = bazel_version,
+            workspace_files = [name + "_workspace_files"],
+            # Override the tags so that the `manual` tag isn't applied.
+            tags = (tags or []) + [
+                # These tests are very heavy weight, so much so that only a couple
+                # can be run in parallel without harming their reliability,
+                # overall runtime, and the system's stability. Unfortunately,
+                # there doesn't appear to be a way to tell Bazel to limit their
+                # concurrency, only disable it entirely with exclusive.
+                "exclusive",
+                # The default_test_runner() assumes it can write to the user's home
+                # directory for caching purposes. Give it access.
+                "no-sandbox",
+                # The CI RBE setup can't successfully run these tests remotely.
+                "no-remote-exec",
+                # A special tag is used so CI can run them as a separate job.
+                "integration-test",
+            ],
+            **kwargs
+        )
diff --git a/tests/multiple_inputs/multiple_inputs.txt b/tests/multiple_inputs/multiple_inputs.txt
index a036c3f..e6fdcf1 100644
--- a/tests/multiple_inputs/multiple_inputs.txt
+++ b/tests/multiple_inputs/multiple_inputs.txt
@@ -10,9 +10,9 @@
     # via
     #   -r tests/multiple_inputs/requirements_2.in
     #   multiple_inputs_2 (tests/multiple_inputs/b/pyproject.toml)
-urllib3==2.0.7 \
-    --hash=sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84 \
-    --hash=sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e
+urllib3==2.2.2 \
+    --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
+    --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
     # via
     #   -r tests/multiple_inputs/requirements_1.in
     #   multiple_inputs_1 (tests/multiple_inputs/a/pyproject.toml)
diff --git a/tests/multiple_inputs/multiple_pyproject_toml.txt b/tests/multiple_inputs/multiple_pyproject_toml.txt
index b8af28a..cd9bc59 100644
--- a/tests/multiple_inputs/multiple_pyproject_toml.txt
+++ b/tests/multiple_inputs/multiple_pyproject_toml.txt
@@ -8,7 +8,7 @@
     --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
     --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
     # via multiple_inputs_2 (tests/multiple_inputs/b/pyproject.toml)
-urllib3==2.0.7 \
-    --hash=sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84 \
-    --hash=sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e
+urllib3==2.2.2 \
+    --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
+    --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
     # via multiple_inputs_1 (tests/multiple_inputs/a/pyproject.toml)
diff --git a/tests/multiple_inputs/multiple_requirements_in.txt b/tests/multiple_inputs/multiple_requirements_in.txt
index 63edfe9..19586ef 100644
--- a/tests/multiple_inputs/multiple_requirements_in.txt
+++ b/tests/multiple_inputs/multiple_requirements_in.txt
@@ -8,7 +8,7 @@
     --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
     --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
     # via -r tests/multiple_inputs/requirements_2.in
-urllib3==2.0.7 \
-    --hash=sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84 \
-    --hash=sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e
+urllib3==2.2.2 \
+    --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
+    --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
     # via -r tests/multiple_inputs/requirements_1.in
diff --git a/tests/py_exec_tools_toolchain/BUILD.bazel b/tests/py_exec_tools_toolchain/BUILD.bazel
new file mode 100644
index 0000000..092e790
--- /dev/null
+++ b/tests/py_exec_tools_toolchain/BUILD.bazel
@@ -0,0 +1,19 @@
+# Copyright 2024 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(":py_exec_tools_toolchain_tests.bzl", "py_exec_tools_toolchain_test_suite")
+
+py_exec_tools_toolchain_test_suite(
+    name = "py_exec_tools_toolchain_tests",
+)
diff --git a/tests/py_exec_tools_toolchain/py_exec_tools_toolchain_tests.bzl b/tests/py_exec_tools_toolchain/py_exec_tools_toolchain_tests.bzl
new file mode 100644
index 0000000..3be2bc3
--- /dev/null
+++ b/tests/py_exec_tools_toolchain/py_exec_tools_toolchain_tests.bzl
@@ -0,0 +1,40 @@
+# Copyright 2024 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.
+"""Starlark tests for py_exec_tools_toolchain rule."""
+
+load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
+load("@rules_testing//lib:test_suite.bzl", "test_suite")
+load("//python/private:py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain")  # buildifier: disable=bzl-visibility
+
+_tests = []
+
+def _test_disable_exec_interpreter(name):
+    py_exec_tools_toolchain(
+        name = name + "_subject",
+        exec_interpreter = "//python/private:sentinel",
+    )
+    analysis_test(
+        name = name,
+        target = name + "_subject",
+        impl = _test_disable_exec_interpreter_impl,
+    )
+
+def _test_disable_exec_interpreter_impl(env, target):
+    exec_tools = target[platform_common.ToolchainInfo].exec_tools
+    env.expect.that_bool(exec_tools.exec_interpreter == None).equals(True)
+
+_tests.append(_test_disable_exec_interpreter)
+
+def py_exec_tools_toolchain_test_suite(name):
+    test_suite(name = name, tests = _tests)
diff --git a/tests/py_runtime/py_runtime_tests.bzl b/tests/py_runtime/py_runtime_tests.bzl
index b47923d..d5a6076 100644
--- a/tests/py_runtime/py_runtime_tests.bzl
+++ b/tests/py_runtime/py_runtime_tests.bzl
@@ -20,8 +20,9 @@
 load("@rules_testing//lib:util.bzl", rt_util = "util")
 load("//python:py_runtime.bzl", "py_runtime")
 load("//python:py_runtime_info.bzl", "PyRuntimeInfo")
-load("//tests:py_runtime_info_subject.bzl", "py_runtime_info_subject")
 load("//tests/base_rules:util.bzl", br_util = "util")
+load("//tests/support:py_runtime_info_subject.bzl", "py_runtime_info_subject")
+load("//tests/support:support.bzl", "PYTHON_VERSION")
 
 _tests = []
 
@@ -528,6 +529,34 @@
 
 _tests.append(_test_interpreter_version_info_parses_values_to_struct)
 
+def _test_version_info_from_flag(name):
+    if not config.enable_pystar:
+        rt_util.skip_test(name)
+        return
+    py_runtime(
+        name = name + "_subject",
+        interpreter_version_info = None,
+        interpreter_path = "/bogus",
+    )
+    analysis_test(
+        name = name,
+        target = name + "_subject",
+        impl = _test_version_info_from_flag_impl,
+        config_settings = {
+            PYTHON_VERSION: "3.12",
+        },
+    )
+
+def _test_version_info_from_flag_impl(env, target):
+    version_info = env.expect.that_target(target).provider(PyRuntimeInfo, factory = py_runtime_info_subject).interpreter_version_info()
+    version_info.major().equals(3)
+    version_info.minor().equals(12)
+    version_info.micro().equals(None)
+    version_info.releaselevel().equals(None)
+    version_info.serial().equals(None)
+
+_tests.append(_test_version_info_from_flag)
+
 def py_runtime_test_suite(name):
     test_suite(
         name = name,
diff --git a/tests/py_runtime_pair/py_runtime_pair_tests.bzl b/tests/py_runtime_pair/py_runtime_pair_tests.bzl
index 236f1ba..e89e080 100644
--- a/tests/py_runtime_pair/py_runtime_pair_tests.bzl
+++ b/tests/py_runtime_pair/py_runtime_pair_tests.bzl
@@ -21,7 +21,8 @@
 load("//python:py_runtime.bzl", "py_runtime")
 load("//python:py_runtime_pair.bzl", "py_runtime_pair")
 load("//python/private:reexports.bzl", "BuiltinPyRuntimeInfo")  # buildifier: disable=bzl-visibility
-load("//tests:py_runtime_info_subject.bzl", "py_runtime_info_subject")
+load("//tests/support:py_runtime_info_subject.bzl", "py_runtime_info_subject")
+load("//tests/support:support.bzl", "CC_TOOLCHAIN")
 
 def _toolchain_factory(value, meta):
     return subjects.struct(
@@ -129,7 +130,7 @@
         config_settings = {
             "//command_line_option:extra_toolchains": [
                 "//tests/py_runtime_pair:{}_toolchain".format(name),
-                "//tests/cc:all",
+                CC_TOOLCHAIN,
             ],
         },
     )
diff --git a/tests/pypi/evaluate_markers/BUILD.bazel b/tests/pypi/evaluate_markers/BUILD.bazel
deleted file mode 100644
index aba9264..0000000
--- a/tests/pypi/evaluate_markers/BUILD.bazel
+++ /dev/null
@@ -1,7 +0,0 @@
-load("@bazel_skylib//rules:build_test.bzl", "build_test")
-load("@dev_pip//:requirements.bzl", "all_whl_requirements")
-
-build_test(
-    name = "all_dev_wheels",
-    targets = all_whl_requirements,
-)
diff --git a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl
index 3d4df14..a860681 100644
--- a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl
+++ b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl
@@ -87,7 +87,6 @@
 def _test_dep_selects(env):
     want = """\
 load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
-load("@rules_python//python/config_settings:config_settings.bzl", "is_python_config_setting")
 load("@rules_python//python:defs.bzl", "py_library", "py_binary")
 
 package(default_visibility = ["//visibility:public"])
@@ -158,9 +157,11 @@
     visibility = ["//visibility:public"],
 )
 
-is_python_config_setting(
+config_setting(
     name = "is_python_3.10_linux_ppc",
-    python_version = "3.10",
+    flag_values = {
+        "@rules_python//python/config_settings:_python_version_major_minor": "3.10",
+    },
     constraint_values = [
         "@platforms//cpu:ppc",
         "@platforms//os:linux",
@@ -168,16 +169,20 @@
     visibility = ["//visibility:private"],
 )
 
-is_python_config_setting(
+config_setting(
     name = "is_python_3.9_anyos_aarch64",
-    python_version = "3.9",
+    flag_values = {
+        "@rules_python//python/config_settings:_python_version_major_minor": "3.9",
+    },
     constraint_values = ["@platforms//cpu:aarch64"],
     visibility = ["//visibility:private"],
 )
 
-is_python_config_setting(
+config_setting(
     name = "is_python_3.9_linux_anyarch",
-    python_version = "3.9",
+    flag_values = {
+        "@rules_python//python/config_settings:_python_version_major_minor": "3.9",
+    },
     constraint_values = ["@platforms//os:linux"],
     visibility = ["//visibility:private"],
 )
diff --git a/tests/pypi/integration/BUILD.bazel b/tests/pypi/integration/BUILD.bazel
new file mode 100644
index 0000000..f846bfb
--- /dev/null
+++ b/tests/pypi/integration/BUILD.bazel
@@ -0,0 +1,20 @@
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+load("@dev_pip//:requirements.bzl", "all_requirements")
+load(":transitions.bzl", "transition_rule")
+
+build_test(
+    name = "all_requirements_build_test",
+    targets = all_requirements,
+)
+
+# Rule that transitions dependencies to be built from sdist
+transition_rule(
+    name = "all_requirements_from_sdist",
+    testonly = True,
+    deps = all_requirements,
+)
+
+build_test(
+    name = "all_requirements_from_sdist_build_test",
+    targets = ["all_requirements_from_sdist"],
+)
diff --git a/tests/pypi/integration/transitions.bzl b/tests/pypi/integration/transitions.bzl
new file mode 100644
index 0000000..b121446
--- /dev/null
+++ b/tests/pypi/integration/transitions.bzl
@@ -0,0 +1,24 @@
+""" Define a custom transition that sets the pip_whl flag to no """
+
+def _flag_transition_impl(_settings, _ctx):
+    return {"//python/config_settings:pip_whl": "no"}
+
+flag_transition = transition(
+    implementation = _flag_transition_impl,
+    inputs = [],
+    outputs = ["//python/config_settings:pip_whl"],
+)
+
+# Define a rule that applies the transition to dependencies
+def _transition_rule_impl(_ctx):
+    return [DefaultInfo()]
+
+transition_rule = rule(
+    implementation = _transition_rule_impl,
+    attrs = {
+        "deps": attr.label_list(cfg = flag_transition),
+        "_allowlist_function_transition": attr.label(
+            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+        ),
+    },
+)
diff --git a/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl b/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl
index aa735b8..d3c42a8 100644
--- a/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl
+++ b/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl
@@ -255,6 +255,40 @@
                 yanked = False,
             ),
         ),
+        (
+            struct(
+                attrs = [
+                    'href="1.0.0/mypy_extensions-1.0.0-py3-none-any.whl#sha256=deadbeef"',
+                ],
+                filename = "mypy_extensions-1.0.0-py3-none-any.whl",
+                url = "https://example.org/simple/mypy_extensions",
+            ),
+            struct(
+                filename = "mypy_extensions-1.0.0-py3-none-any.whl",
+                metadata_sha256 = "",
+                metadata_url = "",
+                sha256 = "deadbeef",
+                url = "https://example.org/simple/mypy_extensions/1.0.0/mypy_extensions-1.0.0-py3-none-any.whl",
+                yanked = False,
+            ),
+        ),
+        (
+            struct(
+                attrs = [
+                    'href="unknown://example.com/mypy_extensions-1.0.0-py3-none-any.whl#sha256=deadbeef"',
+                ],
+                filename = "mypy_extensions-1.0.0-py3-none-any.whl",
+                url = "https://example.org/simple/mypy_extensions",
+            ),
+            struct(
+                filename = "mypy_extensions-1.0.0-py3-none-any.whl",
+                metadata_sha256 = "",
+                metadata_url = "",
+                sha256 = "deadbeef",
+                url = "https://example.org/simple/mypy_extensions/unknown://example.com/mypy_extensions-1.0.0-py3-none-any.whl",
+                yanked = False,
+            ),
+        ),
     ]
 
     for (input, want) in tests:
diff --git a/tests/python/BUILD.bazel b/tests/python/BUILD.bazel
new file mode 100644
index 0000000..2553536
--- /dev/null
+++ b/tests/python/BUILD.bazel
@@ -0,0 +1,17 @@
+# Copyright 2024 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(":python_tests.bzl", "python_test_suite")
+
+python_test_suite(name = "python_tests")
diff --git a/tests/python/python_tests.bzl b/tests/python/python_tests.bzl
new file mode 100644
index 0000000..101313d
--- /dev/null
+++ b/tests/python/python_tests.bzl
@@ -0,0 +1,703 @@
+# Copyright 2024 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("@rules_testing//lib:test_suite.bzl", "test_suite")
+load("//python:versions.bzl", "MINOR_MAPPING")
+load("//python/private:python.bzl", "parse_modules")  # buildifier: disable=bzl-visibility
+
+_tests = []
+
+def _mock_mctx(*modules, environ = {}):
+    return struct(
+        os = struct(environ = environ),
+        modules = [
+            struct(
+                name = modules[0].name,
+                tags = modules[0].tags,
+                is_root = modules[0].is_root,
+            ),
+        ] + [
+            struct(
+                name = mod.name,
+                tags = mod.tags,
+                is_root = False,
+            )
+            for mod in modules[1:]
+        ],
+    )
+
+def _mod(*, name, toolchain = [], override = [], single_version_override = [], single_version_platform_override = [], is_root = True):
+    return struct(
+        name = name,
+        tags = struct(
+            toolchain = toolchain,
+            override = override,
+            single_version_override = single_version_override,
+            single_version_platform_override = single_version_platform_override,
+        ),
+        is_root = is_root,
+    )
+
+def _toolchain(python_version, *, is_default = False, **kwargs):
+    return struct(
+        is_default = is_default,
+        python_version = python_version,
+        **kwargs
+    )
+
+def _override(
+        auth_patterns = {},
+        available_python_versions = [],
+        base_url = "",
+        ignore_root_user_error = False,
+        minor_mapping = {},
+        netrc = "",
+        register_all_versions = False):
+    return struct(
+        auth_patterns = auth_patterns,
+        available_python_versions = available_python_versions,
+        base_url = base_url,
+        ignore_root_user_error = ignore_root_user_error,
+        minor_mapping = minor_mapping,
+        netrc = netrc,
+        register_all_versions = register_all_versions,
+    )
+
+def _single_version_override(
+        python_version = "",
+        sha256 = {},
+        urls = [],
+        patch_strip = 0,
+        patches = [],
+        strip_prefix = "python",
+        distutils_content = "",
+        distutils = None):
+    if not python_version:
+        fail("missing mandatory args: python_version ({})".format(python_version))
+
+    return struct(
+        python_version = python_version,
+        sha256 = sha256,
+        urls = urls,
+        patch_strip = patch_strip,
+        patches = patches,
+        strip_prefix = strip_prefix,
+        distutils_content = distutils_content,
+        distutils = distutils,
+    )
+
+def _single_version_platform_override(
+        coverage_tool = None,
+        patch_strip = 0,
+        patches = [],
+        platform = "",
+        python_version = "",
+        sha256 = "",
+        strip_prefix = "python",
+        urls = []):
+    if not platform or not python_version:
+        fail("missing mandatory args: platform ({}) and python_version ({})".format(platform, python_version))
+
+    return struct(
+        sha256 = sha256,
+        urls = urls,
+        strip_prefix = strip_prefix,
+        platform = platform,
+        coverage_tool = coverage_tool,
+        python_version = python_version,
+        patch_strip = patch_strip,
+        patches = patches,
+    )
+
+def _test_default(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
+        ),
+    )
+
+    # The value there should be consistent in bzlmod with the automatically
+    # calculated value Please update the MINOR_MAPPING in //python:versions.bzl
+    # when this part starts failing.
+    env.expect.that_dict(py.config.minor_mapping).contains_exactly(MINOR_MAPPING)
+    env.expect.that_collection(py.config.kwargs).has_size(0)
+    env.expect.that_collection(py.config.default.keys()).contains_exactly([
+        "base_url",
+        "ignore_root_user_error",
+        "tool_versions",
+    ])
+    env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
+    env.expect.that_str(py.default_python_version).equals("3.11")
+
+    want_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([want_toolchain])
+
+_tests.append(_test_default)
+
+def _test_default_some_module(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")], is_root = False),
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.11")
+
+    want_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([want_toolchain])
+
+_tests.append(_test_default_some_module)
+
+def _test_default_with_patch_version(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11.2")]),
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.11.2")
+
+    want_toolchain = struct(
+        name = "python_3_11_2",
+        python_version = "3.11.2",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([want_toolchain])
+
+_tests.append(_test_default_with_patch_version)
+
+def _test_default_non_rules_python(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            # NOTE @aignas 2024-09-06: the first item in the module_ctx.modules
+            # could be a non-root module, which is the case if the root module
+            # does not make any calls to the extension.
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")], is_root = False),
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.11")
+    rules_python_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([rules_python_toolchain])
+
+_tests.append(_test_default_non_rules_python)
+
+def _test_default_non_rules_python_ignore_root_user_error(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "my_module",
+                toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
+            ),
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
+        ),
+    )
+
+    env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
+    env.expect.that_str(py.default_python_version).equals("3.12")
+
+    my_module_toolchain = struct(
+        name = "python_3_12",
+        python_version = "3.12",
+        register_coverage_tool = False,
+    )
+    rules_python_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        rules_python_toolchain,
+        my_module_toolchain,
+    ]).in_order()
+
+_tests.append(_test_default_non_rules_python_ignore_root_user_error)
+
+def _test_default_non_rules_python_ignore_root_user_error_override(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "my_module",
+                toolchain = [_toolchain("3.12")],
+                override = [_override(ignore_root_user_error = True)],
+            ),
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
+        ),
+    )
+
+    env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
+    env.expect.that_str(py.default_python_version).equals("3.12")
+
+    my_module_toolchain = struct(
+        name = "python_3_12",
+        python_version = "3.12",
+        register_coverage_tool = False,
+    )
+    rules_python_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        rules_python_toolchain,
+        my_module_toolchain,
+    ]).in_order()
+
+_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)
+
+def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(name = "my_module", toolchain = [_toolchain("3.13")]),
+            _mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.13")
+    env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
+
+    my_module_toolchain = struct(
+        name = "python_3_13",
+        python_version = "3.13",
+        register_coverage_tool = False,
+    )
+    some_module_toolchain = struct(
+        name = "python_3_12",
+        python_version = "3.12",
+        register_coverage_tool = False,
+    )
+    rules_python_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        some_module_toolchain,
+        rules_python_toolchain,
+        my_module_toolchain,  # this was the only toolchain, default to that
+    ]).in_order()
+
+_tests.append(_test_default_non_rules_python_ignore_root_user_error_non_root_module)
+
+def _test_first_occurance_of_the_toolchain_wins(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(name = "my_module", toolchain = [_toolchain("3.12")]),
+            _mod(name = "some_module", toolchain = [_toolchain("3.12", configure_coverage_tool = True)]),
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
+            environ = {
+                "RULES_PYTHON_BZLMOD_DEBUG": "1",
+            },
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.12")
+
+    my_module_toolchain = struct(
+        name = "python_3_12",
+        python_version = "3.12",
+        # NOTE: coverage stays disabled even though `some_module` was
+        # configuring something else.
+        register_coverage_tool = False,
+    )
+    rules_python_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        rules_python_toolchain,
+        my_module_toolchain,  # default toolchain is last
+    ]).in_order()
+
+    env.expect.that_dict(py.debug_info).contains_exactly({
+        "toolchains_registered": [
+            {"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
+            {"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
+        ],
+    })
+
+_tests.append(_test_first_occurance_of_the_toolchain_wins)
+
+def _test_auth_overrides(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "my_module",
+                toolchain = [_toolchain("3.12")],
+                override = [
+                    _override(
+                        netrc = "/my/netrc",
+                        auth_patterns = {"foo": "bar"},
+                    ),
+                ],
+            ),
+            _mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
+        ),
+    )
+
+    env.expect.that_dict(py.config.default).contains_at_least({
+        "auth_patterns": {"foo": "bar"},
+        "ignore_root_user_error": False,
+        "netrc": "/my/netrc",
+    })
+    env.expect.that_str(py.default_python_version).equals("3.12")
+
+    my_module_toolchain = struct(
+        name = "python_3_12",
+        python_version = "3.12",
+        register_coverage_tool = False,
+    )
+    rules_python_toolchain = struct(
+        name = "python_3_11",
+        python_version = "3.11",
+        register_coverage_tool = False,
+    )
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        rules_python_toolchain,
+        my_module_toolchain,
+    ]).in_order()
+
+_tests.append(_test_auth_overrides)
+
+def _test_add_new_version(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "my_module",
+                toolchain = [_toolchain("3.13")],
+                single_version_override = [
+                    _single_version_override(
+                        python_version = "3.13.0",
+                        sha256 = {
+                            "aarch64-unknown-linux-gnu": "deadbeef",
+                        },
+                        urls = ["example.org"],
+                        patch_strip = 0,
+                        patches = [],
+                        strip_prefix = "prefix",
+                        distutils_content = "",
+                        distutils = None,
+                    ),
+                ],
+                single_version_platform_override = [
+                    _single_version_platform_override(
+                        sha256 = "deadb00f",
+                        urls = ["something.org", "else.org"],
+                        strip_prefix = "python",
+                        platform = "aarch64-unknown-linux-gnu",
+                        coverage_tool = "specific_cov_tool",
+                        python_version = "3.13.1",
+                        patch_strip = 2,
+                        patches = ["specific-patch.txt"],
+                    ),
+                ],
+                override = [
+                    _override(
+                        base_url = "",
+                        available_python_versions = ["3.12.4", "3.13.0", "3.13.1"],
+                        minor_mapping = {
+                            "3.13": "3.13.0",
+                        },
+                    ),
+                ],
+            ),
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.13")
+    env.expect.that_collection(py.config.default["tool_versions"].keys()).contains_exactly([
+        "3.12.4",
+        "3.13.0",
+        "3.13.1",
+    ])
+    env.expect.that_dict(py.config.default["tool_versions"]["3.13.0"]).contains_exactly({
+        "sha256": {"aarch64-unknown-linux-gnu": "deadbeef"},
+        "strip_prefix": {"aarch64-unknown-linux-gnu": "prefix"},
+        "url": {"aarch64-unknown-linux-gnu": ["example.org"]},
+    })
+    env.expect.that_dict(py.config.default["tool_versions"]["3.13.1"]).contains_exactly({
+        "coverage_tool": {"aarch64-unknown-linux-gnu": "specific_cov_tool"},
+        "patch_strip": {"aarch64-unknown-linux-gnu": 2},
+        "patches": {"aarch64-unknown-linux-gnu": ["specific-patch.txt"]},
+        "sha256": {"aarch64-unknown-linux-gnu": "deadb00f"},
+        "strip_prefix": {"aarch64-unknown-linux-gnu": "python"},
+        "url": {"aarch64-unknown-linux-gnu": ["something.org", "else.org"]},
+    })
+    env.expect.that_dict(py.config.minor_mapping).contains_exactly({
+        "3.13": "3.13.0",
+    })
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        struct(
+            name = "python_3_13",
+            python_version = "3.13",
+            register_coverage_tool = False,
+        ),
+    ])
+
+_tests.append(_test_add_new_version)
+
+def _test_register_all_versions(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "my_module",
+                toolchain = [_toolchain("3.13")],
+                single_version_override = [
+                    _single_version_override(
+                        python_version = "3.13.0",
+                        sha256 = {
+                            "aarch64-unknown-linux-gnu": "deadbeef",
+                        },
+                        urls = ["example.org"],
+                    ),
+                ],
+                single_version_platform_override = [
+                    _single_version_platform_override(
+                        sha256 = "deadb00f",
+                        urls = ["something.org"],
+                        platform = "aarch64-unknown-linux-gnu",
+                        python_version = "3.13.1",
+                    ),
+                ],
+                override = [
+                    _override(
+                        base_url = "",
+                        available_python_versions = ["3.12.4", "3.13.0", "3.13.1"],
+                        register_all_versions = True,
+                    ),
+                ],
+            ),
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.13")
+    env.expect.that_collection(py.config.default["tool_versions"].keys()).contains_exactly([
+        "3.12.4",
+        "3.13.0",
+        "3.13.1",
+    ])
+    env.expect.that_dict(py.config.minor_mapping).contains_exactly({
+        # The mapping is calculated automatically
+        "3.12": "3.12.4",
+        "3.13": "3.13.1",
+    })
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        struct(
+            name = name,
+            python_version = version,
+            register_coverage_tool = False,
+        )
+        for name, version in {
+            "python_3_12": "3.12",
+            "python_3_12_4": "3.12.4",
+            "python_3_13": "3.13",
+            "python_3_13_0": "3.13.0",
+            "python_3_13_1": "3.13.1",
+        }.items()
+    ])
+
+_tests.append(_test_register_all_versions)
+
+def _test_add_patches(env):
+    py = parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "my_module",
+                toolchain = [_toolchain("3.13")],
+                single_version_override = [
+                    _single_version_override(
+                        python_version = "3.13.0",
+                        sha256 = {
+                            "aarch64-apple-darwin": "deadbeef",
+                            "aarch64-unknown-linux-gnu": "deadbeef",
+                        },
+                        urls = ["example.org"],
+                        patch_strip = 1,
+                        patches = ["common.txt"],
+                        strip_prefix = "prefix",
+                        distutils_content = "",
+                        distutils = None,
+                    ),
+                ],
+                single_version_platform_override = [
+                    _single_version_platform_override(
+                        sha256 = "deadb00f",
+                        urls = ["something.org", "else.org"],
+                        strip_prefix = "python",
+                        platform = "aarch64-unknown-linux-gnu",
+                        coverage_tool = "specific_cov_tool",
+                        python_version = "3.13.0",
+                        patch_strip = 2,
+                        patches = ["specific-patch.txt"],
+                    ),
+                ],
+                override = [
+                    _override(
+                        base_url = "",
+                        available_python_versions = ["3.13.0"],
+                        minor_mapping = {
+                            "3.13": "3.13.0",
+                        },
+                    ),
+                ],
+            ),
+        ),
+    )
+
+    env.expect.that_str(py.default_python_version).equals("3.13")
+    env.expect.that_dict(py.config.default["tool_versions"]).contains_exactly({
+        "3.13.0": {
+            "coverage_tool": {"aarch64-unknown-linux-gnu": "specific_cov_tool"},
+            "patch_strip": {"aarch64-apple-darwin": 1, "aarch64-unknown-linux-gnu": 2},
+            "patches": {
+                "aarch64-apple-darwin": ["common.txt"],
+                "aarch64-unknown-linux-gnu": ["specific-patch.txt"],
+            },
+            "sha256": {"aarch64-apple-darwin": "deadbeef", "aarch64-unknown-linux-gnu": "deadb00f"},
+            "strip_prefix": {"aarch64-apple-darwin": "prefix", "aarch64-unknown-linux-gnu": "python"},
+            "url": {
+                "aarch64-apple-darwin": ["example.org"],
+                "aarch64-unknown-linux-gnu": ["something.org", "else.org"],
+            },
+        },
+    })
+    env.expect.that_dict(py.config.minor_mapping).contains_exactly({
+        "3.13": "3.13.0",
+    })
+    env.expect.that_collection(py.toolchains).contains_exactly([
+        struct(
+            name = "python_3_13",
+            python_version = "3.13",
+            register_coverage_tool = False,
+        ),
+    ])
+
+_tests.append(_test_add_patches)
+
+def _test_fail_two_overrides(env):
+    errors = []
+    parse_modules(
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "my_module",
+                toolchain = [_toolchain("3.13")],
+                override = [
+                    _override(base_url = "foo"),
+                    _override(base_url = "bar"),
+                ],
+            ),
+        ),
+        _fail = errors.append,
+    )
+    env.expect.that_collection(errors).contains_exactly([
+        "Only a single 'python.override' can be present",
+    ])
+
+_tests.append(_test_fail_two_overrides)
+
+def _test_single_version_override_errors(env):
+    for test in [
+        struct(
+            overrides = [
+                _single_version_override(python_version = "3.12.4", distutils_content = "foo"),
+                _single_version_override(python_version = "3.12.4", distutils_content = "foo"),
+            ],
+            want_error = "Only a single 'python.single_version_override' can be present for '3.12.4'",
+        ),
+        struct(
+            overrides = [
+                _single_version_override(python_version = "3.12.4+3", distutils_content = "foo"),
+            ],
+            want_error = "The 'python_version' attribute needs to specify an 'X.Y.Z' semver-compatible version, got: '3.12.4+3'",
+        ),
+    ]:
+        errors = []
+        parse_modules(
+            module_ctx = _mock_mctx(
+                _mod(
+                    name = "my_module",
+                    toolchain = [_toolchain("3.13")],
+                    single_version_override = test.overrides,
+                ),
+            ),
+            _fail = errors.append,
+        )
+        env.expect.that_collection(errors).contains_exactly([test.want_error])
+
+_tests.append(_test_single_version_override_errors)
+
+def _test_single_version_platform_override_errors(env):
+    for test in [
+        struct(
+            overrides = [
+                _single_version_platform_override(python_version = "3.12.4", platform = "foo", coverage_tool = "foo"),
+                _single_version_platform_override(python_version = "3.12.4", platform = "foo", coverage_tool = "foo"),
+            ],
+            want_error = "Only a single 'python.single_version_platform_override' can be present for '(\"3.12.4\", \"foo\")'",
+        ),
+        struct(
+            overrides = [
+                _single_version_platform_override(python_version = "3.12", platform = "foo"),
+            ],
+            want_error = "The 'python_version' attribute needs to specify an 'X.Y.Z' semver-compatible version, got: '3.12'",
+        ),
+        struct(
+            overrides = [
+                _single_version_platform_override(python_version = "3.12.1+my_build", platform = "foo"),
+            ],
+            want_error = "The 'python_version' attribute needs to specify an 'X.Y.Z' semver-compatible version, got: '3.12.1+my_build'",
+        ),
+    ]:
+        errors = []
+        parse_modules(
+            module_ctx = _mock_mctx(
+                _mod(
+                    name = "my_module",
+                    toolchain = [_toolchain("3.13")],
+                    single_version_platform_override = test.overrides,
+                ),
+            ),
+            _fail = errors.append,
+        )
+        env.expect.that_collection(errors).contains_exactly([test.want_error])
+
+_tests.append(_test_single_version_platform_override_errors)
+
+# TODO @aignas 2024-09-03: add failure tests:
+# * incorrect platform failure
+# * missing python_version failure
+
+def python_test_suite(name):
+    """Create the test suite.
+
+    Args:
+        name: the name of the test suite
+    """
+    test_suite(name = name, basic_tests = _tests)
diff --git a/tests/runtime_env_toolchain/BUILD.bazel b/tests/runtime_env_toolchain/BUILD.bazel
index 99bdbab..afc6b58 100644
--- a/tests/runtime_env_toolchain/BUILD.bazel
+++ b/tests/runtime_env_toolchain/BUILD.bazel
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:support.bzl", "CC_TOOLCHAIN")
 load(":runtime_env_toolchain_tests.bzl", "runtime_env_toolchain_test_suite")
 
 runtime_env_toolchain_test_suite(name = "runtime_env_toolchain_tests")
@@ -26,7 +27,7 @@
     extra_toolchains = [
         "//python/runtime_env_toolchains:all",
         # Necessary for RBE CI
-        "//tests/cc:all",
+        CC_TOOLCHAIN,
     ],
     main = "toolchain_runs_test.py",
     deps = ["//python/runfiles"],
diff --git a/tests/semver/BUILD.bazel b/tests/semver/BUILD.bazel
new file mode 100644
index 0000000..e12b1e5
--- /dev/null
+++ b/tests/semver/BUILD.bazel
@@ -0,0 +1,17 @@
+# Copyright 2024 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(":semver_test.bzl", "semver_test_suite")
+
+semver_test_suite(name = "semver_tests")
diff --git a/tests/semver/semver_test.bzl b/tests/semver/semver_test.bzl
new file mode 100644
index 0000000..9d13402
--- /dev/null
+++ b/tests/semver/semver_test.bzl
@@ -0,0 +1,113 @@
+# 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.
+
+""
+
+load("@rules_testing//lib:test_suite.bzl", "test_suite")
+load("//python/private:semver.bzl", "semver")  # buildifier: disable=bzl-visibility
+
+_tests = []
+
+def _test_semver_from_major(env):
+    actual = semver("3")
+    env.expect.that_int(actual.major).equals(3)
+    env.expect.that_int(actual.minor).equals(None)
+    env.expect.that_int(actual.patch).equals(None)
+    env.expect.that_str(actual.build).equals("")
+
+_tests.append(_test_semver_from_major)
+
+def _test_semver_from_major_minor_version(env):
+    actual = semver("4.9")
+    env.expect.that_int(actual.major).equals(4)
+    env.expect.that_int(actual.minor).equals(9)
+    env.expect.that_int(actual.patch).equals(None)
+    env.expect.that_str(actual.build).equals("")
+
+_tests.append(_test_semver_from_major_minor_version)
+
+def _test_semver_with_build_info(env):
+    actual = semver("1.2.3+mybuild")
+    env.expect.that_int(actual.major).equals(1)
+    env.expect.that_int(actual.minor).equals(2)
+    env.expect.that_int(actual.patch).equals(3)
+    env.expect.that_str(actual.build).equals("mybuild")
+
+_tests.append(_test_semver_with_build_info)
+
+def _test_semver_with_build_info_multiple_pluses(env):
+    actual = semver("1.2.3-rc0+build+info")
+    env.expect.that_int(actual.major).equals(1)
+    env.expect.that_int(actual.minor).equals(2)
+    env.expect.that_int(actual.patch).equals(3)
+    env.expect.that_str(actual.pre_release).equals("rc0")
+    env.expect.that_str(actual.build).equals("build+info")
+
+_tests.append(_test_semver_with_build_info_multiple_pluses)
+
+def _test_semver_alpha_beta(env):
+    actual = semver("1.2.3-alpha.beta")
+    env.expect.that_int(actual.major).equals(1)
+    env.expect.that_int(actual.minor).equals(2)
+    env.expect.that_int(actual.patch).equals(3)
+    env.expect.that_str(actual.pre_release).equals("alpha.beta")
+
+_tests.append(_test_semver_alpha_beta)
+
+def _test_semver_sort(env):
+    want = [
+        semver(item)
+        for item in [
+            # The items are sorted from lowest to highest version
+            "0.0.1",
+            "0.1.0-rc",
+            "0.1.0",
+            "0.9.11",
+            "0.9.12",
+            "1.0.0-alpha",
+            "1.0.0-alpha.1",
+            "1.0.0-alpha.beta",
+            "1.0.0-beta",
+            "1.0.0-beta.2",
+            "1.0.0-beta.11",
+            "1.0.0-rc.1",
+            "1.0.0-rc.2",
+            "1.0.0",
+            # Also handle missing minor and patch version strings
+            "2.0",
+            "3",
+            # Alphabetic comparison for different builds
+            "3.0.0+build0",
+            "3.0.0+build1",
+        ]
+    ]
+    actual = sorted(want, key = lambda x: x.key())
+    env.expect.that_collection(actual).contains_exactly(want).in_order()
+    for i, greater in enumerate(want[1:]):
+        smaller = actual[i]
+        if greater.key() <= smaller.key():
+            env.fail("Expected '{}' to be smaller than '{}', but got otherwise".format(
+                smaller.str(),
+                greater.str(),
+            ))
+
+_tests.append(_test_semver_sort)
+
+def semver_test_suite(name):
+    """Create the test suite.
+
+    Args:
+        name: the name of the test suite
+    """
+    test_suite(name = name, basic_tests = _tests)
diff --git a/tests/support/BUILD.bazel b/tests/support/BUILD.bazel
index 58c74d6..9fb5cd0 100644
--- a/tests/support/BUILD.bazel
+++ b/tests/support/BUILD.bazel
@@ -18,8 +18,6 @@
 # to force them to resolve in the proper context.
 # ====================
 
-load("//python:py_runtime.bzl", "py_runtime")
-load("//python:py_runtime_pair.bzl", "py_runtime_pair")
 load(":sh_py_run_test.bzl", "current_build_settings")
 
 package(
@@ -89,27 +87,6 @@
     ],
 )
 
-py_runtime(
-    name = "platform_runtime",
-    implementation_name = "fakepy",
-    interpreter_path = "/fake/python3.9",
-    interpreter_version_info = {
-        "major": "4",
-        "minor": "5",
-    },
-)
-
-py_runtime_pair(
-    name = "platform_runtime_pair",
-    py3_runtime = ":platform_runtime",
-)
-
-toolchain(
-    name = "platform_toolchain",
-    toolchain = ":platform_runtime_pair",
-    toolchain_type = "//python:toolchain_type",
-)
-
 current_build_settings(
     name = "current_build_settings",
 )
diff --git a/tests/cc_info_subject.bzl b/tests/support/cc_info_subject.bzl
similarity index 100%
rename from tests/cc_info_subject.bzl
rename to tests/support/cc_info_subject.bzl
diff --git a/tests/support/cc_toolchains/BUILD.bazel b/tests/support/cc_toolchains/BUILD.bazel
new file mode 100644
index 0000000..889f9e0
--- /dev/null
+++ b/tests/support/cc_toolchains/BUILD.bazel
@@ -0,0 +1,150 @@
+# 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.
+
+load("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite")
+load("@rules_testing//lib:util.bzl", "PREVENT_IMPLICIT_BUILDING_TAGS")
+load("//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain")
+load(":fake_cc_toolchain_config.bzl", "fake_cc_toolchain_config")
+
+package(default_visibility = ["//:__subpackages__"])
+
+exports_files(["fake_header.h"])
+
+filegroup(
+    name = "libpython",
+    srcs = ["libpython-fake.so"],
+    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+)
+
+toolchain(
+    name = "fake_py_cc_toolchain",
+    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+    toolchain = ":fake_py_cc_toolchain_impl",
+    toolchain_type = "@rules_python//python/cc:toolchain_type",
+)
+
+py_cc_toolchain(
+    name = "fake_py_cc_toolchain_impl",
+    headers = ":fake_headers",
+    libs = ":fake_libs",
+    python_version = "3.999",
+    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+)
+
+# buildifier: disable=native-cc
+cc_library(
+    name = "fake_headers",
+    hdrs = ["fake_header.h"],
+    data = ["data.txt"],
+    includes = ["fake_include"],
+    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+)
+
+# buildifier: disable=native-cc
+cc_library(
+    name = "fake_libs",
+    srcs = ["libpython3.so"],
+    data = ["libdata.txt"],
+    tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+)
+
+cc_toolchain_suite(
+    name = "cc_toolchain_suite",
+    tags = ["manual"],
+    toolchains = {
+        "darwin_x86_64": ":mac_toolchain",
+        "k8": ":linux_toolchain",
+        "windows_x86_64": ":windows_toolchain",
+    },
+)
+
+filegroup(name = "empty")
+
+cc_toolchain(
+    name = "mac_toolchain",
+    all_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 0,
+    toolchain_config = ":mac_toolchain_config",
+    toolchain_identifier = "mac-toolchain",
+)
+
+toolchain(
+    name = "mac_toolchain_definition",
+    target_compatible_with = ["@platforms//os:macos"],
+    toolchain = ":mac_toolchain",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+fake_cc_toolchain_config(
+    name = "mac_toolchain_config",
+    target_cpu = "darwin_x86_64",
+    toolchain_identifier = "mac-toolchain",
+)
+
+cc_toolchain(
+    name = "linux_toolchain",
+    all_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 0,
+    toolchain_config = ":linux_toolchain_config",
+    toolchain_identifier = "linux-toolchain",
+)
+
+toolchain(
+    name = "linux_toolchain_definition",
+    target_compatible_with = ["@platforms//os:linux"],
+    toolchain = ":linux_toolchain",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+fake_cc_toolchain_config(
+    name = "linux_toolchain_config",
+    target_cpu = "k8",
+    toolchain_identifier = "linux-toolchain",
+)
+
+cc_toolchain(
+    name = "windows_toolchain",
+    all_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 0,
+    toolchain_config = ":windows_toolchain_config",
+    toolchain_identifier = "windows-toolchain",
+)
+
+toolchain(
+    name = "windows_toolchain_definition",
+    target_compatible_with = ["@platforms//os:windows"],
+    toolchain = ":windows_toolchain",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+fake_cc_toolchain_config(
+    name = "windows_toolchain_config",
+    target_cpu = "windows_x86_64",
+    toolchain_identifier = "windows-toolchain",
+)
diff --git a/tests/cc/fake_cc_toolchain_config.bzl b/tests/support/cc_toolchains/fake_cc_toolchain_config.bzl
similarity index 100%
rename from tests/cc/fake_cc_toolchain_config.bzl
rename to tests/support/cc_toolchains/fake_cc_toolchain_config.bzl
diff --git a/tests/py_cc_toolchain_info_subject.bzl b/tests/support/py_cc_toolchain_info_subject.bzl
similarity index 100%
rename from tests/py_cc_toolchain_info_subject.bzl
rename to tests/support/py_cc_toolchain_info_subject.bzl
diff --git a/tests/support/py_executable_info_subject.bzl b/tests/support/py_executable_info_subject.bzl
new file mode 100644
index 0000000..97216ec
--- /dev/null
+++ b/tests/support/py_executable_info_subject.bzl
@@ -0,0 +1,70 @@
+# Copyright 2024 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.
+"""PyExecutableInfo testing subject."""
+
+load("@rules_testing//lib:truth.bzl", "subjects")
+
+def _py_executable_info_subject_new(info, *, meta):
+    """Creates a new `PyExecutableInfoSubject` for a PyExecutableInfo provider instance.
+
+    Method: PyExecutableInfoSubject.new
+
+    Args:
+        info: The PyExecutableInfo object
+        meta: ExpectMeta object.
+
+    Returns:
+        A `PyExecutableInfoSubject` struct
+    """
+
+    # buildifier: disable=uninitialized
+    public = struct(
+        # go/keep-sorted start
+        actual = info,
+        interpreter_path = lambda *a, **k: _py_executable_info_subject_interpreter_path(self, *a, **k),
+        main = lambda *a, **k: _py_executable_info_subject_main(self, *a, **k),
+        runfiles_without_exe = lambda *a, **k: _py_executable_info_subject_runfiles_without_exe(self, *a, **k),
+        # go/keep-sorted end
+    )
+    self = struct(
+        actual = info,
+        meta = meta,
+    )
+    return public
+
+def _py_executable_info_subject_interpreter_path(self):
+    """Returns a subject for `PyExecutableInfo.interpreter_path`."""
+    return subjects.str(
+        self.actual.interpreter_path,
+        meta = self.meta.derive("interpreter_path()"),
+    )
+
+def _py_executable_info_subject_main(self):
+    """Returns a subject for `PyExecutableInfo.main`."""
+    return subjects.file(
+        self.actual.main,
+        meta = self.meta.derive("main()"),
+    )
+
+def _py_executable_info_subject_runfiles_without_exe(self):
+    """Returns a subject for `PyExecutableInfo.runfiles_without_exe`."""
+    return subjects.runfiles(
+        self.actual.runfiles_without_exe,
+        meta = self.meta.derive("runfiles_without_exe()"),
+    )
+
+# buildifier: disable=name-conventions
+PyExecutableInfoSubject = struct(
+    new = _py_executable_info_subject_new,
+)
diff --git a/tests/base_rules/py_info_subject.bzl b/tests/support/py_info_subject.bzl
similarity index 100%
rename from tests/base_rules/py_info_subject.bzl
rename to tests/support/py_info_subject.bzl
diff --git a/tests/py_runtime_info_subject.bzl b/tests/support/py_runtime_info_subject.bzl
similarity index 100%
rename from tests/py_runtime_info_subject.bzl
rename to tests/support/py_runtime_info_subject.bzl
diff --git a/tests/support/py_toolchains/BUILD b/tests/support/py_toolchains/BUILD
new file mode 100644
index 0000000..185c7ae
--- /dev/null
+++ b/tests/support/py_toolchains/BUILD
@@ -0,0 +1,59 @@
+# Copyright 2024 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.
+
+# ====================
+# NOTE: tests/support/support.bzl has constants to easily refer to
+# these toolchains.
+# ====================
+
+load("//python:py_runtime.bzl", "py_runtime")
+load("//python:py_runtime_pair.bzl", "py_runtime_pair")
+load("//python/private:py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain")  # buildifier: disable=bzl-visibility
+
+# NOTE: A platform runtime is used because it doesn't include any files. This
+# makes it easier for analysis tests to verify content.
+py_runtime(
+    name = "platform_runtime",
+    implementation_name = "fakepy",
+    interpreter_path = "/fake/python3.9",
+    interpreter_version_info = {
+        "major": "4",
+        "minor": "5",
+    },
+)
+
+py_runtime_pair(
+    name = "platform_runtime_pair",
+    py3_runtime = ":platform_runtime",
+)
+
+toolchain(
+    name = "platform_toolchain",
+    toolchain = ":platform_runtime_pair",
+    toolchain_type = "//python:toolchain_type",
+)
+
+toolchain(
+    name = "exec_toolchain",
+    toolchain = ":exec_toolchain_impl",
+    toolchain_type = "//python:exec_tools_toolchain_type",
+)
+
+# An exec toolchain is explicitly defined so that the tests pass when run
+# in environments that aren't using the toolchains generated by the
+# hermetic runtimes.
+py_exec_tools_toolchain(
+    name = "exec_toolchain_impl",
+    precompiler = "//tools/precompiler:precompiler",
+)
diff --git a/tests/support/sh_py_run_test.bzl b/tests/support/sh_py_run_test.bzl
index 455f64e..32df5b8 100644
--- a/tests/support/sh_py_run_test.bzl
+++ b/tests/support/sh_py_run_test.bzl
@@ -99,7 +99,7 @@
             doc = """
 Value for the --extra_toolchains flag.
 
-NOTE: You'll likely have to also specify //tests/cc:all (or some CC toolchain)
+NOTE: You'll likely have to also specify //tests/support/cc_toolchains:all (or some CC toolchain)
 to make the RBE presubmits happy, which disable auto-detection of a CC
 toolchain.
 """,
diff --git a/tests/support/support.bzl b/tests/support/support.bzl
index a74346d..150ca7f 100644
--- a/tests/support/support.bzl
+++ b/tests/support/support.bzl
@@ -26,8 +26,9 @@
 WINDOWS = Label("//tests/support:windows")
 WINDOWS_X86_64 = Label("//tests/support:windows_x86_64")
 
-PLATFORM_TOOLCHAIN = str(Label("//tests/support:platform_toolchain"))
-CC_TOOLCHAIN = str(Label("//tests/cc:all"))
+PY_TOOLCHAINS = str(Label("//tests/support/py_toolchains:all"))
+CC_TOOLCHAIN = str(Label("//tests/support/cc_toolchains:all"))
+CROSSTOOL_TOP = Label("//tests/support/cc_toolchains:cc_toolchain_suite")
 
 # str() around Label() is necessary because rules_testing's config_settings
 # doesn't accept yet Label objects.
diff --git a/tools/precompiler/precompiler.py b/tools/precompiler/precompiler.py
index d1b1713..310f2eb 100644
--- a/tools/precompiler/precompiler.py
+++ b/tools/precompiler/precompiler.py
@@ -24,8 +24,8 @@
 
 def _create_parser() -> "argparse.Namespace":
     parser = argparse.ArgumentParser(fromfile_prefix_chars="@")
-    parser.add_argument("--invalidation_mode")
-    parser.add_argument("--optimize", type=int)
+    parser.add_argument("--invalidation_mode", default="CHECKED_HASH")
+    parser.add_argument("--optimize", type=int, default=-1)
     parser.add_argument("--python_version")
 
     parser.add_argument("--src", action="append", dest="srcs")
@@ -40,15 +40,15 @@
 
 def _compile(options: "argparse.Namespace") -> None:
     try:
-        invalidation_mode = getattr(
-            py_compile.PycInvalidationMode, options.invalidation_mode.upper()
-        )
-    except AttributeError as e:
+        invalidation_mode = py_compile.PycInvalidationMode[
+            options.invalidation_mode.upper()
+        ]
+    except KeyError as e:
         raise ValueError(
             f"Unknown PycInvalidationMode: {options.invalidation_mode}"
         ) from e
 
-    if len(options.srcs) != len(options.src_names) != len(options.pycs):
+    if not (len(options.srcs) == len(options.src_names) == len(options.pycs)):
         raise AssertionError(
             "Mismatched number of --src, --src_name, and/or --pyc args"
         )
diff --git a/tools/private/update_bzlmod_lockfiles.sh b/tools/private/update_bzlmod_lockfiles.sh
new file mode 100755
index 0000000..309d64e
--- /dev/null
+++ b/tools/private/update_bzlmod_lockfiles.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+set -euxo pipefail
+
+cd "$(dirname "$0")"/../../examples/bzlmod
+bazel mod deps
diff --git a/tools/wheelmaker.py b/tools/wheelmaker.py
index 8fa3e02..68578b8 100644
--- a/tools/wheelmaker.py
+++ b/tools/wheelmaker.py
@@ -537,9 +537,34 @@
 
         # Search for any `Requires-Dist` entries that refer to other files and
         # expand them.
+
+        def get_new_requirement_line(reqs_text, extra):
+            req = Requirement(reqs_text.strip())
+            if req.marker:
+                if extra:
+                    return f"Requires-Dist: {req.name}{req.specifier}; ({req.marker}) and {extra}"
+                else:
+                    return f"Requires-Dist: {req.name}{req.specifier}; {req.marker}"
+            else:
+                return f"Requires-Dist: {req.name}{req.specifier}; {extra}".strip(" ;")
+
         for meta_line in metadata.splitlines():
-            if not meta_line.startswith("Requires-Dist: @"):
+            if not meta_line.startswith("Requires-Dist: "):
                 continue
+
+            if not meta_line[len("Requires-Dist: ") :].startswith("@"):
+                # This is a normal requirement.
+                package, _, extra = meta_line[len("Requires-Dist: ") :].rpartition(";")
+                if not package:
+                    # This is when the package requirement does not have markers.
+                    continue
+                extra = extra.strip()
+                metadata = metadata.replace(
+                    meta_line, get_new_requirement_line(package, extra)
+                )
+                continue
+
+            # This is a requirement that refers to a file.
             file, _, extra = meta_line[len("Requires-Dist: @") :].partition(";")
             extra = extra.strip()
 
@@ -552,20 +577,7 @@
                 # Strip any comments
                 reqs_text, _, _ = reqs_text.partition("#")
 
-                req = Requirement(reqs_text.strip())
-                if req.marker:
-                    if extra:
-                        reqs.append(
-                            f"Requires-Dist: {req.name}{req.specifier}; ({req.marker}) and {extra}"
-                        )
-                    else:
-                        reqs.append(
-                            f"Requires-Dist: {req.name}{req.specifier}; {req.marker}"
-                        )
-                else:
-                    reqs.append(
-                        f"Requires-Dist: {req.name}{req.specifier}; {extra}".strip(" ;")
-                    )
+                reqs.append(get_new_requirement_line(reqs_text, extra))
 
             metadata = metadata.replace(meta_line, "\n".join(reqs))
 
diff --git a/version.bzl b/version.bzl
index 2e8fc0b..5194f30 100644
--- a/version.bzl
+++ b/version.bzl
@@ -17,7 +17,7 @@
 # against.
 # This version should be updated together with the version of Bazel
 # in .bazelversion.
-BAZEL_VERSION = "7.0.0"
+BAZEL_VERSION = "7.1.0"
 
 # NOTE: Keep in sync with .bazelci/presubmit.yml
 # This is the minimum supported bazel version, that we have some tests for.