Add a non-strict autodetecting Python toolchain
This toolchain works just like the standard autodetecting toolchain, except that if it falls back on the `python` command, it doesn't care what version that interpreter is. This allows users to opt into the legacy behavior of #4815 while still enabling Python toolchains.
This is particularly useful for Mac users who do not have a Python 3 runtime installed. Naturally, such users can only have Python 2 code in their builds, but it's still possible that these Python targets get analyzed by Bazel as PY3. For example, the target author may have forgotten to set the `python_version = "PY2"` attribute, or the downstream user may have not added `--host_force_python=PY2` to their bazelrc. Previously this worked because under #4815 Bazel would just use Python 2 for PY3 targets. Strict version checking breaks these builds, but the new non-strict toolchain provides a workaround.
Fixes #8547
RELNOTES: None
PiperOrigin-RevId: 251535571
diff --git a/tools/python/pywrapper_test.py b/tools/python/pywrapper_test.py
index fa41239..07732a6 100644
--- a/tools/python/pywrapper_test.py
+++ b/tools/python/pywrapper_test.py
@@ -94,14 +94,20 @@
path, msg="Could not locate '%s' command on PATH" % cmd)
self.CopyFile(path, os.path.join("dir", cmd), executable=True)
+ def locate_runfile(self, runfile_path):
+ resolved_path = self.Rlocation(runfile_path)
+ self.assertIsNotNone(
+ resolved_path, msg="Could not locate %s in runfiles" % runfile_path)
+ return resolved_path
+
def setUp(self):
super(PywrapperTest, self).setUp()
- # Locate script under test.
- wrapper_path = self.Rlocation("io_bazel/tools/python/py2wrapper.sh")
- self.assertIsNotNone(
- wrapper_path, msg="Could not locate py2wrapper.sh in runfiles")
- self.wrapper_path = wrapper_path
+ # Locate scripts under test.
+ self.wrapper_path = \
+ self.locate_runfile("io_bazel/tools/python/py2wrapper.sh")
+ self.nonstrict_wrapper_path = \
+ self.locate_runfile("io_bazel/tools/python/py2wrapper_nonstrict.sh")
# Setup scratch directory with all executables the script depends on.
#
@@ -112,10 +118,10 @@
self.setup_tool("echo")
self.setup_tool("grep")
- def run_wrapper(self, title_for_logging=None):
+ def run_with_restricted_path(self, program, title_for_logging=None):
new_env = dict(os.environ)
new_env["PATH"] = self.Path("dir")
- proc = subprocess.Popen([self.wrapper_path],
+ proc = subprocess.Popen([program],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
@@ -136,6 +142,13 @@
""") % (title_for_logging, proc.returncode, out, err))
return proc.returncode, out, err
+ def run_wrapper(self, title_for_logging):
+ return self.run_with_restricted_path(self.wrapper_path, title_for_logging)
+
+ def run_nonstrict_wrapper(self, title_for_logging):
+ return self.run_with_restricted_path(self.nonstrict_wrapper_path,
+ title_for_logging)
+
def assert_wrapper_success(self, returncode, out, err):
self.assertEqual(returncode, 0, msg="Expected to exit without error")
self.assertEqual(
@@ -190,6 +203,13 @@
self.assert_wrapper_failure(returncode, out, err,
"Neither 'python2' nor 'python' were found")
+ def test_wrong_version_ok_for_nonstrict(self):
+ self.ScratchFile(
+ "dir/python2", MockPythonLines.WRONG_VERSION, executable=True)
+ returncode, out, err = \
+ self.run_nonstrict_wrapper("test_wrong_version_ok_for_nonstrict")
+ self.assert_wrapper_success(returncode, out, err)
+
if __name__ == "__main__":
unittest.main()