Skip to content

Commit d4bb2ed

Browse files
authored
test: add py_pex_binary scenarios (#1343)
### Changes are visible to end-users: no ### Test plan - Covered by existing test cases - New test cases added
1 parent 5c5a818 commit d4bb2ed

6 files changed

Lines changed: 111 additions & 12 deletions

File tree

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,28 @@ py_pex_binary(
1111
python_interpreter_constraints = [],
1212
)
1313

14+
# The interpreter is excluded from the PEX by the binary's *own* toolchain, not
15+
# the pex rule's. 3.13 differs from this workspace's default (3.11), so a leak
16+
# here would mean the exclusion tracked the wrong version.
17+
py_binary(
18+
name = "app_mismatch",
19+
srcs = ["app.py"],
20+
main = "app.py",
21+
python_version = "3.13",
22+
)
23+
24+
py_pex_binary(
25+
name = "app_mismatch_pex",
26+
binary = ":app_mismatch",
27+
python_interpreter_constraints = [],
28+
)
29+
1430
py_test(
1531
name = "test_pex_excludes_interpreter",
1632
srcs = ["test_pex_excludes_interpreter.py"],
17-
data = [":app_pex"],
33+
data = [
34+
":app_mismatch_pex",
35+
":app_pex",
36+
],
1837
main = "test_pex_excludes_interpreter.py",
1938
)

e2e/cases/pex-interpreter-exclusion/test_pex_excludes_interpreter.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22
import zipfile
33
from pathlib import Path
44

5-
PEX = "_main/pex-interpreter-exclusion/app_pex.pex"
5+
# app_pex uses the default toolchain; app_mismatch_pex pins python_version 3.13
6+
# (!= the workspace default) so both the same-version and cross-version
7+
# interpreter-exclusion paths are covered.
8+
PEXES = (
9+
"_main/pex-interpreter-exclusion/app_pex.pex",
10+
"_main/pex-interpreter-exclusion/app_mismatch_pex.pex",
11+
)
612
INTERPRETER_REPO_MARKERS = (
713
"aspect_rules_py++python_interpreters+",
814
"aspect_rules_py~~python_interpreters~",
915
)
1016

11-
pex = Path(os.environ["RUNFILES_DIR"]) / PEX
12-
with zipfile.ZipFile(pex) as zf:
13-
leaked = [
14-
name
15-
for name in zf.namelist()
16-
if any(marker in name for marker in INTERPRETER_REPO_MARKERS)
17-
]
18-
19-
assert not leaked, leaked[:10]
17+
for rel in PEXES:
18+
pex = Path(os.environ["RUNFILES_DIR"]) / rel
19+
with zipfile.ZipFile(pex) as zf:
20+
leaked = [
21+
name
22+
for name in zf.namelist()
23+
if any(marker in name for marker in INTERPRETER_REPO_MARKERS)
24+
]
25+
assert not leaked, (rel, leaked[:10])

e2e/cases/uv-sdist-fallback/BUILD.bazel

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_image_layer", "py_test")
1+
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_image_layer", "py_pex_binary", "py_test")
22
load("@bazel_skylib//rules:build_test.bzl", "build_test")
33

44
# Regression coverage for the sdist-fallback behavior preserved by the
@@ -35,6 +35,23 @@ py_image_layer(
3535
binary = ":image_bin",
3636
)
3737

38+
# py_pex_binary must discover the source-built cowsay's install tree through
39+
# PyWheelsInfo (its analysis-time metadata is empty), the same path image
40+
# layering exercises above.
41+
py_pex_binary(
42+
name = "sdist_pex",
43+
testonly = True,
44+
binary = ":image_bin",
45+
python_interpreter_constraints = [],
46+
)
47+
48+
py_test(
49+
name = "sdist_pex_test",
50+
srcs = ["sdist_pex_test.py"],
51+
data = [":sdist_pex"],
52+
main = "sdist_pex_test.py",
53+
)
54+
3855
filegroup(
3956
name = "image_dependency_layers",
4057
testonly = True,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""py_pex_binary must package a source-built (sdist) wheel.
2+
3+
cowsay 6.0 is forced to build from source (no-binary-package), so its
4+
PyWheelsInfo record carries an install_tree but empty analysis-time metadata.
5+
The refactored py_pex_binary discovers deps through PyWheelsInfo, so it must
6+
still emit cowsay as a `--dependency` even without that metadata, while keeping
7+
the interpreter and venv plumbing out.
8+
"""
9+
10+
import os
11+
import zipfile
12+
from pathlib import Path
13+
14+
pex = Path(os.environ["RUNFILES_DIR"]) / "_main/uv-sdist-fallback/sdist_pex.pex"
15+
with zipfile.ZipFile(pex) as zf:
16+
names = zf.namelist()
17+
18+
assert any(
19+
n.endswith("cowsay/__init__.py") for n in names
20+
), "source-built cowsay missing from pex: {}".format(names[:20])
21+
22+
interpreter = [n for n in names if "python_interpreters" in n]
23+
assert not interpreter, interpreter[:10]
24+
25+
venv_plumbing = [
26+
n for n in names if n.endswith("pyvenv.cfg") or n.endswith(".pth") or ".venv/" in n
27+
]
28+
assert not venv_plumbing, venv_plumbing

py/tests/py-pex-binary/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ py_test(
7878
":defaults_pex",
7979
":inherit_fallback_pex",
8080
":inherit_false_pex",
81+
":print_modules_pex",
8182
],
8283
main = "pex_info_test.py",
8384
deps = ["@pypi//bazel_runfiles"],

py/tests/py-pex-binary/pex_info_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,32 @@ def read_pex(name):
5959
_, info = read_pex("inherit_false_pex")
6060
assert info.get("inherit_path", "false") == "false", info
6161

62+
63+
# print_modules_pex has real deps (a venv + wheels), so it exercises the
64+
# structural exclusions: the sibling venv's `.pth`/`pyvenv.cfg` plumbing and the
65+
# interpreter must be filtered out, while first-party `data` files are kept.
66+
def pex_names(name):
67+
path = r.Rlocation("{}/{}.pex".format(PKG, name))
68+
with zipfile.ZipFile(path) as zf:
69+
return zf.namelist()
70+
71+
72+
names = pex_names("print_modules_pex")
73+
74+
venv_plumbing = [
75+
n
76+
for n in names
77+
if n.endswith("pyvenv.cfg") or n.endswith(".pth") or ".venv/" in n
78+
]
79+
assert not venv_plumbing, venv_plumbing
80+
81+
interpreter = [n for n in names if "python_interpreters" in n]
82+
assert not interpreter, interpreter[:10]
83+
84+
# The wheels arrive as `--dependency` under `.deps/`, not as loose sources.
85+
assert any("cowsay" in n for n in names), "cowsay missing from pex"
86+
87+
# Plain data files travel with the sources (transitive_sources is .py-only).
88+
assert any(n.endswith("/data.txt") for n in names), "data.txt missing from pex"
89+
6290
print("PASS")

0 commit comments

Comments
 (0)