docs: generate docs for py_common, PyInfoBuilder APIs (#2920)
I wrote up the docs awhile, but didn't fully wire them through to the
doc gen.
Fixes some various issues with the generated docs along the way.
diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index 25da682..b3e5f52 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -113,6 +113,7 @@
"//python/private:builders_util_bzl",
"//python/private:py_binary_rule_bzl",
"//python/private:py_cc_toolchain_rule_bzl",
+ "//python/private:py_info_bzl",
"//python/private:py_library_rule_bzl",
"//python/private:py_runtime_rule_bzl",
"//python/private:py_test_rule_bzl",
diff --git a/python/api/api.bzl b/python/api/api.bzl
index c8fb921..d41ec73 100644
--- a/python/api/api.bzl
+++ b/python/api/api.bzl
@@ -1,4 +1,23 @@
-"""Public, analysis phase APIs for Python rules."""
+"""Public, analysis phase APIs for Python rules.
+
+To use the analyis-time API, add the attributes to your rule, then
+use `py_common.get()` to get the api object:
+
+```
+load("@rules_python//python/api:api.bzl", "py_common")
+
+def _impl(ctx):
+ py_api = py_common.get(ctx)
+
+myrule = rule(
+ implementation = _impl,
+ attrs = {...} | py_common.API_ATTRS
+)
+```
+
+:::{versionadded} 0.37.0
+:::
+"""
load("//python/private/api:api.bzl", _py_common = "py_common")
diff --git a/python/private/api/api.bzl b/python/private/api/api.bzl
index 06fb729..44f9ab4 100644
--- a/python/private/api/api.bzl
+++ b/python/private/api/api.bzl
@@ -27,6 +27,17 @@
},
)
+def _py_common_typedef():
+ """Typedef for py_common.
+
+ :::{field} API_ATTRS
+ :type: dict[str, Attribute]
+
+ The attributes that rules must have for `py_common.get()` to work.
+ :::
+
+ """
+
def _py_common_get(ctx):
"""Get the py_common API instance.
@@ -45,6 +56,7 @@
return ctx.attr._py_common_api[ApiImplInfo].impl
py_common = struct(
+ TYPEDEF = _py_common_typedef,
get = _py_common_get,
API_ATTRS = {
"_py_common_api": attr.label(
diff --git a/python/private/api/py_common_api.bzl b/python/private/api/py_common_api.bzl
index 401b359..6fed245 100644
--- a/python/private/api/py_common_api.bzl
+++ b/python/private/api/py_common_api.bzl
@@ -22,17 +22,40 @@
py_common_api = rule(
implementation = _py_common_api_impl,
- doc = "Rule implementing py_common API.",
+ doc = "Internal Rule implementing py_common API.",
)
+def _py_common_api_typedef():
+ """The py_common API implementation.
+
+ An instance of this object is obtained using {obj}`py_common.get()`
+ """
+
def _merge_py_infos(transitive, *, direct = []):
- builder = PyInfoBuilder()
+ """Merge PyInfo objects into a single PyInfo.
+
+ This is a convenience wrapper around {obj}`PyInfoBuilder.merge_all`. For
+ more control over merging PyInfo objects, use {obj}`PyInfoBuilder`.
+
+ Args:
+ transitive: {type}`list[PyInfo]` The PyInfo objects with info
+ considered indirectly provided by something (e.g. via
+ its deps attribute).
+ direct: {type}`list[PyInfo]` The PyInfo objects that are
+ considered directly provided by something (e.g. via
+ the srcs attribute).
+
+ Returns:
+ {type}`PyInfo` A PyInfo containing the merged values.
+ """
+ builder = PyInfoBuilder.new()
builder.merge_all(transitive, direct = direct)
return builder.build()
# Exposed for doc generation, not directly used.
# buildifier: disable=name-conventions
PyCommonApi = struct(
+ TYPEDEF = _py_common_api_typedef,
merge_py_infos = _merge_py_infos,
- PyInfoBuilder = PyInfoBuilder,
+ PyInfoBuilder = PyInfoBuilder.new,
)
diff --git a/python/private/common.bzl b/python/private/common.bzl
index 072a1bb..a58a9c0 100644
--- a/python/private/common.bzl
+++ b/python/private/common.bzl
@@ -405,7 +405,7 @@
transitive sources collected from dependencies (the latter is only
necessary for deprecated extra actions support).
"""
- py_info = PyInfoBuilder()
+ py_info = PyInfoBuilder.new()
py_info.site_packages_symlinks.add(site_packages_symlinks)
py_info.direct_original_sources.add(original_sources)
py_info.direct_pyc_files.add(required_pyc_files)
diff --git a/python/private/py_info.bzl b/python/private/py_info.bzl
index dc3cb24..d175eef 100644
--- a/python/private/py_info.bzl
+++ b/python/private/py_info.bzl
@@ -82,7 +82,11 @@
}
PyInfo, _unused_raw_py_info_ctor = define_bazel_6_provider(
- doc = "Encapsulates information provided by the Python rules.",
+ doc = """Encapsulates information provided by the Python rules.
+
+Instead of creating this object directly, use {obj}`PyInfoBuilder` and
+the {obj}`PyCommonApi` utilities.
+""",
init = _PyInfo_init,
fields = {
"direct_original_sources": """
@@ -265,7 +269,65 @@
# The "effective" PyInfo is what the canonical //python:py_info.bzl%PyInfo symbol refers to
_EffectivePyInfo = PyInfo if (config.enable_pystar or BuiltinPyInfo == None) else BuiltinPyInfo
-def PyInfoBuilder():
+def _PyInfoBuilder_typedef():
+ """Builder for PyInfo.
+
+ To create an instance, use {obj}`py_common.get()` and call `PyInfoBuilder()`
+
+ :::{field} direct_original_sources
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} direct_pyc_files
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} direct_pyi_files
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} imports
+ :type: DepsetBuilder[str]
+ :::
+
+ :::{field} transitive_implicit_pyc_files
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} transitive_implicit_pyc_source_files
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} transitive_original_sources
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} transitive_pyc_files
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} transitive_pyi_files
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} transitive_sources
+ :type: DepsetBuilder[File]
+ :::
+
+ :::{field} site_packages_symlinks
+ :type: DepsetBuilder[tuple[str | None, str]]
+
+ NOTE: This depset has `topological` order
+ :::
+ """
+
+def _PyInfoBuilder_new():
+ """Creates an instance.
+
+ Returns:
+ {type}`PyInfoBuilder`
+ """
+
# buildifier: disable=uninitialized
self = struct(
_has_py2_only_sources = [False],
@@ -301,35 +363,116 @@
return self
def _PyInfoBuilder_get_has_py3_only_sources(self):
+ """Get the `has_py3_only_sources` value.
+
+ Args:
+ self: implicitly added.
+
+ Returns:
+ {type}`bool`
+ """
return self._has_py3_only_sources[0]
def _PyInfoBuilder_get_has_py2_only_sources(self):
+ """Get the `has_py2_only_sources` value.
+
+ Args:
+ self: implicitly added.
+
+ Returns:
+ {type}`bool`
+ """
return self._has_py2_only_sources[0]
def _PyInfoBuilder_set_has_py2_only_sources(self, value):
+ """Sets `has_py2_only_sources` to `value`.
+
+ Args:
+ self: implicitly added.
+ value: {type}`bool` The value to set.
+
+ Returns:
+ {type}`PyInfoBuilder` self
+ """
self._has_py2_only_sources[0] = value
return self
def _PyInfoBuilder_set_has_py3_only_sources(self, value):
+ """Sets `has_py3_only_sources` to `value`.
+
+ Args:
+ self: implicitly added.
+ value: {type}`bool` The value to set.
+
+ Returns:
+ {type}`PyInfoBuilder` self
+ """
self._has_py3_only_sources[0] = value
return self
def _PyInfoBuilder_merge_has_py2_only_sources(self, value):
+ """Sets `has_py2_only_sources` based on current and incoming `value`.
+
+ Args:
+ self: implicitly added.
+ value: {type}`bool` Another `has_py2_only_sources` value. It will
+ be merged into this builder's state.
+
+ Returns:
+ {type}`PyInfoBuilder` self
+ """
self._has_py2_only_sources[0] = self._has_py2_only_sources[0] or value
return self
def _PyInfoBuilder_merge_has_py3_only_sources(self, value):
+ """Sets `has_py3_only_sources` based on current and incoming `value`.
+
+ Args:
+ self: implicitly added.
+ value: {type}`bool` Another `has_py3_only_sources` value. It will
+ be merged into this builder's state.
+
+ Returns:
+ {type}`PyInfoBuilder` self
+ """
self._has_py3_only_sources[0] = self._has_py3_only_sources[0] or value
return self
def _PyInfoBuilder_merge_uses_shared_libraries(self, value):
+ """Sets `uses_shared_libraries` based on current and incoming `value`.
+
+ Args:
+ self: implicitly added.
+ value: {type}`bool` Another `uses_shared_libraries` value. It will
+ be merged into this builder's state.
+
+ Returns:
+ {type}`PyInfoBuilder` self
+ """
self._uses_shared_libraries[0] = self._uses_shared_libraries[0] or value
return self
def _PyInfoBuilder_get_uses_shared_libraries(self):
+ """Get the `uses_shared_libraries` value.
+
+ Args:
+ self: implicitly added.
+
+ Returns:
+ {type}`bool`
+ """
return self._uses_shared_libraries[0]
def _PyInfoBuilder_set_uses_shared_libraries(self, value):
+ """Sets `uses_shared_libraries` to `value`.
+
+ Args:
+ self: implicitly added.
+ value: {type}`bool` The value to set.
+
+ Returns:
+ {type}`PyInfoBuilder` self
+ """
self._uses_shared_libraries[0] = value
return self
@@ -344,7 +487,7 @@
direct fields into this object's direct fields.
Returns:
- {type}`PyInfoBuilder` the current object
+ {type}`PyInfoBuilder` self
"""
return self.merge_all(list(infos), direct = direct)
@@ -359,7 +502,7 @@
direct fields into this object's direct fields.
Returns:
- {type}`PyInfoBuilder` the current object
+ {type}`PyInfoBuilder` self
"""
for info in direct:
# BuiltinPyInfo doesn't have this field
@@ -392,11 +535,11 @@
Args:
self: implicitly added.
target: {type}`Target` targets that provide PyInfo, or other relevant
- providers, will be merged into this object. If a target doesn't provide
- any relevant providers, it is ignored.
+ providers, will be merged into this object. If a target doesn't provide
+ any relevant providers, it is ignored.
Returns:
- {type}`PyInfoBuilder` the current object.
+ {type}`PyInfoBuilder` self.
"""
if PyInfo in target:
self.merge(target[PyInfo])
@@ -410,18 +553,26 @@
Args:
self: implicitly added.
targets: {type}`list[Target]`
- targets that provide PyInfo, or other relevant
- providers, will be merged into this object. If a target doesn't provide
- any relevant providers, it is ignored.
+ targets that provide PyInfo, or other relevant
+ providers, will be merged into this object. If a target doesn't provide
+ any relevant providers, it is ignored.
Returns:
- {type}`PyInfoBuilder` the current object.
+ {type}`PyInfoBuilder` self.
"""
for t in targets:
self.merge_target(t)
return self
def _PyInfoBuilder_build(self):
+ """Builds into a {obj}`PyInfo` object.
+
+ Args:
+ self: implicitly added.
+
+ Returns:
+ {type}`PyInfo`
+ """
if config.enable_pystar:
kwargs = dict(
direct_original_sources = self.direct_original_sources.build(),
@@ -447,6 +598,15 @@
)
def _PyInfoBuilder_build_builtin_py_info(self):
+ """Builds into a Bazel-builtin PyInfo object, if available.
+
+ Args:
+ self: implicitly added.
+
+ Returns:
+ {type}`BuiltinPyInfo | None` None is returned if Bazel's
+ builtin PyInfo object is disabled.
+ """
if BuiltinPyInfo == None:
return None
@@ -457,3 +617,25 @@
transitive_sources = self.transitive_sources.build(),
uses_shared_libraries = self._uses_shared_libraries[0],
)
+
+# Provided for documentation purposes
+# buildifier: disable=name-conventions
+PyInfoBuilder = struct(
+ TYPEDEF = _PyInfoBuilder_typedef,
+ new = _PyInfoBuilder_new,
+ build = _PyInfoBuilder_build,
+ build_builtin_py_info = _PyInfoBuilder_build_builtin_py_info,
+ get_has_py2_only_sources = _PyInfoBuilder_get_has_py2_only_sources,
+ get_has_py3_only_sources = _PyInfoBuilder_get_has_py3_only_sources,
+ get_uses_shared_libraries = _PyInfoBuilder_get_uses_shared_libraries,
+ merge = _PyInfoBuilder_merge,
+ merge_all = _PyInfoBuilder_merge_all,
+ merge_has_py2_only_sources = _PyInfoBuilder_merge_has_py2_only_sources,
+ merge_has_py3_only_sources = _PyInfoBuilder_merge_has_py3_only_sources,
+ merge_target = _PyInfoBuilder_merge_target,
+ merge_targets = _PyInfoBuilder_merge_targets,
+ merge_uses_shared_libraries = _PyInfoBuilder_merge_uses_shared_libraries,
+ set_has_py2_only_sources = _PyInfoBuilder_set_has_py2_only_sources,
+ set_has_py3_only_sources = _PyInfoBuilder_set_has_py3_only_sources,
+ set_uses_shared_libraries = _PyInfoBuilder_set_uses_shared_libraries,
+)
diff --git a/python/private/py_package.bzl b/python/private/py_package.bzl
index 1d866a9..adf2b6d 100644
--- a/python/private/py_package.bzl
+++ b/python/private/py_package.bzl
@@ -34,7 +34,7 @@
def _py_package_impl(ctx):
inputs = builders.DepsetBuilder()
- py_info = PyInfoBuilder()
+ py_info = PyInfoBuilder.new()
for dep in ctx.attr.deps:
inputs.add(dep[DefaultInfo].data_runfiles.files)
inputs.add(dep[DefaultInfo].default_runfiles.files)
diff --git a/tests/base_rules/py_info/py_info_tests.bzl b/tests/base_rules/py_info/py_info_tests.bzl
index e160e70..aa252a2 100644
--- a/tests/base_rules/py_info/py_info_tests.bzl
+++ b/tests/base_rules/py_info/py_info_tests.bzl
@@ -162,7 +162,7 @@
direct_pyi,
trans_pyi,
) = targets.misc[DefaultInfo].files.to_list()
- builder = PyInfoBuilder()
+ builder = PyInfoBuilder.new()
builder.direct_pyc_files.add(direct_pyc)
builder.direct_original_sources.add(original_py)
builder.direct_pyi_files.add(direct_pyi)