docs: Better explain when and how to use toolchains for bzlmod (#1349)
This explains the different ways to register toolchains and how to use
them.
Also fixes python_aliases -> python_versions repo name
diff --git a/README.md b/README.md
index 4453436..660e6e2 100644
--- a/README.md
+++ b/README.md
@@ -41,37 +41,98 @@
**IMPORTANT: bzlmod support is still in Beta; APIs are subject to change.**
+The first step to using rules_python with bzlmod is to add the dependency to
+your MODULE.bazel file:
+
+```starlark
+# Update the version "0.0.0" to the release found here:
+# https://github.com/bazelbuild/rules_python/releases.
+bazel_dep(name = "rules_python", version = "0.0.0")
+```
+
+Once added, you can load the rules and use them:
+
+```starlark
+load("@rules_python//python:py_binary.bzl", "py_binary")
+
+py_binary(...)
+```
+
+Depending on what you're doing, you likely want to do some additional
+configuration to control what Python version is used; read the following
+sections for how to do that.
+
#### Toolchain registration with bzlmod
A default toolchain is automatically configured depending on
`rules_python`. Note, however, the version used tracks the most recent Python
release and will change often.
-If you want to register specific Python versions, then use
-`python.toolchain()` for each version you need:
+If you want to use a specific Python version for your programs, then how
+to do so depends on if you're configuring the root module or not. The root
+module is special because it can set the *default* Python version, which
+is used by the version-unaware rules (e.g. `//python:py_binary.bzl` et al). For
+submodules, it's recommended to use the version-aware rules to pin your programs
+to a specific Python version so they don't accidentally run with a different
+version configured by the root module.
+
+##### Configuring and using the default Python version
+
+To specify what the default Python version is, set `is_default = True` when
+calling `python.toolchain()`. This can only be done by the root module; it is
+silently ignored if a submodule does it. Similarly, using the version-unaware
+rules (which always use the default Python version) should only be done by the
+root module. If submodules use them, then they may run with a different Python
+version than they expect.
```starlark
-# Update the version "0.0.0" to the release found here:
-# https://github.com/bazelbuild/rules_python/releases.
-bazel_dep(name = "rules_python", version = "0.0.0")
+python = use_extension("@rules_python//python/extensions:python.bzl", "python")
+
+python.toolchain(
+ python_version = "3.11",
+ is_default = True,
+)
+```
+
+Then use the base rules from e.g. `//python:py_binary.bzl`.
+
+##### Pinning to a Python version
+
+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:
+
+1. For submodules to ensure they run with the appropriate Python version
+2. To allow incremental, per-target, upgrading to newer Python versions,
+ typically in a mono-repo situation.
+
+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:
+
+```starlark
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
python_version = "3.11",
)
-use_repo(python, "python_3_11", "python_aliases")
+use_repo(python, "python_versions")
```
-The `use_repo` statement above is essential as it imports one or more
-repositories into the current module's scope. The two repositories `python_3_11`
-and `python_aliases` are created internally by the `python` extension.
-The name `python_versions` is a constant and is always imported. The identifier
-`python_3_11` was created by using `"python_{}".format("3.11".replace(".","_"))`.
-This rule takes the Python version and creates the repository name using
-the version.
+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 [examples](examples) folder. Look for the examples that contain a `MODULE.bazel` file.
+##### Other toolchain details
+
+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")`
+
### Using a WORKSPACE file
To import rules_python in your project, you first need to add it to your
diff --git a/python/extensions/pip.bzl b/python/extensions/pip.bzl
index 4e1cf70..3cecc4e 100644
--- a/python/extensions/pip.bzl
+++ b/python/extensions/pip.bzl
@@ -345,9 +345,10 @@
"python_version": attr.string(
mandatory = True,
doc = """
-The Python version to use for resolving the pip dependencies. If not specified,
-then the default Python version (as set by the root module or rules_python)
-will be used.
+The Python version to use for resolving the pip dependencies, in Major.Minor
+format (e.g. "3.11"). Patch level granularity (e.g. "3.11.1") is not supported.
+If not specified, then the default Python version (as set by the root module or
+rules_python) will be used.
The version specified here must have a corresponding `python.toolchain()`
configured.
diff --git a/python/extensions/python.bzl b/python/extensions/python.bzl
index 2d4032a..2d00726 100644
--- a/python/extensions/python.bzl
+++ b/python/extensions/python.bzl
@@ -250,7 +250,9 @@
),
"python_version": attr.string(
mandatory = True,
- doc = "The Python version, in `major.minor` format, e.g '3.12', to create a toolchain for.",
+ doc = "The Python version, in `major.minor` format, e.g " +
+ "'3.12', to create a toolchain for. Patch level " +
+ "granularity (e.g. '3.12.1') is not supported.",
),
},
),