From 3fe5f9d3ef0f7d3b193054928cd35fae031a567e Mon Sep 17 00:00:00 2001 From: 13steinj <13steinj@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:18:58 -0500 Subject: [PATCH 1/2] fix: make executable implicit defaults public so other modules can use rule builders (#3919) `create_executable_rule_builder()` (used by `py_binary_rule_builder()` / `py_test_rule_builder()`, the public `python/api/executables.bzl` API) bundles three implicit attrs whose defaults point at private targets under `//python/private`: `build_data_writer`, `debugger_if_target_config`, and `uncachable_version_file`. These targets had no explicit visibility, so they inherited the package's `default_visibility` (`//:__subpackages__`), which only covers packages inside the rules_python repo itself. Because the builder API is designed to let external modules call `rule()` themselves (via `builder.build()`), Bazel checks visibility of these attr defaults from the *calling* module's package, not from rules_python's. Any external repo constructing a rule via `py_binary_rule_builder()` / `py_test_rule_builder()` therefore fails at analysis time with a visibility error. This has been broken since the builder API's introduction; there's prior art in this same file for exactly this situation (e.g. `stage1_bootstrap_template`, among others) Co-authored-by: Richard Levasseur (cherry picked from commit 19ffd215cff201eccbe12b2a26c4a287413b902d) --- .bazelrc.deleted_packages | 1 + .../other_module/rule_builder/BUILD.bazel | 13 +++++++++++++ .../other_module/rule_builder/app.py | 1 + .../other_module/rule_builder/rule.bzl | 17 +++++++++++++++++ examples/bzlmod/tests/other_module/BUILD.bazel | 11 +++++++++++ news/3919.fixed.md | 3 +++ python/private/BUILD.bazel | 17 +++++++++++++++++ python/private/uncachable_version_file.bzl | 3 ++- 8 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 examples/bzlmod/other_module/other_module/rule_builder/BUILD.bazel create mode 100644 examples/bzlmod/other_module/other_module/rule_builder/app.py create mode 100644 examples/bzlmod/other_module/other_module/rule_builder/rule.bzl create mode 100644 news/3919.fixed.md diff --git a/.bazelrc.deleted_packages b/.bazelrc.deleted_packages index 2d8a8075fa..2cd1090ebd 100644 --- a/.bazelrc.deleted_packages +++ b/.bazelrc.deleted_packages @@ -7,6 +7,7 @@ common --deleted_packages=examples/bzlmod/entry_points/tests common --deleted_packages=examples/bzlmod/libs/my_lib common --deleted_packages=examples/bzlmod/other_module common --deleted_packages=examples/bzlmod/other_module/other_module/pkg +common --deleted_packages=examples/bzlmod/other_module/other_module/rule_builder common --deleted_packages=examples/bzlmod/patches common --deleted_packages=examples/bzlmod/runfiles common --deleted_packages=examples/bzlmod/tests diff --git a/examples/bzlmod/other_module/other_module/rule_builder/BUILD.bazel b/examples/bzlmod/other_module/other_module/rule_builder/BUILD.bazel new file mode 100644 index 0000000000..40f66b3322 --- /dev/null +++ b/examples/bzlmod/other_module/other_module/rule_builder/BUILD.bazel @@ -0,0 +1,13 @@ +load(":rule.bzl", "custom_py_binary") + +# This target's mere existence is the regression test: analyzing it exercises +# the implicit attr defaults (build_data_writer, debugger_if_target_config, +# uncachable_version_file) that py_binary_rule_builder() bundles from +# rules_python's private package, from a rule() call made in this external +# module. +custom_py_binary( + name = "app", + srcs = ["app.py"], + main = "app.py", + visibility = ["//visibility:public"], +) diff --git a/examples/bzlmod/other_module/other_module/rule_builder/app.py b/examples/bzlmod/other_module/other_module/rule_builder/app.py new file mode 100644 index 0000000000..db51494ed6 --- /dev/null +++ b/examples/bzlmod/other_module/other_module/rule_builder/app.py @@ -0,0 +1 @@ +print("hello from a rule built via py_binary_rule_builder()") diff --git a/examples/bzlmod/other_module/other_module/rule_builder/rule.bzl b/examples/bzlmod/other_module/other_module/rule_builder/rule.bzl new file mode 100644 index 0000000000..7b1505987a --- /dev/null +++ b/examples/bzlmod/other_module/other_module/rule_builder/rule.bzl @@ -0,0 +1,17 @@ +"""A minimal custom py_binary built via the executables rule builder API. + +Regression coverage for https://github.com/bazel-contrib/rules_python/pull/3919: +py_binary_rule_builder()'s implicit attr defaults (build_data_writer, +debugger_if_target_config, uncachable_version_file) previously had no +visibility outside of rules_python, so any external module (like this one) +constructing a rule via the builder failed at analysis time with a +visibility error. +""" + +load("@rules_python//python/api:executables.bzl", "executables") + +def _make_rule(): + builder = executables.py_binary_rule_builder() + return builder.build() + +custom_py_binary = _make_rule() diff --git a/examples/bzlmod/tests/other_module/BUILD.bazel b/examples/bzlmod/tests/other_module/BUILD.bazel index 1bd8a900a9..906554f0f7 100644 --- a/examples/bzlmod/tests/other_module/BUILD.bazel +++ b/examples/bzlmod/tests/other_module/BUILD.bazel @@ -12,3 +12,14 @@ build_test( "@our_other_module//other_module/pkg:bin", ], ) + +# Regression test for https://github.com/bazel-contrib/rules_python/pull/3919: py_binary_rule_builder()'s implicit +# attr defaults previously had no visibility outside of rules_python, so +# building this target (defined via the builder from an external module) +# failed at analysis time with a visibility error. +build_test( + name = "other_module_rule_builder_build_test", + targets = [ + "@our_other_module//other_module/rule_builder:app", + ], +) diff --git a/news/3919.fixed.md b/news/3919.fixed.md new file mode 100644 index 0000000000..0e45d3ce9c --- /dev/null +++ b/news/3919.fixed.md @@ -0,0 +1,3 @@ +Fixed `py_binary_rule_builder()` / `py_test_rule_builder()` (from `python/api/executables.bzl`) +failing at analysis time with a visibility error when used to construct a custom rule from an +external module. diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index 70f7f86413..2e2ed9269a 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -109,6 +109,9 @@ alias( "@platforms//os:windows": ":build_data_writer.ps1", "//conditions:default": ":build_data_writer.sh", }), + # Not actually public. Only public because it's an implicit dependency of + # rules built via py_binary_rule_builder() / py_test_rule_builder(). + visibility = ["//visibility:public"], ) bzl_library( @@ -700,6 +703,9 @@ bzl_library( define_uncachable_version_file( name = "uncachable_version_file", + # Not actually public. Only public because it's an implicit dependency of + # rules built via py_binary_rule_builder() / py_test_rule_builder(). + visibility = ["//visibility:public"], ) bzl_library( @@ -837,6 +843,9 @@ alias( ":is_bazel_config_mode_target": "//python/config_settings:debugger", "//conditions:default": "//python/private:empty", }), + # Not actually public. Only public because it's an implicit dependency of + # rules built via py_binary_rule_builder() / py_test_rule_builder(). + visibility = ["//visibility:public"], ) bazel_config_mode(name = "bazel_config_mode") @@ -888,8 +897,16 @@ current_interpreter_executable( py_library( name = "empty", + # Not actually public. Only public because it's the resolved default of + # debugger_if_target_config, an implicit dependency of rules built via + # py_binary_rule_builder() / py_test_rule_builder(). + visibility = ["//visibility:public"], ) sentinel( name = "sentinel", + # Not actually public. Only public because it's the resolved default of + # uncachable_version_file, an implicit dependency of rules built via + # py_binary_rule_builder() / py_test_rule_builder(). + visibility = ["//visibility:public"], ) diff --git a/python/private/uncachable_version_file.bzl b/python/private/uncachable_version_file.bzl index 9b1d65a469..0f58df8043 100644 --- a/python/private/uncachable_version_file.bzl +++ b/python/private/uncachable_version_file.bzl @@ -28,12 +28,13 @@ actions depending on this file will always re-run. implementation = _uncachable_version_file_impl, ) -def define_uncachable_version_file(name): +def define_uncachable_version_file(name, visibility = None): native.alias( name = name, actual = select({ ":stamp_detect": ":uncachable_version_file_impl", "//conditions:default": ":sentinel", }), + visibility = visibility, ) uncachable_version_file(name = "uncachable_version_file_impl") From 0720bc510974445fe84db5c53147a46acf8ee232 Mon Sep 17 00:00:00 2001 From: Jonathan Stein Date: Tue, 28 Jul 2026 16:42:45 -0500 Subject: [PATCH 2/2] Release 1.9.2: Update changelog --- CHANGELOG.md | 13 +++++++++++++ news/3919.fixed.md | 3 --- 2 files changed, 13 insertions(+), 3 deletions(-) delete mode 100644 news/3919.fixed.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 80412cffbb..0a73505318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,19 @@ BEGIN_UNRELEASED_TEMPLATE END_UNRELEASED_TEMPLATE --> +{#v1-9-2} +## [1.9.2] - 2026-07-28 + +[1.9.2]: https://github.com/bazel-contrib/rules_python/releases/tag/1.9.2 + +{#v1-9-2-fixed} +### Fixed +* (executables) `py_binary_rule_builder()` / `py_test_rule_builder()` (from + `python/api/executables.bzl`) no longer fail at analysis time with a + visibility error when used to construct a custom rule from an external + module. Fixes + ([#3919](https://github.com/bazel-contrib/rules_python/pull/3919)). + {#v1-9-1} ## [1.9.1] - 2026-05-14 diff --git a/news/3919.fixed.md b/news/3919.fixed.md deleted file mode 100644 index 0e45d3ce9c..0000000000 --- a/news/3919.fixed.md +++ /dev/null @@ -1,3 +0,0 @@ -Fixed `py_binary_rule_builder()` / `py_test_rule_builder()` (from `python/api/executables.bzl`) -failing at analysis time with a visibility error when used to construct a custom rule from an -external module.