Skip to content

Commit 924ec59

Browse files
committed
refactor: merge PyVirtualInfo into PyInfo
1 parent 1701b07 commit 924ec59

11 files changed

Lines changed: 78 additions & 43 deletions

File tree

py/private/BUILD.bazel

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ bzl_library(
4949
deps = [
5050
":py_info",
5151
"@bazel_lib//lib:transitions",
52+
"@rules_python//python:defs_bzl",
5253
"@tar.bzl//tar:mtree",
5354
"@tar.bzl//tar:tar",
5455
],
@@ -60,6 +61,7 @@ bzl_library(
6061
deps = [
6162
":py_info",
6263
"@bazel_skylib//lib:paths",
64+
"@rules_python//python:defs_bzl",
6365
],
6466
)
6567

@@ -124,13 +126,13 @@ bzl_library(
124126
],
125127
)
126128

129+
# keep
127130
bzl_library(
128131
name = "py_info",
129132
srcs = ["py_info.bzl"],
130133
# PyInfo is re-exported as public API from //py:defs.bzl, so its bzl_library
131134
# is public too, allowing sibling trees (e.g. //uv) to depend on it.
132135
visibility = ["//visibility:public"],
133-
deps = ["@rules_python//python:defs_bzl"],
134136
)
135137

136138
bzl_library(

py/private/providers.bzl

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,3 @@ def make_wheel_record(
144144
metadata_top_levels = tuple(metadata_top_levels),
145145
cs_claims = tuple(cs_claims),
146146
)
147-
148-
PyVirtualInfo = provider(
149-
doc = "FIXME",
150-
fields = {
151-
"dependencies": "Depset of required virtual dependencies, independent of their resolution status",
152-
"resolutions": "FIXME",
153-
},
154-
)

py/private/pth.bzl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"""Helper functions for building imports depsets."""
22

33
load("@bazel_skylib//lib:paths.bzl", "paths")
4+
5+
# API edge: deps may expose rules_py's PyInfo or native `@rules_python`'s.
6+
load("@rules_python//python:defs.bzl", RulesPythonPyInfo = "PyInfo")
47
load("//py/private:py_info.bzl", "PyInfo")
58

69
def _make_import_path(label, workspace, imp):
@@ -72,11 +75,16 @@ def make_imports_depset(deps, imports, workspace_name, label = None, extra_impor
7275
if label and label.workspace_name:
7376
import_paths.append(label.workspace_name)
7477

78+
# `deps` come from py_library/py_binary/py_test, whose deps attribute accepts
79+
# both rules_py (PyInfo) and native @rules_python (RulesPythonPyInfo) targets;
80+
# both expose `imports`.
81+
transitive = [
82+
(target[PyInfo] if PyInfo in target else target[RulesPythonPyInfo]).imports
83+
for target in deps
84+
if PyInfo in target or RulesPythonPyInfo in target
85+
]
86+
7587
return depset(
7688
direct = import_paths,
77-
transitive = [
78-
target[PyInfo].imports
79-
for target in deps
80-
if PyInfo in target
81-
] + extra_imports_depsets,
89+
transitive = transitive + extra_imports_depsets,
8290
)

py/private/py_image_layer.bzl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Sharing model:
3232
- Ungrouped pip packages: squashed by the rule into one per-rule tar.
3333
"""
3434

35+
load("@rules_python//python:defs.bzl", RulesPythonPyInfo = "PyInfo")
3536
load("//py/private:providers.bzl", "PyWheelsInfo")
3637
load("//py/private:py_info.bzl", "PyInfo")
3738
load("//py/private/toolchain:types.bzl", "PY_TOOLCHAIN")
@@ -373,6 +374,10 @@ def _layer_aspect_impl(target, ctx):
373374
interpreter_layer = None
374375
interpreter_files = None
375376
kind = ctx.rule.kind
377+
378+
# The binary being layered must be a rules_py py_binary (it carries rules_py's
379+
# PyInfo); rules_python's py_binary is not supported. rules_python *library*
380+
# deps within the graph are still handled by the membership checks below.
376381
is_binary = (
377382
PyInfo in target and
378383
target[DefaultInfo].files_to_run.executable != None
@@ -394,14 +399,14 @@ def _layer_aspect_impl(target, ctx):
394399

395400
# Skip PyInfo deps (including wheel-leaf targets, which also emit PyInfo) —
396401
# they self-capture via the aspect.
397-
if kind not in _PY_VENV_KINDS and PyInfo in target and not is_binary:
402+
if kind not in _PY_VENV_KINDS and (PyInfo in target or RulesPythonPyInfo in target) and not is_binary:
398403
own_parts = [target[DefaultInfo].files]
399404
for attr_name in ("data", "deps"):
400405
attr_val = getattr(ctx.rule.attr, attr_name, None)
401406
if not attr_val:
402407
continue
403408
for dep in attr_val:
404-
if PyInfo in dep:
409+
if PyInfo in dep or RulesPythonPyInfo in dep:
405410
continue
406411
if DefaultInfo in dep:
407412
own_parts.append(dep[DefaultInfo].files)

py/private/py_info.bzl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
"""The `PyInfo` provider produced and consumed by rules_py targets.
22
3-
`PyInfo` carries the two pieces of information rules_py needs to assemble a
4-
target's dependency closure: the transitive set of first-party Python sources,
5-
and the import roots to place on `sys.path`. Targets in a dependency graph
6-
aggregate these fields from their deps to build the eventual venv or wheel.
3+
`PyInfo` carries the information rules_py needs to assemble a target's dependency
4+
closure: the transitive set of first-party Python sources, the import roots to
5+
place on `sys.path`, and the virtual-dependency declarations and their
6+
resolutions. Targets in a dependency graph aggregate these fields from their
7+
deps to build the eventual venv or wheel.
78
89
Defined here in one module and re-exported from `//py:defs.bzl` as public API.
10+
11+
rules_py can *consume* deps built by `@rules_python` (e.g. a `py_proto_library`),
12+
which expose `@rules_python`'s `PyInfo` rather than this one. Rules that ingest
13+
`deps` handle that at the API edge by also reading `@rules_python`'s `PyInfo`.
14+
(The reverse — a `@rules_python` rule consuming a rules_py target — is not
15+
supported: rules_py does not emit `@rules_python`'s `PyInfo`.)
916
"""
1017

1118
PyInfo = provider(
12-
doc = "Python source and import-path information for a target's dependency closure.",
19+
doc = "Python source, import-path, and virtual-dependency information for a target's dependency closure.",
1320
fields = {
1421
"transitive_sources": "depset[File] — postorder depset of first-party `.py` sources in the transitive closure.",
1522
"imports": "depset[str] — import roots to place on `sys.path` (rlocation-root-relative).",
23+
"virtual_dependencies": "depset[str] — names of required virtual dependencies, independent of their resolution status.",
24+
"virtual_resolutions": "depset[struct(virtual, target)] — virtual-dependency-name to concrete-target resolutions.",
1625
},
1726
)

py/private/py_library.bzl

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ without binding them to a particular version of that package.
77
load("@bazel_skylib//lib:new_sets.bzl", "sets")
88
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
99

10-
# rules_python's PyInfo, referenced ONLY in the `deps` provider constraint so
11-
# native `@rules_python` targets are still accepted as deps. rules_py emits and
12-
# reads its own PyInfo (py_info.bzl).
10+
# rules_python's PyInfo, referenced in the `deps` provider constraint so native
11+
# `@rules_python` targets (e.g. py_proto_library) are accepted as deps. rules_py
12+
# emits its own PyInfo; it reads a dep's sources/imports from either provider via
13+
# the accessors below.
1314
load("@rules_python//python:defs.bzl", RulesPythonPyInfo = "PyInfo")
14-
load("//py/private:providers.bzl", "PyVirtualInfo", "PyWheelsInfo")
15+
load("//py/private:providers.bzl", "PyWheelsInfo")
1516
load("//py/private:pth.bzl", "make_imports_depset")
1617
load("//py/private:py_info.bzl", "PyInfo")
1718

@@ -24,13 +25,15 @@ def _make_instrumented_files_info(ctx):
2425
)
2526

2627
def _make_srcs_depset(ctx):
28+
# `deps` accepts rules_py targets (PyInfo) and native @rules_python targets
29+
# (RulesPythonPyInfo); both expose `transitive_sources`.
2730
return depset(
2831
order = "postorder",
2932
direct = ctx.files.srcs,
3033
transitive = [
31-
target[PyInfo].transitive_sources
34+
(target[PyInfo] if PyInfo in target else target[RulesPythonPyInfo]).transitive_sources
3235
for target in ctx.attr.deps
33-
if PyInfo in target
36+
if PyInfo in target or RulesPythonPyInfo in target
3437
],
3538
)
3639

@@ -39,16 +42,17 @@ def _make_virtual_depset(ctx):
3942
order = "postorder",
4043
direct = getattr(ctx.attr, "virtual_deps", []),
4144
transitive = [
42-
target[PyVirtualInfo].dependencies
45+
target[PyInfo].virtual_dependencies
4346
for target in ctx.attr.deps
44-
if PyVirtualInfo in target
47+
if PyInfo in target
4548
],
4649
)
4750

4851
def _make_resolved_virtual_depset(target):
4952
transitive = [target[DefaultInfo].files]
50-
if PyInfo in target:
51-
transitive.append(target[PyInfo].transitive_sources)
53+
if PyInfo in target or RulesPythonPyInfo in target:
54+
info = target[PyInfo] if PyInfo in target else target[RulesPythonPyInfo]
55+
transitive.append(info.transitive_sources)
5256

5357
return depset(
5458
order = "postorder",
@@ -63,9 +67,9 @@ def _make_virtual_resolutions_depset(ctx):
6367
for k, v in ctx.attr.resolutions.items()
6468
],
6569
transitive = [
66-
target[PyVirtualInfo].resolutions
70+
target[PyInfo].virtual_resolutions
6771
for target in ctx.attr.deps
68-
if PyVirtualInfo in target
72+
if PyInfo in target
6973
],
7074
)
7175

@@ -89,8 +93,9 @@ def _resolve_virtuals(ctx):
8993
v_srcs.append(_make_resolved_virtual_depset(resolution.target))
9094
v_runfiles.append(resolution.target[DefaultInfo].default_runfiles.files)
9195

92-
if PyInfo in resolution.target:
93-
v_imports.append(resolution.target[PyInfo].imports)
96+
if PyInfo in resolution.target or RulesPythonPyInfo in resolution.target:
97+
info = resolution.target[PyInfo] if PyInfo in resolution.target else resolution.target[RulesPythonPyInfo]
98+
v_imports.append(info.imports)
9499

95100
missing = sets.to_list(sets.difference(sets.make(virtual), sets.make(seen.keys())))
96101
if len(missing) > 0:
@@ -166,10 +171,8 @@ def _py_library_impl(ctx):
166171
PyInfo(
167172
imports = imports,
168173
transitive_sources = transitive_srcs,
169-
),
170-
PyVirtualInfo(
171-
dependencies = virtuals,
172-
resolutions = resolutions,
174+
virtual_dependencies = virtuals,
175+
virtual_resolutions = resolutions,
173176
),
174177
PyWheelsInfo(
175178
wheels = wheels,
@@ -184,7 +187,12 @@ _attrs = dict({
184187
),
185188
"deps": attr.label_list(
186189
doc = "Targets that produce Python code, commonly `py_library` rules.",
187-
providers = [[PyInfo], [RulesPythonPyInfo], [PyVirtualInfo], [CcInfo]],
190+
# This attribute — shared by py_library, py_binary and py_test — is the
191+
# public surface that supports rules_python interop: a dep may carry
192+
# rules_py's PyInfo, or native @rules_python's PyInfo (e.g. a
193+
# py_proto_library). RulesPythonPyInfo appears here and at the reads in
194+
# this file for exactly that reason; rules_py never emits it.
195+
providers = [[PyInfo], [RulesPythonPyInfo], [CcInfo]],
188196
),
189197
"data": attr.label_list(
190198
doc = """Runtime dependencies of the program.

py/private/py_unpacked_wheel.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ def _py_unpacked_wheel_impl(ctx):
7373
PyInfo(
7474
imports = imports,
7575
transitive_sources = depset([unpack_directory]),
76+
virtual_dependencies = depset(),
77+
virtual_resolutions = depset(),
7678
),
7779
]
7880

py/private/py_venv/py_venv_exec.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ def _py_venv_exec_impl(ctx):
135135
# sibling venv, not on this rule.
136136
imports = vinfo.imports,
137137
transitive_sources = vinfo.transitive_sources,
138+
virtual_dependencies = depset(),
139+
virtual_resolutions = depset(),
138140
),
139141
instrumented_files_info,
140142
RunEnvironmentInfo(

py/tests/py_venv_conflict/collision_order_test.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ printf 'VALUE = "namespace"\n' > "$site/mixed_top/from_namespace.py"
6767
PyInfo(
6868
imports = depset([site_packages]),
6969
transitive_sources = depset([install_tree]),
70+
virtual_dependencies = depset(),
71+
virtual_resolutions = depset(),
7072
),
7173
PyWheelsInfo(wheels = depset([wheel])),
7274
]
@@ -191,6 +193,8 @@ printf 'native' > "$site/collision_order/native_extension.so"
191193
PyInfo(
192194
imports = depset([site_packages]),
193195
transitive_sources = depset([install_tree]),
196+
virtual_dependencies = depset(),
197+
virtual_resolutions = depset(),
194198
),
195199
PyWheelsInfo(wheels = depset([wheel])),
196200
]

py/tests/py_venv_conflict/site_merge_order_test.bzl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ printf 'VALUE = %s\n' "$4" > "$site/other/from_final.py"
122122
PyInfo(
123123
imports = depset(site_packages_paths),
124124
transitive_sources = depset(install_trees),
125+
virtual_dependencies = depset(),
126+
virtual_resolutions = depset(),
125127
),
126128
PyWheelsInfo(wheels = depset(direct = wheels, order = "postorder")),
127129
]
@@ -229,9 +231,8 @@ printf 'VALUE = %s\n' "$4" > "$site/mixed/sibling.py"
229231
PyInfo(
230232
imports = depset(site_packages_paths),
231233
transitive_sources = depset(install_trees),
232-
has_py2_only_sources = False,
233-
has_py3_only_sources = True,
234-
uses_shared_libraries = False,
234+
virtual_dependencies = depset(),
235+
virtual_resolutions = depset(),
235236
),
236237
PyWheelsInfo(wheels = depset(direct = wheels, order = "postorder")),
237238
]

0 commit comments

Comments
 (0)