Bazel Runfiles
The `bazel-runfiles` library provides utilities to locate and access data files bundled as Bazel 'runfiles' within a Python binary or test. It is part of the `rules_python` ecosystem, currently at version 1.9.0, and receives updates aligned with `rules_python` releases, which are fairly regular.
Warnings
- gotcha The `bazel-runfiles` library is designed to operate within a Bazel build environment. It relies on environment variables (`BAZEL_RUNFILES_MANIFEST_FILE` or `BAZEL_RUNFILES_DIR`) set by Bazel during execution. Running scripts directly with `python` outside of a `bazel run` or `bazel test` context will likely result in `FileNotFoundError` or `ValueError` if these variables are not manually provided.
- gotcha Paths provided to `Rlocation()` must be relative to the Bazel workspace root, not relative to the Python source file. For example, if `data.txt` is at `my_package/data.txt` in your workspace, and you add it to a `data` attribute in a `BUILD` file, you would request `r.Rlocation('my_package/data.txt')`.
- breaking Prior to `bazel-runfiles` becoming the standard, `rules_python` used alternative methods for accessing data files (e.g., direct imports from generated `py_path.runfiles`). Migrating older Bazel Python projects to recent `rules_python` versions often requires updating runfiles access to use the `bazel_runfiles.runfiles.Create().Rlocation()` pattern.
Install
-
pip install bazel-runfiles
Imports
- runfiles
from bazel_runfiles import runfiles
Quickstart
import os
from bazel_runfiles import runfiles
# When run via 'bazel run', Bazel automatically sets the necessary
# environment variables (BAZEL_RUNFILES_MANIFEST_FILE or BAZEL_RUNFILES_DIR).
# For local testing or non-Bazel execution, you might need to set them manually
# or use a different 'Create' method, but this is uncommon for typical usage.
try:
# Create a Runfiles object. This will automatically detect the appropriate
# manifest or directory based on environment variables set by Bazel.
r = runfiles.Create()
# Example: Locate a data file included in your Bazel BUILD rule.
# Assume a BUILD target like:
# py_binary(
# name = "my_app",
# srcs = ["my_app.py"],
# data = ["//path/to/my:data.txt"],
# )
# The path here is relative to your workspace root, e.g., 'path/to/my/data.txt'
data_file_path = r.Rlocation('path/to/my/data.txt')
if data_file_path:
print(f"Located data file at: {data_file_path}")
if os.path.exists(data_file_path):
with open(data_file_path, 'r') as f:
content = f.read()
print(f"Content of data.txt: {content[:50]}...")
else:
print("Warning: File path resolved but file does not exist. (Perhaps no such data file was configured for this target?)")
else:
print("Could not locate 'path/to/my/data.txt'. Ensure it's correctly added to a 'data' attribute in your BUILD file.")
except Exception as e:
print(f"An error occurred: {e}")
print("This might happen if not executed in a Bazel runfiles environment.")
print("Ensure BAZEL_RUNFILES_MANIFEST_FILE or BAZEL_RUNFILES_DIR is set.")
# You can also resolve the path of the current executable itself
# Note: CurrentExecutable() requires a special setup for the symlink to be resolvable directly.
# executable_path = r.Rlocation(r.CurrentExecutable())
# print(f"Current executable path in runfiles: {executable_path}")