4.9 KiB
Aspect-based Python Bazel Rules
- 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
Abstract
Proposes modifications to gRPC's Bazel rules for Python codegen to improve dependency tracking.
Background
- gRPC provides build rule logic written in Starlark to support Protobuf and gRPC Python codegen with Bazel.
- These rules (
py_proto_libraryandpy_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_libraryandpy_grpc_librarypropagate Python dependencies.
Related Proposals:
py_proto_library and py_grpc_library are implemented in python_rules.bzl. There doesn't seem to be a proposal for that, though.
Proposal
- We propose rebuilding the
py_proto_libraryandpy_grpc_libraryrules to use aspects to perform code generation steps. This approach corresponds to the Google-internal design for these rules. - The
_gen_py_proto_aspectaspect visitsproto_libraryrules to generate Python code for Protobuf. gRPC-owned code is still responsible for generating the Python code. - The aspect produces a custom providers
PyProtoInfothat wraps aPyInfoprovider to avoid creating spurious dependencies for Python users that interface with theproto_libraryrules through some other means. - The
py_proto_libraryandpy_grpc_libraryrules will only be responsible for collecting thePyInfoproviders from their dependencies. - The
pluginattribute 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_libraryrule is located in a different package than the correspondingproto_libraryrule. 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.protofiles. Since aspect-based code generation associates the generated code with the Bazel package (i.e. repository path) of theproto_libraryrule rather than thepy_proto_libraryrule, we need special handling for this case. When thepy_proto_libraryis in a different Bazel package than theproto_libraryrule, we generate an additional set of Python files that import the generated Python files under the old convention. Additionally, animportsattribute 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_libraryorpy_grpc_libraryunless they rely on the (removed)pluginattribute, or if they use the newimportsattribute.
Rationale
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.
Implementation
The implementation is mostly complete by beardsworth in #27275.
Testing
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.
Open issues (if applicable)
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.