From 849456ec1f6eecc5dfb14e4483924240ef9a3618 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:49:40 +0900 Subject: [PATCH 1/4] feat(pypi): freethreaded support for the builder API DO NOT MERGE: stacked on #3058 This is a continuation of #3058 where we define freethreaded platforms. They need to be used only for particular python versions so I included an extra marker configuration attribute where we are using pipstar marker evaluation before using the platform. I think this in general will be a useful tool to configure only particular platforms for particular python versions Work towards #2548, since this shows how we can define custom platforms Work towards #2747 --- MODULE.bazel | 36 +++++++-- python/private/pypi/extension.bzl | 73 +++++++++++++------ python/private/pypi/pip_repository.bzl | 7 +- .../pypi/requirements_files_by_platform.bzl | 8 +- .../resolve_target_platforms.py | 4 +- tests/pypi/extension/extension_tests.bzl | 33 ++++++--- 6 files changed, 117 insertions(+), 44 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 66297b99a1..49f43568ce 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -70,11 +70,15 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") config_settings = [ "@platforms//cpu:{}".format(cpu), "@platforms//os:linux", + "//python/config_settings:_is_py_freethreaded_{}".format( + "yes" if freethreaded else "no", + ), ], env = {"platform_version": "0"}, + marker = "python_version >= '3.13'" if freethreaded else "", os_name = "linux", - platform = "linux_{}".format(cpu), - whl_abi_tags = [ + platform = "linux_{}{}".format(cpu, freethreaded), + whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ "abi3", "cp{major}{minor}", ], @@ -87,6 +91,10 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") "x86_64", "aarch64", ] + for freethreaded in [ + "", + "_freethreaded", + ] ] [ @@ -95,13 +103,17 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") config_settings = [ "@platforms//cpu:{}".format(cpu), "@platforms//os:osx", + "//python/config_settings:_is_py_freethreaded_{}".format( + "yes" if freethreaded else "no", + ), ], # We choose the oldest non-EOL version at the time when we release `rules_python`. # See https://endoflife.date/macos env = {"platform_version": "14.0"}, + marker = "python_version >= '3.13'" if freethreaded else "", os_name = "osx", - platform = "osx_{}".format(cpu), - whl_abi_tags = [ + platform = "osx_{}{}".format(cpu, freethreaded), + whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ "abi3", "cp{major}{minor}", ], @@ -120,6 +132,10 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") "x86_64", ], }.items() + for freethreaded in [ + "", + "_freethreaded", + ] ] [ @@ -128,11 +144,15 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") config_settings = [ "@platforms//cpu:{}".format(cpu), "@platforms//os:windows", + "//python/config_settings:_is_py_freethreaded_{}".format( + "yes" if freethreaded else "no", + ), ], env = {"platform_version": "0"}, + marker = "python_version >= '3.13'" if freethreaded else "", os_name = "windows", - platform = "windows_{}".format(cpu), - whl_abi_tags = [ + platform = "windows_{}{}".format(cpu, freethreaded), + whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ "abi3", "cp{major}{minor}", ], @@ -141,6 +161,10 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") for cpu, whl_platform_tags in { "x86_64": ["win_amd64"], }.items() + for freethreaded in [ + "", + "_freethreaded", + ] ] pip.parse( diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index 2c7aa8a0e5..0b6165c664 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -30,6 +30,7 @@ load(":hub_repository.bzl", "hub_repository", "whl_config_settings_to_json") load(":parse_requirements.bzl", "parse_requirements") load(":parse_whl_name.bzl", "parse_whl_name") load(":pep508_env.bzl", "env") +load(":pep508_evaluate.bzl", "evaluate") load(":pip_repository_attrs.bzl", "ATTRS") load(":python_tag.bzl", "python_tag") load(":requirements_files_by_platform.bzl", "requirements_files_by_platform") @@ -80,21 +81,27 @@ def _platforms(*, python_version, minor_mapping, config): for platform, values in config.platforms.items(): # TODO @aignas 2025-07-07: this is probably doing the parsing of the version too # many times. - key = "{}{}{}.{}_{}".format( + abi = "{}{}{}.{}".format( python_tag(values.env["implementation_name"]), python_version.release[0], python_version.release[1], python_version.release[2], - platform, ) + key = "{}_{}".format(abi, platform) + + env_ = env( + env = values.env, + os = values.os_name, + arch = values.arch_name, + python_version = python_version.string, + ) + + if values.marker and not evaluate(values.marker, env = env_): + continue platforms[key] = struct( - env = env( - env = values.env, - os = values.os_name, - arch = values.arch_name, - python_version = python_version.string, - ), + env = env_, + triple = "{}_{}_{}".format(abi, values.os_name, values.arch_name), whl_abi_tags = [ v.format( major = python_version.release[0], @@ -203,17 +210,19 @@ def _create_whl_repos( whl_group_mapping = {} requirement_cycles = {} + platforms = _platforms( + python_version = pip_attr.python_version, + minor_mapping = minor_mapping, + config = config, + ) + if evaluate_markers: # This is most likely unit tests pass elif config.enable_pipstar: evaluate_markers = lambda _, requirements: evaluate_markers_star( requirements = requirements, - platforms = _platforms( - python_version = pip_attr.python_version, - minor_mapping = minor_mapping, - config = config, - ), + platforms = platforms, ) else: # NOTE @aignas 2024-08-02: , we will execute any interpreter that we find either @@ -232,7 +241,13 @@ def _create_whl_repos( # spin up a Python interpreter. evaluate_markers = lambda module_ctx, requirements: evaluate_markers_py( module_ctx, - requirements = requirements, + requirements = { + k: { + p: platforms[p].triple + for p in plats + } + for k, plats in requirements.items() + }, python_interpreter = pip_attr.python_interpreter, python_interpreter_target = python_interpreter_target, srcs = pip_attr._evaluate_markers_srcs, @@ -248,18 +263,14 @@ def _create_whl_repos( requirements_osx = pip_attr.requirements_darwin, requirements_windows = pip_attr.requirements_windows, extra_pip_args = pip_attr.extra_pip_args, - platforms = sorted(config.platforms), # here we only need keys + platforms = sorted(platforms), # here we only need keys python_version = full_version( version = pip_attr.python_version, minor_mapping = minor_mapping, ), logger = logger, ), - platforms = _platforms( - python_version = pip_attr.python_version, - minor_mapping = minor_mapping, - config = config, - ), + platforms = platforms, extra_pip_args = pip_attr.extra_pip_args, get_index_urls = get_index_urls, evaluate_markers = evaluate_markers, @@ -346,6 +357,16 @@ def _create_whl_repos( )) whl_libraries[repo_name] = repo.args + if not config.enable_pipstar and "experimental_target_platforms" in repo.args: + whl_libraries[repo_name] |= { + "experimental_target_platforms": sorted({ + # TODO @aignas 2025-07-07: this should be solved in a better way + platforms[candidate].triple.partition("_")[-1]: None + for p in repo.args["experimental_target_platforms"] + for candidate in platforms + if candidate.endswith(p) + }), + } whl_map.setdefault(whl.name, {})[repo.config_setting] = repo_name return struct( @@ -426,7 +447,7 @@ def _whl_repo( ), ) -def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, whl_abi_tags = [], whl_platform_tags = []): +def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, marker = "", whl_abi_tags = [], whl_platform_tags = []): # NOTE @aignas 2025-07-08: the least preferred is the first item in the list if "any" not in whl_platform_tags: # the lowest priority one needs to be the first one @@ -446,6 +467,7 @@ def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, whl_abi_t # defaults for env "implementation_name": "cpython", } | env, + marker = marker, whl_abi_tags = whl_abi_tags, whl_platform_tags = whl_platform_tags, ) @@ -493,13 +515,14 @@ def build_config( config_settings = tag.config_settings, env = tag.env, os_name = tag.os_name, + marker = tag.marker, name = platform.replace("-", "_").lower(), whl_abi_tags = tag.whl_abi_tags, whl_platform_tags = tag.whl_platform_tags, override = mod.is_root, ) - if platform and not (tag.arch_name or tag.config_settings or tag.env or tag.os_name or tag.whl_abi_tags or tag.whl_platform_tags): + if platform and not (tag.arch_name or tag.config_settings or tag.env or tag.os_name or tag.whl_abi_tags or tag.whl_platform_tags or tag.marker): defaults["platforms"].pop(platform) # TODO @aignas 2025-05-19: add more attr groups: @@ -899,6 +922,12 @@ Supported keys: ::::{note} This is only used if the {envvar}`RULES_PYTHON_ENABLE_PIPSTAR` is enabled. :::: +""", + ), + "marker": attr.string( + doc = """\ +A marker which will be evaluated to disable the target platform for certain python versions. This +is especially useful when defining freethreaded platform variants. """, ), # The values for PEP508 env marker evaluation during the lock file parsing diff --git a/python/private/pypi/pip_repository.bzl b/python/private/pypi/pip_repository.bzl index e63bd6c3d1..3df56f24ff 100644 --- a/python/private/pypi/pip_repository.bzl +++ b/python/private/pypi/pip_repository.bzl @@ -94,7 +94,12 @@ def _pip_repository_impl(rctx): extra_pip_args = rctx.attr.extra_pip_args, evaluate_markers = lambda rctx, requirements: evaluate_markers_py( rctx, - requirements = requirements, + requirements = { + # NOTE @aignas 2025-07-07: because we don't distinguish between + # freethreaded and non-freethreaded, it is a 1:1 mapping. + req: {p: p for p in plats} + for req, plats in requirements.items() + }, python_interpreter = rctx.attr.python_interpreter, python_interpreter_target = rctx.attr.python_interpreter_target, srcs = rctx.attr._evaluate_markers_srcs, diff --git a/python/private/pypi/requirements_files_by_platform.bzl b/python/private/pypi/requirements_files_by_platform.bzl index d8d3651461..356bd4416e 100644 --- a/python/private/pypi/requirements_files_by_platform.bzl +++ b/python/private/pypi/requirements_files_by_platform.bzl @@ -37,7 +37,9 @@ def _default_platforms(*, filter, platforms): if not prefix: return platforms - match = [p for p in platforms if p.startswith(prefix)] + match = [p for p in platforms if p.startswith(prefix) or ( + p.startswith("cp") and p.partition("_")[-1].startswith(prefix) + )] else: match = [p for p in platforms if filter in p] @@ -140,7 +142,7 @@ def requirements_files_by_platform( if logger: logger.debug(lambda: "Platforms from pip args: {}".format(platforms_from_args)) - default_platforms = [_platform(p, python_version) for p in platforms] + default_platforms = platforms if platforms_from_args: lock_files = [ @@ -252,6 +254,6 @@ def requirements_files_by_platform( ret = {} for plat, file in requirements.items(): - ret.setdefault(file, []).append(plat) + ret.setdefault(file, []).append(_platform(plat, python_version = python_version)) return ret diff --git a/python/private/pypi/requirements_parser/resolve_target_platforms.py b/python/private/pypi/requirements_parser/resolve_target_platforms.py index c899a943cc..accacf5bfa 100755 --- a/python/private/pypi/requirements_parser/resolve_target_platforms.py +++ b/python/private/pypi/requirements_parser/resolve_target_platforms.py @@ -50,8 +50,8 @@ def main(): hashes = prefix + hashes req = Requirement(entry) - for p in target_platforms: - (platform,) = Platform.from_string(p) + for p, triple in target_platforms.items(): + (platform,) = Platform.from_string(triple) if not req.marker or req.marker.evaluate(platform.env_markers("")): response.setdefault(requirement_line, []).append(p) diff --git a/tests/pypi/extension/extension_tests.bzl b/tests/pypi/extension/extension_tests.bzl index 72cbb61d81..197d07f9e7 100644 --- a/tests/pypi/extension/extension_tests.bzl +++ b/tests/pypi/extension/extension_tests.bzl @@ -58,20 +58,22 @@ def _mod(*, name, default = [], parse = [], override = [], whl_mods = [], is_roo whl_mods = whl_mods, default = default or [ _default( - platform = "{}_{}".format(os, cpu), + platform = "{}_{}{}".format(os, cpu, freethreaded), os_name = os, arch_name = cpu, config_settings = [ "@platforms//os:{}".format(os), "@platforms//cpu:{}".format(cpu), ], + whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else ["abi3", "cp{major}{minor}"], whl_platform_tags = whl_platform_tags, ) - for (os, cpu), whl_platform_tags in { - ("linux", "x86_64"): ["linux_*_x86_64", "manylinux_*_x86_64"], - ("linux", "aarch64"): ["linux_*_aarch64", "manylinux_*_aarch64"], - ("osx", "aarch64"): ["macosx_*_arm64"], - ("windows", "aarch64"): ["win_arm64"], + for (os, cpu, freethreaded), whl_platform_tags in { + ("linux", "x86_64", ""): ["linux_x86_64", "manylinux_*_x86_64"], + ("linux", "x86_64", "_freethreaded"): ["linux_x86_64", "manylinux_*_x86_64"], + ("linux", "aarch64", ""): ["linux_aarch64", "manylinux_*_aarch64"], + ("osx", "aarch64", ""): ["macosx_*_arm64"], + ("windows", "aarch64", ""): ["win_arm64"], }.items() ], ), @@ -113,6 +115,7 @@ def _default( platform = None, whl_platform_tags = None, env = None, + marker = None, whl_abi_tags = None): return struct( arch_name = arch_name, @@ -121,6 +124,7 @@ def _default( whl_platform_tags = whl_platform_tags or [], config_settings = config_settings, env = env or {}, + marker = marker or "", whl_abi_tags = whl_abi_tags or [], ) @@ -447,10 +451,11 @@ torch==2.4.1 ; platform_machine != 'x86_64' \ version = "3.15", ), ], - "pypi_315_torch_linux_x86_64": [ + "pypi_315_torch_linux_x86_64_linux_x86_64_freethreaded": [ whl_config_setting( target_platforms = [ "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", ], version = "3.15", ), @@ -463,7 +468,7 @@ torch==2.4.1 ; platform_machine != 'x86_64' \ "python_interpreter_target": "unit_test_interpreter_target", "requirement": "torch==2.4.1 --hash=sha256:deadbeef", }, - "pypi_315_torch_linux_x86_64": { + "pypi_315_torch_linux_x86_64_linux_x86_64_freethreaded": { "dep_template": "@pypi//{name}:{target}", "python_interpreter_target": "unit_test_interpreter_target", "requirement": "torch==2.4.1+cpu", @@ -853,6 +858,7 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef target_platforms = ( "cp315_linux_aarch64", "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", "cp315_osx_aarch64", "cp315_windows_aarch64", ), @@ -866,6 +872,7 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef target_platforms = ( "cp315_linux_aarch64", "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", "cp315_osx_aarch64", "cp315_windows_aarch64", ), @@ -893,6 +900,7 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef target_platforms = ( "cp315_linux_aarch64", "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", "cp315_osx_aarch64", "cp315_windows_aarch64", ), @@ -906,6 +914,7 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef target_platforms = ( "cp315_linux_aarch64", "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", "cp315_osx_aarch64", "cp315_windows_aarch64", ), @@ -919,6 +928,7 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef target_platforms = ( "cp315_linux_aarch64", "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", "cp315_osx_aarch64", "cp315_windows_aarch64", ), @@ -1072,12 +1082,13 @@ optimum[onnxruntime-gpu]==1.17.1 ; sys_platform == 'linux' pypi.hub_whl_map().contains_exactly({ "pypi": { "optimum": { - "pypi_315_optimum_linux_aarch64_linux_x86_64": [ + "pypi_315_optimum_linux_aarch64_linux_x86_64_linux_x86_64_freethreaded": [ whl_config_setting( version = "3.15", target_platforms = [ "cp315_linux_aarch64", "cp315_linux_x86_64", + "cp315_linux_x86_64_freethreaded", ], config_setting = None, filename = None, @@ -1098,7 +1109,7 @@ optimum[onnxruntime-gpu]==1.17.1 ; sys_platform == 'linux' }) pypi.whl_libraries().contains_exactly({ - "pypi_315_optimum_linux_aarch64_linux_x86_64": { + "pypi_315_optimum_linux_aarch64_linux_x86_64_linux_x86_64_freethreaded": { "dep_template": "@pypi//{name}:{target}", "python_interpreter_target": "unit_test_interpreter_target", "requirement": "optimum[onnxruntime-gpu]==1.17.1", @@ -1124,6 +1135,7 @@ def _test_pipstar_platforms(env): platform = "my{}_{}".format(os, cpu), os_name = os, arch_name = cpu, + marker = "python_version ~= \"3.13\"", config_settings = [ "@platforms//os:{}".format(os), "@platforms//cpu:{}".format(cpu), @@ -1240,6 +1252,7 @@ def _test_build_pipstar_platform(env): "@platforms//cpu:x86_64", ], env = {"implementation_name": "cpython"}, + marker = "", whl_abi_tags = ["none", "abi3", "cp{major}{minor}"], whl_platform_tags = ["any"], ), From c385868f32666fd1a4261aa14af20864190361c6 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 17 Aug 2025 11:18:57 +0900 Subject: [PATCH 2/4] comment: change 'marker' to 'marker_expression' --- MODULE.bazel | 6 +++--- python/private/pypi/extension.bzl | 22 ++++++++++++++-------- tests/pypi/extension/extension_tests.bzl | 8 ++++---- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 49f43568ce..7b266283e4 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -75,7 +75,7 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") ), ], env = {"platform_version": "0"}, - marker = "python_version >= '3.13'" if freethreaded else "", + marker_expression = "python_version >= '3.13'" if freethreaded else "", os_name = "linux", platform = "linux_{}{}".format(cpu, freethreaded), whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ @@ -110,7 +110,7 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") # We choose the oldest non-EOL version at the time when we release `rules_python`. # See https://endoflife.date/macos env = {"platform_version": "14.0"}, - marker = "python_version >= '3.13'" if freethreaded else "", + marker_expression = "python_version >= '3.13'" if freethreaded else "", os_name = "osx", platform = "osx_{}{}".format(cpu, freethreaded), whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ @@ -149,7 +149,7 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") ), ], env = {"platform_version": "0"}, - marker = "python_version >= '3.13'" if freethreaded else "", + marker_expression = "python_version >= '3.13'" if freethreaded else "", os_name = "windows", platform = "windows_{}{}".format(cpu, freethreaded), whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index 0b6165c664..a7b5afe74d 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -96,7 +96,7 @@ def _platforms(*, python_version, minor_mapping, config): python_version = python_version.string, ) - if values.marker and not evaluate(values.marker, env = env_): + if values.marker_expression and not evaluate(values.marker_expression, env = env_): continue platforms[key] = struct( @@ -447,7 +447,7 @@ def _whl_repo( ), ) -def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, marker = "", whl_abi_tags = [], whl_platform_tags = []): +def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, marker_expression = "", whl_abi_tags = [], whl_platform_tags = []): # NOTE @aignas 2025-07-08: the least preferred is the first item in the list if "any" not in whl_platform_tags: # the lowest priority one needs to be the first one @@ -467,7 +467,7 @@ def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, marker = # defaults for env "implementation_name": "cpython", } | env, - marker = marker, + marker_expression = marker_expression, whl_abi_tags = whl_abi_tags, whl_platform_tags = whl_platform_tags, ) @@ -515,14 +515,14 @@ def build_config( config_settings = tag.config_settings, env = tag.env, os_name = tag.os_name, - marker = tag.marker, + marker_expression = tag.marker_expression, name = platform.replace("-", "_").lower(), whl_abi_tags = tag.whl_abi_tags, whl_platform_tags = tag.whl_platform_tags, override = mod.is_root, ) - if platform and not (tag.arch_name or tag.config_settings or tag.env or tag.os_name or tag.whl_abi_tags or tag.whl_platform_tags or tag.marker): + if platform and not (tag.arch_name or tag.config_settings or tag.env or tag.os_name or tag.whl_abi_tags or tag.whl_platform_tags or tag.marker_expression): defaults["platforms"].pop(platform) # TODO @aignas 2025-05-19: add more attr groups: @@ -924,10 +924,16 @@ This is only used if the {envvar}`RULES_PYTHON_ENABLE_PIPSTAR` is enabled. :::: """, ), - "marker": attr.string( + "marker_expression": attr.string( doc = """\ -A marker which will be evaluated to disable the target platform for certain python versions. This -is especially useful when defining freethreaded platform variants. +An environment marker expression that is used to enable/disable platforms for specific python +versions, operating systems or CPU architectures. + +The expression is evaluated during the `bzlmod` extension evaluation and if it resolves to true, +then the platform will be available, otherwise, it will not be available. + +This is especially useful for setting up freethreaded platform variants only for particular Python +versions for which the interpreter builds are available. """, ), # The values for PEP508 env marker evaluation during the lock file parsing diff --git a/tests/pypi/extension/extension_tests.bzl b/tests/pypi/extension/extension_tests.bzl index 197d07f9e7..2dabc510e9 100644 --- a/tests/pypi/extension/extension_tests.bzl +++ b/tests/pypi/extension/extension_tests.bzl @@ -115,7 +115,7 @@ def _default( platform = None, whl_platform_tags = None, env = None, - marker = None, + marker_expression = None, whl_abi_tags = None): return struct( arch_name = arch_name, @@ -124,7 +124,7 @@ def _default( whl_platform_tags = whl_platform_tags or [], config_settings = config_settings, env = env or {}, - marker = marker or "", + marker_expression = marker_expression or "", whl_abi_tags = whl_abi_tags or [], ) @@ -1135,7 +1135,7 @@ def _test_pipstar_platforms(env): platform = "my{}_{}".format(os, cpu), os_name = os, arch_name = cpu, - marker = "python_version ~= \"3.13\"", + marker_expression = "python_version ~= \"3.13\"", config_settings = [ "@platforms//os:{}".format(os), "@platforms//cpu:{}".format(cpu), @@ -1252,7 +1252,7 @@ def _test_build_pipstar_platform(env): "@platforms//cpu:x86_64", ], env = {"implementation_name": "cpython"}, - marker = "", + marker_expression = "", whl_abi_tags = ["none", "abi3", "cp{major}{minor}"], whl_platform_tags = ["any"], ), From b5c01c759d9829d0229f29118aaad99fe9f569c6 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 17 Aug 2025 16:03:59 +0900 Subject: [PATCH 3/4] comment: rename back to marker --- MODULE.bazel | 6 +++--- python/private/pypi/extension.bzl | 12 ++++++------ tests/pypi/extension/extension_tests.bzl | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 35352173b7..4f442bacec 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -75,7 +75,7 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") ), ], env = {"platform_version": "0"}, - marker_expression = "python_version >= '3.13'" if freethreaded else "", + marker = "python_version >= '3.13'" if freethreaded else "", os_name = "linux", platform = "linux_{}{}".format(cpu, freethreaded), whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ @@ -110,7 +110,7 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") # We choose the oldest non-EOL version at the time when we release `rules_python`. # See https://endoflife.date/macos env = {"platform_version": "14.0"}, - marker_expression = "python_version >= '3.13'" if freethreaded else "", + marker = "python_version >= '3.13'" if freethreaded else "", os_name = "osx", platform = "osx_{}{}".format(cpu, freethreaded), whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ @@ -149,7 +149,7 @@ pip = use_extension("//python/extensions:pip.bzl", "pip") ), ], env = {"platform_version": "0"}, - marker_expression = "python_version >= '3.13'" if freethreaded else "", + marker = "python_version >= '3.13'" if freethreaded else "", os_name = "windows", platform = "windows_{}{}".format(cpu, freethreaded), whl_abi_tags = ["cp{major}{minor}t"] if freethreaded else [ diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index 5b9282a223..b23b756aba 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -96,7 +96,7 @@ def _platforms(*, python_version, minor_mapping, config): python_version = python_version.string, ) - if values.marker_expression and not evaluate(values.marker_expression, env = env_): + if values.marker and not evaluate(values.marker, env = env_): continue platforms[key] = struct( @@ -458,7 +458,7 @@ def _whl_repo( ), ) -def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, marker_expression = "", whl_abi_tags = [], whl_platform_tags = []): +def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, marker = "", whl_abi_tags = [], whl_platform_tags = []): # NOTE @aignas 2025-07-08: the least preferred is the first item in the list if "any" not in whl_platform_tags: # the lowest priority one needs to be the first one @@ -478,7 +478,7 @@ def _plat(*, name, arch_name, os_name, config_settings = [], env = {}, marker_ex # defaults for env "implementation_name": "cpython", } | env, - marker_expression = marker_expression, + marker = marker, whl_abi_tags = whl_abi_tags, whl_platform_tags = whl_platform_tags, ) @@ -526,14 +526,14 @@ def build_config( config_settings = tag.config_settings, env = tag.env, os_name = tag.os_name, - marker_expression = tag.marker_expression, + marker = tag.marker, name = platform.replace("-", "_").lower(), whl_abi_tags = tag.whl_abi_tags, whl_platform_tags = tag.whl_platform_tags, override = mod.is_root, ) - if platform and not (tag.arch_name or tag.config_settings or tag.env or tag.os_name or tag.whl_abi_tags or tag.whl_platform_tags or tag.marker_expression): + if platform and not (tag.arch_name or tag.config_settings or tag.env or tag.os_name or tag.whl_abi_tags or tag.whl_platform_tags or tag.marker): defaults["platforms"].pop(platform) _configure( @@ -942,7 +942,7 @@ This is only used if the {envvar}`RULES_PYTHON_ENABLE_PIPSTAR` is enabled. :::: """, ), - "marker_expression": attr.string( + "marker": attr.string( doc = """\ An environment marker expression that is used to enable/disable platforms for specific python versions, operating systems or CPU architectures. diff --git a/tests/pypi/extension/extension_tests.bzl b/tests/pypi/extension/extension_tests.bzl index 25b3ad25f3..55de99b7d9 100644 --- a/tests/pypi/extension/extension_tests.bzl +++ b/tests/pypi/extension/extension_tests.bzl @@ -115,7 +115,7 @@ def _default( auth_patterns = None, config_settings = None, env = None, - marker_expression = None, + marker = None, netrc = None, os_name = None, platform = None, @@ -126,7 +126,7 @@ def _default( auth_patterns = auth_patterns or {}, config_settings = config_settings, env = env or {}, - marker_expression = marker_expression or "", + marker = marker or "", netrc = netrc, os_name = os_name, platform = platform, @@ -1137,7 +1137,7 @@ def _test_pipstar_platforms(env): platform = "my{}{}".format(os, cpu), os_name = os, arch_name = cpu, - marker_expression = "python_version ~= \"3.13\"", + marker = "python_version ~= \"3.13\"", config_settings = [ "@platforms//os:{}".format(os), "@platforms//cpu:{}".format(cpu), @@ -1260,7 +1260,7 @@ def _test_build_pipstar_platform(env): "@platforms//cpu:x86_64", ], env = {"implementation_name": "cpython"}, - marker_expression = "", + marker = "", whl_abi_tags = ["none", "abi3", "cp{major}{minor}"], whl_platform_tags = ["any"], ), From d25c5a660e2b9772a4ab40b511845bff29ba05d1 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 17 Aug 2025 16:07:46 +0900 Subject: [PATCH 4/4] doc: clarify the intent and how the marker is used --- python/private/pypi/extension.bzl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index b23b756aba..331ecf2340 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -947,11 +947,13 @@ This is only used if the {envvar}`RULES_PYTHON_ENABLE_PIPSTAR` is enabled. An environment marker expression that is used to enable/disable platforms for specific python versions, operating systems or CPU architectures. -The expression is evaluated during the `bzlmod` extension evaluation and if it resolves to true, -then the platform will be available, otherwise, it will not be available. +If specified, the expression is evaluated during the `bzlmod` extension evaluation phase and if it +evaluates to `True`, then the platform will be used to construct the hub repositories, otherwise, it +will be skipped. This is especially useful for setting up freethreaded platform variants only for particular Python -versions for which the interpreter builds are available. +versions for which the interpreter builds are available. However, this could be also used for other +things, such as setting up platforms for different `libc` variants. """, ), # The values for PEP508 env marker evaluation during the lock file parsing