refactor(internal): make the usage of MINOR_MAPPING variable explicit in full_version (#2219)
This PR just makes the `MINOR_MAPPING` overridable and explicit in
many macros/rules that we own. Even though technically new API is
exposed, I am not sure if it is possible to use it and I am not sure
if we should advertise it.
Explicit minor_mapping results in easier wiring of `python.override`
`bzlmod` extension tag class planned for #2081.
diff --git a/examples/bzlmod/MODULE.bazel.lock b/examples/bzlmod/MODULE.bazel.lock
index d747ed3..af31a12 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": "vzdh1M3LRVqyF10AVUO1+FOE7CZwlZaFT+7RgQ4OKXg=",
+ "bzlTransitiveDigest": "QxV2PiqVV2B5LpnSrlzLgYyKNbUEXyVc1u+ahMrefws=",
"usagesDigest": "MChlcSw99EuW3K7OOoMcXQIdcJnEh6YmfyjJm+9mxIg=",
"recordedFileInputs": {
"@@other_module~//requirements_lock_3_11.txt": "a7d0061366569043d5efcf80e34a32c732679367cb3c831c4cdc606adc36d314",
@@ -6140,7 +6140,7 @@
},
"@@rules_python~//python/private/pypi:pip.bzl%pip_internal": {
"general": {
- "bzlTransitiveDigest": "TgRegkReKbGzK4VxYz9up697gcf5Q8NFuZYnZHryck8=",
+ "bzlTransitiveDigest": "P0W31OsSgVVNQ3oRHHFiRWK7NLBLyI+KbQQBCPhou7w=",
"usagesDigest": "Y8ihY+R57BAFhalrVLVdJFrpwlbsiKz9JPJ99ljF7HA=",
"recordedFileInputs": {
"@@rules_python~//tools/publish/requirements.txt": "031e35d03dde03ae6305fe4b3d1f58ad7bdad857379752deede0f93649991b8a",
diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel
index 7b913df..3d23614 100644
--- a/python/private/BUILD.bazel
+++ b/python/private/BUILD.bazel
@@ -114,7 +114,6 @@
bzl_library(
name = "full_version_bzl",
srcs = ["full_version.bzl"],
- deps = ["//python:versions_bzl"],
)
bzl_library(
@@ -132,6 +131,7 @@
name = "python_bzl",
srcs = ["python.bzl"],
deps = [
+ ":full_version_bzl",
":pythons_hub_bzl",
":repo_utils_bzl",
":toolchains_repo_bzl",
@@ -164,7 +164,6 @@
deps = [
":full_version_bzl",
":py_toolchain_suite_bzl",
- "//python:versions_bzl",
],
)
diff --git a/python/private/config_settings.bzl b/python/private/config_settings.bzl
index 99b8b94..301a97b 100644
--- a/python/private/config_settings.bzl
+++ b/python/private/config_settings.bzl
@@ -27,14 +27,15 @@
micro, _, s = s.partition(".")
return (int(major), int(minor), int(micro))
-def _flag_values(python_versions):
+def _flag_values(*, python_versions, minor_mapping):
"""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
+ python_versions: {type}`list[str]` X.Y.Z` python versions.
+ minor_mapping: {type}`dict[str, str]` `X.Y` to `X.Y.Z` mapping.
Returns:
A `map[str, list[str]]`. Each key is a python_version flag value. Each value
@@ -61,13 +62,13 @@
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]
+ # 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())
+VERSION_FLAG_VALUES = _flag_values(python_versions = TOOL_VERSIONS.keys(), minor_mapping = MINOR_MAPPING)
def is_python_config_setting(name, *, python_version, reuse_conditions = None, **kwargs):
"""Create a config setting for matching 'python_version' configuration flag.
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/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel
index 3b11dbe..21f69bf 100644
--- a/python/private/pypi/BUILD.bazel
+++ b/python/private/pypi/BUILD.bazel
@@ -156,7 +156,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(
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/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 e1d13b9..9a9a240 100644
--- a/python/private/python.bzl
+++ b/python/private/python.bzl
@@ -16,9 +16,10 @@
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", "MINOR_MAPPING", "TOOL_VERSIONS")
+load(":full_version.bzl", "full_version")
load(":pythons_hub.bzl", "hub_repo")
+load(":repo_utils.bzl", "repo_utils")
load(":text_util.bzl", "render")
load(":toolchains_repo.bzl", "multi_toolchain_aliases")
load(":util.bzl", "IS_BAZEL_6_4_OR_HIGHER")
@@ -184,6 +185,11 @@
fail("more than {} python versions are not supported".format(_MAX_NUM_TOOLCHAINS))
return struct(
+ debug_info = debug_info,
+ default_python_version = toolchains[-1].python_version,
+ defaults = {
+ "ignore_root_user_error": ignore_root_user_error,
+ },
toolchains = [
struct(
python_version = t.python_version,
@@ -192,11 +198,6 @@
)
for t in toolchains
],
- debug_info = debug_info,
- default_python_version = toolchains[-1].python_version,
- defaults = {
- "ignore_root_user_error": ignore_root_user_error,
- },
)
def _python_impl(module_ctx):
@@ -207,6 +208,7 @@
name = toolchain_info.name,
python_version = toolchain_info.python_version,
register_coverage_tool = toolchain_info.register_coverage_tool,
+ minor_mapping = MINOR_MAPPING,
**py.defaults
)
@@ -220,7 +222,10 @@
render.toolchain_prefix(index, toolchain.name, _TOOLCHAIN_INDEX_PAD_LENGTH)
for index, toolchain in enumerate(py.toolchains)
],
- toolchain_python_versions = [t.python_version for t in py.toolchains],
+ toolchain_python_versions = [
+ full_version(version = t.python_version, minor_mapping = 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 = [
diff --git a/python/private/python_repositories.bzl b/python/private/python_repositories.bzl
index b65127b..c4988ee 100644
--- a/python/private/python_repositories.bzl
+++ b/python/private/python_repositories.bzl
@@ -22,6 +22,7 @@
load(
"//python:versions.bzl",
"DEFAULT_RELEASE_BASE_URL",
+ "MINOR_MAPPING",
"PLATFORMS",
"TOOL_VERSIONS",
"get_release_info",
@@ -583,6 +584,7 @@
register_coverage_tool = False,
set_python_version_constraint = False,
tool_versions = None,
+ minor_mapping = None,
**kwargs):
"""Convenience macro for users which does typical setup.
@@ -607,6 +609,8 @@
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.
"""
@@ -616,8 +620,9 @@
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(python_version)
+ python_version = full_version(version = python_version, minor_mapping = minor_mapping)
toolchain_repo_name = "{name}_toolchains".format(name = name)
@@ -716,6 +721,7 @@
name,
python_versions,
default_version = None,
+ minor_mapping = None,
**kwargs):
"""Convenience macro for registering multiple Python toolchains.
@@ -724,11 +730,15 @@
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:
@@ -742,12 +752,14 @@
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
)
@@ -757,4 +769,5 @@
python_version: name + "_" + python_version.replace(".", "_")
for python_version in (python_versions + [default_version])
},
+ minor_mapping = minor_mapping,
)
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/toolchains_repo.bzl b/python/private/toolchains_repo.bzl
index 21c4e90..528b86f 100644
--- a/python/private/toolchains_repo.bzl
+++ b/python/private/toolchains_repo.bzl
@@ -358,11 +358,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)
@@ -371,6 +373,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")),
},