Skip to content

refactor(venv): rename venv.bzl to assemble_venv.bzl and split out wheel plan helpers#1317

Open
xangcastle wants to merge 2 commits into
mainfrom
venv-final-assemble-venv-module
Open

refactor(venv): rename venv.bzl to assemble_venv.bzl and split out wheel plan helpers#1317
xangcastle wants to merge 2 commits into
mainfrom
venv-final-assemble-venv-module

Conversation

@xangcastle

@xangcastle xangcastle commented Jul 16, 2026

Copy link
Copy Markdown
Member

This renames venv.bzl to assemble_venv.bzl to better reflect what the module actually contains, then refactors its internals to consume the compute_wheel_plan entry point so wheel lookup tables live alongside the collision resolver. The py_venv_exec implementation is split into smaller focused helpers, and the console-script template attribute is pointed at the correct template file.


Changes are visible to end-users: no

Test plan

  • Covered by existing test cases

Pure rename to preserve git history across the upcoming content refactor.
No logic changes; only the bzl_library name, its srcs, and the load in
py_venv.bzl follow the new filename.
@aspect-workflows

aspect-workflows Bot commented Jul 16, 2026

Copy link
Copy Markdown

✨ Aspect Workflows Tasks

📅 Thu Jul 16 23:17:42 UTC 2026

✅ 40 successful tasks

  • ✅ buildifier · ⏱ 16.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Format complete (clean)
  • ✅ gazelle · ⏱ 20.2s · 🐙 GitHub Actions · ☑️ Check
    💬 Gazelle complete (clean)
  • ✅ test-e2e-bazel-8 [test] · ⏱ 2m 19s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (197/197 passed)
  • ✅ test-e2e-bazel-9 [test] · ⏱ 2m 2s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (191/191 passed)
  • ✅ test-e2e-interpreter-build-config-bazel-8 [test] · ⏱ 22.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-build-config-bazel-9 [test] · ⏱ 1m 15s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-input-validation-bazel-8 [test] · ⏱ 19.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-input-validation-bazel-9 [test] · ⏱ 38.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-runtime-metadata-bazel-8 [test] · ⏱ 22.1s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-e2e-interpreter-runtime-metadata-bazel-9 [test] · ⏱ 1m 15s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-e2e-interpreter-toolchain-settings-bazel-8 [test] · ⏱ 15.9s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-toolchain-settings-bazel-9 [test] · ⏱ 46.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-proto-grpc-python-bazel-8 [test] · ⏱ 1m 44s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-proto-grpc-python-bazel-9 [test] · ⏱ 1m 34s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-python-interop-bazel-8 [test] · ⏱ 25.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (6/6 passed)
  • ✅ test-e2e-rules-python-interop-bazel-9 [test] · ⏱ 38s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (6/6 passed)
  • ✅ test-examples-debugger-bazel-8 [test] · ⏱ 29.7s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-debugger-bazel-9 [test] · ⏱ 49s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-dev_deps-bazel-8 [test] · ⏱ 26.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-dev_deps-bazel-9 [test] · ⏱ 1m 29s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-django-bazel-8 [test] · ⏱ 24s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-django-bazel-9 [test] · ⏱ 51.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-multi_version-bazel-8 [test] · ⏱ 26s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-examples-multi_version-bazel-9 [test] · ⏱ 59.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-examples-protobuf-bazel-8 [test] · ⏱ 1m 12s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-protobuf-bazel-9 [test] · ⏱ 1m 52s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-py_binary-bazel-8 [test] · ⏱ 23.2s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_binary-bazel-9 [test] · ⏱ 50.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-py_pex_binary-bazel-8 [test] · ⏱ 21.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_pex_binary-bazel-9 [test] · ⏱ 45.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-py_venv-bazel-8 [test] · ⏱ 19.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (3/3 passed)
  • ✅ test-examples-py_venv-bazel-9 [test] · ⏱ 51.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (3/3 passed)
  • ✅ test-examples-pytest-bazel-8 [test] · ⏱ 49.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (9/9 passed)
  • ✅ test-examples-pytest-bazel-9 [test] · ⏱ 2m 5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (9/9 passed)
  • ✅ test-examples-uv_pip_compile-bazel-8 [test] · ⏱ 26.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-uv_pip_compile-bazel-9 [test] · ⏱ 1m 8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-virtual_deps-bazel-8 [test] · ⏱ 26.2s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-virtual_deps-bazel-9 [test] · ⏱ 1m 6s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-root-bazel-8 [test] · ⏱ 3m 1s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (250/250 passed)
  • ✅ test-root-bazel-9 [test] · ⏱ 3m 31s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (249/249 passed)

⏱ Last updated Thu Jul 16 23:21:41 UTC 2026 · 📊 GitHub API quota 1,770/15,000 (12% used, resets in 34m)
🚀 Powered by Aspect CLI (v2026.28.2)  |  Aspect Build · X · LinkedIn · YouTube

@github-actions

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) 175.456 174.713 ±3.536 27.40
HEAD main 56.003 55.980 ±0.495 -68.1% 10.28
This PR 56.301 56.103 ±0.624 -67.9% +0.5% 7.29

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) 10183.639 10182.974 ±215.258 101 301
HEAD main 9724.922 9732.172 ±173.241 -4.5% 101 301
This PR 9525.215 9476.842 ±186.467 -6.5% -2.1% 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

@xangcastle xangcastle changed the title refactor(py_venv): assemble venv module refactor(venv): rename venv.bzl to assemble_venv.bzl and split out wheel plan helpers Jul 16, 2026
@xangcastle
xangcastle marked this pull request as ready for review July 16, 2026 19:59
@xangcastle
xangcastle requested a review from jbedard July 16, 2026 19:59
venv, unless `expose_venv = True` routes them to a sibling py_venv) and
the standalone `py_venv` rule call `assemble_venv` to keep their layouts
bit-identical.
up a Python venv. Both ``py_binary`` / ``py_test`` (each with its own

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

these all have double-` now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

because looks better when //py:defs.doc_extract parse the docstring

`foo` → interpreted text
``foo`` → literal/code

file names, attrs, function names and inline code should be literal/code

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is that the case everywehre or only the defs.doc_extract? What about IDEs or BCR?

Comment thread py/private/py_venv/py_venv.bzl Outdated
# overrides with its own absolute value when invoked directly.
passed_env["VIRTUAL_ENV"] = venv_root(shared.venv.bin_python)

_expand_launcher(ctx, shared)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What's the reason for doing ones like this? IMO this just makes it more confusing... glancing at _py_venv_rule_impl now required scrolling up and down across this file while previously it all fit on one screen?

Comment thread py/private/py_venv/py_venv.bzl Outdated
load(":py_venv_exec.bzl", _py_venv_exec = "py_venv_exec")
load(":types.bzl", "VirtualenvInfo", "venv_root")

_VENV_ONLY_ATTRS = [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Lets at least leave basic comments for things like this

…_venv_exec

Refactors the content of assemble_venv.bzl (renamed in the previous
commit) to consume compute_wheel_plan instead of calling
resolve_wheel_collisions directly, moving the wheel lookup tables
(tree_by_sp, known_layout) into the resolver plan. Extracts
_declare_console_scripts and the .pth formatter into helpers.

py_venv_exec_impl is split into focused helpers (_validate_main,
_merge_environment, _set_contextual_env, _strip_contextual_from_inherited).

py_venv_conflict, venv snapshots, and console-scripts tests pass on a
forced re-run; bazel test //... is 250/250 green.

@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.

I found one runtime-behavior change and one avoidable hot-path cost in the venv refactor.

if not ctx.attr.isolated:
flags = [f for f in flags if f != "-I"]
base = [f for f in base if f != "-I"]
return base + list(ctx.attr.interpreter_options)

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.

This changes isolated = False behavior: previously -I was removed from the combined default and user flags, while the refactor appends a user-supplied -I after filtering and silently restores isolation (ignoring PYTHONPATH/user site). The standalone/exposed venv helper makes the same change. Please retain combined filtering, or call out and cover the behavior change with a non-isolated/exposed test that passes interpreter_options = ["-I"].

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 latest revision restores combined filtering for the standalone/exposed py_venv REPL, which fixes that half. py_venv_exec.bzl still filters only the base flags and then appends user interpreter_options, so a normal py_binary/py_test with isolated=False and interpreter_options=["-I"] still changes behavior. Please restore combined filtering there (and cover the binary/test path) before resolving.

venv_name = venv_name,
)

plan = compute_wheel_plan(ctx, _py_library.make_wheels_depset(ctx).to_list())

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.

compute_wheel_plan sorts every wheel path into wheel_fingerprints, but this field has no consumer in the repository. Calling it here adds an O(wheels log wheels) allocation/sort for every venv on an already hot analysis path. Can the unused fingerprint computation/field be dropped (or the lighter resolver/lookup path retained) until a real consumer needs it?

@xangcastle
xangcastle force-pushed the venv-final-assemble-venv-module branch from dc78b18 to e403c87 Compare July 16, 2026 23:17

@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 latest simplification restores combined -I filtering for the executable venv, but the normal binary/test launcher still appends a user -I after filtering and the unused per-venv wheel-fingerprint sort remains. One small rename cleanup: py/private/py_venv/py_venv.bzl:24 still points readers at the now-deleted venv.bzl::assemble_venv; update it to assemble_venv.bzl.

tamird commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

All test and benchmark steps passed on e403c87. The sole red check is the benchmark comment job: GitHub returned HTTP 503 while listing this PR’s comments. A maintainer will need to rerun the failed job; the substantive review threads are still open.

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