Skip to content

Commit 4862a8d

Browse files
authored
internal(pystar): Make py_runtime_pair and autodetecting toolchain mostly loadable. (#1439)
They aren't quite usable yet. This mostly fixes references and adds surrounding infrastructure to make this mostly loadable and somewhat runnable. Note that the "autodetecting" name is misleading: it doesn't really autodetect anything. But it's the established name and part of the public API. The autodetecting toolchain is trimmed down from what Bazel does. The Bazel version has a Python 2 variant and a "non strict" mode to support that. With Python 2 no longer supported, that code is removed. * Also alphabetically sorts the bzl_libraries in //python/private Work towards #1069
1 parent 0c1ce6f commit 4862a8d

File tree

5 files changed

+130
-81
lines changed

5 files changed

+130
-81
lines changed

python/private/BUILD.bazel

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,12 @@ filegroup(
4444
)
4545

4646
bzl_library(
47-
name = "reexports_bzl",
48-
srcs = ["reexports.bzl"],
49-
visibility = [
50-
"//docs:__pkg__",
51-
"//python:__pkg__",
47+
name = "autodetecting_toolchain_bzl",
48+
srcs = ["autodetecting_toolchain.bzl"],
49+
deps = [
50+
"//python:py_runtime_bzl",
51+
"//python:py_runtime_pair_bzl",
5252
],
53-
deps = [":bazel_tools_bzl"],
5453
)
5554

5655
bzl_library(
@@ -63,15 +62,6 @@ bzl_library(
6362
deps = ["@bazel_skylib//lib:types"],
6463
)
6564

66-
bzl_library(
67-
name = "which_bzl",
68-
srcs = ["which.bzl"],
69-
visibility = [
70-
"//docs:__subpackages__",
71-
"//python:__subpackages__",
72-
],
73-
)
74-
7565
bzl_library(
7666
name = "py_cc_toolchain_bzl",
7767
srcs = [
@@ -112,6 +102,21 @@ bzl_library(
112102
visibility = ["//:__subpackages__"],
113103
)
114104

105+
bzl_library(
106+
name = "py_runtime_pair_macro_bzl",
107+
srcs = ["py_runtime_pair_rule.bzl"],
108+
deps = [":py_runtime_pair_rule_bzl"],
109+
)
110+
111+
bzl_library(
112+
name = "py_runtime_pair_rule_bzl",
113+
srcs = ["py_runtime_pair_rule.bzl"],
114+
deps = [
115+
"//python:py_runtime_bzl",
116+
"//python:py_runtime_info_bzl",
117+
],
118+
)
119+
115120
bzl_library(
116121
name = "py_wheel_bzl",
117122
srcs = ["py_wheel.bzl"],
@@ -122,12 +127,31 @@ bzl_library(
122127
],
123128
)
124129

130+
bzl_library(
131+
name = "reexports_bzl",
132+
srcs = ["reexports.bzl"],
133+
visibility = [
134+
"//docs:__pkg__",
135+
"//python:__pkg__",
136+
],
137+
deps = [":bazel_tools_bzl"],
138+
)
139+
125140
bzl_library(
126141
name = "stamp_bzl",
127142
srcs = ["stamp.bzl"],
128143
visibility = ["//:__subpackages__"],
129144
)
130145

146+
bzl_library(
147+
name = "which_bzl",
148+
srcs = ["which.bzl"],
149+
visibility = [
150+
"//docs:__subpackages__",
151+
"//python:__subpackages__",
152+
],
153+
)
154+
131155
# @bazel_tools can't define bzl_library itself, so we just put a wrapper around it.
132156
bzl_library(
133157
name = "bazel_tools_bzl",

python/private/autodetecting_toolchain.bzl

Lines changed: 4 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,21 @@
1414

1515
"""Definitions related to the Python toolchain."""
1616

17-
load(":utils.bzl", "expand_pyversion_template")
17+
load("//python:py_runtime.bzl", "py_runtime")
18+
load("//python:py_runtime_pair.bzl", "py_runtime_pair")
1819

19-
def define_autodetecting_toolchain(
20-
name,
21-
pywrapper_template,
22-
windows_config_setting):
20+
def define_autodetecting_toolchain(name):
2321
"""Defines the autodetecting Python toolchain.
2422
25-
This includes both strict and non-strict variants.
26-
27-
For use only by @bazel_tools//tools/python:BUILD; see the documentation
28-
comment there.
29-
3023
Args:
3124
name: The name of the toolchain to introduce. Must have value
3225
"autodetecting_toolchain". This param is present only to make the
3326
BUILD file more readable.
34-
pywrapper_template: The label of the pywrapper_template.txt file.
35-
windows_config_setting: The label of a config_setting that matches when
36-
the platform is windows, in which case the toolchain is configured
37-
in a way that triggers a workaround for #7844.
3827
"""
39-
if native.package_name() != "tools/python":
40-
fail("define_autodetecting_toolchain() is private to " +
41-
"@bazel_tools//tools/python")
4228
if name != "autodetecting_toolchain":
4329
fail("Python autodetecting toolchain must be named " +
4430
"'autodetecting_toolchain'")
4531

46-
expand_pyversion_template(
47-
name = "_generate_wrappers",
48-
template = pywrapper_template,
49-
out2 = ":py2wrapper.sh",
50-
out3 = ":py3wrapper.sh",
51-
out2_nonstrict = ":py2wrapper_nonstrict.sh",
52-
out3_nonstrict = ":py3wrapper_nonstrict.sh",
53-
visibility = ["//visibility:private"],
54-
)
55-
56-
# Note that the pywrapper script is a .sh file, not a sh_binary target. If
57-
# we needed to make it a proper shell target, e.g. because it needed to
58-
# access runfiles and needed to depend on the runfiles library, then we'd
59-
# have to use a workaround to allow it to be depended on by py_runtime. See
60-
# https://github.com/bazelbuild/bazel/issues/4286#issuecomment-475661317.
61-
6232
# buildifier: disable=native-py
6333
py_runtime(
6434
name = "_autodetecting_py3_runtime",
@@ -68,15 +38,6 @@ def define_autodetecting_toolchain(
6838
visibility = ["//visibility:private"],
6939
)
7040

71-
# buildifier: disable=native-py
72-
py_runtime(
73-
name = "_autodetecting_py3_runtime_nonstrict",
74-
interpreter = ":py3wrapper_nonstrict.sh",
75-
python_version = "PY3",
76-
stub_shebang = "#!/usr/bin/env python3",
77-
visibility = ["//visibility:private"],
78-
)
79-
8041
# This is a dummy runtime whose interpreter_path triggers the native rule
8142
# logic to use the legacy behavior on Windows.
8243
# TODO(#7844): Remove this target.
@@ -95,33 +56,15 @@ def define_autodetecting_toolchain(
9556
# that we attempted to use the autodetecting toolchain and need to
9657
# switch back to legacy behavior.
9758
# TODO(#7844): Remove this hack.
98-
windows_config_setting: ":_magic_sentinel_runtime",
59+
"@platforms//os:windows": ":_magic_sentinel_runtime",
9960
"//conditions:default": ":_autodetecting_py3_runtime",
10061
}),
10162
visibility = ["//visibility:public"],
10263
)
10364

104-
py_runtime_pair(
105-
name = "_autodetecting_py_runtime_pair_nonstrict",
106-
py3_runtime = select({
107-
# Same hack as above.
108-
# TODO(#7844): Remove this hack.
109-
windows_config_setting: ":_magic_sentinel_runtime",
110-
"//conditions:default": ":_autodetecting_py3_runtime_nonstrict",
111-
}),
112-
visibility = ["//visibility:public"],
113-
)
114-
11565
native.toolchain(
11666
name = name,
11767
toolchain = ":_autodetecting_py_runtime_pair",
11868
toolchain_type = ":toolchain_type",
11969
visibility = ["//visibility:public"],
12070
)
121-
122-
native.toolchain(
123-
name = name + "_nonstrict",
124-
toolchain = ":_autodetecting_py_runtime_pair_nonstrict",
125-
toolchain_type = ":toolchain_type",
126-
visibility = ["//visibility:public"],
127-
)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/bin/sh
2+
3+
# Don't set -e because we don't have robust trapping and printing of errors.
4+
set -u
5+
6+
# We use /bin/sh rather than /bin/bash for portability. See discussion here:
7+
# https://groups.google.com/forum/?nomobile=true#!topic/bazel-dev/4Ql_7eDcLC0
8+
# We do lose the ability to set -o pipefail.
9+
10+
FAILURE_HEADER="\
11+
Error occurred while attempting to use the default Python toolchain \
12+
(@rules_python//python:autodetecting_toolchain)."
13+
14+
die() {
15+
echo "$FAILURE_HEADER" 1>&2
16+
echo "$1" 1>&2
17+
exit 1
18+
}
19+
20+
# We use `which` to locate the Python interpreter command on PATH. `command -v`
21+
# is another option, but it doesn't check whether the file it finds has the
22+
# executable bit.
23+
#
24+
# A tricky situation happens when this wrapper is invoked as part of running a
25+
# tool, e.g. passing a py_binary target to `ctx.actions.run()`. Bazel will unset
26+
# the PATH variable. Then the shell will see there's no PATH and initialize its
27+
# own, sometimes without exporting it. This causes `which` to fail and this
28+
# script to think there's no Python interpreter installed. To avoid this we
29+
# explicitly pass PATH to each `which` invocation. We can't just export PATH
30+
# because that would modify the environment seen by the final user Python
31+
# program.
32+
#
33+
# See also:
34+
#
35+
# https://github.com/bazelbuild/continuous-integration/issues/578
36+
# https://github.com/bazelbuild/bazel/issues/8414
37+
# https://github.com/bazelbuild/bazel/issues/8415
38+
39+
# Try the "python3" command name first, then fall back on "python".
40+
PYTHON_BIN="$(PATH="$PATH" which python3 2> /dev/null)"
41+
if [ -z "${PYTHON_BIN:-}" ]; then
42+
PYTHON_BIN="$(PATH="$PATH" which python 2>/dev/null)"
43+
fi
44+
if [ -z "${PYTHON_BIN:-}" ]; then
45+
die "Neither 'python3' nor 'python' were found on the target \
46+
platform's PATH, which is:
47+
48+
$PATH
49+
50+
Please ensure an interpreter is available on this platform (and marked \
51+
executable), or else register an appropriate Python toolchain as per the \
52+
documentation for py_runtime_pair \
53+
(https://github.com/bazelbuild/rules_python/blob/master/docs/python.md#py_runtime_pair)."
54+
fi
55+
56+
exec "$PYTHON_BIN" "$@"
57+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2023 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Implementation of py_runtime_pair macro portion."""
16+
17+
load(":py_runtime_pair_rule.bzl", _py_runtime_pair = "py_runtime_pair")
18+
19+
# A fronting macro is used because macros have user-observable behavior;
20+
# using one from the onset avoids introducing those changes in the future.
21+
def py_runtime_pair(**kwargs):
22+
"""Creates a py_runtime_pair target.
23+
24+
Args:
25+
**kwargs: Keyword args to pass onto underlying rule.
26+
"""
27+
_py_runtime_pair(**kwargs)
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414

1515
"""Implementation of py_runtime_pair."""
1616

17-
# TODO: move py_runtime_pair into rules_python (and the rest of @bazel_tools//python)
18-
# py_runtime should be loaded from rules_python, but this creates a circular dep, because py_runtime_pair is imported there.
19-
py_runtime = native.py_runtime
17+
load("//python:py_runtime_info.bzl", "PyRuntimeInfo")
2018

2119
def _py_runtime_pair_impl(ctx):
2220
if ctx.attr.py2_runtime != None:
@@ -47,9 +45,9 @@ def _py_runtime_pair_impl(ctx):
4745

4846
# buildifier: disable=unused-variable
4947
def _is_py2_disabled(ctx):
50-
# In Google, this file isn't bundled with Bazel, so we have to conditionally
48+
# Because this file isn't bundled with Bazel, so we have to conditionally
5149
# check for this flag.
52-
# TODO: Remove this once a build with the flag is released in Google
50+
# TODO: Remove this once all supported Balze versions have this flag.
5351
if not hasattr(ctx.fragments.py, "disable_py"):
5452
return False
5553
return ctx.fragments.py.disable_py2

0 commit comments

Comments
 (0)