From 0b9e65d99b0c0e91757790d0f065260ed55af27d Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:19:49 +0900 Subject: [PATCH 01/15] fix(python): correctly order the toolchains Since toolchain matching is done by matching the first target that matches target settings, the `minor_mapping` config setting is special, because e.g. all `3.11.X` toolchains match the `python_version = "3.11"` setting. This just reshuffles the list so that we have toolchains that are in the `minor_mapping` before the rest. Fixes #2685 --- python/private/python.bzl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/private/python.bzl b/python/private/python.bzl index 44eb09f766..9e645579a8 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -243,6 +243,18 @@ def parse_modules(*, module_ctx, _fail = fail): if len(toolchains) > _MAX_NUM_TOOLCHAINS: fail("more than {} python versions are not supported".format(_MAX_NUM_TOOLCHAINS)) + # sort the toolchains so that the toolchain versions that are in the + # `minor_mapping` are coming first. This ensures that `python_version = + # "3.X"` transitions work as expected. + minor_version_toolchains = [] + other_toolchains = [] + for t in toolchains: + if t.python_version in config.minor_mapping: + minor_version_toolchains.append(t) + else: + other_toolchains.append(t) + toolchains = minor_version_toolchains + other_toolchains + return struct( config = config, debug_info = debug_info, From 3c7f542f6d3dcbafebc2da03937a995042492387 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:25:31 +0900 Subject: [PATCH 02/15] doc: changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbcf2561c8..b11270cb25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,9 @@ Unreleased changes template. * (toolchains) Do not try to run `chmod` when downloading non-windows hermetic toolchain repositories on Windows. Fixes [#2660](https://github.com/bazel-contrib/rules_python/issues/2660). +* (toolchains) The toolchain matching is has been fixed when writing + transitions transitioning on the `python_version` flag. + Fixes [#2685](https://github.com/bazel-contrib/rules_python/issues/2685). {#v0-0-0-added} ### Added From 6c02eb54de95cb892c0bb9c09919e03ee72ebbae Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:28:06 +0900 Subject: [PATCH 03/15] remove workaround from the uv lock --- python/uv/private/BUILD.bazel | 2 -- python/uv/private/lock.bzl | 31 +++++++++---------------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/python/uv/private/BUILD.bazel b/python/uv/private/BUILD.bazel index d17ca39490..587ad9a0f9 100644 --- a/python/uv/private/BUILD.bazel +++ b/python/uv/private/BUILD.bazel @@ -43,10 +43,8 @@ bzl_library( ":toolchain_types_bzl", "//python:py_binary_bzl", "//python/private:bzlmod_enabled_bzl", - "//python/private:full_version_bzl", "//python/private:toolchain_types_bzl", "@bazel_skylib//lib:shell", - "@pythons_hub//:versions_bzl", ], ) diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl index 69d277d653..45a3819ee6 100644 --- a/python/uv/private/lock.bzl +++ b/python/uv/private/lock.bzl @@ -16,10 +16,8 @@ """ load("@bazel_skylib//lib:shell.bzl", "shell") -load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") load("//python:py_binary.bzl", "py_binary") load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility -load("//python/private:full_version.bzl", "full_version") load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility load(":toolchain_types.bzl", "UV_TOOLCHAIN_TYPE") @@ -75,15 +73,15 @@ def _args(ctx): def _lock_impl(ctx): srcs = ctx.files.srcs - python_version = full_version( - version = ctx.attr.python_version or DEFAULT_PYTHON_VERSION, - minor_mapping = MINOR_MAPPING, - ) - output = ctx.actions.declare_file("{}.{}.out".format( - ctx.label.name, - python_version.replace(".", "_"), - )) + fname = "{}.out".format(ctx.label.name) + python_version = ctx.attr.python_version + if python_version: + fname = "{}.{}.out".format( + ctx.label.name, + python_version.replace(".", "_"), + ) + output = ctx.actions.declare_file(fname) toolchain_info = ctx.toolchains[UV_TOOLCHAIN_TYPE] uv = toolchain_info.uv_toolchain_info.uv[DefaultInfo].files_to_run.executable @@ -166,15 +164,7 @@ def _transition_impl(input_settings, attr): _PYTHON_VERSION_FLAG: input_settings[_PYTHON_VERSION_FLAG], } if attr.python_version: - # FIXME @aignas 2025-03-20: using `full_version` is a workaround for a bug in - # how we order toolchains in bazel. If I set the `python_version` flag - # to `3.12`, I would expect the latest version to be selected, i.e. the - # one that is in MINOR_MAPPING, but it seems that 3.12.0 is selected, - # because of how the targets are ordered. - settings[_PYTHON_VERSION_FLAG] = full_version( - version = attr.python_version, - minor_mapping = MINOR_MAPPING, - ) + settings[_PYTHON_VERSION_FLAG] = attr.python_version return settings _python_version_transition = transition( @@ -436,9 +426,6 @@ def lock( if not BZLMOD_ENABLED: kwargs["target_compatible_with"] = ["@platforms//:incompatible"] - # FIXME @aignas 2025-03-17: should we have one more target that transitions - # the python_version to ensure that if somebody calls `bazel build - # :requirements` that it is locked with the right `python_version`? _lock( name = name, args = args, From 3f14780a6f548a1cf9949d07b42e745bf5e9a9a4 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:56:26 +0900 Subject: [PATCH 04/15] wip --- python/private/python.bzl | 6 ++- python/uv/private/lock.bzl | 1 + .../transition/multi_version_tests.bzl | 3 +- tests/python/python_tests.bzl | 48 +++++++++++++++++++ tests/toolchains/defs.bzl | 44 +++++++++++++++-- 5 files changed, 95 insertions(+), 7 deletions(-) diff --git a/python/private/python.bzl b/python/private/python.bzl index 9e645579a8..2c85d06752 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -248,8 +248,10 @@ def parse_modules(*, module_ctx, _fail = fail): # "3.X"` transitions work as expected. minor_version_toolchains = [] other_toolchains = [] + minor_mapping = config.minor_mapping for t in toolchains: - if t.python_version in config.minor_mapping: + # TODO @aignas 2025-04-04: I am getting lost here when unit testing + if t.python_version == minor_mapping.get(t.python_version): minor_version_toolchains.append(t) else: other_toolchains.append(t) @@ -258,7 +260,7 @@ def parse_modules(*, module_ctx, _fail = fail): return struct( config = config, debug_info = debug_info, - default_python_version = toolchains[-1].python_version, + default_python_version = default_toolchain.python_version, toolchains = [ struct( python_version = t.python_version, diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl index 45a3819ee6..703a004054 100644 --- a/python/uv/private/lock.bzl +++ b/python/uv/private/lock.bzl @@ -106,6 +106,7 @@ def _lock_impl(ctx): exec_tools = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE].exec_tools runtime = exec_tools.exec_interpreter[platform_common.ToolchainInfo].py3_runtime + fail(runtime.interpreter_version_info) python = runtime.interpreter or runtime.interpreter_path python_files = runtime.files args.add("--python", python) diff --git a/tests/config_settings/transition/multi_version_tests.bzl b/tests/config_settings/transition/multi_version_tests.bzl index aca341a295..93f6efd728 100644 --- a/tests/config_settings/transition/multi_version_tests.bzl +++ b/tests/config_settings/transition/multi_version_tests.bzl @@ -13,6 +13,7 @@ # limitations under the License. """Tests for py_test.""" +load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION") load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") load("@rules_testing//lib:util.bzl", "TestingAspectInfo", rt_util = "util") @@ -29,7 +30,7 @@ load("//tests/support:support.bzl", "CC_TOOLCHAIN") # If the toolchain is not resolved then you will have a weird message telling # you that your transition target does not have a PyRuntime provider, which is # caused by there not being a toolchain detected for the target. -_PYTHON_VERSION = "3.11" +_PYTHON_VERSION = DEFAULT_PYTHON_VERSION _tests = [] diff --git a/tests/python/python_tests.bzl b/tests/python/python_tests.bzl index 1679794e15..c582ce97ab 100644 --- a/tests/python/python_tests.bzl +++ b/tests/python/python_tests.bzl @@ -284,6 +284,54 @@ def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env): _tests.append(_test_default_non_rules_python_ignore_root_user_error_non_root_module) +def _test_toolchain_ordering(env): + py = parse_modules( + module_ctx = _mock_mctx( + _mod( + name = "my_module", + toolchain = [ + _toolchain("3.10"), + _toolchain("3.10.15"), + _toolchain("3.10.16"), + _toolchain("3.10.11"), + _toolchain("3.11.1"), + _toolchain("3.11.10"), + _toolchain("3.11.11", is_default = True), + ], + ), + _mod(name = "rules_python", toolchain = [_toolchain("3.11")]), + ), + ) + got_versions = [ + t.python_version + for t in py.toolchains + ] + + env.expect.that_str(py.default_python_version).equals("3.11") + env.expect.that_dict(py.config.minor_mapping).contains_exactly({ + "3.10": "3.10.16", + "3.11": "3.11.11", + "3.12": "3.12.9", + "3.13": "3.13.2", + "3.8": "3.8.20", + "3.9": "3.9.21", + }) + env.expect.that_collection(got_versions).contains_exactly([ + # First the full-version toolchains that are in minor_mapping + # so that they get matched first if only the `python_version` is in MINOR_MAPPING + "3.10.16", + # Next, the rest, where we will match things based on the `python_version` being + # the same + "3.10", + "3.10.15", + "3.10.11", + "3.11.1", + "3.11.10", + "3.11.11", + ]).in_order() + +_tests.append(_test_toolchain_ordering) + def _test_default_from_defaults(env): py = parse_modules( module_ctx = _mock_mctx( diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index fbb70820c9..ce1fba3471 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -14,7 +14,9 @@ "" +load("@pythons_hub//:versions.bzl", "MINOR_MAPPING", "DEFAULT_PYTHON_VERSION") load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") +load("//python/private:full_version.bzl", "full_version") load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") def define_toolchain_tests(name): @@ -30,7 +32,41 @@ def define_toolchain_tests(name): constraint_values = platform_info.compatible_with, ) - for python_version, meta in TOOL_VERSIONS.items(): + # First we expect the transitions with a specific version to always + # give us that specific version + exact_version_tests = { + (v, v): "python_{}_test".format(v) + for v in TOOL_VERSIONS + } + native.test_suite( + name = "exact_version_tests", + tests = exact_version_tests.values(), + ) + + # Then we expect to get the version in the MINOR_MAPPING if we provide + # the version from the MINOR_MAPPING + minor_mapping_tests = { + (minor, full): "python_{}_test".format(minor) + for minor, full in MINOR_MAPPING.items() + } + native.test_suite( + name = "minor_mapping_tests", + tests = minor_mapping_tests.values(), + ) + + # Lastly, if we don't provide any version to the transition, we should + # get the default version + default_version = full_version( + version = DEFAULT_PYTHON_VERSION, + minor_mapping = MINOR_MAPPING, + ) + default_version_tests = { + (None, default_version): "default_version_test" + } + tests = exact_version_tests | minor_mapping_tests | default_version_tests + + for (input_python_version, expect_python_version), test_name in tests.items(): + meta = TOOL_VERSIONS[expect_python_version] target_compatible_with = { "//conditions:default": ["@platforms//:incompatible"], } @@ -39,12 +75,12 @@ def define_toolchain_tests(name): target_compatible_with[is_platform] = [] py_reconfig_test( - name = "python_{}_test".format(python_version), + name = test_name, srcs = ["python_toolchain_test.py"], main = "python_toolchain_test.py", - python_version = python_version, + python_version = input_python_version, env = { - "EXPECT_PYTHON_VERSION": python_version, + "EXPECT_PYTHON_VERSION": expect_python_version, }, deps = ["//python/runfiles"], data = ["//tests/support:current_build_settings"], From c5a43f40d642cf2107c6a6f43cb4d178a77a83dd Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:57:58 +0900 Subject: [PATCH 05/15] wip --- python/private/python.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/private/python.bzl b/python/private/python.bzl index 2c85d06752..390c0e66eb 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -248,10 +248,10 @@ def parse_modules(*, module_ctx, _fail = fail): # "3.X"` transitions work as expected. minor_version_toolchains = [] other_toolchains = [] - minor_mapping = config.minor_mapping + minor_mapping = list(config.minor_mapping.values()) for t in toolchains: # TODO @aignas 2025-04-04: I am getting lost here when unit testing - if t.python_version == minor_mapping.get(t.python_version): + if config.minor_mapping.get(t.python_version, t.python_version) in minor_mapping: minor_version_toolchains.append(t) else: other_toolchains.append(t) From ec006ee0d0379c1e26db86e14ea6f7d1994c3e28 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:03:08 +0900 Subject: [PATCH 06/15] add unit tests and refine --- python/private/python.bzl | 3 +- tests/python/python_tests.bzl | 58 +++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/python/private/python.bzl b/python/private/python.bzl index 390c0e66eb..296fb0ab7d 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -250,7 +250,8 @@ def parse_modules(*, module_ctx, _fail = fail): other_toolchains = [] minor_mapping = list(config.minor_mapping.values()) for t in toolchains: - # TODO @aignas 2025-04-04: I am getting lost here when unit testing + # FIXME @aignas 2025-04-04: How can we unit test that this ordering is + # consistent with what would actually work? if config.minor_mapping.get(t.python_version, t.python_version) in minor_mapping: minor_version_toolchains.append(t) else: diff --git a/tests/python/python_tests.bzl b/tests/python/python_tests.bzl index c582ce97ab..c35d13bb71 100644 --- a/tests/python/python_tests.bzl +++ b/tests/python/python_tests.bzl @@ -288,16 +288,16 @@ def _test_toolchain_ordering(env): py = parse_modules( module_ctx = _mock_mctx( _mod( - name = "my_module", - toolchain = [ - _toolchain("3.10"), - _toolchain("3.10.15"), - _toolchain("3.10.16"), - _toolchain("3.10.11"), - _toolchain("3.11.1"), - _toolchain("3.11.10"), - _toolchain("3.11.11", is_default = True), - ], + name = "my_module", + toolchain = [ + _toolchain("3.10"), + _toolchain("3.10.15"), + _toolchain("3.10.16"), + _toolchain("3.10.11"), + _toolchain("3.11.1"), + _toolchain("3.11.10"), + _toolchain("3.11.11", is_default = True), + ], ), _mod(name = "rules_python", toolchain = [_toolchain("3.11")]), ), @@ -309,25 +309,29 @@ def _test_toolchain_ordering(env): env.expect.that_str(py.default_python_version).equals("3.11") env.expect.that_dict(py.config.minor_mapping).contains_exactly({ - "3.10": "3.10.16", - "3.11": "3.11.11", - "3.12": "3.12.9", - "3.13": "3.13.2", - "3.8": "3.8.20", - "3.9": "3.9.21", + "3.10": "3.10.16", + "3.11": "3.11.11", + "3.12": "3.12.9", + "3.13": "3.13.2", + "3.8": "3.8.20", + "3.9": "3.9.21", }) env.expect.that_collection(got_versions).contains_exactly([ - # First the full-version toolchains that are in minor_mapping - # so that they get matched first if only the `python_version` is in MINOR_MAPPING - "3.10.16", - # Next, the rest, where we will match things based on the `python_version` being - # the same - "3.10", - "3.10.15", - "3.10.11", - "3.11.1", - "3.11.10", - "3.11.11", + # First the full-version toolchains that are in minor_mapping + # so that they get matched first if only the `python_version` is in MINOR_MAPPING + # + # The default version is always set in the `python_version` flag, so know, that + # the default match will be somewhere in the first bunch. + "3.10", + "3.10.16", + "3.11", + "3.11.11", + # Next, the rest, where we will match things based on the `python_version` being + # the same + "3.10.15", + "3.10.11", + "3.11.1", + "3.11.10", ]).in_order() _tests.append(_test_toolchain_ordering) From 3253ae68a7236e8a390b38411436b42c1062a59c Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:04:06 +0900 Subject: [PATCH 07/15] remove the lock fail --- python/uv/private/lock.bzl | 1 - 1 file changed, 1 deletion(-) diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl index 703a004054..45a3819ee6 100644 --- a/python/uv/private/lock.bzl +++ b/python/uv/private/lock.bzl @@ -106,7 +106,6 @@ def _lock_impl(ctx): exec_tools = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE].exec_tools runtime = exec_tools.exec_interpreter[platform_common.ToolchainInfo].py3_runtime - fail(runtime.interpreter_version_info) python = runtime.interpreter or runtime.interpreter_path python_files = runtime.files args.add("--python", python) From 1728c0d06d1c5637d2ff4072a911250346d68c56 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:08:52 +0900 Subject: [PATCH 08/15] fixup --- tests/python/python_tests.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/python_tests.bzl b/tests/python/python_tests.bzl index c35d13bb71..97c47b57db 100644 --- a/tests/python/python_tests.bzl +++ b/tests/python/python_tests.bzl @@ -307,7 +307,7 @@ def _test_toolchain_ordering(env): for t in py.toolchains ] - env.expect.that_str(py.default_python_version).equals("3.11") + env.expect.that_str(py.default_python_version).equals("3.11.11") env.expect.that_dict(py.config.minor_mapping).contains_exactly({ "3.10": "3.10.16", "3.11": "3.11.11", From 48bc1abbcf82202a906ba773488dd7a198afea64 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:16:07 +0900 Subject: [PATCH 09/15] buildifier --- tests/toolchains/defs.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index ce1fba3471..14af155613 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -14,9 +14,9 @@ "" -load("@pythons_hub//:versions.bzl", "MINOR_MAPPING", "DEFAULT_PYTHON_VERSION") +load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") -load("//python/private:full_version.bzl", "full_version") +load("//python/private:full_version.bzl", "full_version") # buildifier: disable=bzl-visibility load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") def define_toolchain_tests(name): @@ -61,7 +61,7 @@ def define_toolchain_tests(name): minor_mapping = MINOR_MAPPING, ) default_version_tests = { - (None, default_version): "default_version_test" + (None, default_version): "default_version_test", } tests = exact_version_tests | minor_mapping_tests | default_version_tests From a112dafe93a150c0254f21624a9531014f4156b3 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:18:33 +0900 Subject: [PATCH 10/15] cardcode the version for WORKSPACE integration tests --- tests/toolchains/defs.bzl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index 14af155613..7166ea2987 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -16,6 +16,7 @@ load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") +load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility load("//python/private:full_version.bzl", "full_version") # buildifier: disable=bzl-visibility load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") @@ -57,7 +58,8 @@ def define_toolchain_tests(name): # Lastly, if we don't provide any version to the transition, we should # get the default version default_version = full_version( - version = DEFAULT_PYTHON_VERSION, + # note, this hard codes the version that is in //:WORKSPACE + version = DEFAULT_PYTHON_VERSION if BZLMOD_ENABLED else "3.11", minor_mapping = MINOR_MAPPING, ) default_version_tests = { From 961962460cd2eee8f97f01b65cf05e8803377346 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 5 Apr 2025 10:47:55 +0900 Subject: [PATCH 11/15] disable the extra tests in non-bzlmod --- tests/toolchains/defs.bzl | 47 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index 7166ea2987..0d1c2f91df 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -44,28 +44,33 @@ def define_toolchain_tests(name): tests = exact_version_tests.values(), ) - # Then we expect to get the version in the MINOR_MAPPING if we provide - # the version from the MINOR_MAPPING - minor_mapping_tests = { - (minor, full): "python_{}_test".format(minor) - for minor, full in MINOR_MAPPING.items() - } - native.test_suite( - name = "minor_mapping_tests", - tests = minor_mapping_tests.values(), - ) + if BZLMOD_ENABLED: + # Then we expect to get the version in the MINOR_MAPPING if we provide + # the version from the MINOR_MAPPING + minor_mapping_tests = { + (minor, full): "python_{}_test".format(minor) + for minor, full in MINOR_MAPPING.items() + } + native.test_suite( + name = "minor_mapping_tests", + tests = minor_mapping_tests.values(), + ) - # Lastly, if we don't provide any version to the transition, we should - # get the default version - default_version = full_version( - # note, this hard codes the version that is in //:WORKSPACE - version = DEFAULT_PYTHON_VERSION if BZLMOD_ENABLED else "3.11", - minor_mapping = MINOR_MAPPING, - ) - default_version_tests = { - (None, default_version): "default_version_test", - } - tests = exact_version_tests | minor_mapping_tests | default_version_tests + # Lastly, if we don't provide any version to the transition, we should + # get the default version + default_version = full_version( + version = DEFAULT_PYTHON_VERSION, + minor_mapping = MINOR_MAPPING, + ) + default_version_tests = { + (None, default_version): "default_version_test", + } + tests = exact_version_tests | minor_mapping_tests | default_version_tests + else: + # Outside bzlmod the default version and the minor mapping tests do not + # make sense because the user loading things in the WORKSPACE ultimately defines + # the matching order. + tests = exact_version_tests for (input_python_version, expect_python_version), test_name in tests.items(): meta = TOOL_VERSIONS[expect_python_version] From 09ae63cb891f2beb348087d42aa525882f34b229 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 5 Apr 2025 21:13:42 +0900 Subject: [PATCH 12/15] wip --- :w | 143 ++++++++++++++++++++++++++++++++++++++ tests/toolchains/defs.bzl | 124 +++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 :w diff --git a/:w b/:w new file mode 100644 index 0000000000..eb2d9dced3 --- /dev/null +++ b/:w @@ -0,0 +1,143 @@ +# 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. + +"" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") +load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") +load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility +load("//python/private:full_version.bzl", "full_version") # buildifier: disable=bzl-visibility +load("//tests/support:support.bzl", "PYTHON_VERSION") +load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility +load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") +load("//python:py_runtime_info", "PyRuntimeInfo") +# +# # This includes a simple transition implementation +# def _transition_impl(input_settings, attr): +# settings = { +# PYTHON_VERSION: input_settings[PYTHON_VERSION], +# } +# if attr.python_version: +# settings[PYTHON_VERSION] = attr.python_version +# return settings +# +# _python_version_transition = transition( +# implementation = _transition_impl, +# inputs = [PYTHON_VERSION], +# outputs = [PYTHON_VERSION], +# ) + +_analysis_tests = [] + +def _test_toolchain_precedence(name): + analysis_test( + name = name, + impl = _test_toolchain_precedence_impl, + target = "//python:current_py_toolchain", + config_settings = { + PYTHON_VERSION: "3.12", + }, + ) + +def _test_toolchain_precedence_impl(env, target): + # Check that the forwarded UvToolchainInfo looks vaguely correct. + py_runtime = env.expect.that_target(target).provider( + PyRuntimeInfo, + factory = lambda v, meta: v, + ) + fail(py_runtime) + env.expect.that_str(str(py_runtime.label)).contains("//tests/uv/uv:fake_foof") + +_analysis_tests.append(_test_toolchain_precedence) + +def define_toolchain_tests(name): + """Define the toolchain tests. + + Args: + name: Only present to satisfy tooling. + """ + test_suite( + name = name, + tests = _analysis_tests, + ) + + for platform_key, platform_info in PLATFORMS.items(): + native.config_setting( + name = "_is_{}".format(platform_key), + flag_values = platform_info.flag_values, + constraint_values = platform_info.compatible_with, + ) + + # First we expect the transitions with a specific version to always + # give us that specific version + exact_version_tests = { + (v, v): "python_{}_test".format(v) + for v in TOOL_VERSIONS + } + native.test_suite( + name = "exact_version_tests", + tests = exact_version_tests.values(), + ) + + if BZLMOD_ENABLED: + # Then we expect to get the version in the MINOR_MAPPING if we provide + # the version from the MINOR_MAPPING + minor_mapping_tests = { + (minor, full): "python_{}_test".format(minor) + for minor, full in MINOR_MAPPING.items() + } + native.test_suite( + name = "minor_mapping_tests", + tests = minor_mapping_tests.values(), + ) + + # Lastly, if we don't provide any version to the transition, we should + # get the default version + default_version = full_version( + version = DEFAULT_PYTHON_VERSION, + minor_mapping = MINOR_MAPPING, + ) + default_version_tests = { + (None, default_version): "default_version_test", + } + tests = exact_version_tests | minor_mapping_tests | default_version_tests + else: + # Outside bzlmod the default version and the minor mapping tests do not + # make sense because the user loading things in the WORKSPACE ultimately defines + # the matching order. + tests = exact_version_tests + + for (input_python_version, expect_python_version), test_name in tests.items(): + meta = TOOL_VERSIONS[expect_python_version] + target_compatible_with = { + "//conditions:default": ["@platforms//:incompatible"], + } + for platform_key in meta["sha256"].keys(): + is_platform = "_is_{}".format(platform_key) + target_compatible_with[is_platform] = [] + + py_reconfig_test( + name = test_name, + srcs = ["python_toolchain_test.py"], + main = "python_toolchain_test.py", + python_version = input_python_version, + env = { + "EXPECT_PYTHON_VERSION": expect_python_version, + }, + deps = ["//python/runfiles"], + data = ["//tests/support:current_build_settings"], + target_compatible_with = select(target_compatible_with), + ) diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index 0d1c2f91df..cd4266472f 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -15,10 +15,129 @@ "" load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("@rules_testing//lib:util.bzl", rt_util = "util") load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility load("//python/private:full_version.bzl", "full_version") # buildifier: disable=bzl-visibility +load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") +load("//tests/support:support.bzl", "PYTHON_VERSION") + +_analysis_tests = [] + +def _transition_impl(input_settings, attr): + settings = { + PYTHON_VERSION: input_settings[PYTHON_VERSION], + } + if attr.python_version: + settings[PYTHON_VERSION] = attr.python_version + return settings + +_python_version_transition = transition( + implementation = _transition_impl, + inputs = [PYTHON_VERSION], + outputs = [PYTHON_VERSION], +) + +TestInfo = provider( + doc = "", + fields = { + "got": "", + "want": "", + }, +) + +def _lock_impl(ctx): + exec_tools = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE].exec_tools + got_version = exec_tools.exec_interpreter[platform_common.ToolchainInfo].py3_runtime.interpreter_version_info + return [ + TestInfo( + got = "{}.{}.{}".format( + got_version.major, + got_version.minor, + got_version.micro, + ), + want = ctx.attr.want_version, + ), + ] + +_simple_transition = rule( + implementation = _lock_impl, + attrs = { + "python_version": attr.string( + doc = "Public, see the docs in the macro.", + ), + "want_version": attr.string( + doc = "Public, see the docs in the macro.", + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + toolchains = [ + EXEC_TOOLS_TOOLCHAIN_TYPE, + ], + cfg = _python_version_transition, +) + +def _test_toolchain_precedence(name): + # First we expect the transitions with a specific version to always + # give us that specific version + exact_version_tests = { + (v, v): v.replace(".", "_") + for v in TOOL_VERSIONS + } + + if BZLMOD_ENABLED: + # Then we expect to get the version in the MINOR_MAPPING if we provide + # the version from the MINOR_MAPPING + minor_mapping_tests = { + (minor, full): minor.replace(".", "_") + for minor, full in MINOR_MAPPING.items() + } + + # Lastly, if we don't provide any version to the transition, we should + # get the default version + default_version = full_version( + version = DEFAULT_PYTHON_VERSION, + minor_mapping = MINOR_MAPPING, + ) + default_version_tests = { + (None, default_version): "default", + } + tests = exact_version_tests | minor_mapping_tests | default_version_tests + else: + # Outside bzlmod the default version and the minor mapping tests do not + # make sense because the user loading things in the WORKSPACE ultimately defines + # the matching order. + tests = exact_version_tests + + analysis_test( + name = name, + impl = _test_toolchain_precedence_impl, + targets = { + "{}_{}".format(name, test_name): rt_util.helper_target( + _simple_transition, + name = "{}_{}".format(name, test_name), + python_version = input_version, + want_version = want_version, + ) + for (input_version, want_version), test_name in tests.items() + }, + ) + +def _test_toolchain_precedence_impl(env, targets): + # Check that the forwarded PyRuntimeInfo looks vaguely correct. + for target in dir(targets): + test_info = env.expect.that_target(target).provider( + TestInfo, + factory = lambda v, meta: v, + ) + env.expect.that_str(test_info.got).equals(test_info.want) + +_analysis_tests.append(_test_toolchain_precedence) def define_toolchain_tests(name): """Define the toolchain tests. @@ -26,6 +145,11 @@ def define_toolchain_tests(name): Args: name: Only present to satisfy tooling. """ + test_suite( + name = name, + tests = _analysis_tests, + ) + for platform_key, platform_info in PLATFORMS.items(): native.config_setting( name = "_is_{}".format(platform_key), From a5079f5e5b1dadc9df842fe3d55aa5cd073e6a22 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 5 Apr 2025 21:13:49 +0900 Subject: [PATCH 13/15] wip --- :w | 143 ------------------------------------------------------------- 1 file changed, 143 deletions(-) delete mode 100644 :w diff --git a/:w b/:w deleted file mode 100644 index eb2d9dced3..0000000000 --- a/:w +++ /dev/null @@ -1,143 +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. - -"" - -load("@rules_testing//lib:analysis_test.bzl", "analysis_test") -load("@rules_testing//lib:test_suite.bzl", "test_suite") -load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") -load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") -load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility -load("//python/private:full_version.bzl", "full_version") # buildifier: disable=bzl-visibility -load("//tests/support:support.bzl", "PYTHON_VERSION") -load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility -load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") -load("//python:py_runtime_info", "PyRuntimeInfo") -# -# # This includes a simple transition implementation -# def _transition_impl(input_settings, attr): -# settings = { -# PYTHON_VERSION: input_settings[PYTHON_VERSION], -# } -# if attr.python_version: -# settings[PYTHON_VERSION] = attr.python_version -# return settings -# -# _python_version_transition = transition( -# implementation = _transition_impl, -# inputs = [PYTHON_VERSION], -# outputs = [PYTHON_VERSION], -# ) - -_analysis_tests = [] - -def _test_toolchain_precedence(name): - analysis_test( - name = name, - impl = _test_toolchain_precedence_impl, - target = "//python:current_py_toolchain", - config_settings = { - PYTHON_VERSION: "3.12", - }, - ) - -def _test_toolchain_precedence_impl(env, target): - # Check that the forwarded UvToolchainInfo looks vaguely correct. - py_runtime = env.expect.that_target(target).provider( - PyRuntimeInfo, - factory = lambda v, meta: v, - ) - fail(py_runtime) - env.expect.that_str(str(py_runtime.label)).contains("//tests/uv/uv:fake_foof") - -_analysis_tests.append(_test_toolchain_precedence) - -def define_toolchain_tests(name): - """Define the toolchain tests. - - Args: - name: Only present to satisfy tooling. - """ - test_suite( - name = name, - tests = _analysis_tests, - ) - - for platform_key, platform_info in PLATFORMS.items(): - native.config_setting( - name = "_is_{}".format(platform_key), - flag_values = platform_info.flag_values, - constraint_values = platform_info.compatible_with, - ) - - # First we expect the transitions with a specific version to always - # give us that specific version - exact_version_tests = { - (v, v): "python_{}_test".format(v) - for v in TOOL_VERSIONS - } - native.test_suite( - name = "exact_version_tests", - tests = exact_version_tests.values(), - ) - - if BZLMOD_ENABLED: - # Then we expect to get the version in the MINOR_MAPPING if we provide - # the version from the MINOR_MAPPING - minor_mapping_tests = { - (minor, full): "python_{}_test".format(minor) - for minor, full in MINOR_MAPPING.items() - } - native.test_suite( - name = "minor_mapping_tests", - tests = minor_mapping_tests.values(), - ) - - # Lastly, if we don't provide any version to the transition, we should - # get the default version - default_version = full_version( - version = DEFAULT_PYTHON_VERSION, - minor_mapping = MINOR_MAPPING, - ) - default_version_tests = { - (None, default_version): "default_version_test", - } - tests = exact_version_tests | minor_mapping_tests | default_version_tests - else: - # Outside bzlmod the default version and the minor mapping tests do not - # make sense because the user loading things in the WORKSPACE ultimately defines - # the matching order. - tests = exact_version_tests - - for (input_python_version, expect_python_version), test_name in tests.items(): - meta = TOOL_VERSIONS[expect_python_version] - target_compatible_with = { - "//conditions:default": ["@platforms//:incompatible"], - } - for platform_key in meta["sha256"].keys(): - is_platform = "_is_{}".format(platform_key) - target_compatible_with[is_platform] = [] - - py_reconfig_test( - name = test_name, - srcs = ["python_toolchain_test.py"], - main = "python_toolchain_test.py", - python_version = input_python_version, - env = { - "EXPECT_PYTHON_VERSION": expect_python_version, - }, - deps = ["//python/runfiles"], - data = ["//tests/support:current_build_settings"], - target_compatible_with = select(target_compatible_with), - ) From d640490625aeb0825a7db7fd8da0744063abbb9c Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 5 Apr 2025 21:54:01 +0900 Subject: [PATCH 14/15] add a test and revert the old toolchain integration tests --- tests/toolchains/defs.bzl | 175 +---------------- tests/toolchains/transitions/BUILD.bazel | 5 + .../transitions/transitions_tests.bzl | 178 ++++++++++++++++++ 3 files changed, 187 insertions(+), 171 deletions(-) create mode 100644 tests/toolchains/transitions/BUILD.bazel create mode 100644 tests/toolchains/transitions/transitions_tests.bzl diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index cd4266472f..fbb70820c9 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -14,130 +14,8 @@ "" -load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") -load("@rules_testing//lib:analysis_test.bzl", "analysis_test") -load("@rules_testing//lib:test_suite.bzl", "test_suite") -load("@rules_testing//lib:util.bzl", rt_util = "util") load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") -load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility -load("//python/private:full_version.bzl", "full_version") # buildifier: disable=bzl-visibility -load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") -load("//tests/support:support.bzl", "PYTHON_VERSION") - -_analysis_tests = [] - -def _transition_impl(input_settings, attr): - settings = { - PYTHON_VERSION: input_settings[PYTHON_VERSION], - } - if attr.python_version: - settings[PYTHON_VERSION] = attr.python_version - return settings - -_python_version_transition = transition( - implementation = _transition_impl, - inputs = [PYTHON_VERSION], - outputs = [PYTHON_VERSION], -) - -TestInfo = provider( - doc = "", - fields = { - "got": "", - "want": "", - }, -) - -def _lock_impl(ctx): - exec_tools = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE].exec_tools - got_version = exec_tools.exec_interpreter[platform_common.ToolchainInfo].py3_runtime.interpreter_version_info - return [ - TestInfo( - got = "{}.{}.{}".format( - got_version.major, - got_version.minor, - got_version.micro, - ), - want = ctx.attr.want_version, - ), - ] - -_simple_transition = rule( - implementation = _lock_impl, - attrs = { - "python_version": attr.string( - doc = "Public, see the docs in the macro.", - ), - "want_version": attr.string( - doc = "Public, see the docs in the macro.", - ), - "_allowlist_function_transition": attr.label( - default = "@bazel_tools//tools/allowlists/function_transition_allowlist", - ), - }, - toolchains = [ - EXEC_TOOLS_TOOLCHAIN_TYPE, - ], - cfg = _python_version_transition, -) - -def _test_toolchain_precedence(name): - # First we expect the transitions with a specific version to always - # give us that specific version - exact_version_tests = { - (v, v): v.replace(".", "_") - for v in TOOL_VERSIONS - } - - if BZLMOD_ENABLED: - # Then we expect to get the version in the MINOR_MAPPING if we provide - # the version from the MINOR_MAPPING - minor_mapping_tests = { - (minor, full): minor.replace(".", "_") - for minor, full in MINOR_MAPPING.items() - } - - # Lastly, if we don't provide any version to the transition, we should - # get the default version - default_version = full_version( - version = DEFAULT_PYTHON_VERSION, - minor_mapping = MINOR_MAPPING, - ) - default_version_tests = { - (None, default_version): "default", - } - tests = exact_version_tests | minor_mapping_tests | default_version_tests - else: - # Outside bzlmod the default version and the minor mapping tests do not - # make sense because the user loading things in the WORKSPACE ultimately defines - # the matching order. - tests = exact_version_tests - - analysis_test( - name = name, - impl = _test_toolchain_precedence_impl, - targets = { - "{}_{}".format(name, test_name): rt_util.helper_target( - _simple_transition, - name = "{}_{}".format(name, test_name), - python_version = input_version, - want_version = want_version, - ) - for (input_version, want_version), test_name in tests.items() - }, - ) - -def _test_toolchain_precedence_impl(env, targets): - # Check that the forwarded PyRuntimeInfo looks vaguely correct. - for target in dir(targets): - test_info = env.expect.that_target(target).provider( - TestInfo, - factory = lambda v, meta: v, - ) - env.expect.that_str(test_info.got).equals(test_info.want) - -_analysis_tests.append(_test_toolchain_precedence) def define_toolchain_tests(name): """Define the toolchain tests. @@ -145,11 +23,6 @@ def define_toolchain_tests(name): Args: name: Only present to satisfy tooling. """ - test_suite( - name = name, - tests = _analysis_tests, - ) - for platform_key, platform_info in PLATFORMS.items(): native.config_setting( name = "_is_{}".format(platform_key), @@ -157,47 +30,7 @@ def define_toolchain_tests(name): constraint_values = platform_info.compatible_with, ) - # First we expect the transitions with a specific version to always - # give us that specific version - exact_version_tests = { - (v, v): "python_{}_test".format(v) - for v in TOOL_VERSIONS - } - native.test_suite( - name = "exact_version_tests", - tests = exact_version_tests.values(), - ) - - if BZLMOD_ENABLED: - # Then we expect to get the version in the MINOR_MAPPING if we provide - # the version from the MINOR_MAPPING - minor_mapping_tests = { - (minor, full): "python_{}_test".format(minor) - for minor, full in MINOR_MAPPING.items() - } - native.test_suite( - name = "minor_mapping_tests", - tests = minor_mapping_tests.values(), - ) - - # Lastly, if we don't provide any version to the transition, we should - # get the default version - default_version = full_version( - version = DEFAULT_PYTHON_VERSION, - minor_mapping = MINOR_MAPPING, - ) - default_version_tests = { - (None, default_version): "default_version_test", - } - tests = exact_version_tests | minor_mapping_tests | default_version_tests - else: - # Outside bzlmod the default version and the minor mapping tests do not - # make sense because the user loading things in the WORKSPACE ultimately defines - # the matching order. - tests = exact_version_tests - - for (input_python_version, expect_python_version), test_name in tests.items(): - meta = TOOL_VERSIONS[expect_python_version] + for python_version, meta in TOOL_VERSIONS.items(): target_compatible_with = { "//conditions:default": ["@platforms//:incompatible"], } @@ -206,12 +39,12 @@ def define_toolchain_tests(name): target_compatible_with[is_platform] = [] py_reconfig_test( - name = test_name, + name = "python_{}_test".format(python_version), srcs = ["python_toolchain_test.py"], main = "python_toolchain_test.py", - python_version = input_python_version, + python_version = python_version, env = { - "EXPECT_PYTHON_VERSION": expect_python_version, + "EXPECT_PYTHON_VERSION": python_version, }, deps = ["//python/runfiles"], data = ["//tests/support:current_build_settings"], diff --git a/tests/toolchains/transitions/BUILD.bazel b/tests/toolchains/transitions/BUILD.bazel new file mode 100644 index 0000000000..a7bef8c0e5 --- /dev/null +++ b/tests/toolchains/transitions/BUILD.bazel @@ -0,0 +1,5 @@ +load(":transitions_tests.bzl", "transitions_test_suite") + +transitions_test_suite( + name = "transitions_tests", +) diff --git a/tests/toolchains/transitions/transitions_tests.bzl b/tests/toolchains/transitions/transitions_tests.bzl new file mode 100644 index 0000000000..b5d332fed4 --- /dev/null +++ b/tests/toolchains/transitions/transitions_tests.bzl @@ -0,0 +1,178 @@ +# 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. + +"" + +load("@pythons_hub//:versions.bzl", "DEFAULT_PYTHON_VERSION", "MINOR_MAPPING") +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("@rules_testing//lib:util.bzl", rt_util = "util") +load("//python:versions.bzl", "TOOL_VERSIONS") +load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility +load("//python/private:full_version.bzl", "full_version") # buildifier: disable=bzl-visibility +load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility +load("//tests/support:support.bzl", "PYTHON_VERSION") + +_analysis_tests = [] + +def _transition_impl(input_settings, attr): + """Transition based on python_version flag. + + This is a simple transition impl that a user of rules_python may implement + for their own rule. + """ + settings = { + PYTHON_VERSION: input_settings[PYTHON_VERSION], + } + if attr.python_version: + settings[PYTHON_VERSION] = attr.python_version + return settings + +_python_version_transition = transition( + implementation = _transition_impl, + inputs = [PYTHON_VERSION], + outputs = [PYTHON_VERSION], +) + +TestInfo = provider( + doc = "A simple test provider to forward the values for the assertion.", + fields = {"got": "", "want": ""}, +) + +def _impl(ctx): + if ctx.attr.skip: + return [TestInfo(got = "", want = "")] + + exec_tools = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE].exec_tools + got_version = exec_tools.exec_interpreter[platform_common.ToolchainInfo].py3_runtime.interpreter_version_info + + return [ + TestInfo( + got = "{}.{}.{}".format( + got_version.major, + got_version.minor, + got_version.micro, + ), + want = ctx.attr.want_version, + ), + ] + +_simple_transition = rule( + implementation = _impl, + attrs = { + "python_version": attr.string( + doc = "The input python version which we transition on.", + ), + "skip": attr.bool( + doc = "Whether to skip the test", + ), + "want_version": attr.string( + doc = "The python version that we actually expect to receive.", + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + toolchains = [ + config_common.toolchain_type( + EXEC_TOOLS_TOOLCHAIN_TYPE, + mandatory = False, + ), + ], + cfg = _python_version_transition, +) + +def _test_transitions(*, name, tests, skip = False): + """A reusable rule so that we can split the tests.""" + targets = {} + for test_name, (input_version, want_version) in tests.items(): + target_name = "{}_{}".format(name, test_name) + targets["python_" + test_name] = target_name + rt_util.helper_target( + _simple_transition, + name = target_name, + python_version = input_version, + want_version = want_version, + skip = skip, + ) + + analysis_test( + name = name, + impl = _test_transition_impl, + targets = targets, + ) + +def _test_transition_impl(env, targets): + # Check that the forwarded version from the PyRuntimeInfo is correct + for target in dir(targets): + test_info = env.expect.that_target(getattr(targets, target)).provider( + TestInfo, + factory = lambda v, meta: v, + ) + env.expect.that_str(test_info.got).equals(test_info.want) + +def _test_full_version(name): + """Check that python_version transitions work. + + Expectation is to get the same full version that we input. + """ + _test_transitions( + name = name, + tests = { + v.replace(".", "_"): (v, v) + for v in TOOL_VERSIONS + }, + ) + +_analysis_tests.append(_test_full_version) + +def _test_minor_versions(name): + """Ensure that MINOR_MAPPING versions are correctly selected.""" + _test_transitions( + name = name, + skip = not BZLMOD_ENABLED, + tests = { + minor.replace(".", "_"): (minor, full) + for minor, full in MINOR_MAPPING.items() + }, + ) + +_analysis_tests.append(_test_minor_versions) + +def _test_default(name): + """Check the default version. + + Lastly, if we don't provide any version to the transition, we should + get the default version + """ + default_version = full_version( + version = DEFAULT_PYTHON_VERSION, + minor_mapping = MINOR_MAPPING, + ) if DEFAULT_PYTHON_VERSION else "" + + _test_transitions( + name = name, + skip = not BZLMOD_ENABLED, + tests = { + "default": (None, default_version), + }, + ) + +_analysis_tests.append(_test_default) + +def transitions_test_suite(name): + test_suite( + name = name, + tests = _analysis_tests, + ) From 20fd356ebb4b17e9a369e362bc64a9ed79875d34 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 5 Apr 2025 22:08:12 +0900 Subject: [PATCH 15/15] fix tests on bazel <8 --- tests/toolchains/transitions/transitions_tests.bzl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/toolchains/transitions/transitions_tests.bzl b/tests/toolchains/transitions/transitions_tests.bzl index b5d332fed4..bddd1745f0 100644 --- a/tests/toolchains/transitions/transitions_tests.bzl +++ b/tests/toolchains/transitions/transitions_tests.bzl @@ -116,6 +116,10 @@ def _test_transitions(*, name, tests, skip = False): def _test_transition_impl(env, targets): # Check that the forwarded version from the PyRuntimeInfo is correct for target in dir(targets): + if not target.startswith("python"): + # Skip other attributes that might be not the ones we set (e.g. to_json, to_proto). + continue + test_info = env.expect.that_target(getattr(targets, target)).provider( TestInfo, factory = lambda v, meta: v,