Skip to content

Commit 5ea06cb

Browse files
authored
fix(py_venv): Repair runfiles-relative venv roots (#579)
Due to differences between the MacOS and Linux implementations of Bazel's sandboxing (reported as #573) the current implementation of manipulating `argv[0]` to make an interpreter see itself as having a path of `.runfiles/.../.venv/bin/python3` isn't effective. Specifically on Linux it appears that we're more eagerly resolving symlinks. This means that `bazel-bin/.../*.runfiles/*/.venv/bin/python3` is being immediately resolved to an `/execroot/bazel-bin/...` entry which _contains only generated files_. An interpreter with relative path based import roots within the execroot will be able to import relocated sources (copied into the venv) but will not be able to import from source files or external generated files. Effectively spawning the interpreter is escaping the `.runfiles` tree which breaks a bunch of our other assumptions. The repair is to come up with a more portable way to identify the interpreter without escaping from the runfiles sandbox. As a solution this PR makes the interpreter shim depend both on the `$VIRTUAL_ENV` variable which also serves as an anchor to the "real" `.runfiles` tree. Due to the removals of `realpath` calls in the `activate` script, it's possible that we could provide fallback behavior by introspecting `$0`, but that verges on #581 so punting for now. --- ### Changes are visible to end-users: no ### Test plan - [x] Add imports from unmodified sources to the test cases
1 parent 8af732a commit 5ea06cb

File tree

10 files changed

+168
-47
lines changed

10 files changed

+168
-47
lines changed

py/private/py_venv/py_venv.bzl

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,13 @@ def _py_venv_base_impl(ctx):
5757

5858
# Check for duplicate virtual dependency names. Those that map to the same resolution target would have been merged by the depset for us.
5959
virtual_resolution = _py_library.resolve_virtuals(ctx)
60+
61+
# Note that this adds the workspace root for us (sigh), don't need to add to it
6062
imports_depset = _py_library.make_imports_depset(ctx, extra_imports_depsets = virtual_resolution.imports)
6163

6264
pth_lines = ctx.actions.args()
6365
pth_lines.use_param_file("%s", use_always = True)
6466
pth_lines.set_param_file_format("multiline")
65-
66-
# FIXME: This was hardcoded in the original rule_py venv and is preserved
67-
# for compatibility. Repo-absolute imports are Bad (TM) and shouldn't be on
68-
# by default. I believe that as of recent rules_python, creating these
69-
# repo-absolute imports is handled as part of the PyInfo calculation. If we
70-
# get this from rules_python, it should be removed. Or it should be moved so
71-
# that we calculate it as part of the imports depset logic.
72-
pth_lines.add(".")
7367
pth_lines.add_all(imports_depset)
7468

7569
site_packages_pth_file = ctx.actions.declare_file("{}.pth".format(ctx.attr.name))

py/tests/py_venv_conflict/BUILD.bazel

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
load("@bazel_skylib//rules:build_test.bzl", "build_test")
2-
load("//py/unstable:defs.bzl", "py_venv")
2+
load("//py:defs.bzl", "py_library")
3+
load("//py/unstable:defs.bzl", "py_venv", "py_venv_test")
4+
5+
py_library(
6+
name = "lib",
7+
srcs = [
8+
"lib.py",
9+
],
10+
imports = [
11+
"..",
12+
],
13+
)
314

415
py_venv(
516
name = "test_venv_error",
@@ -9,6 +20,7 @@ py_venv(
920
"manual",
1021
],
1122
deps = [
23+
":lib",
1224
"//py/tests/py_venv_conflict/a",
1325
"//py/tests/py_venv_conflict/b",
1426
],
@@ -18,6 +30,7 @@ py_venv(
1830
name = "test_venv_warning",
1931
package_collisions = "warning",
2032
deps = [
33+
":lib",
2134
"//py/tests/py_venv_conflict/a",
2235
"//py/tests/py_venv_conflict/b",
2336
],
@@ -27,6 +40,7 @@ py_venv(
2740
name = "test_venv_ignore",
2841
package_collisions = "ignore",
2942
deps = [
43+
":lib",
3044
"//py/tests/py_venv_conflict/a",
3145
"//py/tests/py_venv_conflict/b",
3246
],
@@ -39,3 +53,10 @@ build_test(
3953
":test_venv_ignore",
4054
],
4155
)
56+
57+
py_venv_test(
58+
name = "validate_import_roots",
59+
srcs = ["test_import_roots.py"],
60+
main = "test_import_roots.py",
61+
venv = ":test_venv_ignore",
62+
)

py/tests/py_venv_conflict/lib.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env python3
2+
3+
def add(a, b):
4+
return a + b
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
for k, v in os.environ.items():
5+
if k.startswith("BUILD_") or k.startswith("RUNFILES_"):
6+
print(k, ":", v)
7+
8+
print("---")
9+
10+
from pathlib import Path
11+
12+
# prefix components:
13+
space = ' '
14+
branch = '| '
15+
# pointers:
16+
tee = '+-- '
17+
last = '+-- '
18+
19+
20+
def tree(dir_path: Path, prefix: str=''):
21+
"""A recursive generator, given a directory Path object
22+
will yield a visual tree structure line by line
23+
with each line prefixed by the same characters
24+
"""
25+
contents = list(dir_path.iterdir())
26+
# contents each get pointers that are ├── with a final └── :
27+
pointers = [tee] * (len(contents) - 1) + [last]
28+
for pointer, path in zip(pointers, contents):
29+
yield prefix + pointer + path.name
30+
if path.is_dir(): # extend the prefix and recurse:
31+
extension = branch if pointer == tee else space
32+
# i.e. space because last, └── , above so no more |
33+
yield from tree(path, prefix=prefix+extension)
34+
35+
here = Path(".")
36+
print(here.absolute().resolve())
37+
for line in tree(here):
38+
print(line)
39+
40+
print("---")
41+
42+
import sys
43+
for e in sys.path:
44+
print("-", e)
45+
46+
print("---")
47+
48+
print(sys.prefix)
49+
50+
import conflict
51+
print(conflict.__file__)
52+
assert conflict.__file__.startswith(sys.prefix)
53+
54+
import noconflict
55+
print(noconflict.__file__)
56+
assert noconflict.__file__.startswith(sys.prefix)
57+
58+
import py_venv_conflict.lib as srclib
59+
print(srclib.__file__)
60+
assert not srclib.__file__.startswith(sys.prefix)

py/tests/py_venv_image_layer/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ platform_transition_filegroup(
4545
)
4646

4747
assert_tar_listing(
48-
name = "my_app_amd64_layers_test",
48+
name = "my_app_amd64_layers",
4949
actual = [":amd64_layers"],
5050
expected = ":my_app_amd64_layers_listing.yaml",
5151
)
@@ -57,7 +57,7 @@ platform_transition_filegroup(
5757
)
5858

5959
assert_tar_listing(
60-
name = "my_app_arm64_layers_test",
60+
name = "my_app_arm64_layers",
6161
actual = [":arm64_layers"],
6262
expected = ":my_app_arm64_layers_listing.yaml",
6363
)

py/tests/py_venv_image_layer/my_app_amd64_layers_listing.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2442,7 +2442,7 @@ files:
24422442
layer: 1
24432443
files:
24442444
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/
2445-
- -rwxr-xr-x 0 0 0 356 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_aspect.pth
2445+
- -rwxr-xr-x 0 0 0 328 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_aspect.pth
24462446
- -rwxr-xr-x 0 0 0 19 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_virtualenv.pth
24472447
- -rwxr-xr-x 0 0 0 4342 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_virtualenv.py
24482448
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/colorama-0.4.6.dist-info/
@@ -2510,10 +2510,10 @@ files:
25102510
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/
25112511
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/
25122512
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/
2513-
- -rwxr-xr-x 0 0 0 7827 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/activate
2514-
- -rwxr-xr-x 0 0 0 813320 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python
2515-
- -rwxr-xr-x 0 0 0 813320 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3
2516-
- -rwxr-xr-x 0 0 0 813320 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3.9
2513+
- -rwxr-xr-x 0 0 0 8099 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/activate
2514+
- -rwxr-xr-x 0 0 0 817416 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python
2515+
- -rwxr-xr-x 0 0 0 817416 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3
2516+
- -rwxr-xr-x 0 0 0 817416 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3.9
25172517
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/
25182518
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/
25192519
- -rwxr-xr-x 0 0 0 323 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/pyvenv.cfg

py/tests/py_venv_image_layer/my_app_arm64_layers_listing.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,7 +2423,7 @@ files:
24232423
layer: 1
24242424
files:
24252425
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/
2426-
- -rwxr-xr-x 0 0 0 356 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_aspect.pth
2426+
- -rwxr-xr-x 0 0 0 328 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_aspect.pth
24272427
- -rwxr-xr-x 0 0 0 19 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_virtualenv.pth
24282428
- -rwxr-xr-x 0 0 0 4342 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/_virtualenv.py
24292429
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/site-packages/colorama-0.4.6.dist-info/
@@ -2491,10 +2491,10 @@ files:
24912491
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/
24922492
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/
24932493
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/
2494-
- -rwxr-xr-x 0 0 0 7828 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/activate
2495-
- -rwxr-xr-x 0 0 0 693968 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python
2496-
- -rwxr-xr-x 0 0 0 693968 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3
2497-
- -rwxr-xr-x 0 0 0 693968 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3.9
2494+
- -rwxr-xr-x 0 0 0 8100 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/activate
2495+
- -rwxr-xr-x 0 0 0 698064 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python
2496+
- -rwxr-xr-x 0 0 0 698064 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3
2497+
- -rwxr-xr-x 0 0 0 698064 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/bin/python3.9
24982498
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/
24992499
- drwxr-xr-x 0 0 0 0 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/lib/python3.9/
25002500
- -rwxr-xr-x 0 0 0 323 Jan 1 2023 ./py/tests/py_venv_image_layer/my_app_bin.runfiles/aspect_rules_py/py/tests/py_venv_image_layer/.my_app_bin/pyvenv.cfg

py/tools/py/src/activate.tmpl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,15 @@ deactivate nondestructive
5858
# The runfiles library code has some deps on this so we just set it :/
5959
: "${BASH_SOURCE:=$0}"
6060

61-
VIRTUAL_ENV="$(realpath "$(dirname "$(dirname "${BASH_SOURCE}")")")"
61+
VIRTUAL_ENV="$(dirname "$(dirname "${BASH_SOURCE}")")"
6262
export VIRTUAL_ENV
6363

64+
# HACK: (Ab)use the MacOS $PYTHONEXECUTABLE to record the `.runfiles`-relative
65+
# interpreter path. This helps us avoid issues with the interpreter's path being
66+
# `realpath`-ed in such a way that it escapes the `.runfiles` tree.
67+
PYTHONEXECUTABLE="${VIRTUAL_ENV}/bin/python"
68+
export PYTHONEXECUTABLE
69+
6470
# unset PYTHONHOME if set
6571
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
6672
# could use `if (set -u; : $PYTHONHOME) ;` in bash.

py/tools/py/src/runfiles_interpreter.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ source "${RUNFILES_DIR:-/dev/null}/${f}" 2>/dev/null || \
8080
} >/dev/null
8181

8282
# Look up the runfiles-based interpreter and put its dir _first_ on the path.
83-
INTERPRETER="$(realpath $(rlocation {{INTERPRETER_TARGET}}))"
83+
INTERPRETER="$(rlocation {{INTERPRETER_TARGET}})"
8484

8585
# Figure out if we're dealing with just some program or a real install
8686
# <SOMEDIR> <- possible $PYTHONHOME

0 commit comments

Comments
 (0)