Skip to content

Commit 0d53a81

Browse files
committed
trying to fix local toolchains
1 parent e0bc3de commit 0d53a81

File tree

2 files changed

+52
-10
lines changed

2 files changed

+52
-10
lines changed

python/private/local_runtime_repo.bzl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,18 @@ def _local_runtime_repo_impl(rctx):
8484
info = json.decode(exec_result.stdout)
8585
logger.info(lambda: _format_get_info_result(info))
8686

87-
# The base executable is the final, actual, underlying interpreter.
88-
# If an intermediate wrapper is used, then it can cause venvs to not
89-
# be recognized as active.
87+
# We use base_executable because we want the path within a Python
88+
# installation directory ("PYTHONHOME"). The problems with sys.executable
89+
# are:
90+
# * If we're in an activated venv, then we don't want the venv's
91+
# `bin/python3` path to be used -- it isn't an actual Python installation.
92+
# * If sys.executable is a wrapper (e.g. pyenv), then (1) it may not be
93+
# located within an actual Python installation directory, and (2) it
94+
# can interfer with Python recognizing when it's within a venv.
95+
#
96+
# In some cases, it may be a symlink (usually e.g. `python3->python3.12`),
97+
# but we don't realpath() it to respect what it has decided is the
98+
# appropriate path.
9099
interpreter_path = info["base_executable"]
91100

92101
# NOTE: Keep in sync with recursive glob in define_local_runtime_toolchain_impl

tests/integration/local_toolchains/test.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,59 @@
22
import subprocess
33
import sys
44
import unittest
5-
5+
import tempfile
6+
import os.path
67

78
class LocalToolchainTest(unittest.TestCase):
89
maxDiff = None
910

1011
def test_python_from_path_used(self):
12+
# NOTE: This is a bit brittle. It assumes the environment during the
13+
# repo-phase and when the test is run are roughly the same. It's
14+
# easy to violate this condition if there are shell-local changes
15+
# that wouldn't be reflected when sub-shells are run later.
1116
shell_path = shutil.which("python3")
1217

1318
# We call the interpreter and print its executable because of
1419
# things like pyenv: they install a shim that re-execs python.
1520
# The shim is e.g. /home/user/.pyenv/shims/python3, which then
1621
# runs e.g. /usr/bin/python3
17-
expected = subprocess.check_output(
18-
[shell_path, "-c", "import os, sys; print(sys._base_executable)"],
19-
text=True,
20-
)
21-
expected = expected.strip().lower()
22+
with tempfile.NamedTemporaryFile(suffix="_info.py", mode="w+") as f:
23+
f.write("""
24+
import sys
25+
print(sys.executable)
26+
print(sys._base_executable)
27+
"""
28+
)
29+
f.flush()
30+
output_lines = subprocess.check_output(
31+
[shell_path, f.name],
32+
text=True,
33+
).strip().splitlines()
34+
shell_exe, shell_base_exe = output_lines
35+
36+
# Call realpath() to help normalize away differences from symlinks.
37+
# Use base executable to ignore a venv the test may be running within.
38+
expected = os.path.realpath(shell_base_exe.strip().lower())
39+
actual = os.path.realpath(sys._base_executable.lower())
40+
41+
msg = f"""
42+
details of executables:
43+
test's runtime:
44+
{sys.executable=}
45+
{sys._base_executable=}
46+
realpath exe : {os.path.realpath(sys.executable)}
47+
realpath base_exe: {os.path.realpath(sys._base_executable)}
48+
49+
from shell resolution:
50+
{shell_exe=}
51+
{shell_base_exe=}
52+
realpath exe : {os.path.realpath(shell_exe)}
53+
realpath base_exe: {os.path.realpath(shell_base_exe)}
54+
""".strip()
2255

2356
# Normalize case: Windows may have case differences
24-
self.assertEqual(expected.lower(), sys._base_executable.lower())
57+
self.assertEqual(expected.lower(), actual.lower(), msg=msg)
2558

2659

2760
if __name__ == "__main__":

0 commit comments

Comments
 (0)