Skip to content

feat: freethreaded support for the builder API #3063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ END_UNRELEASED_TEMPLATE
* (toolchains) `local_runtime_repo` now checks if the include directory exists
before attempting to watch it, fixing issues on macOS with system Python
({gh-issue}`3043`).
* (pypi) The pipstar `defaults` configuration now supports any custom platform
name.

{#v0-0-0-added}
### Added
Expand Down
115 changes: 112 additions & 3 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,121 @@ register_toolchains("@pythons_hub//:all")
# Install twine for our own runfiles wheel publishing and allow bzlmod users to use it.

pip = use_extension("//python/extensions:pip.bzl", "pip")

# NOTE @aignas 2025-07-06: we define these platforms to keep backwards compatibility with the
# current `experimental_index_url` implementation. Whilst we stabilize the API this list may be
# updated with a mention in the CHANGELOG.
[
pip.default(
arch_name = cpu,
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 "",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this should be >= 3.13

os_name = "linux",
platform = "linux_{}{}".format(cpu, freethreaded),
platform_tags = [
"linux_*_{}".format(cpu),
"manylinux_*_{}".format(cpu),
],
want_abis = [
"cp{0}{1}t",
"none",
] if freethreaded else [],
)
for cpu in [
"x86_64",
"aarch64",
# TODO @aignas 2025-05-19: only leave tier 0-1 cpus when stabilizing the
# `pip.default` extension. i.e. drop the below values - users will have to
# define themselves if they need them.
"arm",
"ppc",
"s390x",
]
for freethreaded in [
"",
"_freethreaded",
]
]

[
pip.default(
arch_name = cpu,
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, freethreaded),
platform_tags = [
"macosx_*_{}".format(suffix)
for suffix in platform_tag_cpus
],
want_abis = [
"cp{0}{1}t",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: {0}{1} is fine for experimental api, but we should use named params for the final API. Please add a todo, at the least.

"none",
] if freethreaded else [],
)
for cpu, platform_tag_cpus in {
"aarch64": [
"universal2",
"arm64",
],
"x86_64": [
"universal2",
"x86_64",
],
}.items()
for freethreaded in [
"",
"_freethreaded",
]
]

[
pip.default(
arch_name = "x86_64",
config_settings = [
"@platforms//cpu:x86_64",
"@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_x86_64{}".format(freethreaded),
platform_tags = ["win_amd64"],
want_abis = [
"cp{0}{1}t",
"none",
] if freethreaded else [],
)
for freethreaded in [
"",
"_freethreaded",
]
]

pip.parse(
# NOTE @aignas 2024-10-26: We have an integration test that depends on us
# being able to build sdists for this hub, so explicitly set this to False.
#
# how do we test sdists? Maybe just worth adding a single sdist somewhere?
download_only = False,
experimental_index_url = "https://pypi.org/simple",
hub_name = "rules_python_publish_deps",
Expand Down Expand Up @@ -155,22 +267,19 @@ dev_pip = use_extension(
dev_dependency = True,
)
dev_pip.parse(
download_only = True,
experimental_index_url = "https://pypi.org/simple",
hub_name = "dev_pip",
parallel_download = False,
python_version = "3.11",
requirements_lock = "//docs:requirements.txt",
)
dev_pip.parse(
download_only = True,
experimental_index_url = "https://pypi.org/simple",
hub_name = "dev_pip",
python_version = "3.13",
requirements_lock = "//docs:requirements.txt",
)
dev_pip.parse(
download_only = True,
experimental_index_url = "https://pypi.org/simple",
hub_name = "pypiserver",
python_version = "3.11",
Expand Down
17 changes: 17 additions & 0 deletions examples/bzlmod/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ pip.whl_mods(
)
use_repo(pip, "whl_mods_hub")

# Because below we are using `windows_aarch64` platform, we have to define various
# properties for it.
pip.default(
arch_name = "aarch64",
config_settings = [
"@platforms//os:windows",
"@platforms//cpu:aarch64",
],
env = {
"platform_version": "0",
},
os_name = "windows",
platform = "windows_aarch64",
platform_tags = ["win_amd64"],
want_abis = [], # default to all ABIs
)

# To fetch pip dependencies, use pip.parse. We can pass in various options,
# but typically we pass requirements and the Python version. The Python
# version must have been configured by a corresponding `python.toolchain()`
Expand Down
17 changes: 10 additions & 7 deletions python/private/pypi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ bzl_library(
":whl_config_setting_bzl",
":whl_library_bzl",
":whl_repo_name_bzl",
":whl_target_platforms_bzl",
"//python/private:full_version_bzl",
"//python/private:normalize_name_bzl",
"//python/private:version_bzl",
Expand Down Expand Up @@ -209,7 +208,7 @@ bzl_library(
":parse_requirements_txt_bzl",
":pypi_repo_utils_bzl",
":requirements_files_by_platform_bzl",
":whl_target_platforms_bzl",
":select_whl_bzl",
"//python/private:normalize_name_bzl",
"//python/private:repo_utils_bzl",
],
Expand Down Expand Up @@ -252,10 +251,6 @@ bzl_library(
bzl_library(
name = "pep508_env_bzl",
srcs = ["pep508_env.bzl"],
deps = [
":pep508_platform_bzl",
"//python/private:version_bzl",
],
)

bzl_library(
Expand Down Expand Up @@ -363,6 +358,15 @@ bzl_library(
],
)

bzl_library(
name = "select_whl_bzl",
srcs = ["select_whl.bzl"],
deps = [
":parse_whl_name_bzl",
"//python/private:version_bzl",
],
)

bzl_library(
name = "simpleapi_download_bzl",
srcs = ["simpleapi_download.bzl"],
Expand Down Expand Up @@ -426,5 +430,4 @@ bzl_library(
bzl_library(
name = "whl_target_platforms_bzl",
srcs = ["whl_target_platforms.bzl"],
deps = [":parse_whl_name_bzl"],
)
10 changes: 5 additions & 5 deletions python/private/pypi/evaluate_markers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ def evaluate_markers(*, requirements, platforms):
for req_string, platform_strings in requirements.items():
req = requirement(req_string)
for platform_str in platform_strings:
env = platforms.get(platform_str)
if not env:
fail("Please define platform: '{}'".format(platform_str))
plat = platforms.get(platform_str)
if not plat:
continue

if evaluate(req.marker, env = env):
if evaluate(req.marker, env = plat.env):
ret.setdefault(req_string, []).append(platform_str)

return ret
Expand All @@ -57,7 +57,7 @@ def evaluate_markers_py(mrctx, *, requirements, python_interpreter, python_inter

Args:
mrctx: repository_ctx or module_ctx.
requirements: list[str] of the requirement file lines to evaluate.
requirements: {type}`dict[str, list[str]]` of the requirement file lines to evaluate.
python_interpreter: str, path to the python_interpreter to use to
evaluate the env markers in the given requirements files. It will
be only called if the requirements files have env markers. This
Expand Down
Loading