Skip to content

fix(py): stamp py_pex_binary interpreter constraints from the binary's own interpreter#1344

Merged
jbedard merged 1 commit into
mainfrom
pex-on-providers
Jul 23, 2026
Merged

fix(py): stamp py_pex_binary interpreter constraints from the binary's own interpreter#1344
jbedard merged 1 commit into
mainfrom
pex-on-providers

Conversation

@jbedard

@jbedard jbedard commented Jul 19, 2026

Copy link
Copy Markdown
Member

Changes are visible to end-users: yes

  • Searched for relevant documentation and updated as needed: yes
  • Breaking change (forces users to change their own code or config): no
  • Suggested release notes appear below: yes

No API changes. py_pex_binary no longer resolves a Python toolchain itself.

  • Interpreter constraints now track the binary's own interpreter version. The {major}/{minor}/{patch} placeholders in python_interpreter_constraints are substituted from the version of the packaged binary (its python_version), not the py_pex_binary rule's toolchain. Previously a binary pinned to e.g. 3.13 in a 3.11 workspace produced a PEX advertising 3.11 — a version it wasn't built for. No action needed; PEXes built from version-mismatched binaries now advertise the correct interpreter.
  • The brittle repo-name exclusion list (bzlmod vs. WORKSPACE, + vs. ~ separators) is gone, so interpreter/venv exclusion is robust across Bazel versions and setups.

Test plan

  • Covered by existing test cases
  • New test cases added

@jbedard
jbedard requested a review from xangcastle July 19, 2026 08:49
@aspect-workflows

aspect-workflows Bot commented Jul 19, 2026

Copy link
Copy Markdown

✨ Aspect Workflows Tasks

📅 Wed Jul 22 21:21:52 UTC 2026

✅ 40 successful tasks

  • ✅ buildifier · ⏱ 19.7s · 🐙 GitHub Actions · ☑️ Check
    💬 Format complete (clean)
  • ✅ gazelle · ⏱ 21.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Gazelle complete (clean)
  • ✅ test-e2e-bazel-8 [test] · ⏱ 3m 10s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (232/232 passed)
  • ✅ test-e2e-bazel-9 [test] · ⏱ 3m 12s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (226/226 passed)
  • ✅ test-e2e-interpreter-build-config-bazel-8 [test] · ⏱ 19.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-build-config-bazel-9 [test] · ⏱ 58.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-input-validation-bazel-8 [test] · ⏱ 14.9s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-input-validation-bazel-9 [test] · ⏱ 18.6s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-runtime-metadata-bazel-8 [test] · ⏱ 20s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-e2e-interpreter-runtime-metadata-bazel-9 [test] · ⏱ 42.2s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-e2e-interpreter-toolchain-settings-bazel-8 [test] · ⏱ 17.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-toolchain-settings-bazel-9 [test] · ⏱ 39.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-proto-grpc-python-bazel-8 [test] · ⏱ 1m 50s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-proto-grpc-python-bazel-9 [test] · ⏱ 1m 15s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-python-interop-bazel-8 [test] · ⏱ 32.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (7/7 passed)
  • ✅ test-e2e-rules-python-interop-bazel-9 [test] · ⏱ 55s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (7/7 passed)
  • ✅ test-examples-debugger-bazel-8 [test] · ⏱ 23.2s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-debugger-bazel-9 [test] · ⏱ 1m 9s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-dev_deps-bazel-8 [test] · ⏱ 34.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-dev_deps-bazel-9 [test] · ⏱ 1m 16s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-django-bazel-8 [test] · ⏱ 24.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-django-bazel-9 [test] · ⏱ 1m 12s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-multi_version-bazel-8 [test] · ⏱ 29.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-examples-multi_version-bazel-9 [test] · ⏱ 1m 6s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-examples-protobuf-bazel-8 [test] · ⏱ 1m 15s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-protobuf-bazel-9 [test] · ⏱ 1m 42s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-py_binary-bazel-8 [test] · ⏱ 19s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_binary-bazel-9 [test] · ⏱ 36.1s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-py_pex_binary-bazel-8 [test] · ⏱ 25.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_pex_binary-bazel-9 [test] · ⏱ 41.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-py_venv-bazel-8 [test] · ⏱ 27.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (3/3 passed)
  • ✅ test-examples-py_venv-bazel-9 [test] · ⏱ 37s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (3/3 passed)
  • ✅ test-examples-pytest-bazel-8 [test] · ⏱ 36.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (11/11 passed)
  • ✅ test-examples-pytest-bazel-9 [test] · ⏱ 1m 13s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (11/11 passed)
  • ✅ test-examples-uv_pip_compile-bazel-8 [test] · ⏱ 25.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-uv_pip_compile-bazel-9 [test] · ⏱ 42.7s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-virtual_deps-bazel-8 [test] · ⏱ 21.7s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-virtual_deps-bazel-9 [test] · ⏱ 46.9s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-root-bazel-8 [test] · ⏱ 3m 11s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (269/269 passed)
  • ✅ test-root-bazel-9 [test] · ⏱ 3m 28s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (268/268 passed)

⏱ Last updated Wed Jul 22 21:26:31 UTC 2026 · 📊 GitHub API quota 244/15,000 (2% used, resets in 56m)
🚀 Powered by Aspect CLI (v2026.28.2)  |  Aspect Build · X · LinkedIn · YouTube

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

py_binary startup benchmark

Version Mean (ms) Median (ms) ± stddev vs BCR vs main Build (s)
BCR 1.11.7 (baseline) 186.931 184.012 ±10.434 57.05
HEAD main 60.080 59.441 ±1.954 -67.9% 11.34
This PR 60.443 60.008 ±1.941 -67.7% +0.6% 7.60

Measured with hyperfine --warmup 5 --runs 50 on Linux
Gate: PR vs HEAD main (threshold: 10%). BCR is shown only as a historical baseline.
Build time: cold bazel build //:bench with isolated output base, no disk cache.

sys.path quality

Version sys.path entries distinct site-packages roots duplicate realpaths
BCR 1.11.7 (baseline) 6 1 0
HEAD main 7 2 0
This PR 7 2 0

sys.path quality measured by bench_syspath inside the assembled venv. Duplicate realpaths indicate symlink redundancy; many distinct site-packages roots suggest an inefficient venv layout.

Bazel analysis benchmark

Version Mean (ms) Median (ms) ± stddev vs BCR vs main Packages Targets
BCR 2.0.0-alpha.4 (baseline) 10158.400 10158.865 ±140.071 101 301
HEAD main 8964.862 8964.203 ±174.592 -11.7% 101 301
This PR 9127.482 9162.433 ±109.724 -10.1% +1.8% 101 301

Measured with hyperfine --warmup 1 --runs 10 on Linux
Gate: PR vs HEAD main (threshold: 10%). BCR is shown only as a historical baseline.
Command: cold bazel build --nobuild //workspace/... with isolated output base, no disk cache.

Auxiliary metrics

Version Loaded packages Configured targets
BCR 2.0.0-alpha.4 (baseline) 101 301
HEAD main 101 301
This PR 101 301

@jbedard
jbedard force-pushed the pex-on-providers branch from bf4e90a to 5e98541 Compare July 20, 2026 01:51
@jbedard

jbedard commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

@tamird (the robot) please check if there are any concerns with the proposed 2.x breaking changes here

@tamird tamird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The version transition is sound, but I found four correctness gaps around free-threaded interpreters, interpreter-file filtering, arbitrary runtime-data rules, and the documented py_venv input. OpenAI has no current py_pex_binary consumers; these affect the upstream API contract.

Comment thread py/private/py_pex_binary.bzl
Comment thread py/private/py_pex_binary.bzl
Comment thread py/private/py_pex_binary.bzl
Comment thread py/private/py_pex_binary.bzl Outdated
@jbedard
jbedard force-pushed the pex-on-providers branch from 5e98541 to a100f12 Compare July 20, 2026 23:15
@jbedard
jbedard requested a review from tamird July 21, 2026 19:29

@tamird tamird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-reviewed because Jason requested another pass in #ext-aspect-build. The current head still has the four correctness gaps from the prior review; the provider/aspect source files are byte-identical to the previously reviewed head, and the only changed PR file is a stronger PEX-content test.

Free-threaded binaries can still advertise an ABI-agnostic constraint with a pinned PEX version that predates the required constraint syntax; filtering still drops whole external interpreter repositories while retaining a supported main-repository interpreter; scalar/non-list data attributes can still fail aspect analysis; and the documented direct py_venv input still fails the single-entrypoint invariant. The original inline anchors remain current:
#1344 (comment)
#1344 (comment)
#1344 (comment)
#1344 (comment)

The version-transition regression and current CI are otherwise sound. These API-contract issues should be resolved or the supported surface narrowed before 2.x.

— tamirdex

@tamird tamird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two additional issues from the cumulative packaging pass:

A wheel behind the common filegroup(srcs=[...]) data wrapper is no longer recognized: the closure aspect walks only deps/data/actual/venv, so the expanded install tree is sent as loose --source content instead of a PEX distribution. The previous runfile/site-packages classifier handled this case. Please cover a wheel-under-filegroup input and either traverse label-bearing srcs/propagate the provider or retain a safe fallback (py/private/py_pex_binary.bzl:114-123,187-211).

The new source mapper also compares every expanded runfile child with every wheel-tree prefix, making packaging O(files x wheels). Large PEXes can have both many wheels and many installed files; use structural filtering or an indexed lookup before this becomes the 2.x path.

— tamirdex

@jbedard
jbedard force-pushed the pex-on-providers branch 2 times, most recently from cd0f0c9 to 2defdec Compare July 22, 2026 01:06
@jbedard
jbedard enabled auto-merge (squash) July 22, 2026 21:19
@jbedard
jbedard force-pushed the pex-on-providers branch from 2defdec to 6e719c6 Compare July 22, 2026 21:21
@jbedard
jbedard requested a review from tamird July 22, 2026 21:21

@tamird tamird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-reviewed at Jason’s explicit request. The updated head fixes the earlier scalar-attribute and filegroup-wheel handling and correctly derives the interpreter version from the packaged binary.

One blocker remains in the existing ABI discussion: #1344 (comment). interpreter_files_and_version drops the interpreter ABI, so a free-threaded 3.13 binary containing cp313t extensions is advertised as CPython==3.13.*; PEX may then select ordinary cp313 and fail at runtime. The tool is still pinned to PEX 2.3.1, which predates Python 3.13 and free-threaded interpreter support. The added regression only inspects PEX-INFO and does not execute a free-threaded native-wheel PEX.

Please propagate the ABI and upgrade PEX to a version supporting Python 3.13 free-threaded constraints, or explicitly reject unsupported free-threaded inputs, and add a real cp313t execution regression. Exact-head CI is still pending.

— tamirdex

@jbedard
jbedard merged commit f20cf52 into main Jul 23, 2026
87 checks passed
@jbedard
jbedard deleted the pex-on-providers branch July 23, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants