Skip to content

fix: expose private EXECUTABLE_ATTRS defaults to external rule builders#3919

Open
13steinj wants to merge 1 commit into
bazel-contrib:mainfrom
chicagotrading:fix/private-targets-visibility-for-rule-builders
Open

fix: expose private EXECUTABLE_ATTRS defaults to external rule builders#3919
13steinj wants to merge 1 commit into
bazel-contrib:mainfrom
chicagotrading:fix/private-targets-visibility-for-rule-builders

Conversation

@13steinj

Copy link
Copy Markdown

Why

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:

ERROR: <BUILD file>: in <custom_rule> rule <target>: Visibility error:
alias '@@rules_python+//python/private:build_data_writer' referring to target
'@@rules_python+//python/private:build_data_writer.sh' is not visible from
target '<calling .bzl file>'

(and the same for debugger_if_target_config and uncachable_version_file).

This has been broken since the builder API's introduction; there's prior art
in this same file for exactly this situation — stage1_bootstrap_template,
stage2_bootstrap_template, site_init_template, bootstrap_template, and
current_interpreter_executable are all public for the identical reason
(implicit deps of py_runtime), each with a
# Not actually public. Only public because it's an implicit dependency of ...
comment.

Before / after

Before: any external module using py_binary_rule_builder() /
py_test_rule_builder() fails at analysis time with the visibility error
above.

After: it analyzes and builds successfully.

Changes

  • python/private/BUILD.bazel: mark build_data_writer,
    debugger_if_target_config, uncachable_version_file (plus empty and
    sentinel, which the alias chains resolve through) //visibility:public,
    matching the existing comment convention for implicit-dependency targets.
  • python/private/uncachable_version_file.bzl: define_uncachable_version_file()
    is a macro, not a rule(), so it doesn't get a visibility kwarg for free —
    added one and forwarded it to the underlying native.alias().
  • examples/bzlmod/other_module/other_module/rule_builder/: new fixture — a
    minimal custom py_binary built via py_binary_rule_builder() from
    other_module (a separate bzlmod module, simulating a real external
    consumer). Verified this fails on the parent commit and passes with the fix.
  • examples/bzlmod/tests/other_module/BUILD.bazel: build_test wiring the
    fixture into other_module's existing CI-covered //... build.

Manually verified in an isolated minimal repro (bare BCR, git_override
pointing at this branch) as well as against the examples/bzlmod example
included in this PR.

build_data_writer, debugger_if_target_config, and uncachable_version_file
are added as implicit attrs to all py_binary/py_test targets via
EXECUTABLE_ATTRS in 1.9.0. When an external package uses
py_binary_rule_builder() to construct a custom rule, Bazel checks
visibility of those attr defaults from the external package. These
targets inherited the package default (//python/private:__subpackages__)
making them inaccessible, causing a visibility error at analysis time.

Set visibility = ["//visibility:public"] on the three targets.
@13steinj 13steinj force-pushed the fix/private-targets-visibility-for-rule-builders branch from a3f1a05 to dd3f1f5 Compare July 11, 2026 02:39
@13steinj 13steinj marked this pull request as ready for review July 11, 2026 02:39

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request resolves a visibility issue where implicit attribute defaults used by py_binary_rule_builder() and py_test_rule_builder() were private to rules_python, causing analysis failures for external modules. The changes expose these targets as public and add regression tests. The review feedback suggests forwarding the visibility parameter to uncachable_version_file_impl to prevent failures when stamping is enabled, and cleaning up placeholder TODO(pr-number) comments in the test files.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +31 to 40
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")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When --stamp is enabled, the uncachable_version_file alias resolves to :uncachable_version_file_impl. Since :uncachable_version_file_impl is a rule target and remains private (inheriting the package's default visibility), external rule builders will fail with a visibility error during analysis when stamping is active. Forward the visibility parameter to uncachable_version_file_impl as well to ensure it is visible to external consumers.

Suggested change
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")
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",
visibility = visibility,
)

@@ -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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid leaving unresolved TODO(pr-number) placeholders in comments. We can describe the regression coverage more generally without referencing a specific PR number.

Suggested change
Regression coverage for https://github.com/bazel-contrib/rules_python/pull/3919:
Regression coverage for the executables rule builder API visibility issue:

],
)

# Regression test for https://github.com/bazel-contrib/rules_python/pull/3919: py_binary_rule_builder()'s implicit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid leaving unresolved TODO(pr-number) placeholders in comments. We can simplify the comment to refer to the regression test generally.

Suggested change
# Regression test for https://github.com/bazel-contrib/rules_python/pull/3919: py_binary_rule_builder()'s implicit
# Regression test: py_binary_rule_builder()'s implicit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant