Skip to content

Commit 60d4b91

Browse files
committed
add tests for relative_path
1 parent f0b36ae commit 60d4b91

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

python/private/py_executable_bazel.bzl

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -347,22 +347,26 @@ def _create_zip_main(ctx, *, stage2_bootstrap, runtime_details, venv):
347347

348348
# Return a relative path from one path to another, where both paths are each
349349
# relative paths from a common root.
350-
def _relative_path(from_, to):
350+
def relative_path(from_, to):
351351
from_parts = from_.split("/")
352352
to_parts = to.split("/")
353353

354-
# Strip common "../" parts from both paths
354+
# Strip common leading parts from both paths
355355
# (no while loops in starlark :( )
356-
n = max(len(from_parts), len(to_parts))
356+
n = min(len(from_parts), len(to_parts))
357357
for _ in range(n):
358-
if from_parts[0] == ".." and to_parts[0] == "..":
358+
if from_parts[0] == to_parts[0]:
359359
from_parts.pop(0)
360360
to_parts.pop(0)
361361
else:
362362
break
363363

364-
parent = "/".join([".."] * len(from_parts))
365-
return "/".join([parent] + to_parts)
364+
# Impossible to compute a relative path without knowing what ".." is
365+
if from_parts and from_parts[0] == "..":
366+
fail("cannot compute relative path from '%s' to '%s'", from_, to)
367+
368+
parts = ([".."] * len(from_parts)) + to_parts
369+
return "/".join(parts)
366370

367371

368372
# Create a venv the executable can use.
@@ -391,7 +395,7 @@ def _create_venv(ctx, output_prefix, imports, runtime_details):
391395
interpreter = ctx.actions.declare_symlink("{}/bin/{}".format(venv, py_exe_basename))
392396
interpreter_actual_path = runtime.interpreter.short_path
393397
venv_bin_dir = paths.dirname(interpreter.short_path)
394-
rel_path = _relative_path(from_=venv_bin_dir, to=interpreter_actual_path)
398+
rel_path = relative_path(from_=venv_bin_dir, to=interpreter_actual_path)
395399
ctx.actions.symlink(output = interpreter, target_path = rel_path)
396400
else:
397401
py_exe_basename = paths.basename(runtime.interpreter_path)

tests/bootstrap_impls/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,7 @@ sh_py_run_test(
8787
sh_src = "sys_executable_inherits_sys_path_test.sh",
8888
target_compatible_with = _SUPPORTS_BOOTSTRAP_SCRIPT,
8989
)
90+
91+
load(":venv_relative_path_tests.bzl", "relative_path_test_suite")
92+
93+
relative_path_test_suite("relative_path_test")
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
"Unit tests for yaml.bzl"
16+
17+
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts", "unittest")
18+
load("//python/private:py_executable_bazel.bzl", "relative_path") # buildifier: disable=bzl-visibility
19+
20+
def _relative_path_test_impl(ctx):
21+
env = unittest.begin(ctx)
22+
23+
# Basic test cases
24+
25+
asserts.equals(
26+
env,
27+
"../../c/d",
28+
relative_path(
29+
from_ = "a/b",
30+
to = "c/d",
31+
),
32+
)
33+
34+
asserts.equals(
35+
env,
36+
"../../c/d",
37+
relative_path(
38+
from_ = "../a/b",
39+
to = "../c/d",
40+
),
41+
)
42+
43+
asserts.equals(
44+
env,
45+
"../../../c/d",
46+
relative_path(
47+
from_ = "../a/b",
48+
to = "../../c/d",
49+
),
50+
)
51+
52+
asserts.equals(
53+
env,
54+
"../../d",
55+
relative_path(
56+
from_ = "a/b/c",
57+
to = "a/d",
58+
),
59+
)
60+
61+
asserts.equals(
62+
env,
63+
"d/e",
64+
relative_path(
65+
from_ = "a/b/c",
66+
to = "a/b/c/d/e",
67+
),
68+
)
69+
70+
# Real examples
71+
72+
# external py_binary uses external python runtime
73+
asserts.equals(
74+
env,
75+
"../../../../../rules_python~~python~python_3_9_x86_64-unknown-linux-gnu/bin/python3",
76+
relative_path(
77+
from_ = "../rules_python~/python/private/_py_console_script_gen_py.venv/bin",
78+
to = "../rules_python~~python~python_3_9_x86_64-unknown-linux-gnu/bin/python3",
79+
),
80+
)
81+
82+
# internal py_binary uses external python runtime
83+
asserts.equals(
84+
env,
85+
"../../../../rules_python~~python~python_3_9_x86_64-unknown-linux-gnu/bin/python3",
86+
relative_path(
87+
from_ = "test/version_default.venv/bin",
88+
to = "../rules_python~~python~python_3_9_x86_64-unknown-linux-gnu/bin/python3",
89+
),
90+
)
91+
92+
# external py_binary uses internal python runtime
93+
# asserts.equals(
94+
# env,
95+
# "???",
96+
# relative_path(
97+
# from_ = "../rules_python~/python/private/_py_console_script_gen_py.venv/bin",
98+
# to = "python/python_3_9_x86_64-unknown-linux-gnu/bin/python3",
99+
# ),
100+
#)
101+
# ^ TODO: Technically we can infer ".." to be the workspace name?
102+
103+
# internal py_binary uses internal python runtime
104+
asserts.equals(
105+
env,
106+
"../../../python/python_3_9_x86_64-unknown-linux-gnu/bin/python3",
107+
relative_path(
108+
from_ = "scratch/main.venv/bin",
109+
to = "python/python_3_9_x86_64-unknown-linux-gnu/bin/python3",
110+
),
111+
)
112+
113+
return unittest.end(env)
114+
115+
relative_path_test = unittest.make(
116+
_relative_path_test_impl,
117+
attrs = {},
118+
)
119+
120+
def relative_path_test_suite(name):
121+
unittest.suite(name, relative_path_test)

0 commit comments

Comments
 (0)