bazel: incompatible_use_python_toolchains: The Python runtime is obtained from a toolchain rather than a flag
Flag: --incompatible_use_python_toolchains
Available since: 0.25
Will be flipped in: 0.27
Feature tracking issue: #7375
FAQ (common problems)
I’m getting Python 2 vs 3 errors
This flag fixes #4815 on non-Windows platforms, so your code might now be running under a different version of Python than it was in previous Bazel versions. You may notice this as a Python stack trace complaining about bad print
syntax, problems with bytes
vs str
(encode
/decode
), unknown imports, etc.
In order for your code to run under the proper version of Python, make sure Python 2 binaries and tests have the attribute python_version = "PY2"
(the default is PY3
).
For targets that are built in the host configuration (for example, genrule tools in particular), python_version
has no effect. It is currently impossible for PY2 and PY3 host-configured targets to co-exist in the same build; they will always be overridden to one or the other, depending on the value of --host_force_python
. This incompatible change does not affect how the host config works, it just makes it so targets actually run with the version the host config specifies. If you (or your dependencies) have host-configured tools that require Python 2, and which are now failing because they’re running under Python 3, add --host_force_python=PY2
to your bazelrc (the default value is PY3
).
Bazel 0.27 introduces a diagnostic message when a host-configured tool fails at run time (non-zero exit code), alerting you when it may be necessary to set this flag.
The default Python toolchain can’t find the interpreter
If you get an error like this:
Error: The default python toolchain (@bazel_tools//tools/python:autodetecting_toolchain) was unable to locate a suitable Python interpreter on the target platform at execution time. Please register an appropriate Python toolchain. […] Failure reason: Cannot locate ‘python3’ or ‘python’ on the target platform’s PATH, which is: […]
Determine whether you have python2
, python3
, and/or python
on your shell PATH
. For py_test
targets, and for py_binary
targets used as tools (in genrules, etc.), also check whether your PATH
is being manipulated by the flags --incompatible_strict_action_env
and/or --action_env=PATH=[...]
. For instance, the strict action environment does not include /usr/local/bin
in PATH
by default, which is where python3
is typically located on Mac, if it is installed at all. See also #8536.
If modifying your PATH
is not feasible, try defining and registering your own Python toolchain as described at the bottom of this post.
I don’t have Python 3 installed (e.g. default Mac environment)
Previously, if you didn’t have a Python 3 interpreter but all your code was compatible with Python 2, Bazel would happily analyze it as PY3 and execute it using a Python 2 python
command. Now this breaks because the autodetecting toolchain validates that python
is actually Python 3.
The ideal solution is to not depend on Python 3 code, or else install a Python 3 environment on the target system. The practical workaround is to opt out of version checking by using the non-strict autodetecting toolchain. The error message tells you how: Add to your bazelrc
build --extra_toolchains=@bazel_tools//tools/python:autodetecting_toolchain_nonstrict
Note that you will not benefit from the fix to #4815 as long as you are using this toolchain.
If you’re using a custom Python toolchain (using py_runtime_pair
, as described at the bottom of this post), you can have the py3_runtime
attribute point to a py_runtime
that declares itself as PY3
but in actuality references a Python 2 interpreter. This abuse of version information achieves the same result: PY3-analyzed targets get run with a Python 2 interpreter.
Neither of these approaches is recommended for anyone but end-users, since they affect how Python targets get run globally throughout the build.
I’m a rule author and I want my target to run regardless of whether the downstream user has Python 2 or 3
See this comment.
Did the behavior of toolchains change between 0.26 and 0.27
This incompatible change was available since 0.25 and flipped to true by default in 0.27. Bazel 0.27 introduces some bug fixes in the behavior of the autodetecting toolchain, better diagnostic messages, and the non-strict toolchain.
Motivation
For background on toolchains, see here.
Previously, the Python runtime (i.e., the interpreter used to execute py_binary
and py_test
targets) could only be controlled globally, and required passing flags like --python_top
to the bazel invocation. This is out-of-step with our ambitions for flagless builds and remote-execution-friendly toolchains. Using the toolchain mechanism means that each Python target can automatically select an appropriate runtime based on what target platform it is being built for.
Change
Enabling this flag triggers the following changes.
-
Executable Python targets will retrieve their runtime from the new Python toolchain.
-
It is forbidden to set any of the legacy flags
--python_top
,--python2_path
, or--python3_path
. Note that the last two of those are already no-ops. It is also strongly discouraged to set--python_path
, but this flag will be removed in a later cleanup due to #7901. -
The
python_version
attribute of thepy_runtime
rule becomes mandatory. It must be either"PY2"
or"PY3"
, indicating which kind of runtime it is describing.
For builds that rely on a Python interpreter installed on the system, it is recommended that users (or platform rule authors) ensure that each platform has an appropriate Python toolchain definition.
If no Python toolchain is explicitly registered, on non-Windows platforms there is a new default toolchain that automatically detects and executes an interpreter (of the appropriate version) from PATH
. This resolves longstanding issue #4815. A Windows version of this toolchain will come later (#7844).
Migration
See the above FAQ for common issues with the autodetecting toolchain.
If you were relying on --python_top
, and you want your whole build to continue to use the py_runtime
you were pointing it to, you just need to follow the steps below to define a py_runtime_pair
and toolchain
, and register this toolchain in your workspace. So long as you don’t add any platform constraints that would prevent your toolchain from matching, it will take precedence over the default toolchain described above.
If you were relying on --python_path
, and you want your whole build to use the interpreter located at the absolute path you were passing in this flag, the steps are the same, except you also have to define a new py_runtime
with the interpreter_path
attribute set to that path.
Otherwise, if you were only relying on the default behavior that resolved python
from PATH
, just enjoy the new default behavior, which is:
- First try
python2
orpython3
(depending on the target’s version) - Then fall back on
python
if not found - Fail-fast if the interpreter that is found doesn’t match the target’s major Python version (
PY2
orPY3
), as per thepython -V
flag.
On Windows the default behavior is currently unchanged (#7844).
Example toolchain definition
# In your BUILD file...
load("@bazel_tools//tools/python/toolchain.bzl", "py_runtime_pair")
py_runtime(
name = "my_py2_runtime",
interpreter_path = "/system/python2",
python_version = "PY2",
)
py_runtime(
name = "my_py3_runtime",
interpreter_path = "/system/python3",
python_version = "PY3",
)
py_runtime_pair(
name = "my_py_runtime_pair",
py2_runtime = ":my_py2_runtime",
py3_runtime = ":my_py3_runtime",
)
toolchain(
name = "my_toolchain",
target_compatible_with = [...], # optional platform constraints
toolchain = ":my_py_runtime_pair",
toolchain_type = "@bazel_tools//tools/python:toolchain_type",
)
# In your WORKSPACE...
register_toolchains("//my_pkg:my_toolchain")
Of course, you can define and register many different toolchains and use platform constraints to restrict them to appropriate target platforms. It is recommended to use the constraint settings @bazel_tools//tools/python:py2_interpreter_path
and [...]:py3_interpreter_path
as the namespaces for constraints about where a platform’s Python interpreters are located.
The new toolchain-related rules and default toolchain are implemented in Starlark under @bazel_tools
. Their source code and documentation strings can be read here.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 1
- Comments: 38 (32 by maintainers)
Commits related to this issue
- Introduce --incompatible_use_python_toolchains This renames --experimental_use_python_toolchains to --incompatible. It also adds the behavior to the flag that 1) py_runtime's python_version attrib... — committed to bazelbuild/bazel by brandjon 5 years ago
- Release 0.25.0 (2019-05-01) Baseline: 03662462941953dad23af88693804b8b1d4430b9 Cherry picks: + 3f7f255f9a8345b8898e3953e7e53d68106cc63d: Windows: fix native test wrapper's arg. escaping ... — committed to bazelbuild/bazel by a-googler 5 years ago
- Improve test compatibility with Python toolchain flag This rolls-forward the parts of https://github.com/bazelbuild/bazel/commit/bf66dc7210b46a13f86e144447f8d18505b79c2c that made our tests compatibl... — committed to bazelbuild/bazel by brandjon 5 years ago
- Release 0.25.0 (2019-05-01) Baseline: 03662462941953dad23af88693804b8b1d4430b9 Cherry picks: + 3f7f255f9a8345b8898e3953e7e53d68106cc63d: Windows: fix native test wrapper's arg. escaping ... — committed to bazelbuild/bazel by a-googler 5 years ago
- Annotate android tools/tests as PY2 This is needed to ensure we continue to give these tests/binaries a Python 2 runtime once --incompatible_use_python_toolchains is enabled. Work toward #7899. REL... — committed to bazelbuild/bazel by brandjon 5 years ago
- Add a warning to Python host tools built for the wrong version The Python stub script now emits a warning message when its payload exits abnormally, if the target was built 1) in the host configurati... — committed to bazelbuild/bazel by brandjon 5 years ago
- Fix the autodetecting Python toolchain on Mac See bazelbuild/continuous-integration#578 for context. The gist of it is that when PATH is not set (as happens when a binary using this toolchain is used... — committed to bazelbuild/bazel by brandjon 5 years ago
- Fix the autodetecting Python toolchain on Mac See bazelbuild/continuous-integration#578 for context. The gist of it is that when PATH is not set (as happens when a binary using this toolchain is used... — committed to bazelbuild/bazel by brandjon 5 years ago
- Fix the autodetecting Python toolchain on Mac See bazelbuild/continuous-integration#578 for context. The gist of it is that when PATH is not set (as happens when a binary using this toolchain is used... — committed to bazelbuild/bazel by brandjon 5 years ago
- Fix the autodetecting Python toolchain on Mac See bazelbuild/continuous-integration#578 for context. The gist of it is that when PATH is not set (as happens when a binary using this toolchain is used... — committed to bazelbuild/bazel by brandjon 5 years ago
- Release 0.26.0 (2019-05-28) Baseline: daa8ae565ab2023e49134f0aad233b0a8bd7a5d0 Cherry picks: + 61c7ffa60ae9c1937dd6d8ee30a8c0471094ee71: Automated rollback of commit 87388e24814b177f54... — committed to Pandinosaurus/bazel by a-googler 5 years ago
- Enable Python toolchains by default (This is a roll-forward of https://github.com/bazelbuild/bazel/commit/bf66dc7210b46a13f86e144447f8d18505b79c2c.) This flips --incompatible_use_python_toolchains, ... — committed to bazelbuild/bazel by brandjon 5 years ago
- Release 0.26.0 (2019-05-28) Baseline: daa8ae565ab2023e49134f0aad233b0a8bd7a5d0 Cherry picks: + 61c7ffa60ae9c1937dd6d8ee30a8c0471094ee71: Automated rollback of commit 87388e24814b177f54... — committed to bazelbuild/bazel by a-googler 5 years ago
- Warn in more cases of possible Python version mismatch in host config We already emit a diagnostic message when the host config's Python version overrides a Python tool's normal version. This helps u... — committed to bazelbuild/bazel by brandjon 5 years ago
- Warn in more cases of possible Python version mismatch in host config We already emit a diagnostic message when the host config's Python version overrides a Python tool's normal version. This helps u... — committed to bazelbuild/bazel by brandjon 5 years ago
- Warn in more cases of possible Python version mismatch in host config We already emit a diagnostic message when the host config's Python version overrides a Python tool's normal version. This helps u... — committed to bazelbuild/bazel by brandjon 5 years ago
- Release 0.27.0 (2019-06-17) Baseline: 5935259724bebd1c4bdebc90e159d0f655c8c219 Cherry picks: + fe81b49e727efdcc90a270520af193af75f4e31d: Support of using the absolute path profile for LLVM'... — committed to bazelbuild/bazel by a-googler 5 years ago
- Annotate android tools/tests as PY2 This is needed to ensure we continue to give these tests/binaries a Python 2 runtime once --incompatible_use_python_toolchains is enabled. Work toward #7899. REL... — committed to irengrig/bazel by brandjon 5 years ago
- Add a warning to Python host tools built for the wrong version The Python stub script now emits a warning message when its payload exits abnormally, if the target was built 1) in the host configurati... — committed to irengrig/bazel by brandjon 5 years ago
- Fix the autodetecting Python toolchain on Mac See bazelbuild/continuous-integration#578 for context. The gist of it is that when PATH is not set (as happens when a binary using this toolchain is used... — committed to irengrig/bazel by brandjon 5 years ago
@brandjon can we remove this diagnostic, or add a way to silence it? We’re on bazel 5.0.0 now, I think it has been enough time since 0.27 that this is probably never the reason that a python rule fails (and it has never been the case in my org, confusing devs), but this is still always printed:
Also python2 was EOL in 2020
Documentation here has a small typo. Currently says:
load("@bazel_tools//tools/python/toolchain.bzl", "py_runtime_pair")
when it should beload("@bazel_tools//tools/python:toolchain.bzl", "py_runtime_pair")
Note/toolchain.bzl
vs:toolchain.bzl
The new Python-toolchain system doesn’t support conveying header information any more than the old
--python_top
/--python_path
system did. You get around this by grabbing header information in a repo rule. Presumably the problem is that the toolchain change made it so the interpreter used at execution time is no longer the same one discovered by the repo rule.Assuming that you didn’t register any Python toolchains in your build, the default behavior at execution time is still to do a lookup in
PATH
to find the interpreter. The change is that now it cares whether the target is PY2 or PY3, and looks forpython2
orpython3
accordingly instead of justpython
. I suspect what’s happening is that your repo rule locates python2 headers but your targets are declared (perhaps implicitly since it’s the default) as PY3.You might change your targets to be PY2 using the
python_version = "PY2"
attribute and if necessary--host_force_python=PY2
. Alternatively, you can augment your repo rule to find headers for python3 and ensure your targets select the appropriate dependency based on their own version. (select()
can use@bazel_tools//tools/python:PY[2|3]
to tell what the version is.)