This documents how to configure the Python toolchain and runtimes for different use cases.
How to configure rules_python
in your MODULE.bazel file depends on how and why you're using Python. There are 4 basic use cases:
Root modules are always the top-most module. These are special in two ways:
rules_python
bzlmod APIs are only respected by the root module.When configuring rules_python
for a root module, you typically want to explicitly specify the Python version you want to use. This ensures that dependencies don't change the Python version out from under you. Remember that rules_python
will set a version by default, but it will change regularly as it tracks a recent Python version.
NOTE: If your root module only uses Python for development of the module itself, you should read the dev-only library module section.
bazel_dep(name="rules_python", version=...) python = use_extension("@rules_python//extensions:python.bzl", "python") python.toolchain(python_version = "3.12", is_default = True)
A library module is a module that can show up in arbitrary locations in the bzlmod module graph -- it's unknown where in the breadth-first search order the module will be relative to other modules. For example, rules_python
is a library module.
A library module with dev-only Python usage is usually one where Python is only used as part of its tests. For example, a module for Java rules might run some Python program to generate test data, but real usage of the rules don't need Python to work. To configure this, follow the root-module setup, but remember to specify dev_dependency = True
to the bzlmod APIs:
# MODULE.bazel bazel_dep(name = "rules_python", version=..., dev_dependency = True) python = use_extension( "@rules_python//extensions:python.bzl", "python", dev_dependency = True ) python.toolchain(python_version = "3.12", is_default=True)
A library module without version constraints is one where the version of Python used for the Python programs it runs isn‘t chosen by the module itself. Instead, it’s up to the root module to pick an appropriate version of Python.
For this case, configuration is simple: just depend on rules_python
and use the normal //python:py_binary.bzl
et al rules. There is no need to call python.toolchain
-- rules_python ensures some Python version is available, but more often the root module will specify some version.
# MODULE.bazel bazel_dep(name = "rules_python", version=...)
A library module with version constraints is one where the module requires a specific Python version be used with its tools. This has some pros/cons:
To configure this, request the Python versions needed in MODULE.bazel and use the version-aware rules for py_binary
.
# MODULE.bazel bazel_dep(name = "rules_python", version=...) python = use_extension("@rules_python//extensions:python.bzl", "python") python.toolchain(python_version = "3.12") # BUILD.bazel load("@python_versions//3.12:defs.bzl", "py_binary") py_binary(...)
Pinning to a version allows targets to force that a specific Python version is used, even if the root module configures a different version as a default. This is most useful for two cases:
To configure a submodule with the version-aware rules, request the particular version you need, then use the @python_versions
repo to use the rules that force specific versions:
python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain( python_version = "3.11", ) use_repo(python, "python_versions")
Then use e.g. load("@python_versions//3.11:defs.bzl", "py_binary")
to use the rules that force that particular version. Multiple versions can be specified and use within a single build.
For more documentation, see the bzlmod examples under the {gh-path}examples
folder. Look for the examples that contain a MODULE.bazel
file.
The python.toolchain()
call makes its contents available under a repo named python_X_Y
, where X and Y are the major and minor versions. For example, python.toolchain(python_version="3.11")
creates the repo @python_3_11
. Remember to call use_repo()
to make repos visible to your module: use_repo(python, "python_3_11")
Python toolchains can be utilized in other bazel rules, such as genrule()
, by adding the toolchains=["@rules_python//python:current_py_toolchain"]
attribute. You can obtain the path to the Python interpreter using the $(PYTHON2)
and $(PYTHON3)
“Make” Variables. See the {gh-path}test_current_py_toolchain <tests/load_from_macro/BUILD.bazel>
target for an example.
To import rules_python in your project, you first need to add it to your WORKSPACE
file, using the snippet provided in the release you choose
To depend on a particular unreleased version, you can do the following:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Update the SHA and VERSION to the lastest version available here: # https://github.com/bazelbuild/rules_python/releases. SHA="84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841" VERSION="0.23.1" http_archive( name = "rules_python", sha256 = SHA, strip_prefix = "rules_python-{}".format(VERSION), url = "https://github.com/bazelbuild/rules_python/releases/download/{}/rules_python-{}.tar.gz".format(VERSION,VERSION), ) load("@rules_python//python:repositories.bzl", "py_repositories") py_repositories()
To register a hermetic Python toolchain rather than rely on a system-installed interpreter for runtime execution, you can add to the WORKSPACE
file:
load("@rules_python//python:repositories.bzl", "python_register_toolchains") python_register_toolchains( name = "python_3_11", # Available versions are listed in @rules_python//python:versions.bzl. # We recommend using the same version your team is already standardized on. python_version = "3.11", ) load("@python_3_11//:defs.bzl", "interpreter") load("@rules_python//python:pip.bzl", "pip_parse") pip_parse( ... python_interpreter_target = interpreter, ... )
After registration, your Python targets will use the toolchain's interpreter during execution, but a system-installed interpreter is still used to ‘bootstrap’ Python targets (see https://github.com/bazelbuild/rules_python/issues/691). You may also find some quirks while using this toolchain. Please refer to python-build-standalone documentation's Quirks section.