blob: 11993e2940b0e9ad5238b410bcb7c3918081f44f [file]
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helpers to access and use the Python interpreter directly."""
# Attributes to add to your rule to use generate_python_build_action() in its implementation.
PY_TOOLCHAIN_ATTRS = {
"_py_toolchain": attr.label(
default = "@rules_python//python:current_py_toolchain",
cfg = "exec",
providers = [DefaultInfo, platform_common.ToolchainInfo],
),
}
def find_python_interpreter_and_runfiles(ctx):
"""Finds the Python interpreter and its runfiles.
Args:
ctx: The context of the rule. The corresponding rule definition
should include PY_TOOLCHAIN_ATTRS in its `attrs`.
Returns:
A tuple of the Python interpreter and its runfiles.
"""
if not hasattr(ctx.attr, "_py_toolchain"):
fail("Missing _py_toolchain attribute, did you forget to include 'PY_TOOLCHAIN_ATTRS' in your rule's 'attrs'?")
toolchain_info = ctx.attr._py_toolchain[platform_common.ToolchainInfo]
if not toolchain_info.py3_runtime:
fail("A Bazel python3 runtime is required, and none was configured!")
python3_executable = toolchain_info.py3_runtime.interpreter
python3_runfiles = ctx.runfiles(transitive_files = toolchain_info.py3_runtime.files)
return python3_executable, python3_runfiles
def generate_python_build_action(ctx, py_script, inputs, outputs, arguments = [], **kwargs):
"""Generates an action invoking a Python script directly with the Python interpreter.
In practice this is much faster than using a py_binary(), which will wrap everything with an
intermediate script and a tree of runfiles files before running the action.
Recommended for build actions that do not need to be invoked with `bazel run` later.
Args:
ctx: The context of the rule. The corresponding rule definition
must include PY_TOOLCHAIN_ATTRS in its `attrs`.
py_script [File]: The Python script to run.
arguments [List[str]]: The script's arguments, default to empty list.
inputs [List[File]]: The action's inputs. This must include all modules imported
transitively from the script, that are not part of the standard library.
outputs [List[File]]: The action's outputs.
**kwargs: Additional arguments to pass to ctx.actions.run().
"""
# Get Python3 interpreter and its runfiles.
python3_executable, python3_runfiles = find_python_interpreter_and_runfiles(ctx)
runfiles = ctx.runfiles(
files = [py_script] + inputs,
transitive_files = python3_runfiles.files,
)
ctx.actions.run(
executable = python3_executable,
arguments = [py_script.path] + arguments,
inputs = runfiles.files,
outputs = outputs,
**kwargs
)