Skip to content

Conversation

@rickeylev
Copy link
Collaborator

@rickeylev rickeylev commented Dec 20, 2025

The system_python bootstrap is basically just an alternative stage1 bootstrap now.

Finish unifying it with bootstrap=script by having it create a full venv.

Notes:

  • Needs rules_pkg 1.2+. This is because of tar+py_binary+relative symlinks
  • Needs py 3.11+ OR runtime venv creation. CI machines have py 3.10. This is because
    the pyvenv.cfg trick only works in 3.11+. The runtime_env toolchain has a conditional
    to mark the toolchain as supporting build time venv or not based on python version.

WIP -- DO NOT MERGE

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @rickeylev, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the Python bootstrapping mechanism to ensure that the system_python bootstrap option consistently creates a full virtual environment (venv), aligning its behavior with the script bootstrap. The primary goal is to unify these bootstrapping methods, simplifying the underlying logic for venv creation and interpreter handling. This change improves consistency in how Python environments are managed within the build system.

Highlights

  • Unification of Bootstrap Methods: The pull request aims to unify the system_python bootstrap with the script bootstrap by ensuring system_python always creates a full virtual environment (venv).
  • Consistent Venv Creation: The _create_venv function in py_executable.bzl has been modified to always create a pyvenv.cfg file, removing a conditional check that previously determined its creation based on the bootstrap implementation flag.
  • Refactored Interpreter Symlink Logic: The logic for creating the interpreter symlink within the venv has been refactored, removing the create_full_venv conditional block to streamline the process.
  • Dependency Update: An archive_override for rules_pkg version 1.2.0 has been added to MODULE.bazel, likely to support the updated venv creation process.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a good refactoring that unifies the system_python and script bootstrap implementations by always creating a full virtual environment. The changes simplify the codebase by removing conditional logic, which improves maintainability.

I've added one suggestion to further refactor a section of the code to reduce duplication. Additionally, as a result of these changes, some conditional checks in other parts of the file (like if venv.interpreter:) have become redundant, as venv.interpreter will no longer be None. These could be cleaned up in a subsequent change to remove the now-unreachable else branches.

Comment on lines 544 to 570
if not venvs_use_declare_symlink_enabled or not runtime.supports_build_time_venv:
recreate_venv_at_runtime = True

# When the venv symlinks are disabled, the $venv/bin/python3 file isn't
# needed or used at runtime. However, the zip code uses the interpreter
# File object to figure out some paths.
interpreter = ctx.actions.declare_file("{}/{}".format(bin_dir, py_exe_basename))
ctx.actions.write(interpreter, "actual:{}".format(interpreter_actual_path))

elif runtime.interpreter:
# Even though ctx.actions.symlink() is used, using
# declare_symlink() is required to ensure that the resulting file
# in runfiles is always a symlink. An RBE implementation, for example,
# may choose to write what symlink() points to instead.
interpreter = ctx.actions.declare_symlink("{}/{}".format(bin_dir, py_exe_basename))

rel_path = relative_path(
# dirname is necessary because a relative symlink is relative to
# the directory the symlink resides within.
from_ = paths.dirname(runfiles_root_path(ctx, interpreter.short_path)),
to = interpreter_actual_path,
)

ctx.actions.symlink(output = interpreter, target_path = rel_path)
else:
interpreter = ctx.actions.declare_symlink("{}/{}".format(bin_dir, py_exe_basename))
ctx.actions.symlink(output = interpreter, target_path = runtime.interpreter_path)
ctx.actions.symlink(output = interpreter, target_path = rel_path)
else:
interpreter = None
interpreter = ctx.actions.declare_symlink("{}/{}".format(bin_dir, py_exe_basename))
ctx.actions.symlink(output = interpreter, target_path = runtime.interpreter_path)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The elif and else blocks here have some repeated logic for creating the interpreter symlink. You can refactor this to reduce duplication by declaring the symlink first and then conditionally determining the target path. This would make the code a bit cleaner and easier to maintain.

    if not venvs_use_declare_symlink_enabled or not runtime.supports_build_time_venv:
        recreate_venv_at_runtime = True

        # When the venv symlinks are disabled, the $venv/bin/python3 file isn't
        # needed or used at runtime. However, the zip code uses the interpreter
        # File object to figure out some paths.
        interpreter = ctx.actions.declare_file("{}/{}".format(bin_dir, py_exe_basename))
        ctx.actions.write(interpreter, "actual:{}".format(interpreter_actual_path))
    else:
        # Even though ctx.actions.symlink() is used, using
        # declare_symlink() is required to ensure that the resulting file
        # in runfiles is always a symlink. An RBE implementation, for example,
        # may choose to write what symlink() points to instead.
        interpreter = ctx.actions.declare_symlink("{}/{}".format(bin_dir, py_exe_basename))

        if runtime.interpreter:
            target_path = relative_path(
                # dirname is necessary because a relative symlink is relative to
                # the directory the symlink resides within.
                from_ = paths.dirname(runfiles_root_path(ctx, interpreter.short_path)),
                to = interpreter_actual_path,
            )
        else:
            target_path = runtime.interpreter_path

        ctx.actions.symlink(output = interpreter, target_path = target_path)

@rickeylev
Copy link
Collaborator Author

This is almost done, but tow issues remain:

  1. rules_pkg 1.2 requires bazel 8+ for some reason (not clear why); rules_pkg 1.2 is needed for pkg_tar to work with declare_symlink.
  2. Switching system_python to a full build-time venv using declare_symlink breaks packaging using py_wheel and/or pkg_tar (same issue as with bootstrap=script)

I'm not sure if both of these can be fixed. I guess if we drop bazel 7? We'll do that in a month or so anyways. Maybe don't test bazel 7 + packaging?

@aignas
Copy link
Collaborator

aignas commented Dec 23, 2025

I personally think that it could be best to fail if we detect bazel 7 and we cannot support it. +1 for excluding the tests via target_compatible_with. That said, why are we breaking packaging? I think we should exclude the venv from packaging altogether?

@rickeylev
Copy link
Collaborator Author

rickeylev commented Dec 23, 2025

The issue with bazel 7 isn't with our rules, but with rules_pkg, in two respects:

  1. There is some sort of low-level bzlmod issue with it. Whatever rules_pkg 1.2 is doing, its MODULE doesn't work with bazel 7.
  2. Bazel 7 doesn't have the File.is_symlink() apis to understand the venv's we create.

Our venv's work fine on bazel 7; it's just rules_pkg that has an issue.

A py_binary doesn't know if its being consumed by pkg_tar, so it can't automatically disable the symlink-based venv creation.

edit -- if we disable build-time venv creation for bazel 7, that could work. Its somewhat a regression in functionality, though. One only necessary if rules_pkg is used.

@rickeylev
Copy link
Collaborator Author

Ah -- how about, for bazel 7, we don't create a full venv for system_python? That would match existing behavior. Bazel 8 + system_python would do full venv. Then we can drop Bazel 7 support In Jan/Feb timeline

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.

2 participants