Skip to content

Commit f7656d1

Browse files
shayanhoshyariShayan Hoshyari
andauthored
fix(--debugger): Ensure that imports or venv_site_package files are propagated for debugger target (bazel-contrib#3483)
bazel-contrib@a94bd0f recently added support for injecting dependencies for easier use of debugger. It allows injecting deps via `--@rules_python//python/config_settings:debugger=<target>`. While the runfiles from `<target>` were inherited in the final binary, the `imports` or `venv_site_packages` were missing. Hence making the debugger target unusable for various corner cases (e.g. when it uses `imports = ...` or when it is coming from pip hub and `venv_site_packages` are on). This PR fixes that, and extends the unit test to include this situation. Fixes: bazel-contrib#3481 --------- Co-authored-by: Shayan Hoshyari <[email protected]>
1 parent a94bd0f commit f7656d1

File tree

3 files changed

+83
-13
lines changed

3 files changed

+83
-13
lines changed

python/private/common.bzl

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,8 @@ def collect_cc_info(ctx, extra_deps = []):
161161
Returns:
162162
CcInfo provider of merged information.
163163
"""
164-
deps = ctx.attr.deps
165-
if extra_deps:
166-
deps = list(deps)
167-
deps.extend(extra_deps)
168164
cc_infos = []
169-
for dep in deps:
165+
for dep in collect_deps(ctx, extra_deps):
170166
if CcInfo in dep:
171167
cc_infos.append(dep[CcInfo])
172168

@@ -175,17 +171,19 @@ def collect_cc_info(ctx, extra_deps = []):
175171

176172
return cc_common.merge_cc_infos(cc_infos = cc_infos)
177173

178-
def collect_imports(ctx):
174+
def collect_imports(ctx, extra_deps = []):
179175
"""Collect the direct and transitive `imports` strings.
180176
181177
Args:
182178
ctx: {type}`ctx` the current target ctx
179+
extra_deps: list of Target to also collect imports from.
183180
184181
Returns:
185182
{type}`depset[str]` of import paths
186183
"""
184+
187185
transitive = []
188-
for dep in ctx.attr.deps:
186+
for dep in collect_deps(ctx, extra_deps):
189187
if PyInfo in dep:
190188
transitive.append(dep[PyInfo].imports)
191189
if BuiltinPyInfo != None and BuiltinPyInfo in dep:
@@ -479,3 +477,19 @@ def runfiles_root_path(ctx, short_path):
479477
return short_path[3:]
480478
else:
481479
return "{}/{}".format(ctx.workspace_name, short_path)
480+
481+
def collect_deps(ctx, extra_deps = []):
482+
"""Collect the dependencies from the rule's context.
483+
484+
Args:
485+
ctx: rule ctx
486+
extra_deps: list of Target to also collect dependencies from.
487+
488+
Returns:
489+
list of Target
490+
"""
491+
deps = ctx.attr.deps
492+
if extra_deps:
493+
deps = list(deps)
494+
deps.extend(extra_deps)
495+
return deps

python/private/py_executable.bzl

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ load(":cc_helper.bzl", "cc_helper")
3737
load(
3838
":common.bzl",
3939
"collect_cc_info",
40+
"collect_deps",
4041
"collect_imports",
4142
"collect_runfiles",
4243
"create_binary_semantics_struct",
@@ -293,7 +294,8 @@ def _create_executable(
293294
runtime_details,
294295
cc_details,
295296
native_deps_details,
296-
runfiles_details):
297+
runfiles_details,
298+
extra_deps):
297299
_ = is_test, cc_details, native_deps_details # @unused
298300

299301
is_windows = target_platform_has_any_constraint(ctx, ctx.attr._windows_constraints)
@@ -323,6 +325,7 @@ def _create_executable(
323325
add_runfiles_root_to_sys_path = (
324326
"1" if BootstrapImplFlag.get_value(ctx) == BootstrapImplFlag.SYSTEM_PYTHON else "0"
325327
),
328+
extra_deps = extra_deps,
326329
)
327330

328331
stage2_bootstrap = _create_stage2_bootstrap(
@@ -486,7 +489,7 @@ def _create_zip_main(ctx, *, stage2_bootstrap, runtime_details, venv):
486489
# * https://snarky.ca/how-virtual-environments-work/
487490
# * https://github.com/python/cpython/blob/main/Modules/getpath.py
488491
# * https://github.com/python/cpython/blob/main/Lib/site.py
489-
def _create_venv(ctx, output_prefix, imports, runtime_details, add_runfiles_root_to_sys_path):
492+
def _create_venv(ctx, output_prefix, imports, runtime_details, add_runfiles_root_to_sys_path, extra_deps):
490493
create_full_venv = BootstrapImplFlag.get_value(ctx) == BootstrapImplFlag.SCRIPT
491494
venv = "_{}.venv".format(output_prefix.lstrip("_"))
492495

@@ -587,7 +590,11 @@ def _create_venv(ctx, output_prefix, imports, runtime_details, add_runfiles_root
587590
VenvSymlinkKind.BIN: bin_dir,
588591
VenvSymlinkKind.LIB: site_packages,
589592
}
590-
venv_app_files = create_venv_app_files(ctx, ctx.attr.deps, venv_dir_map)
593+
venv_app_files = create_venv_app_files(
594+
ctx,
595+
deps = collect_deps(ctx, extra_deps),
596+
venv_dir_map = venv_dir_map,
597+
)
591598

592599
files_without_interpreter = [pth, site_init] + venv_app_files.venv_files
593600
if pyvenv_cfg:
@@ -1016,16 +1023,15 @@ def py_executable_base_impl(ctx, *, semantics, is_test, inherited_environment =
10161023
default_outputs.add(precompile_result.keep_srcs)
10171024
default_outputs.add(required_pyc_files)
10181025

1019-
imports = collect_imports(ctx)
1020-
1021-
runtime_details = _get_runtime_details(ctx)
10221026
extra_deps = []
10231027

10241028
# The debugger dependency should be prevented by select() config elsewhere,
10251029
# but just to be safe, also guard against adding it to the output here.
10261030
if not _is_tool_config(ctx):
10271031
extra_deps.append(ctx.attr._debugger_flag)
10281032

1033+
imports = collect_imports(ctx, extra_deps = extra_deps)
1034+
runtime_details = _get_runtime_details(ctx)
10291035
cc_details = _get_cc_details_for_binary(ctx, extra_deps = extra_deps)
10301036
native_deps_details = _get_native_deps_details(
10311037
ctx,
@@ -1058,6 +1064,7 @@ def py_executable_base_impl(ctx, *, semantics, is_test, inherited_environment =
10581064
cc_details = cc_details,
10591065
native_deps_details = native_deps_details,
10601066
runfiles_details = runfiles_details,
1067+
extra_deps = extra_deps,
10611068
)
10621069
default_outputs.add(exec_result.extra_files_to_build)
10631070

tests/base_rules/py_executable_base_tests.bzl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
1919
load("@rules_testing//lib:truth.bzl", "matching")
2020
load("@rules_testing//lib:util.bzl", rt_util = "util")
2121
load("//python:py_executable_info.bzl", "PyExecutableInfo")
22+
load("//python:py_info.bzl", "PyInfo")
2223
load("//python:py_library.bzl", "py_library")
2324
load("//python/private:common_labels.bzl", "labels") # buildifier: disable=bzl-visibility
2425
load("//python/private:reexports.bzl", "BuiltinPyRuntimeInfo") # buildifier: disable=bzl-visibility
@@ -172,9 +173,11 @@ def _test_executable_in_runfiles_impl(env, target):
172173
])
173174

174175
def _test_debugger(name, config):
176+
# Using imports
175177
rt_util.helper_target(
176178
py_library,
177179
name = name + "_debugger",
180+
imports = ["."],
178181
srcs = [rt_util.empty_file(name + "_debugger.py")],
179182
)
180183

@@ -187,24 +190,70 @@ def _test_debugger(name, config):
187190
labels.DEBUGGER: "//{}:{}_debugger".format(native.package_name(), name),
188191
},
189192
)
193+
194+
# Using venv
195+
rt_util.helper_target(
196+
py_library,
197+
name = name + "_debugger_venv",
198+
imports = [native.package_name() + "/site-packages"],
199+
experimental_venvs_site_packages = "@rules_python//python/config_settings:venvs_site_packages",
200+
srcs = [rt_util.empty_file("site-packages/" + name + "_debugger_venv.py")],
201+
)
202+
203+
rt_util.helper_target(
204+
config.rule,
205+
name = name + "_subject_venv",
206+
srcs = [rt_util.empty_file(name + "_subject_venv.py")],
207+
config_settings = {
208+
# config_settings requires a fully qualified label
209+
labels.DEBUGGER: "//{}:{}_debugger_venv".format(native.package_name(), name),
210+
},
211+
)
212+
190213
analysis_test(
191214
name = name,
192215
impl = _test_debugger_impl,
193216
targets = {
194217
"exec_target": name + "_subject",
195218
"target": name + "_subject",
219+
"target_venv": name + "_subject_venv",
196220
},
197221
attrs = {
198222
"exec_target": attr.label(cfg = "exec"),
199223
},
224+
config_settings = {
225+
labels.VENVS_SITE_PACKAGES: "yes",
226+
labels.PYTHON_VERSION: "3.13",
227+
},
200228
)
201229

202230
_tests.append(_test_debugger)
203231

204232
def _test_debugger_impl(env, targets):
233+
# 1. Subject
234+
235+
# Check the file from debugger dep is injected.
205236
env.expect.that_target(targets.target).runfiles().contains_at_least([
206237
"{workspace}/{package}/{test_name}_debugger.py",
207238
])
239+
240+
# #3481: Ensure imports are setup correcty.
241+
meta = env.expect.meta.derive(format_str_kwargs = {"package": targets.target.label.package})
242+
env.expect.that_target(targets.target).has_provider(PyInfo)
243+
imports = targets.target[PyInfo].imports.to_list()
244+
env.expect.that_collection(imports).contains(meta.format_str("{workspace}/{package}"))
245+
246+
# 2. Subject venv
247+
248+
# #3481: Ensure that venv site-packages is setup correctly, if the dependency is coming
249+
# from pip integration.
250+
env.expect.that_target(targets.target_venv).runfiles().contains_at_least([
251+
"{workspace}/{package}/_{name}.venv/lib/python3.13/site-packages/{test_name}_debugger_venv.py",
252+
])
253+
254+
# 3. Subject exec
255+
256+
# Ensure that tools don't inherit debugger.
208257
env.expect.that_target(targets.exec_target).runfiles().not_contains(
209258
"{workspace}/{package}/{test_name}_debugger.py",
210259
)

0 commit comments

Comments
 (0)