Skip to content

Commit a506d77

Browse files
jsing-canvaaignas
andauthored
fix(runfiles): correct Python runfiles path assumption (#3086)
The current _FindPythonRunfilesRoot() implementation assumes that the Python module has been unpacked four levels below the runfiles directory. This is not the case in multiple situations, for example when rules_pycross is in use and has installed the module via pypi (in which case it is five levels below runfiles). Both strategies already know where the runfiles directory exists - implement _GetRunfilesDir() on the _DirectoryBased strategy, then call _GetRunfilesDir() in order to populate self._python_runfiles_dir. Stop passing a bogus path to runfiles.Create() in testCurrentRepository(), such that the test actually uses the appropriate runfiles path. Fixes #3085 --------- Co-authored-by: Ignas Anikevicius <[email protected]>
1 parent c181f93 commit a506d77

File tree

5 files changed

+37
-17
lines changed

5 files changed

+37
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ BEGIN_UNRELEASED_TEMPLATE
4242
### Fixed
4343
* (gazelle) Remove {obj}`py_binary` targets with invalid `srcs`. This includes files
4444
that are not generated or regular files.
45+
* (runfiles) Fix incorrect Python runfiles path assumption - the existing
46+
implementation assumes that it is always four levels below the runfiles
47+
directory, leading to incorrect path checks
48+
([#3085](https://github.com/bazel-contrib/rules_python/issues/3085)).
4549
4650
{#v0-0-0-added}
4751
### Added

python/runfiles/runfiles.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ def RlocationChecked(self, path: str) -> str:
229229
# runfiles strategy on those platforms.
230230
return posixpath.join(self._runfiles_root, path)
231231

232+
def _GetRunfilesDir(self) -> str:
233+
return self._runfiles_root
234+
232235
def EnvVars(self) -> Dict[str, str]:
233236
return {
234237
"RUNFILES_DIR": self._runfiles_root,
@@ -246,7 +249,7 @@ class Runfiles:
246249

247250
def __init__(self, strategy: Union[_ManifestBased, _DirectoryBased]) -> None:
248251
self._strategy = strategy
249-
self._python_runfiles_root = _FindPythonRunfilesRoot()
252+
self._python_runfiles_root = strategy._GetRunfilesDir()
250253
self._repo_mapping = _RepositoryMapping.create_from_file(
251254
strategy.RlocationChecked("_repo_mapping")
252255
)
@@ -469,18 +472,6 @@ def Create(env: Optional[Dict[str, str]] = None) -> Optional["Runfiles"]:
469472
_Runfiles = Runfiles
470473

471474

472-
def _FindPythonRunfilesRoot() -> str:
473-
"""Finds the root of the Python runfiles tree."""
474-
root = __file__
475-
# Walk up our own runfiles path to the root of the runfiles tree from which
476-
# the current file is being run. This path coincides with what the Bazel
477-
# Python stub sets up as sys.path[0]. Since that entry can be changed at
478-
# runtime, we rederive it here.
479-
for _ in range("rules_python/python/runfiles/runfiles.py".count("/") + 1):
480-
root = os.path.dirname(root)
481-
return root
482-
483-
484475
def CreateManifestBased(manifest_path: str) -> Runfiles:
485476
return Runfiles.CreateManifestBased(manifest_path)
486477

tests/runfiles/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ load("@rules_python//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # bu
55
py_test(
66
name = "runfiles_test",
77
srcs = ["runfiles_test.py"],
8+
data = [
9+
"//tests/support:current_build_settings",
10+
],
811
env = {
912
"BZLMOD_ENABLED": "1" if BZLMOD_ENABLED else "0",
1013
},

tests/runfiles/runfiles_test.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import json
1516
import os
17+
import pathlib
1618
import tempfile
1719
import unittest
1820
from typing import Any, List, Optional
@@ -63,6 +65,16 @@ def testRlocationArgumentValidation(self) -> None:
6365
lambda: r.Rlocation("\\foo"),
6466
)
6567

68+
def testRlocationWithData(self) -> None:
69+
r = runfiles.Create()
70+
assert r is not None # mypy doesn't understand the unittest api.
71+
settings_path = r.Rlocation(
72+
"rules_python/tests/support/current_build_settings.json"
73+
)
74+
assert settings_path is not None
75+
settings = json.loads(pathlib.Path(settings_path).read_text())
76+
self.assertIn("bootstrap_impl", settings)
77+
6678
def testCreatesManifestBasedRunfiles(self) -> None:
6779
with _MockFile(contents=["a/b c/d"]) as mf:
6880
r = runfiles.Create(
@@ -692,7 +704,7 @@ def testCurrentRepository(self) -> None:
692704
expected = ""
693705
else:
694706
expected = "rules_python"
695-
r = runfiles.Create({"RUNFILES_DIR": "whatever"})
707+
r = runfiles.Create()
696708
assert r is not None # mypy doesn't understand the unittest api.
697709
self.assertEqual(r.CurrentRepository(), expected)
698710

tests/runtime_env_toolchain/toolchain_runs_test.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,20 @@
1010
class RunTest(unittest.TestCase):
1111
def test_ran(self):
1212
rf = runfiles.Create()
13-
settings_path = rf.Rlocation(
14-
"rules_python/tests/support/current_build_settings.json"
15-
)
13+
try:
14+
settings_path = rf.Rlocation(
15+
"rules_python/tests/support/current_build_settings.json"
16+
)
17+
except ValueError as e:
18+
# The current toolchain being used has a buggy zip file bootstrap, which
19+
# leaves RUNFILES_DIR pointing at the first stage path and not the module
20+
# path.
21+
if platform.system() != "Windows" or "does not lie under the runfiles root" not in str(e):
22+
raise e
23+
settings_path = "./tests/support/current_build_settings.json"
24+
1625
settings = json.loads(pathlib.Path(settings_path).read_text())
26+
1727
if platform.system() == "Windows":
1828
self.assertEqual(
1929
"/_magic_pyruntime_sentinel_do_not_use", settings["interpreter_path"]

0 commit comments

Comments
 (0)