fix(bzlmod): set the default_version to the python_version flag (#2253)

With this change we set the default value of `--python_version` when
the `python.toolchain` is used in `bzlmod` and we generate the
appropriate config settings based on the registered toolchains and
given overrides by the root module.

This means that we expect the `--python_version` to be always set to
a non-empty value under `bzlmod` and we can cleanup code which was
handling `//conditions:default` case. This also means that we can
in theory drop the requirement for `python_version` in `pip.parse`
and just setup dependencies for all packages that we find in the
`requirements.txt` file and move on. This is left as future work
by myself or anyone willing to contribute. We can also start reusing
the same `whl_library` instance for multi-platform packages because
the python version flag is always set - this will simplify the layout
and makes the feature non-experimental anymore under bzlmod.

Summary:
* Add `@pythons_hub` to the `WORKSPACE` with dummy data to make
  pythons_hub work.
* Add `MINOR_MAPPING` and `PYTHON_VERSIONS` to pythons_hub to
  generate the config settings.
* Remove handling of the default version in `pypi` code under bzlmod.

Work towards #2081, #260, #1708

---------

Co-authored-by: Richard Levasseur <rlevasseur@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b348e6..cad8784 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,11 @@
   (previously it defaulted to None).
 
 ### Fixed
+* (bzlmod) The `python.override(minor_mapping)` now merges the default and the
+  overridden versions ensuring that the resultant `minor_mapping` will always
+  have all of the python versions.
+* (bzlmod) The default value for the {obj}`--python_version` flag will now be
+  always set to the default python toolchain version value.
 * (bzlmod) correctly wire the {attr}`pip.parse.extra_pip_args` all the
   way to {obj}`whl_library`. What is more we will pass the `extra_pip_args` to
   {obj}`whl_library` for `sdist` distributions when using
diff --git a/WORKSPACE b/WORKSPACE
index 02b3b6e..ba05ec5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -41,14 +41,14 @@
 
 rules_python_internal_setup()
 
+load("@pythons_hub//:versions.bzl", "MINOR_MAPPING", "PYTHON_VERSIONS")
 load("//python:repositories.bzl", "python_register_multi_toolchains")
-load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
 
 python_register_multi_toolchains(
     name = "python",
     default_version = MINOR_MAPPING.values()[-2],
     # Integration tests verify each version, so register all of them.
-    python_versions = TOOL_VERSIONS.keys(),
+    python_versions = PYTHON_VERSIONS,
 )
 
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
diff --git a/examples/bzlmod/MODULE.bazel b/examples/bzlmod/MODULE.bazel
index 4ac8191..5504172 100644
--- a/examples/bzlmod/MODULE.bazel
+++ b/examples/bzlmod/MODULE.bazel
@@ -42,17 +42,16 @@
 python.override(
     available_python_versions = [
         "3.10.9",
+        "3.9.18",
         "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.
+    # instead of rules_python's defaulting to the latest available version,
+    # 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",
     },
 )
@@ -90,7 +89,7 @@
 # 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
 # rules based on the `python_version` arg values.
-use_repo(python, "python_3_10", "python_3_9", "python_versions")
+use_repo(python, "python_3_10", "python_3_9", "python_versions", "pythons_hub")
 
 # EXPERIMENTAL: This is experimental and may be removed without notice
 uv = use_extension("@rules_python//python/uv:extensions.bzl", "uv")
diff --git a/examples/bzlmod/MODULE.bazel.lock b/examples/bzlmod/MODULE.bazel.lock
index 604aef1..9a3eded 100644
--- a/examples/bzlmod/MODULE.bazel.lock
+++ b/examples/bzlmod/MODULE.bazel.lock
@@ -1231,7 +1231,7 @@
     },
     "@@rules_python~//python/extensions:pip.bzl%pip": {
       "general": {
-        "bzlTransitiveDigest": "W8FWi7aL0uqh7djg6csTMzkL1RB6WGRgfO/MOcbqYZI=",
+        "bzlTransitiveDigest": "vHagWYWcU/mNBRAz0GLQ3uxZZsgz/7/7i2GEWboxKd0=",
         "usagesDigest": "MChlcSw99EuW3K7OOoMcXQIdcJnEh6YmfyjJm+9mxIg=",
         "recordedFileInputs": {
           "@@other_module~//requirements_lock_3_11.txt": "a7d0061366569043d5efcf80e34a32c732679367cb3c831c4cdc606adc36d314",
@@ -2955,7 +2955,6 @@
               "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": {}
             }
@@ -4267,7 +4266,6 @@
                 "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",
@@ -6140,7 +6138,7 @@
     },
     "@@rules_python~//python/private/pypi:pip.bzl%pip_internal": {
       "general": {
-        "bzlTransitiveDigest": "9aEp4XU4yVL6sJqODxio5iSCoqf37Ro3o+Mt1izDnq8=",
+        "bzlTransitiveDigest": "qRRffSjIlTY99Raninpbkvh1elRwuufP+ZZaGb00rdo=",
         "usagesDigest": "Y8ihY+R57BAFhalrVLVdJFrpwlbsiKz9JPJ99ljF7HA=",
         "recordedFileInputs": {
           "@@rules_python~//tools/publish/requirements.txt": "031e35d03dde03ae6305fe4b3d1f58ad7bdad857379752deede0f93649991b8a",
@@ -8105,7 +8103,6 @@
                 "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",
diff --git a/examples/bzlmod/tests/BUILD.bazel b/examples/bzlmod/tests/BUILD.bazel
index 9f7aa1b..a778ca1 100644
--- a/examples/bzlmod/tests/BUILD.bazel
+++ b/examples/bzlmod/tests/BUILD.bazel
@@ -1,8 +1,8 @@
 load("@python_versions//3.10:defs.bzl", py_binary_3_10 = "py_binary", py_test_3_10 = "py_test")
 load("@python_versions//3.11:defs.bzl", py_binary_3_11 = "py_binary", py_test_3_11 = "py_test")
 load("@python_versions//3.9:defs.bzl", py_binary_3_9 = "py_binary", py_test_3_9 = "py_test")
+load("@pythons_hub//:versions.bzl", "MINOR_MAPPING")
 load("@rules_python//python:defs.bzl", "py_binary", "py_test")
-load("@rules_python//python:versions.bzl", "MINOR_MAPPING")
 load("@rules_python//python/config_settings:transition.bzl", py_versioned_binary = "py_binary", py_versioned_test = "py_test")
 
 py_binary(
diff --git a/internal_setup.bzl b/internal_setup.bzl
index 1967c0e..3029c15 100644
--- a/internal_setup.bzl
+++ b/internal_setup.bzl
@@ -22,13 +22,25 @@
 load("@rules_bazel_integration_test//bazel_integration_test:repo_defs.bzl", "bazel_binaries")
 load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
 load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS")
+load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
 load("//python/private:internal_config_repo.bzl", "internal_config_repo")  # buildifier: disable=bzl-visibility
+load("//python/private:pythons_hub.bzl", "hub_repo")  # buildifier: disable=bzl-visibility
 load("//python/private/pypi:deps.bzl", "pypi_deps")  # buildifier: disable=bzl-visibility
 
 def rules_python_internal_setup():
     """Setup for rules_python tests and tools."""
 
     internal_config_repo(name = "rules_python_internal")
+    hub_repo(
+        name = "pythons_hub",
+        minor_mapping = MINOR_MAPPING,
+        default_python_version = "",
+        toolchain_prefixes = [],
+        toolchain_python_versions = [],
+        toolchain_set_python_version_constraints = [],
+        toolchain_user_repository_names = [],
+        python_versions = sorted(TOOL_VERSIONS.keys()),
+    )
 
     # Because we don't use the pip_install rule, we have to call this to fetch its deps
     pypi_deps()
diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel
index c31d69f..9fb3957 100644
--- a/python/config_settings/BUILD.bazel
+++ b/python/config_settings/BUILD.bazel
@@ -1,5 +1,5 @@
 load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
-load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
+load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING", "PYTHON_VERSIONS")
 load(
     "//python/private:flags.bzl",
     "BootstrapImplFlag",
@@ -28,8 +28,9 @@
 
 construct_config_settings(
     name = "construct_config_settings",
+    default_version = DEFAULT_PYTHON_VERSION,
     minor_mapping = MINOR_MAPPING,
-    versions = TOOL_VERSIONS.keys(),
+    versions = PYTHON_VERSIONS,
 )
 
 string_flag(
diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel
index f128742..cb0fd5c 100644
--- a/python/private/BUILD.bazel
+++ b/python/private/BUILD.bazel
@@ -165,6 +165,8 @@
     deps = [
         ":bazel_tools_bzl",
         ":internal_config_repo_bzl",
+        ":pythons_hub_bzl",
+        "//python:versions_bzl",
         "//python/private/pypi:deps_bzl",
     ],
 )
@@ -212,6 +214,7 @@
     srcs = ["pythons_hub.bzl"],
     deps = [
         ":py_toolchain_suite_bzl",
+        ":text_util_bzl",
     ],
 )
 
diff --git a/python/private/config_settings.bzl b/python/private/config_settings.bzl
index b15d6a8..eceeb54 100644
--- a/python/private/config_settings.bzl
+++ b/python/private/config_settings.bzl
@@ -22,7 +22,7 @@
 _PYTHON_VERSION_FLAG = Label("//python/config_settings:python_version")
 _PYTHON_VERSION_MAJOR_MINOR_FLAG = Label("//python/config_settings:_python_version_major_minor")
 
-def construct_config_settings(*, name, versions, minor_mapping):  # buildifier: disable=function-docstring
+def construct_config_settings(*, name, default_version, 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
@@ -30,17 +30,14 @@
 
     Args:
         name: {type}`str` A dummy name value that is no-op for now.
+        default_version: {type}`str` the default value for the `python_version` flag.
         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.
     """
     _ = 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 = "",
+        build_setting_default = default_version,
         visibility = ["//visibility:public"],
     )
 
diff --git a/python/private/py_repositories.bzl b/python/private/py_repositories.bzl
index 8ddcb5d..ff8a638 100644
--- a/python/private/py_repositories.bzl
+++ b/python/private/py_repositories.bzl
@@ -16,8 +16,10 @@
 
 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", "MINOR_MAPPING", "TOOL_VERSIONS")
 load("//python/private/pypi:deps.bzl", "pypi_deps")
 load(":internal_config_repo.bzl", "internal_config_repo")
+load(":pythons_hub.bzl", "hub_repo")
 
 def http_archive(**kwargs):
     maybe(_http_archive, **kwargs)
@@ -32,6 +34,17 @@
         internal_config_repo,
         name = "rules_python_internal",
     )
+    maybe(
+        hub_repo,
+        name = "pythons_hub",
+        minor_mapping = MINOR_MAPPING,
+        default_python_version = "",
+        toolchain_prefixes = [],
+        toolchain_python_versions = [],
+        toolchain_set_python_version_constraints = [],
+        toolchain_user_repository_names = [],
+        python_versions = sorted(TOOL_VERSIONS.keys()),
+    )
     http_archive(
         name = "bazel_skylib",
         sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel
index 8cfd3d6..e76f9d3 100644
--- a/python/private/pypi/BUILD.bazel
+++ b/python/private/pypi/BUILD.bazel
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
-load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")
 
 package(default_visibility = ["//:__subpackages__"])
 
@@ -59,9 +58,9 @@
     srcs = ["extension.bzl"],
     deps = [
         ":attrs_bzl",
+        ":evaluate_markers_bzl",
         ":hub_repository_bzl",
         ":parse_requirements_bzl",
-        ":evaluate_markers_bzl",
         ":parse_whl_name_bzl",
         ":pip_repository_attrs_bzl",
         ":simpleapi_download_bzl",
@@ -69,12 +68,11 @@
         ":whl_repo_name_bzl",
         "//python/private:full_version_bzl",
         "//python/private:normalize_name_bzl",
-        "//python/private:version_label_bzl",
         "//python/private:semver_bzl",
+        "//python/private:version_label_bzl",
         "@bazel_features//:features",
-    ] + [
         "@pythons_hub//:interpreters_bzl",
-    ] if BZLMOD_ENABLED else [],
+    ],
 )
 
 bzl_library(
diff --git a/python/private/pypi/config_settings.bzl b/python/private/pypi/config_settings.bzl
index 9741217..9ccb646 100644
--- a/python/private/pypi/config_settings.bzl
+++ b/python/private/pypi/config_settings.bzl
@@ -109,9 +109,15 @@
 
     for python_version in [""] + python_versions:
         is_python = "is_python_{}".format(python_version or "version_unset")
-        native.alias(
+
+        # The aliases defined in @rules_python//python/config_settings may not
+        # have config settings for the versions we need, so define our own
+        # config settings instead.
+        native.config_setting(
             name = is_python,
-            actual = Label("//python/config_settings:" + is_python),
+            flag_values = {
+                Label("//python/config_settings:_python_version_major_minor"): python_version,
+            },
             visibility = visibility,
         )
 
diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl
index 712d2fe..200aa43 100644
--- a/python/private/pypi/extension.bzl
+++ b/python/private/pypi/extension.bzl
@@ -15,7 +15,7 @@
 "pip module extension for use with bzlmod"
 
 load("@bazel_features//:features.bzl", "bazel_features")
-load("@pythons_hub//:interpreters.bzl", "DEFAULT_PYTHON_VERSION", "INTERPRETER_LABELS")
+load("@pythons_hub//:interpreters.bzl", "INTERPRETER_LABELS")
 load("//python/private:auth.bzl", "AUTH_ATTRS")
 load("//python/private:normalize_name.bzl", "normalize_name")
 load("//python/private:repo_utils.bzl", "repo_utils")
@@ -506,7 +506,6 @@
                 key: json.encode(value)
                 for key, value in whl_map.items()
             },
-            default_version = _major_minor_version(DEFAULT_PYTHON_VERSION),
             packages = sorted(exposed_packages.get(hub_name, {})),
             groups = hub_group_map.get(hub_name),
         )
diff --git a/python/private/pypi/hub_repository.bzl b/python/private/pypi/hub_repository.bzl
index f589dd4..7afb616 100644
--- a/python/private/pypi/hub_repository.bzl
+++ b/python/private/pypi/hub_repository.bzl
@@ -35,8 +35,6 @@
             key: [whl_alias(**v) for v in json.decode(values)]
             for key, values in rctx.attr.whl_map.items()
         },
-        default_version = rctx.attr.default_version,
-        default_config_setting = "//_config:is_python_" + rctx.attr.default_version,
         requirement_cycles = rctx.attr.groups,
     )
     for path, contents in aliases.items():
@@ -67,13 +65,6 @@
 
 hub_repository = repository_rule(
     attrs = {
-        "default_version": attr.string(
-            mandatory = True,
-            doc = """\
-This is the default python version in the format of X.Y. This should match
-what is setup by the 'python' extension using the 'is_default = True'
-setting.""",
-        ),
         "groups": attr.string_list_dict(
             mandatory = False,
         ),
diff --git a/python/private/pypi/render_pkg_aliases.bzl b/python/private/pypi/render_pkg_aliases.bzl
index 9e5158f..0086bff 100644
--- a/python/private/pypi/render_pkg_aliases.bzl
+++ b/python/private/pypi/render_pkg_aliases.bzl
@@ -73,7 +73,6 @@
 def _render_whl_library_alias(
         *,
         name,
-        default_config_setting,
         aliases,
         target_name,
         **kwargs):
@@ -97,9 +96,6 @@
     for alias in sorted(aliases, key = lambda x: x.version):
         actual = "@{repo}//:{name}".format(repo = alias.repo, name = target_name)
         selects.setdefault(actual, []).append(alias.config_setting)
-        if alias.config_setting == default_config_setting:
-            selects[actual].append("//conditions:default")
-            no_match_error = None
 
     return render.alias(
         name = name,
@@ -121,7 +117,7 @@
         **kwargs
     )
 
-def _render_common_aliases(*, name, aliases, default_config_setting = None, group_name = None):
+def _render_common_aliases(*, name, aliases, group_name = None):
     lines = [
         """load("@bazel_skylib//lib:selects.bzl", "selects")""",
         """package(default_visibility = ["//visibility:public"])""",
@@ -131,9 +127,7 @@
     if aliases:
         config_settings = sorted([v.config_setting for v in aliases if v.config_setting])
 
-    if not config_settings or default_config_setting in config_settings:
-        pass
-    else:
+    if config_settings:
         error_msg = NO_MATCH_ERROR_MESSAGE_TEMPLATE_V2.format(
             config_settings = render.indent(
                 "\n".join(config_settings),
@@ -145,10 +139,6 @@
             error_msg = error_msg,
         ))
 
-        # This is to simplify the code in _render_whl_library_alias and to ensure
-        # that we don't pass a 'default_version' that is not in 'versions'.
-        default_config_setting = None
-
     lines.append(
         render.alias(
             name = name,
@@ -159,7 +149,6 @@
         [
             _render_whl_library_alias(
                 name = name,
-                default_config_setting = default_config_setting,
                 aliases = aliases,
                 target_name = target_name,
                 visibility = ["//_groups:__subpackages__"] if name.startswith("_") else None,
@@ -188,7 +177,7 @@
 
     return "\n\n".join(lines)
 
-def render_pkg_aliases(*, aliases, default_config_setting = None, requirement_cycles = None):
+def render_pkg_aliases(*, aliases, requirement_cycles = None):
     """Create alias declarations for each PyPI package.
 
     The aliases should be appended to the pip_repository BUILD.bazel file. These aliases
@@ -198,7 +187,6 @@
     Args:
         aliases: dict, the keys are normalized distribution names and values are the
             whl_alias instances.
-        default_config_setting: the default to be used for the aliases.
         requirement_cycles: any package groups to also add.
 
     Returns:
@@ -227,7 +215,6 @@
         "{}/BUILD.bazel".format(normalize_name(name)): _render_common_aliases(
             name = normalize_name(name),
             aliases = pkg_aliases,
-            default_config_setting = default_config_setting,
             group_name = whl_group_mapping.get(normalize_name(name)),
         ).strip()
         for name, pkg_aliases in aliases.items()
@@ -278,13 +265,12 @@
         target_platforms = target_platforms,
     )
 
-def render_multiplatform_pkg_aliases(*, aliases, default_version = None, **kwargs):
+def render_multiplatform_pkg_aliases(*, aliases, **kwargs):
     """Render the multi-platform pkg aliases.
 
     Args:
         aliases: dict[str, list(whl_alias)] A list of aliases that will be
           transformed from ones having `filename` to ones having `config_setting`.
-        default_version: str, the default python version. Defaults to None.
         **kwargs: extra arguments passed to render_pkg_aliases.
 
     Returns:
@@ -302,7 +288,6 @@
     config_setting_aliases = {
         pkg: multiplatform_whl_aliases(
             aliases = pkg_aliases,
-            default_version = default_version,
             glibc_versions = flag_versions.get("glibc_versions", []),
             muslc_versions = flag_versions.get("muslc_versions", []),
             osx_versions = flag_versions.get("osx_versions", []),
@@ -317,14 +302,13 @@
     contents["_config/BUILD.bazel"] = _render_config_settings(**flag_versions)
     return contents
 
-def multiplatform_whl_aliases(*, aliases, default_version = None, **kwargs):
+def multiplatform_whl_aliases(*, aliases, **kwargs):
     """convert a list of aliases from filename to config_setting ones.
 
     Args:
         aliases: list(whl_alias): The aliases to process. Any aliases that have
             the filename set will be converted to a list of aliases, each with
             an appropriate config_setting value.
-        default_version: string | None, the default python version to use.
         **kwargs: Extra parameters passed to get_filename_config_settings.
 
     Returns:
@@ -344,7 +328,6 @@
             filename = alias.filename,
             target_platforms = alias.target_platforms,
             python_version = alias.version,
-            python_default = default_version == alias.version,
             **kwargs
         )
 
@@ -511,8 +494,7 @@
         glibc_versions,
         muslc_versions,
         osx_versions,
-        python_version = "",
-        python_default = True):
+        python_version):
     """Get the filename config settings.
 
     Args:
@@ -522,7 +504,6 @@
         muslc_versions: list[tuple[int, int]], list of versions.
         osx_versions: list[tuple[int, int]], list of versions.
         python_version: the python version to generate the config_settings for.
-        python_default: if we should include the setting when python_version is not set.
 
     Returns:
         A tuple:
@@ -573,18 +554,9 @@
         prefixes = ["sdist"]
         suffixes = [_non_versioned_platform(p) for p in target_platforms or []]
 
-    if python_default and python_version:
-        prefixes += ["cp{}_{}".format(python_version, p) for p in prefixes]
-    elif python_version:
-        prefixes = ["cp{}_{}".format(python_version, p) for p in prefixes]
-    elif python_default:
-        pass
-    else:
-        fail("BUG: got no python_version and it is not default")
-
     versioned = {
-        ":is_{}_{}".format(p, suffix): {
-            version: ":is_{}_{}".format(p, setting)
+        ":is_cp{}_{}_{}".format(python_version, p, suffix): {
+            version: ":is_cp{}_{}_{}".format(python_version, p, setting)
             for version, setting in versions.items()
         }
         for p in prefixes
@@ -592,9 +564,9 @@
     }
 
     if suffixes or versioned:
-        return [":is_{}_{}".format(p, s) for p in prefixes for s in suffixes], versioned
+        return [":is_cp{}_{}_{}".format(python_version, p, s) for p in prefixes for s in suffixes], versioned
     else:
-        return [":is_{}".format(p) for p in prefixes], setting_supported_versions
+        return [":is_cp{}_{}".format(python_version, p) for p in prefixes], setting_supported_versions
 
 def _whl_config_setting_suffixes(
         platform_tag,
diff --git a/python/private/python.bzl b/python/private/python.bzl
index cedf39a..83bc43f 100644
--- a/python/private/python.bzl
+++ b/python/private/python.bzl
@@ -236,6 +236,7 @@
         name = "pythons_hub",
         # Last toolchain is default
         default_python_version = py.default_python_version,
+        minor_mapping = py.config.minor_mapping,
         toolchain_prefixes = [
             render.toolchain_prefix(index, toolchain.name, _TOOLCHAIN_INDEX_PAD_LENGTH)
             for index, toolchain in enumerate(py.toolchains)
@@ -493,20 +494,23 @@
         _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))
+    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()
-        }
+    minor_mapping = {
+        major_minor: max(subset)[1]
+        for major_minor, subset in versions.items()
+    }
+
+    # The following ensures that all of the versions will be present in the minor_mapping
+    minor_mapping_overrides = default.pop("minor_mapping", {})
+    for major_minor, full in minor_mapping_overrides.items():
+        minor_mapping[major_minor] = full
 
     return struct(
         kwargs = kwargs,
@@ -705,6 +709,10 @@
 "3.11": "3.11.4",
 }
 ```
+
+:::{versionchanged} 0.37.0
+The values in this mapping override the default values and do not replace them.
+:::
 """,
             default = {},
         ),
diff --git a/python/private/python_register_multi_toolchains.bzl b/python/private/python_register_multi_toolchains.bzl
index 68f5249..1c7138d 100644
--- a/python/private/python_register_multi_toolchains.bzl
+++ b/python/private/python_register_multi_toolchains.bzl
@@ -15,6 +15,10 @@
 """This file contains repository rules and macros to support toolchain registration.
 """
 
+# NOTE @aignas 2024-10-07:  we are not importing this from `@pythons_hub` because of this
+# leading to a backwards incompatible change - the `//python:repositories.bzl` is loading
+# from this file and it will cause a circular import loop and an error. If the users in
+# WORKSPACE world want to override the `minor_mapping`, they will have to pass an argument.
 load("//python:versions.bzl", "MINOR_MAPPING")
 load(":python_register_toolchains.bzl", "python_register_toolchains")
 load(":toolchains_repo.bzl", "multi_toolchain_aliases")
diff --git a/python/private/pythons_hub.bzl b/python/private/pythons_hub.bzl
index da6c80d..fdaad60 100644
--- a/python/private/pythons_hub.bzl
+++ b/python/private/pythons_hub.bzl
@@ -14,10 +14,8 @@
 
 "Repo rule used by bzlmod extension to create a repo that has a map of Python interpreters and their labels"
 
-load(
-    "//python/private:toolchains_repo.bzl",
-    "python_toolchain_build_file_content",
-)
+load(":text_util.bzl", "render")
+load(":toolchains_repo.bzl", "python_toolchain_build_file_content")
 
 def _have_same_length(*lists):
     if not lists:
@@ -34,6 +32,12 @@
     visibility = ["@rules_python//:__subpackages__"],
 )
 
+bzl_library(
+    name = "versions_bzl",
+    srcs = ["versions.bzl"],
+    visibility = ["@rules_python//:__subpackages__"],
+)
+
 {toolchains}
 """
 
@@ -82,6 +86,12 @@
     "{name}_host": Label("@{name}_host//:python"),
 """
 
+_versions_bzl_template = """
+DEFAULT_PYTHON_VERSION = "{default_python_version}"
+MINOR_MAPPING = {minor_mapping}
+PYTHON_VERSIONS = {python_versions}
+"""
+
 def _hub_repo_impl(rctx):
     # Create the various toolchain definitions and
     # write them to the BUILD file.
@@ -107,8 +117,22 @@
     rctx.file(
         "interpreters.bzl",
         _interpreters_bzl_template.format(
-            interpreter_labels = interpreter_labels,
+            # TODO @aignas 2024-09-28: before 1.0 remove the value from here
             default_python_version = rctx.attr.default_python_version,
+            interpreter_labels = interpreter_labels,
+        ),
+        executable = False,
+    )
+
+    rctx.file(
+        "versions.bzl",
+        _versions_bzl_template.format(
+            default_python_version = rctx.attr.default_python_version,
+            minor_mapping = render.dict(rctx.attr.minor_mapping),
+            python_versions = rctx.attr.python_versions or render.list(sorted({
+                v: None
+                for v in rctx.attr.toolchain_python_versions
+            })),
         ),
         executable = False,
     )
@@ -125,6 +149,14 @@
             doc = "Default Python version for the build in `X.Y` or `X.Y.Z` format.",
             mandatory = True,
         ),
+        "minor_mapping": attr.string_dict(
+            doc = "The minor mapping of the `X.Y` to `X.Y.Z` format that is used in config settings.",
+            mandatory = True,
+        ),
+        "python_versions": attr.string_list(
+            doc = "The list of python versions to include in the `interpreters.bzl` if the toolchains are not specified. Used in `WORKSPACE` builds.",
+            mandatory = False,
+        ),
         "toolchain_prefixes": attr.string_list(
             doc = "List prefixed for the toolchains",
             mandatory = True,
diff --git a/tests/config_settings/construct_config_settings_tests.bzl b/tests/config_settings/construct_config_settings_tests.bzl
index 9e6b6e1..3abedac 100644
--- a/tests/config_settings/construct_config_settings_tests.bzl
+++ b/tests/config_settings/construct_config_settings_tests.bzl
@@ -13,7 +13,7 @@
 # limitations under the License.
 """Tests for construction of Python version matching config settings."""
 
-load("@//python:versions.bzl", "MINOR_MAPPING")
+load("@pythons_hub//:versions.bzl", "MINOR_MAPPING")
 load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
 load("@rules_testing//lib:test_suite.bzl", "test_suite")
 load("@rules_testing//lib:truth.bzl", "subjects")
diff --git a/tests/pypi/config_settings/config_settings_tests.bzl b/tests/pypi/config_settings/config_settings_tests.bzl
index 87e18b4..a77fa5b 100644
--- a/tests/pypi/config_settings/config_settings_tests.bzl
+++ b/tests/pypi/config_settings/config_settings_tests.bzl
@@ -55,6 +55,8 @@
     config_settings = dict(config_settings)
     if not config_settings:
         fail("For reproducibility on different platforms, the config setting must be specified")
+    python_version, default_value = _flag.python_version("3.7.10")
+    config_settings.setdefault(python_version, default_value)
 
     analysis_test(
         name = name,
@@ -75,7 +77,7 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_sdist": "sdist",
+            "is_cp3.7_sdist": "sdist",
         },
         want = "sdist",
     )
@@ -86,7 +88,7 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_sdist": "sdist",
+            "is_cp3.7_sdist": "sdist",
         },
         config_settings = [
             _flag.platform("linux_aarch64"),
@@ -101,7 +103,7 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_sdist": "sdist",
+            "is_cp3.7_sdist": "sdist",
         },
         config_settings = [
             _flag.platform("linux_aarch64"),
@@ -118,8 +120,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py_none_any": "whl",
-            "is_sdist": "sdist",
+            "is_cp3.7_py_none_any": "whl",
+            "is_cp3.7_sdist": "sdist",
         },
         want = "whl",
     )
@@ -130,8 +132,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py_none_any": "whl",
-            "is_sdist": "sdist",
+            "is_cp3.7_py_none_any": "whl",
+            "is_cp3.7_sdist": "sdist",
         },
         config_settings = [
             _flag.platform("linux_aarch64"),
@@ -146,8 +148,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py_none_any": "whl",
-            "is_sdist": "sdist",
+            "is_cp3.7_py_none_any": "whl",
+            "is_cp3.7_sdist": "sdist",
         },
         config_settings = [
             _flag.platform("linux_aarch64"),
@@ -162,8 +164,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py3_none_any": "whl",
-            "is_py_none_any": "basic_whl",
+            "is_cp3.7_py3_none_any": "whl",
+            "is_cp3.7_py_none_any": "basic_whl",
         },
         want = "whl",
     )
@@ -174,8 +176,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py3_none_any": "whl",
-            "is_py_none_any": "basic_whl",
+            "is_cp3.7_py3_none_any": "whl",
+            "is_cp3.7_py_none_any": "basic_whl",
         },
         config_settings = [
             _flag.platform("linux_aarch64"),
@@ -190,7 +192,7 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py3_none_any": "whl",
+            "is_cp3.7_py3_none_any": "whl",
         },
         config_settings = [
             _flag.platform("linux_aarch64"),
@@ -205,8 +207,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py3_abi3_any": "abi_whl",
-            "is_py3_none_any": "whl",
+            "is_cp3.7_py3_abi3_any": "abi_whl",
+            "is_cp3.7_py3_none_any": "whl",
         },
         want = "abi_whl",
     )
@@ -217,9 +219,9 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py3_none_any": "default_whl",
-            "is_py3_none_any_linux_aarch64": "whl",
-            "is_py3_none_any_linux_x86_64": "amd64_whl",
+            "is_cp3.7_py3_none_any": "default_whl",
+            "is_cp3.7_py3_none_any_linux_aarch64": "whl",
+            "is_cp3.7_py3_none_any_linux_x86_64": "amd64_whl",
         },
         want = "whl",
     )
@@ -230,9 +232,9 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_cp3x_none_any": "cp",
-            "is_py3_abi3_any": "py3_abi3",
-            "is_py3_none_any": "py3",
+            "is_cp3.7_cp3x_none_any": "cp",
+            "is_cp3.7_py3_abi3_any": "py3_abi3",
+            "is_cp3.7_py3_none_any": "py3",
         },
         want = "cp",
     )
@@ -243,8 +245,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_cp3x_abi3_any": "cp",
-            "is_py3_abi3_any": "py3",
+            "is_cp3.7_cp3x_abi3_any": "cp",
+            "is_cp3.7_py3_abi3_any": "py3",
         },
         want = "cp",
     )
@@ -258,7 +260,6 @@
             "is_cp3.10_cp3x_none_any": "cp310",
             "is_cp3.8_cp3x_none_any": "cp38",
             "is_cp3.9_cp3x_none_any": "cp39",
-            "is_cp3x_none_any": "cp_default",
         },
         want = "cp310",
         config_settings = [
@@ -319,11 +320,11 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py3_abi3_any": "better_default_whl",
-            "is_py3_abi3_any_linux_aarch64": "better_default_any_whl",
-            "is_py3_none_any": "default_whl",
-            "is_py3_none_any_linux_aarch64": "whl",
-            "is_py3_none_linux_aarch64": "platform_whl",
+            "is_cp3.7_py3_abi3_any": "better_default_whl",
+            "is_cp3.7_py3_abi3_any_linux_aarch64": "better_default_any_whl",
+            "is_cp3.7_py3_none_any": "default_whl",
+            "is_cp3.7_py3_none_any_linux_aarch64": "whl",
+            "is_cp3.7_py3_none_linux_aarch64": "platform_whl",
         },
         want = "platform_whl",
     )
@@ -334,8 +335,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_py3_abi3_linux_aarch64": "abi3_platform",
-            "is_py3_none_linux_aarch64": "platform",
+            "is_cp3.7_py3_abi3_linux_aarch64": "abi3_platform",
+            "is_cp3.7_py3_none_linux_aarch64": "platform",
         },
         want = "abi3_platform",
     )
@@ -346,8 +347,8 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_cp3x_cp_manylinux_aarch64": "glibc",
-            "is_py3_abi3_linux_aarch64": "abi3_platform",
+            "is_cp3.7_cp3x_cp_manylinux_aarch64": "glibc",
+            "is_cp3.7_py3_abi3_linux_aarch64": "abi3_platform",
         },
         want = "glibc",
     )
@@ -358,9 +359,9 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_cp3x_cp_manylinux_2_14_aarch64": "glibc",
-            "is_cp3x_cp_manylinux_2_17_aarch64": "glibc",
-            "is_py3_abi3_linux_aarch64": "abi3_platform",
+            "is_cp3.7_cp3x_cp_manylinux_2_14_aarch64": "glibc",
+            "is_cp3.7_cp3x_cp_manylinux_2_17_aarch64": "glibc",
+            "is_cp3.7_py3_abi3_linux_aarch64": "abi3_platform",
         },
         want = "glibc",
         config_settings = [
@@ -378,8 +379,8 @@
         dist = {
             # Code using the conditions will need to construct selects, which
             # do the version matching correctly.
-            "is_cp3x_cp_manylinux_2_14_aarch64": "2_14_whl_via_2_14_branch",
-            "is_cp3x_cp_manylinux_2_17_aarch64": "2_14_whl_via_2_17_branch",
+            "is_cp3.7_cp3x_cp_manylinux_2_14_aarch64": "2_14_whl_via_2_14_branch",
+            "is_cp3.7_cp3x_cp_manylinux_2_17_aarch64": "2_14_whl_via_2_17_branch",
         },
         want = "2_14_whl_via_2_17_branch",
         config_settings = [
@@ -395,7 +396,7 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_cp3x_cp_musllinux_aarch64": "musl",
+            "is_cp3.7_cp3x_cp_musllinux_aarch64": "musl",
         },
         want = "musl",
         config_settings = [
@@ -410,7 +411,7 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_cp3x_cp_windows_x86_64": "whl",
+            "is_cp3.7_cp3x_cp_windows_x86_64": "whl",
         },
         want = "whl",
         config_settings = [
@@ -425,8 +426,8 @@
         name = name,
         dist = {
             # We prefer arch specific whls over universal
-            "is_cp3x_cp_osx_x86_64": "whl",
-            "is_cp3x_cp_osx_x86_64_universal2": "universal_whl",
+            "is_cp3.7_cp3x_cp_osx_x86_64": "whl",
+            "is_cp3.7_cp3x_cp_osx_x86_64_universal2": "universal_whl",
         },
         want = "whl",
         config_settings = [
@@ -441,7 +442,7 @@
         name = name,
         dist = {
             # We default to universal if only that exists
-            "is_cp3x_cp_osx_x86_64_universal2": "whl",
+            "is_cp3.7_cp3x_cp_osx_x86_64_universal2": "whl",
         },
         want = "whl",
         config_settings = [
@@ -456,8 +457,8 @@
         name = name,
         dist = {
             # If we prefer universal, then we use that
-            "is_cp3x_cp_osx_x86_64": "whl",
-            "is_cp3x_cp_osx_x86_64_universal2": "universal",
+            "is_cp3.7_cp3x_cp_osx_x86_64": "whl",
+            "is_cp3.7_cp3x_cp_osx_x86_64_universal2": "universal",
         },
         want = "universal",
         config_settings = [
@@ -474,7 +475,7 @@
         dist = {
             # Similarly to the libc version, the user of the config settings will have to
             # construct the select so that the version selection is correct.
-            "is_cp3x_cp_osx_10_9_x86_64": "whl",
+            "is_cp3.7_cp3x_cp_osx_10_9_x86_64": "whl",
         },
         want = "whl",
         config_settings = [
@@ -489,7 +490,7 @@
     _analysis_test(
         name = name,
         dist = {
-            "is_" + f: f
+            "is_cp3.7_" + f: f
             for f in [
                 "{py}_{abi}_{plat}".format(py = valid_py, abi = valid_abi, plat = valid_plat)
                 # we have py2.py3, py3, cp3x
@@ -528,7 +529,7 @@
 
     config_settings(
         name = "dummy",
-        python_versions = ["3.8", "3.9", "3.10"],
+        python_versions = ["3.7", "3.8", "3.9", "3.10"],
         glibc_versions = [(2, 14), (2, 17)],
         muslc_versions = [(1, 1)],
         osx_versions = [(10, 9), (11, 0)],
diff --git a/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl b/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl
index 09a0631..9de309b 100644
--- a/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl
+++ b/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl
@@ -110,7 +110,6 @@
 def _test_bzlmod_aliases(env):
     # Use this function as it is used in pip_repository
     actual = render_multiplatform_pkg_aliases(
-        default_config_setting = "//:my_config_setting",
         aliases = {
             "bar-baz": [
                 whl_alias(version = "3.2", repo = "pypi_32_bar_baz", config_setting = "//:my_config_setting"),
@@ -124,6 +123,23 @@
 
 package(default_visibility = ["//visibility:public"])
 
+_NO_MATCH_ERROR = \"\"\"\\
+No matching wheel for current configuration's Python version.
+
+The current build configuration's Python version doesn't match any of the Python
+wheels available for this wheel. This wheel supports the following Python
+configuration settings:
+    //:my_config_setting
+
+To determine the current configuration's Python version, run:
+    `bazel config <config id>` (shown further below)
+and look for
+    rules_python//python/config_settings:python_version
+
+If the value is missing, then the "default" Python version is being used,
+which has a "null" version value and will not match version constraints.
+\"\"\"
+
 alias(
     name = "bar_baz",
     actual = ":pkg",
@@ -133,11 +149,9 @@
     name = "pkg",
     actual = selects.with_or(
         {
-            (
-                "//:my_config_setting",
-                "//conditions:default",
-            ): "@pypi_32_bar_baz//:pkg",
+            "//:my_config_setting": "@pypi_32_bar_baz//:pkg",
         },
+        no_match_error = _NO_MATCH_ERROR,
     ),
 )
 
@@ -145,11 +159,9 @@
     name = "whl",
     actual = selects.with_or(
         {
-            (
-                "//:my_config_setting",
-                "//conditions:default",
-            ): "@pypi_32_bar_baz//:whl",
+            "//:my_config_setting": "@pypi_32_bar_baz//:whl",
         },
+        no_match_error = _NO_MATCH_ERROR,
     ),
 )
 
@@ -157,11 +169,9 @@
     name = "data",
     actual = selects.with_or(
         {
-            (
-                "//:my_config_setting",
-                "//conditions:default",
-            ): "@pypi_32_bar_baz//:data",
+            "//:my_config_setting": "@pypi_32_bar_baz//:data",
         },
+        no_match_error = _NO_MATCH_ERROR,
     ),
 )
 
@@ -169,11 +179,9 @@
     name = "dist_info",
     actual = selects.with_or(
         {
-            (
-                "//:my_config_setting",
-                "//conditions:default",
-            ): "@pypi_32_bar_baz//:dist_info",
+            "//:my_config_setting": "@pypi_32_bar_baz//:dist_info",
         },
+        no_match_error = _NO_MATCH_ERROR,
     ),
 )"""
 
@@ -198,7 +206,6 @@
 
 def _test_bzlmod_aliases_with_no_default_version(env):
     actual = render_multiplatform_pkg_aliases(
-        default_config_setting = None,
         aliases = {
             "bar-baz": [
                 whl_alias(
@@ -291,106 +298,8 @@
 
 _tests.append(_test_bzlmod_aliases_with_no_default_version)
 
-def _test_bzlmod_aliases_for_non_root_modules(env):
-    actual = render_pkg_aliases(
-        # NOTE @aignas 2024-01-17: if the default X.Y version coincides with the
-        # versions that are used in the root module, then this would be the same as
-        # as _test_bzlmod_aliases.
-        #
-        # However, if the root module uses a different default version than the
-        # non-root module, then we will have a no-match-error because the
-        # default_config_setting is not in the list of the versions in the
-        # whl_map.
-        default_config_setting = "//_config:is_python_3.3",
-        aliases = {
-            "bar-baz": [
-                whl_alias(version = "3.2", repo = "pypi_32_bar_baz"),
-                whl_alias(version = "3.1", repo = "pypi_31_bar_baz"),
-            ],
-        },
-    )
-
-    want_key = "bar_baz/BUILD.bazel"
-    want_content = """\
-load("@bazel_skylib//lib:selects.bzl", "selects")
-
-package(default_visibility = ["//visibility:public"])
-
-_NO_MATCH_ERROR = \"\"\"\\
-No matching wheel for current configuration's Python version.
-
-The current build configuration's Python version doesn't match any of the Python
-wheels available for this wheel. This wheel supports the following Python
-configuration settings:
-    //_config:is_python_3.1
-    //_config:is_python_3.2
-
-To determine the current configuration's Python version, run:
-    `bazel config <config id>` (shown further below)
-and look for
-    rules_python//python/config_settings:python_version
-
-If the value is missing, then the "default" Python version is being used,
-which has a "null" version value and will not match version constraints.
-\"\"\"
-
-alias(
-    name = "bar_baz",
-    actual = ":pkg",
-)
-
-alias(
-    name = "pkg",
-    actual = selects.with_or(
-        {
-            "//_config:is_python_3.1": "@pypi_31_bar_baz//:pkg",
-            "//_config:is_python_3.2": "@pypi_32_bar_baz//:pkg",
-        },
-        no_match_error = _NO_MATCH_ERROR,
-    ),
-)
-
-alias(
-    name = "whl",
-    actual = selects.with_or(
-        {
-            "//_config:is_python_3.1": "@pypi_31_bar_baz//:whl",
-            "//_config:is_python_3.2": "@pypi_32_bar_baz//:whl",
-        },
-        no_match_error = _NO_MATCH_ERROR,
-    ),
-)
-
-alias(
-    name = "data",
-    actual = selects.with_or(
-        {
-            "//_config:is_python_3.1": "@pypi_31_bar_baz//:data",
-            "//_config:is_python_3.2": "@pypi_32_bar_baz//:data",
-        },
-        no_match_error = _NO_MATCH_ERROR,
-    ),
-)
-
-alias(
-    name = "dist_info",
-    actual = selects.with_or(
-        {
-            "//_config:is_python_3.1": "@pypi_31_bar_baz//:dist_info",
-            "//_config:is_python_3.2": "@pypi_32_bar_baz//:dist_info",
-        },
-        no_match_error = _NO_MATCH_ERROR,
-    ),
-)"""
-
-    env.expect.that_collection(actual.keys()).contains_exactly([want_key])
-    env.expect.that_str(actual[want_key]).equals(want_content)
-
-_tests.append(_test_bzlmod_aliases_for_non_root_modules)
-
 def _test_aliases_are_created_for_all_wheels(env):
     actual = render_pkg_aliases(
-        default_config_setting = "//_config:is_python_3.2",
         aliases = {
             "bar": [
                 whl_alias(version = "3.1", repo = "pypi_31_bar"),
@@ -414,7 +323,6 @@
 
 def _test_aliases_with_groups(env):
     actual = render_pkg_aliases(
-        default_config_setting = "//_config:is_python_3.2",
         aliases = {
             "bar": [
                 whl_alias(version = "3.1", repo = "pypi_31_bar"),
@@ -555,13 +463,12 @@
         *,
         filename,
         want,
+        python_version,
         want_versions = {},
         target_platforms = [],
         glibc_versions = [],
         muslc_versions = [],
-        osx_versions = [],
-        python_version = "",
-        python_default = True):
+        osx_versions = []):
     got, got_default_version_settings = get_filename_config_settings(
         filename = filename,
         target_platforms = target_platforms,
@@ -569,7 +476,6 @@
         muslc_versions = muslc_versions,
         osx_versions = osx_versions,
         python_version = python_version,
-        python_default = python_default,
     )
     env.expect.that_collection(got).contains_exactly(want)
     env.expect.that_dict(got_default_version_settings).contains_exactly(want_versions)
@@ -580,42 +486,21 @@
         _test_config_settings(
             env,
             filename = "foo-0.0.1" + ext,
-            want = [":is_sdist"],
+            python_version = "3.2",
+            want = [":is_cp3.2_sdist"],
         )
 
     ext = ".zip"
     _test_config_settings(
         env,
         filename = "foo-0.0.1" + ext,
-        target_platforms = [
-            "linux_aarch64",
-        ],
-        want = [":is_sdist_linux_aarch64"],
-    )
-
-    _test_config_settings(
-        env,
-        filename = "foo-0.0.1" + ext,
         python_version = "3.2",
-        want = [
-            ":is_sdist",
-            ":is_cp3.2_sdist",
-        ],
-    )
-
-    _test_config_settings(
-        env,
-        filename = "foo-0.0.1" + ext,
-        python_version = "3.2",
-        python_default = True,
         target_platforms = [
             "linux_aarch64",
             "linux_x86_64",
         ],
         want = [
-            ":is_sdist_linux_aarch64",
             ":is_cp3.2_sdist_linux_aarch64",
-            ":is_sdist_linux_x86_64",
             ":is_cp3.2_sdist_linux_x86_64",
         ],
     )
@@ -626,25 +511,8 @@
     _test_config_settings(
         env,
         filename = "foo-0.0.1-py2.py3-none-any.whl",
-        want = [":is_py_none_any"],
-    )
-
-    _test_config_settings(
-        env,
-        filename = "foo-0.0.1-py2.py3-none-any.whl",
-        target_platforms = [
-            "linux_aarch64",
-        ],
-        want = [":is_py_none_any_linux_aarch64"],
-    )
-
-    _test_config_settings(
-        env,
-        filename = "foo-0.0.1-py2.py3-none-any.whl",
         python_version = "3.2",
-        python_default = True,
         want = [
-            ":is_py_none_any",
             ":is_cp3.2_py_none_any",
         ],
     )
@@ -653,13 +521,10 @@
         env,
         filename = "foo-0.0.1-py2.py3-none-any.whl",
         python_version = "3.2",
-        python_default = False,
         target_platforms = [
             "osx_x86_64",
         ],
-        want = [
-            ":is_cp3.2_py_none_any_osx_x86_64",
-        ],
+        want = [":is_cp3.2_py_none_any_osx_x86_64"],
     )
 
 _tests.append(_test_py2_py3_none_any)
@@ -668,14 +533,16 @@
     _test_config_settings(
         env,
         filename = "foo-0.0.1-py3-none-any.whl",
-        want = [":is_py3_none_any"],
+        python_version = "3.1",
+        want = [":is_cp3.1_py3_none_any"],
     )
 
     _test_config_settings(
         env,
         filename = "foo-0.0.1-py3-none-any.whl",
+        python_version = "3.1",
         target_platforms = ["linux_x86_64"],
-        want = [":is_py3_none_any_linux_x86_64"],
+        want = [":is_cp3.1_py3_none_any_linux_x86_64"],
     )
 
 _tests.append(_test_py3_none_any)
@@ -684,19 +551,20 @@
     _test_config_settings(
         env,
         filename = "foo-0.0.1-py3-none-macosx_10_9_universal2.whl",
+        python_version = "3.1",
         osx_versions = [
             (10, 9),
             (11, 0),
         ],
         want = [],
         want_versions = {
-            ":is_py3_none_osx_aarch64_universal2": {
-                (10, 9): ":is_py3_none_osx_10_9_aarch64_universal2",
-                (11, 0): ":is_py3_none_osx_11_0_aarch64_universal2",
+            ":is_cp3.1_py3_none_osx_aarch64_universal2": {
+                (10, 9): ":is_cp3.1_py3_none_osx_10_9_aarch64_universal2",
+                (11, 0): ":is_cp3.1_py3_none_osx_11_0_aarch64_universal2",
             },
-            ":is_py3_none_osx_x86_64_universal2": {
-                (10, 9): ":is_py3_none_osx_10_9_x86_64_universal2",
-                (11, 0): ":is_py3_none_osx_11_0_x86_64_universal2",
+            ":is_cp3.1_py3_none_osx_x86_64_universal2": {
+                (10, 9): ":is_cp3.1_py3_none_osx_10_9_x86_64_universal2",
+                (11, 0): ":is_cp3.1_py3_none_osx_11_0_x86_64_universal2",
             },
         },
     )
@@ -707,20 +575,8 @@
     _test_config_settings(
         env,
         filename = "foo-0.0.1-cp37-abi3-linux_x86_64.whl",
-        want = [
-            ":is_cp3x_abi3_linux_x86_64",
-        ],
-    )
-
-    _test_config_settings(
-        env,
-        filename = "foo-0.0.1-cp37-abi3-linux_x86_64.whl",
-        python_version = "3.2",
-        python_default = True,
-        want = [
-            ":is_cp3x_abi3_linux_x86_64",
-            ":is_cp3.2_cp3x_abi3_linux_x86_64",
-        ],
+        python_version = "3.7",
+        want = [":is_cp3.7_cp3x_abi3_linux_x86_64"],
     )
 
 _tests.append(_test_cp37_abi3_linux_x86_64)
@@ -729,9 +585,8 @@
     _test_config_settings(
         env,
         filename = "foo-0.0.1-cp37-abi3-windows_x86_64.whl",
-        want = [
-            ":is_cp3x_abi3_windows_x86_64",
-        ],
+        python_version = "3.7",
+        want = [":is_cp3.7_cp3x_abi3_windows_x86_64"],
     )
 
 _tests.append(_test_cp37_abi3_windows_x86_64)
@@ -740,6 +595,7 @@
     _test_config_settings(
         env,
         filename = "foo-0.0.1-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl",
+        python_version = "3.7",
         glibc_versions = [
             (2, 16),
             (2, 17),
@@ -747,9 +603,9 @@
         ],
         want = [],
         want_versions = {
-            ":is_cp3x_abi3_manylinux_x86_64": {
-                (2, 17): ":is_cp3x_abi3_manylinux_2_17_x86_64",
-                (2, 18): ":is_cp3x_abi3_manylinux_2_18_x86_64",
+            ":is_cp3.7_cp3x_abi3_manylinux_x86_64": {
+                (2, 17): ":is_cp3.7_cp3x_abi3_manylinux_2_17_x86_64",
+                (2, 18): ":is_cp3.7_cp3x_abi3_manylinux_2_18_x86_64",
             },
         },
     )
@@ -761,6 +617,7 @@
     _test_config_settings(
         env,
         filename = "foo-0.0.1-cp37-cp37-manylinux_2_17_arm64.musllinux_1_1_arm64.whl",
+        python_version = "3.7",
         glibc_versions = [
             (2, 16),
             (2, 17),
@@ -771,12 +628,12 @@
         ],
         want = [],
         want_versions = {
-            ":is_cp3x_cp_manylinux_aarch64": {
-                (2, 17): ":is_cp3x_cp_manylinux_2_17_aarch64",
-                (2, 18): ":is_cp3x_cp_manylinux_2_18_aarch64",
+            ":is_cp3.7_cp3x_cp_manylinux_aarch64": {
+                (2, 17): ":is_cp3.7_cp3x_cp_manylinux_2_17_aarch64",
+                (2, 18): ":is_cp3.7_cp3x_cp_manylinux_2_18_aarch64",
             },
-            ":is_cp3x_cp_musllinux_aarch64": {
-                (1, 1): ":is_cp3x_cp_musllinux_1_1_aarch64",
+            ":is_cp3.7_cp3x_cp_musllinux_aarch64": {
+                (1, 1): ":is_cp3.7_cp3x_cp_musllinux_1_1_aarch64",
             },
         },
     )
@@ -785,7 +642,7 @@
 
 def _test_multiplatform_whl_aliases_empty(env):
     # Check that we still work with an empty requirements.txt
-    got = multiplatform_whl_aliases(aliases = [], default_version = None)
+    got = multiplatform_whl_aliases(aliases = [])
     env.expect.that_collection(got).contains_exactly([])
 
 _tests.append(_test_multiplatform_whl_aliases_empty)
@@ -798,7 +655,7 @@
             version = "3.1",
         ),
     ]
-    got = multiplatform_whl_aliases(aliases = aliases, default_version = None)
+    got = multiplatform_whl_aliases(aliases = aliases)
     env.expect.that_collection(got).contains_exactly(aliases)
 
 _tests.append(_test_multiplatform_whl_aliases_nofilename)
@@ -827,7 +684,6 @@
     ]
     got = multiplatform_whl_aliases(
         aliases = aliases,
-        default_version = "3.1",
         glibc_versions = [],
         muslc_versions = [],
         osx_versions = [],
@@ -837,9 +693,6 @@
         whl_alias(config_setting = "//_config:is_cp3.1_py3_none_any_linux_aarch64", repo = "foo-0.0.2", version = "3.1"),
         whl_alias(config_setting = "//_config:is_cp3.1_py3_none_any_linux_x86_64", repo = "foo-0.0.2", version = "3.1"),
         whl_alias(config_setting = "//_config:is_cp3.2_py3_none_any", repo = "foo-py3-0.0.3", version = "3.2"),
-        whl_alias(config_setting = "//_config:is_py3_none_any", repo = "foo-py3-0.0.1", version = "3.1"),
-        whl_alias(config_setting = "//_config:is_py3_none_any_linux_aarch64", repo = "foo-0.0.2", version = "3.1"),
-        whl_alias(config_setting = "//_config:is_py3_none_any_linux_x86_64", repo = "foo-0.0.2", version = "3.1"),
     ]
     env.expect.that_collection(got).contains_exactly(want)
 
@@ -865,7 +718,6 @@
     ]
     got = multiplatform_whl_aliases(
         aliases = aliases,
-        default_version = None,
         glibc_versions = [(2, 17), (2, 18)],
         muslc_versions = [(1, 1), (1, 2)],
         osx_versions = [],
@@ -931,7 +783,6 @@
 
                 got_aliases = multiplatform_whl_aliases(
                     aliases = aliases,
-                    default_version = None,
                     glibc_versions = kwargs.get("glibc_versions", []),
                     muslc_versions = kwargs.get("muslc_versions", []),
                     osx_versions = kwargs.get("osx_versions", []),
diff --git a/tests/python/python_tests.bzl b/tests/python/python_tests.bzl
index 101313d..4050430 100644
--- a/tests/python/python_tests.bzl
+++ b/tests/python/python_tests.bzl
@@ -14,8 +14,8 @@
 
 ""
 
+load("@pythons_hub//:versions.bzl", "MINOR_MAPPING")
 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 = []
@@ -451,6 +451,7 @@
         "url": {"aarch64-unknown-linux-gnu": ["something.org", "else.org"]},
     })
     env.expect.that_dict(py.config.minor_mapping).contains_exactly({
+        "3.12": "3.12.4",  # The `minor_mapping` will be overriden only for the missing keys
         "3.13": "3.13.0",
     })
     env.expect.that_collection(py.toolchains).contains_exactly([