Skip to content

Commit bf4e90a

Browse files
committed
fix: migrate py_pex_binary to use providers
1 parent d4bb2ed commit bf4e90a

8 files changed

Lines changed: 253 additions & 83 deletions

File tree

e2e/cases/pex-interpreter-exclusion/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ py_pex_binary(
2727
python_interpreter_constraints = [],
2828
)
2929

30+
# Default constraints are stamped from the binary's version (3.13), not the pex
31+
# rule's toolchain (workspace default 3.11).
32+
py_pex_binary(
33+
name = "app_mismatch_constrained_pex",
34+
binary = ":app_mismatch",
35+
)
36+
3037
py_test(
3138
name = "test_pex_excludes_interpreter",
3239
srcs = ["test_pex_excludes_interpreter.py"],
@@ -36,3 +43,10 @@ py_test(
3643
],
3744
main = "test_pex_excludes_interpreter.py",
3845
)
46+
47+
py_test(
48+
name = "test_pex_constraint_version",
49+
srcs = ["test_pex_constraint_version.py"],
50+
data = [":app_mismatch_constrained_pex"],
51+
main = "test_pex_constraint_version.py",
52+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""The default python_interpreter_constraints must be stamped from the binary's
2+
own interpreter version, not the pex rule's toolchain.
3+
4+
app_mismatch pins python_version 3.13 while this workspace's default is 3.11.
5+
With the default constraint (`CPython=={major}.{minor}.*`) the PEX must advertise
6+
3.13 — the interpreter it was actually built for — otherwise it would refuse to
7+
run on its own target interpreter.
8+
"""
9+
10+
import json
11+
import os
12+
import zipfile
13+
from pathlib import Path
14+
15+
pex = (
16+
Path(os.environ["RUNFILES_DIR"])
17+
/ "_main/pex-interpreter-exclusion/app_mismatch_constrained_pex.pex"
18+
)
19+
with zipfile.ZipFile(pex) as zf:
20+
info = json.loads(zf.read("PEX-INFO"))
21+
22+
assert info["interpreter_constraints"] == ["CPython==3.13.*"], info[
23+
"interpreter_constraints"
24+
]

py/private/BUILD.bazel

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,11 @@ bzl_library(
4747
name = "py_image_layer",
4848
srcs = ["py_image_layer.bzl"],
4949
deps = [
50+
":providers",
5051
":py_info",
5152
":py_info_interop",
53+
"//py/private/py_venv:types",
54+
"//py/private/toolchain:types",
5255
"@bazel_lib//lib:transitions",
5356
"@tar.bzl//tar:mtree",
5457
"@tar.bzl//tar:tar",
@@ -120,8 +123,9 @@ bzl_library(
120123
name = "py_pex_binary",
121124
srcs = ["py_pex_binary.bzl"],
122125
deps = [
126+
":providers",
123127
":py_info",
124-
":py_semantics",
128+
"//py/private/py_venv:types",
125129
"//py/private/toolchain:types",
126130
],
127131
)

py/private/py_image_layer.bzl

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ Sharing model:
3535
load("//py/private:providers.bzl", "PyWheelsInfo")
3636
load("//py/private:py_info.bzl", "PyInfo")
3737
load("//py/private:py_info_interop.bzl", "has_py_info")
38-
load("//py/private/toolchain:types.bzl", "PY_TOOLCHAIN")
38+
load("//py/private/py_venv:types.bzl", "PY_VENV_KINDS")
39+
load("//py/private/toolchain:types.bzl", "PY_TOOLCHAIN", "interpreter_files_and_version")
3940

4041
_TAR_TOOLCHAIN = "@tar.bzl//tar/toolchain:type"
4142

@@ -182,8 +183,6 @@ _LayerInfo = provider(
182183
},
183184
)
184185

185-
_PY_VENV_KINDS = ("py_venv", "_py_venv", "_py_venv_lib")
186-
187186
def _collect_from_deps(ctx, provider):
188187
"""Walk deps/data/actual/venv and return a list of provider values from each matching dep."""
189188
results = []
@@ -307,11 +306,9 @@ def _layer_aspect_impl(target, ctx):
307306
# (NOT ctx.toolchains) is how the binary reads it back, which correctly
308307
# follows the binary's target-platform transition.
309308
if platform_common.ToolchainInfo in target:
310-
py3 = getattr(target[platform_common.ToolchainInfo], "py3_runtime", None)
311-
if py3 == None or py3.files == None:
309+
interp_depset, _ = interpreter_files_and_version(target)
310+
if interp_depset == None:
312311
return []
313-
direct = [py3.interpreter] if getattr(py3, "interpreter", None) != None else []
314-
interp_depset = depset(direct = direct, transitive = [py3.files])
315312
plan = ctx.attr._layer_tier[PyLayerTierInfo]
316313
interp_group = plan.interpreter_group
317314
interp_layer = None
@@ -399,7 +396,7 @@ def _layer_aspect_impl(target, ctx):
399396

400397
# Skip PyInfo deps (including wheel-leaf targets, which also emit PyInfo) —
401398
# they self-capture via the aspect.
402-
if kind not in _PY_VENV_KINDS and has_py_info(target) and not is_binary:
399+
if kind not in PY_VENV_KINDS and has_py_info(target) and not is_binary:
403400
own_parts = [target[DefaultInfo].files]
404401
for attr_name in ("data", "deps"):
405402
attr_val = getattr(ctx.rule.attr, attr_name, None)
@@ -424,7 +421,7 @@ def _layer_aspect_impl(target, ctx):
424421
else:
425422
own_source.append(own_depset)
426423

427-
if kind in _PY_VENV_KINDS:
424+
if kind in PY_VENV_KINDS:
428425
if PY_TOOLCHAIN in ctx.rule.toolchains:
429426
py_tc = ctx.rule.toolchains[PY_TOOLCHAIN]
430427
if _LayerInfo in py_tc:

0 commit comments

Comments
 (0)