-
-
Notifications
You must be signed in to change notification settings - Fork 650
fix: handle unsubstituted template placeholders for external native py_binary #3495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
c027306
44b9e8a
dc68724
a91096d
ad6659f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,7 +7,22 @@ if [[ -n "${RULES_PYTHON_BOOTSTRAP_VERBOSE:-}" ]]; then | |||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # runfiles-relative path | ||||||||||||||||||||||||||
| # NOTE: The sentinel strings are split (e.g., "%stage2""_bootstrap%") so that | ||||||||||||||||||||||||||
| # the substitution logic won't replace them. This allows runtime detection of | ||||||||||||||||||||||||||
| # unsubstituted placeholders, which occurs when native py_binary is used in | ||||||||||||||||||||||||||
| # external repositories. In that case, we fall back to %main% which Bazel's | ||||||||||||||||||||||||||
| # native rule does substitute. | ||||||||||||||||||||||||||
| STAGE2_BOOTSTRAP_SENTINEL="%stage2""_bootstrap%" | ||||||||||||||||||||||||||
| MAIN_SENTINEL="%main""%" | ||||||||||||||||||||||||||
| STAGE2_BOOTSTRAP="%stage2_bootstrap%" | ||||||||||||||||||||||||||
| MAIN="%main%" | ||||||||||||||||||||||||||
| if [[ "$STAGE2_BOOTSTRAP" == "$STAGE2_BOOTSTRAP_SENTINEL" ]]; then | ||||||||||||||||||||||||||
| if [[ "$MAIN" != "$MAIN_SENTINEL" && -n "$MAIN" ]]; then | ||||||||||||||||||||||||||
| STAGE2_BOOTSTRAP="$MAIN" | ||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||
| STAGE2_BOOTSTRAP="" | ||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # runfiles-relative path to python interpreter to use. | ||||||||||||||||||||||||||
| # This is the `bin/python3` path in the binary's venv. | ||||||||||||||||||||||||||
|
|
@@ -35,6 +50,17 @@ VENV_REL_SITE_PACKAGES="%venv_rel_site_packages%" | |||||||||||||||||||||||||
| declare -a INTERPRETER_ARGS_FROM_TARGET=( | ||||||||||||||||||||||||||
| %interpreter_args% | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| # Sentinel split to detect unsubstituted placeholder (see STAGE2_BOOTSTRAP above). | ||||||||||||||||||||||||||
| INTERPRETER_ARGS_SENTINEL="%interpreter""_args%" | ||||||||||||||||||||||||||
| if [[ "${#INTERPRETER_ARGS_FROM_TARGET[@]}" -eq 1 && | ||||||||||||||||||||||||||
| "${INTERPRETER_ARGS_FROM_TARGET[0]}" == "$INTERPRETER_ARGS_SENTINEL" ]]; then | ||||||||||||||||||||||||||
| INTERPRETER_ARGS_FROM_TARGET=() | ||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||
|
Comment on lines
+54
to
+58
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic to detect an unsubstituted A more robust approach is to use a unique sentinel for the case of empty arguments, which requires a small change in Here is the recommended change for # python/private/py_executable.bzl
_INTERPRETER_ARGS_SENTINEL_EMPTY = "__py_interpreter_args_empty_sentinel__"
subs = {
"%interpreter_args%": "\n".join([
'"{}"'.format(v)
for v in ctx.attr.interpreter_args
]) if ctx.attr.interpreter_args else '"{}"'.format(_INTERPRETER_ARGS_SENTINEL_EMPTY),
# ... other substitutions
}With that change, the logic in this file can be updated to be more robust. Note that the sentinel for the unsubstituted case also needs to be unquoted to work correctly.
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also seems off, if a user is intentionally passing the exact sentinel string that would be unfortunate, but probably not worth complicating the fix for? Defer to maintainers though |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if [[ -z "$STAGE2_BOOTSTRAP" ]]; then | ||||||||||||||||||||||||||
| echo >&2 "ERROR: %stage2_bootstrap% (or %main%) was not substituted." | ||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if [[ "$IS_ZIPFILE" == "1" ]]; then | ||||||||||||||||||||||||||
| # NOTE: Macs have an old version of mktemp, so we must use only the | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package(default_visibility = ["//visibility:public"]) | ||
|
|
||
| sh_test( | ||
| name = "external_native_py_binary_test", | ||
| srcs = ["external_native_py_binary_test.sh"], | ||
| data = [ | ||
| "@bazel_tools//tools/bash/runfiles", | ||
| "@native_py_binary_repo//:external_native_py_binary", | ||
| ], | ||
| env = { | ||
| "BIN_RLOCATION": "native_py_binary_repo/external_native_py_binary", | ||
| }, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| workspace(name = "external_native_py_binary") | ||
|
|
||
| load( | ||
| "@bazel_tools//tools/build_defs/repo:local.bzl", | ||
| "local_repository", | ||
| "new_local_repository", | ||
| ) | ||
|
|
||
| local_repository( | ||
| name = "rules_python", | ||
| path = "../../..", | ||
| ) | ||
|
|
||
| load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") | ||
|
|
||
| py_repositories() | ||
|
|
||
| python_register_toolchains( | ||
| name = "python_3_11", | ||
| python_version = "3.11", | ||
| ) | ||
|
|
||
| new_local_repository( | ||
| name = "native_py_binary_repo", | ||
| path = "external_repo", | ||
| build_file_content = """ | ||
| package(default_visibility = ["//visibility:public"]) | ||
|
|
||
| py_binary( | ||
| name = "external_native_py_binary", | ||
| srcs = ["main.py"], | ||
| main = "main.py", | ||
| ) | ||
| """, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # --- begin runfiles.bash initialization v3 --- | ||
| set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash | ||
| source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ | ||
| source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ | ||
| source "$0.runfiles/$f" 2>/dev/null || \ | ||
| source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ | ||
| source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ | ||
| { echo >&2 "ERROR: cannot find $f"; exit 1; } | ||
| set -euo pipefail | ||
| # --- end runfiles.bash initialization v3 --- | ||
|
|
||
| bin=$(rlocation "$BIN_RLOCATION") | ||
| output="$("$bin")" | ||
| if [[ "$output" != "external-native-ok" ]]; then | ||
| echo >&2 "Expected 'external-native-ok' but got: $output" | ||
| exit 1 | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| print("external-native-ok") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sentinel definitions for
STAGE2_BOOTSTRAPandMAINare quoted. When the placeholders are not substituted, the variables are assigned the unquoted placeholder string (e.g.,STAGE2_BOOTSTRAPbecomes%stage2_bootstrap%). This causes the comparison with the quoted sentinel (e.g.,"%stage2_bootstrap%") to fail, as[[ "%stage2_bootstrap%" == "\"%stage2_bootstrap%\"" ]]evaluates to false. This breaks the intended fallback logic. To fix this, the sentinels should be defined without quotes.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Gemini is just wrong here?