- Author(s): Michael Beardsworth
- Approver: gnossen
- Status: Draft
- Implemented in: Bazel and Starlark
- Last updated: Sept. 8, 2021
- Discussion at: https://groups.google.com/g/grpc-io/c/NXdtW5xe9Js
Proposes modifications to gRPC's Bazel rules for Python codegen to improve dependency tracking.
- gRPC provides build rule logic written in Starlark to support Protobuf and gRPC Python codegen with Bazel.
- These rules (
py_proto_library
andpy_grpc_library
) each generate code from a singleproto_library
's protos. - The produced Python libraries (or equivalently-functioning PyInfo providers) do not express dependencies on the generated Python code for the
proto_library
's dependencies. - Users are surprised by this behavior. See #23769 for an example bug.
- This behavior is particularly surprising for Google-internal users. The internal versions of
py_proto_library
andpy_grpc_library
propagate Python dependencies.
py_proto_library
and py_grpc_library
are implemented in python_rules.bzl. There doesn't seem to be a proposal for that, though.
- We propose rebuilding the
py_proto_library
andpy_grpc_library
rules to use aspects to perform code generation steps. This approach corresponds to the Google-internal design for these rules. - The
_gen_py_proto_aspect
aspect visitsproto_library
rules to generate Python code for Protobuf. gRPC-owned code is still responsible for generating the Python code. - The aspect produces a custom providers
PyProtoInfo
that wraps aPyInfo
provider to avoid creating spurious dependencies for Python users that interface with theproto_library
rules through some other means. - The
py_proto_library
andpy_grpc_library
rules will only be responsible for collecting thePyInfo
providers from their dependencies. - The
plugin
attribute must be removed frompy_proto_library
. Aspects require the declaration of all possible parameter values up front, so it would not be possible for the new aspects to continue supporting arbitrary plugins. (Note that the plugin feature is not used in gRPC. It was introduced to support GAPIC, which no longer uses the feature.) - In some use cases in gRPC e.g. grpcio_channelz, the
py_proto_library
rule is located in a different package than the correspondingproto_library
rule. This rule layout is needed to generate Python code with import paths that match the Python package layout, rather than the directory structure containing the.proto
files. Since aspect-based code generation associates the generated code with the Bazel package (i.e. repository path) of theproto_library
rule rather than thepy_proto_library
rule, we need special handling for this case. When thepy_proto_library
is in a different Bazel package than theproto_library
rule, we generate an additional set of Python files that import the generated Python files under the old convention. Additionally, animports
attribute is added, to allow the caller to add import paths (similar to the behavior ofpy_library
. With these two changes, existing Python code can remain unmodified, with a minimal increase in BUILD file complexity. - No behavior change should be observed by the user of
py_proto_library
orpy_grpc_library
unless they rely on the (removed)plugin
attribute, or if they use the newimports
attribute.
The proposed approach addresses the open bug and corrects the dependency issues. However, it requires the removal of a (likely unused) feature of the build rules.
Alternatively a set of repository rules could be introduced that allow users to inject py_proto_library
and py_grpc_library
implementations into the gRPC Bazel build system.
This alternative approach would allow users to work around the dependency issues by taking on additional burden.
The new build logic could be moved into a separate repository, or potentially upstreamed to a Bazel-owned repository.
The implementation is mostly complete by beardsworth in #27275.
Existing unit tests cover the current API surface. An additional test case will be added to the Bazel Python test repo that covers transitive dependency resolution.
It is difficult to determine how many users rely on the plugin
attribute. If many users rely on this behavior then it may not be feasible to adopt this proposal.