Skip to content

fix(bootstrap): restore imports for native Python targets - #4235

Open
gleyba wants to merge 2 commits into
bazel-contrib:mainfrom
gleyba:fix/native-bootstrap-imports
Open

gleyba wants to merge 2 commits into
bazel-contrib:mainfrom
gleyba:fix/native-bootstrap-imports

Conversation

@gleyba

@gleyba gleyba commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #4234. Reproductions and failure details are in
the issue.

Bazel 7's built-in Python rules leave newer launcher placeholders unexpanded
when using a rules_python runtime. The Python bootstrap crashes while parsing
%runtime_venv_symlinks%; the shell bootstrap passes %interpreter_args% to
Python as a script filename. After bypassing the Python startup crash, its
direct-to-main fallback also omits declared import roots.

This completes the legacy compatibility path discussed in
PR #3495, without
changing normal stage-2 launches or the project's support policy.

Changes

  • Treat the unexpanded venv-symlink placeholder as an empty mapping.
  • Add the native main-file fallback to the shell launcher and clear its
    unexpanded interpreter-argument token only in that fallback.
  • Restore native import ordering in both fallbacks using declared imports and
    Bazel's workspace/repository settings; preserve inherited PYTHONPATH.
  • Strip the native workspace/../ prefix from shell entry-point paths so an
    external binary does not depend on the workspace directory existing.
  • Add shared template-level regression tests for both launchers and a news
    entry for the generated changelog.

After the change, native hello targets start successfully and declared
dependencies are importable. Existing stage-2 import behavior and interpreter
arguments remain unchanged. No public APIs or build flags are added or changed.

Minimal reproduction

For the unpatched shell-launcher failure, use these four files:

# .bazelversion
7.7.1

# MODULE.bazel
bazel_dep(name = "rules_python", version = "2.4.1")

# BUILD.bazel — intentionally use the built-in rule, without a load()
py_binary(name = "hello", srcs = ["hello.py"])

# hello.py
print("launcher reached main")
bazelisk run --@rules_python//python/config_settings:bootstrap_impl=script //:hello

Before: Python tries to open the literal %interpreter_args% filename.
After: the program prints launcher reached main.

The issue also contains the unpatched Python-launcher reproduction and an
explicitly crash-guarded example exposing the subsequent import failure.
These use Bzlmod and default runtimes, without custom toolchains or runfiles
manipulation.

Validation

All six targets passed locally with Bazel 9.1.1:

bazel test --config=fast-tests \
  //tests/bootstrap_impls:native_bootstrap_test \
  //tests/bootstrap_impls:native_bootstrap_script_test \
  //tests/bootstrap_impls:interpreter_args_test \
  //tests/bootstrap_impls:run_binary_bootstrap_script_zip_no_test \
  //tests/bootstrap_impls:run_binary_bootstrap_script_find_runfiles_test \
  //tests/bootstrap_impls:stdlib_shadowing_system_python_test

The new tests render the templates with native Bazel 7 substitutions; they do
not enable deprecated native rules in Bazel 9. They cover startup, import
ordering, workspace and external repository imports, inherited PYTHONPATH,
and unchanged stage-2 interpreter arguments.

The Bzlmod reproductions also pass against this fix with Bazel 7.7.1, including
the shell launcher, an external native target, and declared imports.
Buildifier, Ruff, and Bash syntax checks passed.

The release note is in news/native-bootstrap-imports.fixed.md, following the
contribution guideline to use news fragments instead of editing the generated
CHANGELOG.md directly.

Bazel 7 native Python rules leave newer launcher placeholders
unexpanded. This crashes venv-symlink parsing in the Python bootstrap
and passes a literal interpreter-argument token as a script filename
in the shell bootstrap. The main-file fallback also omits imports.

Handle the unexpanded placeholders and restore native import ordering
in both bootstraps when falling back to the main file. Normalize
external entry-point paths in the shell fallback and preserve
normal stage-2 behavior, interpreter arguments, and inherited
PYTHONPATH.

Add regression tests for both launchers and a release-note entry.
Use the Windows .exe.runfiles suffix when creating the launcher fixture.
Build expected venv symlink paths with the platform path separator.

This fixes the four blocking Windows jobs in PR bazel-contrib#4235 without changing
launcher behavior.

This branch has not been deployed

No deployments
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.

Bazel 7 native py_binary: startup and import failures with rules_python 2.4.1

1 participant