Skip to content

Commit 5cda78b

Browse files
committed
refactor: own the interpreter runtime toolchain rules instead of rules_python's
Generated interpreter repos now instantiate a rules_py py_runtime_toolchain (py/private/interpreter/runtime.bzl, with a rules_py PyRuntimeInfo) instead of rules_python's py_runtime + py_runtime_pair + py_exec_tools_toolchain. One :runtime target per repo backs both toolchain registrations. Toolchain types are unchanged (@bazel_tools python type; rules_python's exec_tools type, with its exec_tools.exec_runtime/precompiler payload shape preserved), so rules_python-provisioned interpreters still satisfy whl_install/site-merge and rules_py still consumes user-registered rules_python py_runtime toolchains. This shrinks each interpreter repo's rules_python loads from 31 .bzl files to the 12 of the deliberately kept py_cc_toolchain chain, insulating provisioned toolchains from rules_python implementation churn, and leaves the runtime layer pinning no rules_python minimum version. New coverage: e2e/rules-python-interop (isolated module) pins that a module mixing both rulesets shares one rules_python-provisioned interpreter, including a $(PYTHON3) location diff_test; e2e/cases/rules-python-consumers pins that rules_python's own toolchain consumers (current_py_toolchain, exec-tools readers) work against rules_py-backed toolchains. Narrow breaks: - Generated interpreter repo targets are renamed: @python_X//:py3_runtime, :runtime_pair, and :exec_tools_toolchain are now a single @python_X//:runtime. Only direct label references need updating; toolchain resolution is unaffected. - rules_py-provisioned runtimes carry rules_py's PyRuntimeInfo, not rules_python's. Provider-indexed access (target[PyRuntimeInfo] with rules_python's symbol) on these targets or resolved toolchains breaks; duck-typed field access keeps working. As a consequence, rules_python's py_binary/py_test cannot execute on rules_py-provisioned toolchains (they read provider fields rules_py does not carry) — the same one-way interop direction #1231 establishes for PyInfo. - The exec-tools toolchain rules_py registers provides precompiler = None; enabling rules_python precompiling while @python_interpreters toolchains are registered first is unsupported.
1 parent f37599e commit 5cda78b

26 files changed

Lines changed: 406 additions & 132 deletions

File tree

docs/interpreter.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,23 @@ This interpreter provisioning is designed to coexist with `rules_python`:
273273
registration, so these interpreters work with all existing Python rules.
274274
- The `@rules_python//python/config_settings:python_version` flag is kept in
275275
sync with our own version flag via build transitions.
276-
- `py_runtime` and `py_runtime_pair` from `rules_python` are used to create
277-
the runtime providers.
276+
- Runtimes registered with `rules_python`'s `py_runtime` / `py_runtime_pair`
277+
(for example a system interpreter) remain usable by rules_py rules, which
278+
read the runtime fields structurally.
279+
- Build actions that run an interpreter (wheel installation, site-packages
280+
merging) resolve `@aspect_rules_py//py/private/toolchain:exec_tools_toolchain_type`,
281+
which `interpreters.toolchain()` registers. Interpreters provisioned solely
282+
through `rules_python`'s `python.toolchain()` do not satisfy it; register a
283+
toolchain providing `ToolchainInfo(exec_runtime = <runtime>)` under that
284+
type to bridge one. rules_py registers nothing under `rules_python`'s
285+
exec-tools type, leaving it — including precompiling — entirely to
286+
`rules_python`.
287+
288+
Note that runtimes provisioned by `interpreters.toolchain()` carry rules_py's
289+
own `PyRuntimeInfo` (exported from `@aspect_rules_py//py:defs.bzl`), not
290+
`rules_python`'s provider. `rules_python`-defined executables analyze and run
291+
on them, but no coverage tool is bundled, and Starlark indexing the runtime
292+
with `rules_python`'s provider symbol must switch to rules_py's.
278293

279294
You can migrate incrementally: replace `python.toolchain()` calls with
280295
`interpreters.toolchain()` and remove the `rules_python` interpreter

e2e/cases/rules-python-consumers/consumers_test.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ def read(rel):
2222
python3 = read("python3_var.txt")
2323
assert "python_interpreters+python_3_13" in python3, python3
2424

25-
# The exec-tools payload carries rules_python's expected shape: an exec
26-
# runtime from a rules_py-provisioned repo, and a precompiler field (None).
25+
# rules_py's exec-tools type serves a rules_py-provisioned runtime. rules_py
26+
# registers nothing under rules_python's own exec-tools type, but it must
27+
# still resolve — rules_python's toolchain wraps the ambient standard-type
28+
# runtime through its own exec-interpreter indirection.
2729
facts = read("exec_tools_facts.txt").splitlines()
2830
assert "python_interpreters+" in facts[0], facts
29-
assert facts[1] == "None", facts
31+
assert facts[1] != "None", facts
3032

3133
assert read("python_launcher.txt") == "3.11"
3234

e2e/cases/rules-python-consumers/exec_tools_facts.bzl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
1-
"""Materialises the resolved exec-tools toolchain payload for assertion.
1+
"""Materialises the resolved exec-tools toolchain payloads for assertion.
22
3-
Reads the fields rules_python's consumers access — `exec_tools.exec_runtime`
4-
and `exec_tools.precompiler` — so the test pins that rules_py's registration
5-
under rules_python's toolchain type keeps their expected shape.
3+
Line 1: the rules_py exec-tools type's runtime — must be a rules_py-provisioned
4+
interpreter. Line 2: rules_python's own exec-tools type — rules_py registers
5+
nothing there, but it must still resolve: rules_python's toolchain wraps
6+
whatever the standard Python toolchain provides (here, a rules_py runtime,
7+
reached through their own exec-interpreter indirection).
68
"""
79

8-
EXEC_TOOLS_TOOLCHAIN = "@rules_python//python:exec_tools_toolchain_type"
10+
EXEC_TOOLS_TOOLCHAIN = "@aspect_rules_py//py/private/toolchain:exec_tools_toolchain_type"
11+
RPY_EXEC_TOOLS_TOOLCHAIN = "@rules_python//python:exec_tools_toolchain_type"
912

1013
def _exec_tools_facts_impl(ctx):
11-
exec_tools = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN].exec_tools
14+
exec_runtime = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN].exec_runtime
15+
rpy_info = ctx.toolchains[RPY_EXEC_TOOLS_TOOLCHAIN]
16+
rpy_runtime = rpy_info.exec_tools.exec_runtime if rpy_info else None
1217
out = ctx.actions.declare_file(ctx.label.name + ".txt")
1318
ctx.actions.write(out, "{}\n{}\n".format(
14-
exec_tools.exec_runtime.interpreter.path,
15-
exec_tools.precompiler,
19+
exec_runtime.interpreter.path,
20+
rpy_runtime.interpreter.path if rpy_runtime else None,
1621
))
1722
return [DefaultInfo(files = depset([out]))]
1823

1924
exec_tools_facts = rule(
2025
implementation = _exec_tools_facts_impl,
21-
toolchains = [EXEC_TOOLS_TOOLCHAIN],
26+
toolchains = [
27+
EXEC_TOOLS_TOOLCHAIN,
28+
config_common.toolchain_type(RPY_EXEC_TOOLS_TOOLCHAIN, mandatory = False),
29+
],
2230
)
2331

2432
def _set_python_version_impl(settings, attr):

e2e/cases/rules-python-consumers/report_exec_version.bzl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"""Runnable that prints the resolved exec-tools runtime version.
22
3-
Resolves rules_python's exec-tools toolchain directly (no py_* version
3+
Resolves rules_py's exec-tools toolchain directly (no py_* version
44
transition), so the reported version reflects the version flag only if the flag
55
is authoritative in the interpreter hub. `bazel run` it and assert on stdout.
66
"""
77

8-
_EXEC_TOOLS_TOOLCHAIN = "@rules_python//python:exec_tools_toolchain_type"
8+
_EXEC_TOOLS_TOOLCHAIN = "@aspect_rules_py//py/private/toolchain:exec_tools_toolchain_type"
99

1010
def _report_exec_version_impl(ctx):
11-
version_info = ctx.toolchains[_EXEC_TOOLS_TOOLCHAIN].exec_tools.exec_runtime.interpreter_version_info
11+
version_info = ctx.toolchains[_EXEC_TOOLS_TOOLCHAIN].exec_runtime.interpreter_version_info
1212
launcher = ctx.actions.declare_file(ctx.label.name + ".sh")
1313
ctx.actions.write(
1414
output = launcher,

e2e/cases/rules-python-consumers/test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env bash
22
#
3-
# report_exec_version resolves rules_python's exec-tools toolchain without the
3+
# report_exec_version resolves rules_py's exec-tools toolchain without the
44
# py_* version transition, so it prints the requested version only if the
55
# version flag is authoritative in the interpreter hub. Both the native flag
66
# and @rules_python's fallback must select the requested version.

e2e/cases/uv-deps-650/crossbuild/toolchain_test.bzl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ path into a file, so sh_test scripts can verify which interpreter was selected
55
under cross-compilation (target platform ≠ exec platform).
66
"""
77

8-
EXEC_TOOLS_TOOLCHAIN = "@rules_python//python:exec_tools_toolchain_type"
8+
EXEC_TOOLS_TOOLCHAIN = "@aspect_rules_py//py/private/toolchain:exec_tools_toolchain_type"
99

1010
def _exec_python_path_impl(ctx):
11-
exec_runtime = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN].exec_tools.exec_runtime
11+
exec_runtime = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN].exec_runtime
1212
out = ctx.actions.declare_file(ctx.label.name + ".txt")
1313
ctx.actions.write(out, exec_runtime.interpreter.path)
1414
return [DefaultInfo(files = depset([out]))]

e2e/interpreter-runtime-metadata/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ load(":runtime_metadata_test.bzl", "runtime_metadata_test")
33
runtime_metadata_test(
44
name = "regular",
55
abi_flags = "",
6-
runtime = "@python_3_15_x86_64_unknown_linux_gnu//:py3_runtime",
6+
runtime = "@python_3_15_x86_64_unknown_linux_gnu//:runtime",
77
)
88

99
runtime_metadata_test(
1010
name = "freethreaded",
1111
abi_flags = "t",
12-
runtime = "@python_3_15_x86_64_unknown_linux_gnu_freethreaded//:py3_runtime",
12+
runtime = "@python_3_15_x86_64_unknown_linux_gnu_freethreaded//:runtime",
1313
)

e2e/interpreter-runtime-metadata/runtime_metadata_test.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Checks metadata on a provisioned PBS Python runtime."""
22

3-
load("@rules_python//python:py_runtime_info.bzl", "PyRuntimeInfo")
3+
load("@aspect_rules_py//py:defs.bzl", "PyRuntimeInfo")
44

55
def _assert_equal(description, expected, actual):
66
if actual != expected:

examples/debugger/.bazelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Default Python version for targets without an explicit python_version.
2+
common --@aspect_rules_py//py:python_version=3.11
3+
14
# Default to debug venv — includes debugpy
25
common --@pypi//dep_group=debug
36

examples/debugger/MODULE.bazel

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
bazel_dep(name = "aspect_rules_py")
44
bazel_dep(name = "bazel_skylib", version = "1.4.2")
55
bazel_dep(name = "rules_cc", version = "0.2.16")
6-
bazel_dep(name = "rules_python", version = "1.9.0")
76
bazel_dep(name = "platforms", version = "1.0.0")
87
bazel_dep(name = "llvm", version = "0.7.1")
98

@@ -12,11 +11,11 @@ local_path_override(
1211
path = "../..",
1312
)
1413

15-
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
16-
python.toolchain(
17-
is_default = True,
18-
python_version = "3.11",
19-
)
14+
interpreters = use_extension("@aspect_rules_py//py:extensions.bzl", "python_interpreters")
15+
interpreters.toolchain(python_version = "3.11")
16+
use_repo(interpreters, "python_interpreters")
17+
18+
register_toolchains("@python_interpreters//:all")
2019

2120
uv = use_extension("@aspect_rules_py//uv:extensions.bzl", "uv")
2221
uv.declare_hub(hub_name = "pypi")

0 commit comments

Comments
 (0)