Skip to content

Commit fcdd0f6

Browse files
committed
feat: add PyInfo fields for storing original sources of a target
1 parent 922929b commit fcdd0f6

File tree

5 files changed

+99
-2
lines changed

5 files changed

+99
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ Unreleased changes template.
9595
* 3.11.11
9696
* 3.12.8
9797
* 3.13.1
98+
* (providers) {obj}`PyInfo` has new fields to aid static analysis tools:
99+
{obj}`direct_original_sources`, {obj}`direct_pyi_files`,
100+
{obj}`transitive_original_sources`, {obj}`transitive_pyi_files`. NOTE: these
101+
are not yet fully populated by `py_library`
98102

99103
[20241206]: https://github.com/astral-sh/python-build-standalone/releases/tag/20241206
100104

python/private/common.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ def collect_runfiles(ctx, files = depset()):
408408
def create_py_info(
409409
ctx,
410410
*,
411+
original_sources,
411412
required_py_files,
412413
required_pyc_files,
413414
implicit_pyc_files,
@@ -417,6 +418,7 @@ def create_py_info(
417418
418419
Args:
419420
ctx: rule ctx.
421+
original_sources: `depset[File]`; the original input sources from `srcs`
420422
required_py_files: `depset[File]`; the direct, `.py` sources for the
421423
target that **must** be included by downstream targets. This should
422424
only be Python source files. It should not include pyc files.
@@ -437,6 +439,8 @@ def create_py_info(
437439
"""
438440

439441
py_info = PyInfoBuilder()
442+
py_info.direct_original_sources.add(original_sources)
443+
py_info.transitive_original_sources.add(original_sources)
440444
py_info.direct_pyc_files.add(required_pyc_files)
441445
py_info.transitive_pyc_files.add(required_pyc_files)
442446
py_info.transitive_implicit_pyc_files.add(implicit_pyc_files)

python/private/py_executable.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,7 @@ def py_executable_base_impl(ctx, *, semantics, is_test, inherited_environment =
985985
runfiles_details = runfiles_details,
986986
main_py = main_py,
987987
imports = imports,
988+
original_sources = direct_sources,
988989
required_py_files = required_py_files,
989990
required_pyc_files = required_pyc_files,
990991
implicit_pyc_files = implicit_pyc_files,
@@ -1548,6 +1549,7 @@ def _create_providers(
15481549
ctx,
15491550
executable,
15501551
main_py,
1552+
original_sources,
15511553
required_py_files,
15521554
required_pyc_files,
15531555
implicit_pyc_files,
@@ -1566,6 +1568,8 @@ def _create_providers(
15661568
ctx: The rule ctx.
15671569
executable: File; the target's executable file.
15681570
main_py: File; the main .py entry point.
1571+
original_sources: `depset[File]` the direct `.py` sources for the
1572+
target that were the original input sources.
15691573
required_py_files: `depset[File]` the direct, `.py` sources for the
15701574
target that **must** be included by downstream targets. This should
15711575
only be Python source files. It should not include pyc files.
@@ -1649,6 +1653,7 @@ def _create_providers(
16491653

16501654
py_info, deps_transitive_sources, builtin_py_info = create_py_info(
16511655
ctx,
1656+
original_sources = original_sources,
16521657
required_py_files = required_py_files,
16531658
required_pyc_files = required_pyc_files,
16541659
implicit_pyc_files = implicit_pyc_files,

python/private/py_info.bzl

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ def _PyInfo_init(
3838
direct_pyc_files = depset(),
3939
transitive_pyc_files = depset(),
4040
transitive_implicit_pyc_files = depset(),
41-
transitive_implicit_pyc_source_files = depset()):
41+
transitive_implicit_pyc_source_files = depset(),
42+
direct_original_sources = depset(),
43+
transitive_original_sources = depset(),
44+
direct_pyi_files = depset(),
45+
transitive_pyi_files = depset()):
4246
_check_arg_type("transitive_sources", "depset", transitive_sources)
4347

4448
# Verify it's postorder compatible, but retain is original ordering.
@@ -53,14 +57,25 @@ def _PyInfo_init(
5357

5458
_check_arg_type("transitive_implicit_pyc_files", "depset", transitive_pyc_files)
5559
_check_arg_type("transitive_implicit_pyc_source_files", "depset", transitive_pyc_files)
60+
61+
_check_arg_type("direct_original_sources", "depset", direct_original_sources)
62+
_check_arg_type("transitive_original_sources", "depset", transitive_original_sources)
63+
64+
_check_arg_type("direct_pyi_files", "depset", direct_pyi_files)
65+
_check_arg_type("transitive_pyi_files", "depset", transitive_pyi_files)
5666
return {
67+
"direct_original_sources": direct_original_sources,
5768
"direct_pyc_files": direct_pyc_files,
69+
"direct_pyi_files": direct_pyi_files,
5870
"has_py2_only_sources": has_py2_only_sources,
5971
"has_py3_only_sources": has_py2_only_sources,
6072
"imports": imports,
6173
"transitive_implicit_pyc_files": transitive_implicit_pyc_files,
6274
"transitive_implicit_pyc_source_files": transitive_implicit_pyc_source_files,
75+
"transitive_original_sources": transitive_original_sources,
6376
"transitive_pyc_files": transitive_pyc_files,
77+
"transitive_pyi_files": transitive_pyi_files,
78+
"transitive_pyi_files": transitive_pyi_files,
6479
"transitive_sources": transitive_sources,
6580
"uses_shared_libraries": uses_shared_libraries,
6681
}
@@ -69,6 +84,18 @@ PyInfo, _unused_raw_py_info_ctor = define_bazel_6_provider(
6984
doc = "Encapsulates information provided by the Python rules.",
7085
init = _PyInfo_init,
7186
fields = {
87+
"direct_original_sources": """
88+
:type: depset[File]
89+
90+
The `.py` source files (if any) that are considered directly provided by
91+
the target. This field is intended so that static analysis tools can recover the
92+
original Python source files, regardless of any build settings (e.g.
93+
precompiling), so they can analyze source code. The values are typically the
94+
`.py` files in the `srcs` attribute (or equivalent).
95+
96+
::::{versionadded} 1.1.0
97+
::::
98+
""",
7299
"direct_pyc_files": """
73100
:type: depset[File]
74101
@@ -78,6 +105,21 @@ by the target and **must be included**.
78105
These files usually come from, e.g., a library setting {attr}`precompile=enabled`
79106
to forcibly enable precompiling for itself. Downstream binaries are expected
80107
to always include these files, as the originating target expects them to exist.
108+
""",
109+
"direct_pyi_files": """
110+
:type: depset[File]
111+
112+
Type definition files (usually `.pyi` files) for the Python modules provided by
113+
this target. Usually they describe the source files listed in
114+
`direct_original_sources`. This field is primarily for static analysis tools.
115+
116+
:::{note}
117+
This may contain implementation-specific file types specific to a particular
118+
type checker.
119+
:::
120+
121+
::::{versionadded} 1.1.0
122+
::::
81123
""",
82124
"has_py2_only_sources": """
83125
:type: bool
@@ -116,6 +158,21 @@ then {obj}`transitive_implicit_pyc_files` should be included instead.
116158
117159
::::{versionadded} 0.37.0
118160
::::
161+
""",
162+
"transitive_original_sources": """
163+
:type: depset[File]
164+
165+
The transitive set of `.py` source files (if any) that are considered the
166+
original sources for this target and its transitive dependencies. This field is
167+
intended so that static analysis tools can recover the original Python source
168+
files, regardless of any build settings (e.g. precompiling), so they can analyze
169+
source code. The values are typically the `.py` files in the `srcs` attribute
170+
(or equivalent).
171+
172+
This is superset of `direct_original_sources`.
173+
174+
::::{versionadded} 1.1.0
175+
::::
119176
""",
120177
"transitive_pyc_files": """
121178
:type: depset[File]
@@ -125,6 +182,22 @@ The transitive set of precompiled files that must be included.
125182
These files usually come from, e.g., a library setting {attr}`precompile=enabled`
126183
to forcibly enable precompiling for itself. Downstream binaries are expected
127184
to always include these files, as the originating target expects them to exist.
185+
""",
186+
"transitive_pyi_files": """
187+
:type: depset[File]
188+
189+
The transitive set of type definition files (usually `.pyi` files) for the
190+
Python modules for this target and its transitive dependencies. this target.
191+
Usually they describe the source files listed in `transitive_original_sources`.
192+
This field is primarily for static analysis tools.
193+
194+
:::{note}
195+
This may contain implementation-specific file types specific to a particular
196+
type checker.
197+
:::
198+
199+
::::{versionadded} 1.1.0
200+
::::
128201
""",
129202
"transitive_sources": """\
130203
:type: depset[File]
@@ -165,7 +238,9 @@ def PyInfoBuilder():
165238
_uses_shared_libraries = [False],
166239
build = lambda *a, **k: _PyInfoBuilder_build(self, *a, **k),
167240
build_builtin_py_info = lambda *a, **k: _PyInfoBuilder_build_builtin_py_info(self, *a, **k),
241+
direct_original_sources = builders.DepsetBuilder(),
168242
direct_pyc_files = builders.DepsetBuilder(),
243+
direct_pyi_files = builders.DepsetBuilder(),
169244
get_has_py2_only_sources = lambda *a, **k: _PyInfoBuilder_get_has_py2_only_sources(self, *a, **k),
170245
get_has_py3_only_sources = lambda *a, **k: _PyInfoBuilder_get_has_py3_only_sources(self, *a, **k),
171246
get_uses_shared_libraries = lambda *a, **k: _PyInfoBuilder_get_uses_shared_libraries(self, *a, **k),
@@ -182,7 +257,9 @@ def PyInfoBuilder():
182257
set_uses_shared_libraries = lambda *a, **k: _PyInfoBuilder_set_uses_shared_libraries(self, *a, **k),
183258
transitive_implicit_pyc_files = builders.DepsetBuilder(),
184259
transitive_implicit_pyc_source_files = builders.DepsetBuilder(),
260+
transitive_original_sources = builders.DepsetBuilder(),
185261
transitive_pyc_files = builders.DepsetBuilder(),
262+
transitive_pyi_files = builders.DepsetBuilder(),
186263
transitive_sources = builders.DepsetBuilder(),
187264
)
188265
return self
@@ -228,13 +305,15 @@ def _PyInfoBuilder_merge_all(self, transitive, *, direct = []):
228305
# BuiltinPyInfo doesn't have this field
229306
if hasattr(info, "direct_pyc_files"):
230307
self.direct_pyc_files.add(info.direct_pyc_files)
308+
self.direct_original_sources.add(info.direct_original_sources)
231309

232310
for info in direct + transitive:
233311
self.imports.add(info.imports)
234312
self.merge_has_py2_only_sources(info.has_py2_only_sources)
235313
self.merge_has_py3_only_sources(info.has_py3_only_sources)
236314
self.merge_uses_shared_libraries(info.uses_shared_libraries)
237315
self.transitive_sources.add(info.transitive_sources)
316+
self.transitive_original_sources.add(info.transitive_original_sources)
238317

239318
# BuiltinPyInfo doesn't have these fields
240319
if hasattr(info, "transitive_pyc_files"):
@@ -259,10 +338,14 @@ def _PyInfoBuilder_merge_targets(self, targets):
259338
def _PyInfoBuilder_build(self):
260339
if config.enable_pystar:
261340
kwargs = dict(
341+
direct_original_sources = self.direct_original_sources.build(),
262342
direct_pyc_files = self.direct_pyc_files.build(),
263-
transitive_pyc_files = self.transitive_pyc_files.build(),
343+
direct_pyi_files = self.direct_pyi_files.build(),
264344
transitive_implicit_pyc_files = self.transitive_implicit_pyc_files.build(),
265345
transitive_implicit_pyc_source_files = self.transitive_implicit_pyc_source_files.build(),
346+
transitive_original_sources = self.transitive_original_sources.build(),
347+
transitive_pyc_files = self.transitive_pyc_files.build(),
348+
transitive_pyi_files = self.transitive_pyi_files.build(),
266349
)
267350
else:
268351
kwargs = {}

python/private/py_library.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def py_library_impl(ctx, *, semantics):
102102
cc_info = semantics.get_cc_info_for_library(ctx)
103103
py_info, deps_transitive_sources, builtins_py_info = create_py_info(
104104
ctx,
105+
original_sources = direct_sources,
105106
required_py_files = required_py_files,
106107
required_pyc_files = required_pyc_files,
107108
implicit_pyc_files = implicit_pyc_files,

0 commit comments

Comments
 (0)