From 9c077f2fbff1143f0b3a6d6dd7696e67f06e95d5 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 7 Mar 2026 16:50:21 -0800 Subject: [PATCH 1/7] basic pycache impl --- .bazelversion | 2 +- MODULE.bazel | 2 +- examples/bzlmod/.bazelversion | 2 +- examples/bzlmod/tests/version.py | 2 +- .../private/hermetic_runtime_repo_setup.bzl | 50 ++++++++++------- python/private/python_repository.bzl | 56 +++++++++++++++++++ python/private/repo_utils.bzl | 32 +++++++++++ tests/repro/BUILD.bazel | 6 ++ tests/repro/bin.py | 4 ++ tests/repro/repro.sh | 33 +++++++++++ 10 files changed, 166 insertions(+), 23 deletions(-) create mode 100644 tests/repro/BUILD.bazel create mode 100644 tests/repro/bin.py create mode 100755 tests/repro/repro.sh diff --git a/.bazelversion b/.bazelversion index c6b7980b68..512e4c889e 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -8.x +9.x diff --git a/MODULE.bazel b/MODULE.bazel index 326bb5b78e..a8af3be582 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -226,7 +226,7 @@ bazel_dep(name = "another_module", version = "0", dev_dependency = True) # Extra gazelle plugin deps so that WORKSPACE.bzlmod can continue including it for e2e tests. # We use `WORKSPACE.bzlmod` because it is impossible to have dev-only local overrides. -bazel_dep(name = "rules_go", version = "0.41.0", dev_dependency = True, repo_name = "io_bazel_rules_go") +bazel_dep(name = "rules_go", version = "0.60.0", dev_dependency = True, repo_name = "io_bazel_rules_go") internal_dev_deps = use_extension( "//python/private:internal_dev_deps.bzl", diff --git a/examples/bzlmod/.bazelversion b/examples/bzlmod/.bazelversion index 35907cd9ca..512e4c889e 100644 --- a/examples/bzlmod/.bazelversion +++ b/examples/bzlmod/.bazelversion @@ -1 +1 @@ -7.x +9.x diff --git a/examples/bzlmod/tests/version.py b/examples/bzlmod/tests/version.py index 2d293c1571..28da2e86c8 100644 --- a/examples/bzlmod/tests/version.py +++ b/examples/bzlmod/tests/version.py @@ -13,5 +13,5 @@ # limitations under the License. import sys - +import importlib print(f"{sys.version_info.major}.{sys.version_info.minor}") diff --git a/python/private/hermetic_runtime_repo_setup.bzl b/python/private/hermetic_runtime_repo_setup.bzl index c3c275546d..1f485ba10b 100644 --- a/python/private/hermetic_runtime_repo_setup.bzl +++ b/python/private/hermetic_runtime_repo_setup.bzl @@ -58,30 +58,42 @@ def define_hermetic_runtime_toolchain_impl( "major": version_info.release[0], "minor": version_info.release[1], } + files_include = [ + "bin/**", + "extensions/**", + "include/**", + "libs/**", + "share/**", + ] + ##files_include += extra_files_glob_include + # This glob include appears to add the watch of `_VOLATILE/.../__pycache__` + files_include += ["lib/**"] + files_exclude = [ + # The VOLATILE directory contains e.g. pyc files generated + # at runtime, which would otherwise invalidate the repo and + # break caching. + "_VOLATILE", + "_VOLATILE/**", + # Unused shared libraries. `python` executable and the `:libpython` target + # depend on `libpython{python_version}.so.1.0`. + "lib/libpython{major}.{minor}*.so".format(**version_dict), + # static libraries + "lib/**/*.a", + # tests for the standard libraries. + "lib/python{major}.{minor}*/**/test/**".format(**version_dict), + "lib/python{major}.{minor}*/**/tests/**".format(**version_dict), + # During pyc creation, temp files named *.pyc.NNN are created + "**/__pycache__/*.pyc.*", + ] + files_exclude += extra_files_glob_exclude + native.filegroup( name = "files", srcs = native.glob( - include = [ - "bin/**", - "extensions/**", - "include/**", - "libs/**", - "share/**", - ] + extra_files_glob_include, + include = files_include, # Platform-agnostic filegroup can't match on all patterns. allow_empty = True, - exclude = [ - # Unused shared libraries. `python` executable and the `:libpython` target - # depend on `libpython{python_version}.so.1.0`. - "lib/libpython{major}.{minor}*.so".format(**version_dict), - # static libraries - "lib/**/*.a", - # tests for the standard libraries. - "lib/python{major}.{minor}*/**/test/**".format(**version_dict), - "lib/python{major}.{minor}*/**/tests/**".format(**version_dict), - # During pyc creation, temp files named *.pyc.NNN are created - "**/__pycache__/*.pyc.*", - ] + extra_files_glob_exclude, + exclude = files_exclude, ), ) cc_import( diff --git a/python/private/python_repository.bzl b/python/private/python_repository.bzl index 3d54b8a26d..f46b22b907 100644 --- a/python/private/python_repository.bzl +++ b/python/private/python_repository.bzl @@ -52,6 +52,52 @@ def is_standalone_interpreter(rctx, python_interpreter_path, *, logger = None): logger = logger, ).return_code == 0 +def _create_pycache_symlinks(rctx, logger): + """Finds all directories with a .py file and creates __pycache__ symlinks. + + Args: + rctx: {type}`repository_ctx` The repository rule's context object. + logger: Optional logger to use for operations. + """ + volatile_dir = repo_utils.mkdir(rctx, "_VOLATILE") + ##volatile_dir = rctx.path("/tmp/volatile") + volatile_dir_str = str(volatile_dir) + + root = rctx.path(".") + root_str = str(root) + + queue = [root] + + # Starlark doesn't support recursion, use a loop with a queue. + # Using a large range as a safeguard. + for _ in range(1000000): + if not queue: + break + p = queue.pop() + + has_py = False + for child in p.readdir(): + # Skip hidden files and directories + if child.basename.startswith("."): + continue + + if child.is_dir: + if child.basename == "__pycache__" or str(child) == volatile_dir_str: + continue + queue.append(child) + elif child.basename.endswith(".py"): + has_py = True + + if has_py: + pycache_dir = p.get_child("__pycache__") + pycache_relative = repo_utils.repo_root_relative_path(rctx, pycache_dir) + target_dir = volatile_dir.get_child(pycache_relative) + + repo_utils.mkdir(rctx, target_dir) + rctx.delete(pycache_dir) + rctx.symlink(target_dir, pycache_dir) + ##rctx.symlink("/tmp/pycache/" + pycache_relative, pycache_dir) + def _python_repository_impl(rctx): if rctx.attr.distutils and rctx.attr.distutils_content: fail("Only one of (distutils, distutils_content) should be set.") @@ -123,6 +169,12 @@ def _python_repository_impl(rctx): logger = logger, ) + # Do bazel clean, then run repro.sh -> no invalidation + # Modify this line (or something in this repo rule) -> invalidation + # happens on every subsequent invocation, even if repo rule is unmodified + # between invocations. + print("==== init python_repository", 8) + _create_pycache_symlinks(rctx, logger) python_bin = "python.exe" if ("windows" in platform) else "bin/python3" if "linux" in platform: @@ -224,11 +276,15 @@ define_hermetic_runtime_toolchain_impl( else: attrs["urls"] = urls + ##return attrs + + # after 1.8.5 # Bazel <8.3.0 lacks repository_ctx.repo_metadata if not hasattr(rctx, "repo_metadata"): return attrs reproducible = rctx.attr.sha256 != "" + reproducible = False return rctx.repo_metadata( reproducible = reproducible, attrs_for_reproducibility = {} if reproducible else attrs, diff --git a/python/private/repo_utils.bzl b/python/private/repo_utils.bzl index 702a333772..f3164a6d76 100644 --- a/python/private/repo_utils.bzl +++ b/python/private/repo_utils.bzl @@ -319,6 +319,36 @@ def _which_describe_failure(binary_name, path): path = path, ) +def _mkdir(mrctx, path): + path = mrctx.path(path) + if path.exists: + return path + placeholder = path.get_child(".placeholder") + mrctx.file(placeholder) + mrctx.delete(placeholder) + return path + +def _repo_root_relative_path(mrctx, path): + """Takes a path object and returns a repo-relative path string. + + Args: + mrctx: module_ctx or repository_ctx + path: {type}`path` a path within `mrctx` + + Returns: + {type}`str` a repo-root-relative path string. + """ + repo_root = str(mrctx.path(".")) + path_str = str(path) + relative_path = path_str[len(repo_root):] + if relative_path[0] != "/": + fail("{path} not under {repo_root}".format( + path = path, + repo_root = repo_root + )) + relative_path = relative_path[1:] + return relative_path + def _args_to_str(arguments): return " ".join([_arg_repr(a) for a in arguments]) @@ -465,6 +495,8 @@ repo_utils = struct( get_platforms_os_name = _get_platforms_os_name, is_repo_debug_enabled = _is_repo_debug_enabled, logger = _logger, + mkdir = _mkdir, + repo_root_relative_path = _repo_root_relative_path, which_checked = _which_checked, which_unchecked = _which_unchecked, ) diff --git a/tests/repro/BUILD.bazel b/tests/repro/BUILD.bazel new file mode 100644 index 0000000000..f30cab0687 --- /dev/null +++ b/tests/repro/BUILD.bazel @@ -0,0 +1,6 @@ +load("//python:py_binary.bzl", "py_binary") + +py_binary( + name = "bin", + srcs = ["bin.py"] +) diff --git a/tests/repro/bin.py b/tests/repro/bin.py new file mode 100644 index 0000000000..2c0936d178 --- /dev/null +++ b/tests/repro/bin.py @@ -0,0 +1,4 @@ +import sys +import importlib +print("hello") +print(sys.version) diff --git a/tests/repro/repro.sh b/tests/repro/repro.sh new file mode 100755 index 0000000000..92dec2ac30 --- /dev/null +++ b/tests/repro/repro.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +export USE_BAZEL_VERSION=8.x +export USE_BAZEL_VERSION=8.2.1 +##export USE_BAZEL_VERSION=8.6.0 +export USE_BAZEL_VERSION=9.x + +##export PYTHONDONTWRITEBYTECODE=1 +##export PYTHONPYCACHEPREFIX=/tmp/bazelpycache + +## --announce_rc \ +## --experimental_check_external_repository_files \ +## --experimental_check_external_other_files \ +## --experimental_repository_cache_hardlinks \ +## --watchfs \ +function run_test() { + bazel run \ + //tests/repro:bin +} +##set -x + +echo "==== Invocation 1 ====" +echo "======================" +run_test + +pydir=bazel-rules_python/external/+python+python_3_11_14_x86_64-unknown-linux-gnu + +#touch $pydir/lib/python3.11/importlib +##rm -fr $pydir/lib/python3.11/importlib/__pycache__/* + +echo "==== Invocation 2 ====" +echo "======================" +run_test From c53c6cf96975197710f0813d90c226946227c101 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 11 Mar 2026 19:44:47 -0700 Subject: [PATCH 2/7] switch to using /tmp --- python/private/python_repository.bzl | 6 ++++-- python/private/repo_utils.bzl | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/python/private/python_repository.bzl b/python/private/python_repository.bzl index f46b22b907..932644d962 100644 --- a/python/private/python_repository.bzl +++ b/python/private/python_repository.bzl @@ -59,8 +59,10 @@ def _create_pycache_symlinks(rctx, logger): rctx: {type}`repository_ctx` The repository rule's context object. logger: Optional logger to use for operations. """ - volatile_dir = repo_utils.mkdir(rctx, "_VOLATILE") - ##volatile_dir = rctx.path("/tmp/volatile") + ##volatile_dir = repo_utils.mkdir(rctx, "_VOLATILE") + volatile_dir = rctx.path("/tmp/rules_python_pycache_{}/{}".format( + hash(str(rctx.workspace_root)), rctx.name + )) volatile_dir_str = str(volatile_dir) root = rctx.path(".") diff --git a/python/private/repo_utils.bzl b/python/private/repo_utils.bzl index f3164a6d76..7657f4acd1 100644 --- a/python/private/repo_utils.bzl +++ b/python/private/repo_utils.bzl @@ -323,9 +323,21 @@ def _mkdir(mrctx, path): path = mrctx.path(path) if path.exists: return path - placeholder = path.get_child(".placeholder") - mrctx.file(placeholder) - mrctx.delete(placeholder) + + repo_root = str(mrctx.path(".")) + path_str = str(path) + + if not path_str.startswith(repo_root + "/"): + _execute_checked( + mrctx, + op = "mkdir", + arguments = [_which_checked(mrctx, "mkdir"), "-p", path_str], + ) + return path + else: + placeholder = path.get_child(".placeholder") + mrctx.file(placeholder) + mrctx.delete(placeholder) return path def _repo_root_relative_path(mrctx, path): From d30d3f0a7abdfe429df03f2a9a7c559711115220 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 11 Mar 2026 20:19:28 -0700 Subject: [PATCH 3/7] add logic for finding temp dir --- python/private/python_repository.bzl | 69 ++++++++++++++++++++++------ python/private/repo_utils.bzl | 15 +++--- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/python/private/python_repository.bzl b/python/private/python_repository.bzl index 932644d962..23dcebd24f 100644 --- a/python/private/python_repository.bzl +++ b/python/private/python_repository.bzl @@ -52,6 +52,46 @@ def is_standalone_interpreter(rctx, python_interpreter_path, *, logger = None): logger = logger, ).return_code == 0 +def _get_pycache_root(rctx): + """Calculates and creates the pycache root directory. + + Returns: + {type}`path | None` The path to the pycache root, or None if it couldn't + be created. + """ + os_name = repo_utils.get_platforms_os_name(rctx) + is_windows = os_name == "windows" + + # 1. RULES_PYTHON_PYCACHE_DIR + res = rctx.getenv("RULES_PYTHON_PYCACHE_DIR") + if res: + return repo_utils.mkdir(rctx, res) + + # Suffix for cases 2-4 + suffix = "rules_python_{}/{}".format(hash(str(rctx.workspace_root)), rctx.name) + + # 2. XDG_CACHE_HOME + res = rctx.getenv("XDG_CACHE_HOME") + if res: + path = repo_utils.mkdir(rctx, rctx.path(res).get_child(suffix)) + if path: + return path + + # 3. TMP or TEMP + res = rctx.getenv("TMP") or rctx.getenv("TEMP") + if res: + path = repo_utils.mkdir(rctx, rctx.path(res).get_child(suffix)) + if path: + return path + + # 4. /tmp or Windows equivalent + if is_windows: + path = rctx.path("C:/Temp").get_child(suffix) + else: + path = rctx.path("/tmp").get_child(suffix) + + return repo_utils.mkdir(rctx, path) + def _create_pycache_symlinks(rctx, logger): """Finds all directories with a .py file and creates __pycache__ symlinks. @@ -59,11 +99,11 @@ def _create_pycache_symlinks(rctx, logger): rctx: {type}`repository_ctx` The repository rule's context object. logger: Optional logger to use for operations. """ - ##volatile_dir = repo_utils.mkdir(rctx, "_VOLATILE") - volatile_dir = rctx.path("/tmp/rules_python_pycache_{}/{}".format( - hash(str(rctx.workspace_root)), rctx.name - )) - volatile_dir_str = str(volatile_dir) + pycache_root = _get_pycache_root(rctx) + pycache_root_str = str(pycache_root) if pycache_root else None + + os_name = repo_utils.get_platforms_os_name(rctx) + null_device = "NUL" if os_name == "windows" else "/dev/null" root = rctx.path(".") root_str = str(root) @@ -84,7 +124,7 @@ def _create_pycache_symlinks(rctx, logger): continue if child.is_dir: - if child.basename == "__pycache__" or str(child) == volatile_dir_str: + if child.basename == "__pycache__" or str(child) == pycache_root_str: continue queue.append(child) elif child.basename.endswith(".py"): @@ -92,13 +132,16 @@ def _create_pycache_symlinks(rctx, logger): if has_py: pycache_dir = p.get_child("__pycache__") - pycache_relative = repo_utils.repo_root_relative_path(rctx, pycache_dir) - target_dir = volatile_dir.get_child(pycache_relative) - - repo_utils.mkdir(rctx, target_dir) - rctx.delete(pycache_dir) - rctx.symlink(target_dir, pycache_dir) - ##rctx.symlink("/tmp/pycache/" + pycache_relative, pycache_dir) + if pycache_root: + pycache_relative = repo_utils.repo_root_relative_path(rctx, pycache_dir) + target_dir = pycache_root.get_child(pycache_relative) + + repo_utils.mkdir(rctx, target_dir) + rctx.delete(pycache_dir) + rctx.symlink(target_dir, pycache_dir) + else: + rctx.delete(pycache_dir) + rctx.symlink(null_device, pycache_dir) def _python_repository_impl(rctx): if rctx.attr.distutils and rctx.attr.distutils_content: diff --git a/python/private/repo_utils.bzl b/python/private/repo_utils.bzl index 7657f4acd1..285f760dc1 100644 --- a/python/private/repo_utils.bzl +++ b/python/private/repo_utils.bzl @@ -327,18 +327,19 @@ def _mkdir(mrctx, path): repo_root = str(mrctx.path(".")) path_str = str(path) - if not path_str.startswith(repo_root + "/"): - _execute_checked( - mrctx, - op = "mkdir", - arguments = [_which_checked(mrctx, "mkdir"), "-p", path_str], - ) + if not path_str.startswith(repo_root): + mkdir_bin = mrctx.which("mkdir") + if not mkdir_bin: + return None + res = mrctx.execute([mkdir_bin, "-p", path_str]) + if res.return_code != 0: + return None return path else: placeholder = path.get_child(".placeholder") mrctx.file(placeholder) mrctx.delete(placeholder) - return path + return path def _repo_root_relative_path(mrctx, path): """Takes a path object and returns a repo-relative path string. From 67e5a1b59906460c9da206514f373ae66c77deaf Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 11 Mar 2026 20:23:00 -0700 Subject: [PATCH 4/7] cleanup --- python/private/hermetic_runtime_repo_setup.bzl | 9 +-------- python/private/python_repository.bzl | 6 +----- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/python/private/hermetic_runtime_repo_setup.bzl b/python/private/hermetic_runtime_repo_setup.bzl index 1f485ba10b..d860983e22 100644 --- a/python/private/hermetic_runtime_repo_setup.bzl +++ b/python/private/hermetic_runtime_repo_setup.bzl @@ -65,15 +65,8 @@ def define_hermetic_runtime_toolchain_impl( "libs/**", "share/**", ] - ##files_include += extra_files_glob_include - # This glob include appears to add the watch of `_VOLATILE/.../__pycache__` - files_include += ["lib/**"] + files_include += extra_files_glob_include files_exclude = [ - # The VOLATILE directory contains e.g. pyc files generated - # at runtime, which would otherwise invalidate the repo and - # break caching. - "_VOLATILE", - "_VOLATILE/**", # Unused shared libraries. `python` executable and the `:libpython` target # depend on `libpython{python_version}.so.1.0`. "lib/libpython{major}.{minor}*.so".format(**version_dict), diff --git a/python/private/python_repository.bzl b/python/private/python_repository.bzl index 23dcebd24f..f089cbd929 100644 --- a/python/private/python_repository.bzl +++ b/python/private/python_repository.bzl @@ -100,6 +100,7 @@ def _create_pycache_symlinks(rctx, logger): logger: Optional logger to use for operations. """ pycache_root = _get_pycache_root(rctx) + logger.info(lambda: "pycache root: {}".format(pycache_root)) pycache_root_str = str(pycache_root) if pycache_root else None os_name = repo_utils.get_platforms_os_name(rctx) @@ -214,11 +215,6 @@ def _python_repository_impl(rctx): logger = logger, ) - # Do bazel clean, then run repro.sh -> no invalidation - # Modify this line (or something in this repo rule) -> invalidation - # happens on every subsequent invocation, even if repo rule is unmodified - # between invocations. - print("==== init python_repository", 8) _create_pycache_symlinks(rctx, logger) python_bin = "python.exe" if ("windows" in platform) else "bin/python3" From b70b8e910edbe8e0dc4cca915832db506b4db4e4 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 11 Mar 2026 20:34:47 -0700 Subject: [PATCH 5/7] cleanup --- examples/bzlmod/tests/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/bzlmod/tests/version.py b/examples/bzlmod/tests/version.py index 28da2e86c8..2d293c1571 100644 --- a/examples/bzlmod/tests/version.py +++ b/examples/bzlmod/tests/version.py @@ -13,5 +13,5 @@ # limitations under the License. import sys -import importlib + print(f"{sys.version_info.major}.{sys.version_info.minor}") From d114b82545df8f0635c6c0cdade6165464d155d9 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 11 Mar 2026 20:35:54 -0700 Subject: [PATCH 6/7] cleanup --- python/private/python_repository.bzl | 4 ---- tests/repro/BUILD.bazel | 6 ----- tests/repro/bin.py | 4 ---- tests/repro/repro.sh | 33 ---------------------------- 4 files changed, 47 deletions(-) delete mode 100644 tests/repro/BUILD.bazel delete mode 100644 tests/repro/bin.py delete mode 100755 tests/repro/repro.sh diff --git a/python/private/python_repository.bzl b/python/private/python_repository.bzl index f089cbd929..d126d25f5d 100644 --- a/python/private/python_repository.bzl +++ b/python/private/python_repository.bzl @@ -317,15 +317,11 @@ define_hermetic_runtime_toolchain_impl( else: attrs["urls"] = urls - ##return attrs - - # after 1.8.5 # Bazel <8.3.0 lacks repository_ctx.repo_metadata if not hasattr(rctx, "repo_metadata"): return attrs reproducible = rctx.attr.sha256 != "" - reproducible = False return rctx.repo_metadata( reproducible = reproducible, attrs_for_reproducibility = {} if reproducible else attrs, diff --git a/tests/repro/BUILD.bazel b/tests/repro/BUILD.bazel deleted file mode 100644 index f30cab0687..0000000000 --- a/tests/repro/BUILD.bazel +++ /dev/null @@ -1,6 +0,0 @@ -load("//python:py_binary.bzl", "py_binary") - -py_binary( - name = "bin", - srcs = ["bin.py"] -) diff --git a/tests/repro/bin.py b/tests/repro/bin.py deleted file mode 100644 index 2c0936d178..0000000000 --- a/tests/repro/bin.py +++ /dev/null @@ -1,4 +0,0 @@ -import sys -import importlib -print("hello") -print(sys.version) diff --git a/tests/repro/repro.sh b/tests/repro/repro.sh deleted file mode 100755 index 92dec2ac30..0000000000 --- a/tests/repro/repro.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -export USE_BAZEL_VERSION=8.x -export USE_BAZEL_VERSION=8.2.1 -##export USE_BAZEL_VERSION=8.6.0 -export USE_BAZEL_VERSION=9.x - -##export PYTHONDONTWRITEBYTECODE=1 -##export PYTHONPYCACHEPREFIX=/tmp/bazelpycache - -## --announce_rc \ -## --experimental_check_external_repository_files \ -## --experimental_check_external_other_files \ -## --experimental_repository_cache_hardlinks \ -## --watchfs \ -function run_test() { - bazel run \ - //tests/repro:bin -} -##set -x - -echo "==== Invocation 1 ====" -echo "======================" -run_test - -pydir=bazel-rules_python/external/+python+python_3_11_14_x86_64-unknown-linux-gnu - -#touch $pydir/lib/python3.11/importlib -##rm -fr $pydir/lib/python3.11/importlib/__pycache__/* - -echo "==== Invocation 2 ====" -echo "======================" -run_test From 3adadfe9d8891c10e6a3ea66024824d0bddc742c Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 11 Mar 2026 20:46:25 -0700 Subject: [PATCH 7/7] cleanup --- python/private/python_repository.bzl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/python/private/python_repository.bzl b/python/private/python_repository.bzl index d126d25f5d..7fc6a253ab 100644 --- a/python/private/python_repository.bzl +++ b/python/private/python_repository.bzl @@ -106,10 +106,7 @@ def _create_pycache_symlinks(rctx, logger): os_name = repo_utils.get_platforms_os_name(rctx) null_device = "NUL" if os_name == "windows" else "/dev/null" - root = rctx.path(".") - root_str = str(root) - - queue = [root] + queue = [rctx.path(".")] # Starlark doesn't support recursion, use a loop with a queue. # Using a large range as a safeguard.