Skip to content

Commit cd22820

Browse files
Add gRPC Python code generation (#10937)
Users activate this on a per-target basis: ```python # BUILD protobuf_library(grpc=True) ``` This signals that all relevant code generators should use the gRPC plugin. For now, that is only Python. But when adding other languages like Java, it will need to respect this flag. Closes #10497. [ci skip-rust] [ci skip-build-wheels]
1 parent de974d2 commit cd22820

File tree

3 files changed

+64
-2
lines changed

3 files changed

+64
-2
lines changed

src/python/pants/backend/codegen/protobuf/python/rules.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
from pants.backend.codegen.protobuf.protoc import Protoc
77
from pants.backend.codegen.protobuf.python.additional_fields import PythonSourceRootField
8+
from pants.backend.codegen.protobuf.python.grpc_python_plugin import GrpcPythonPlugin
89
from pants.backend.codegen.protobuf.python.python_protobuf_subsystem import PythonProtobufSubsystem
9-
from pants.backend.codegen.protobuf.target_types import ProtobufSources
10+
from pants.backend.codegen.protobuf.target_types import ProtobufGrcpToggle, ProtobufSources
1011
from pants.backend.python.target_types import PythonSources
1112
from pants.backend.python.util_rules import extract_pex, pex
1213
from pants.backend.python.util_rules.extract_pex import ExtractedPexDistributions
@@ -52,6 +53,7 @@ class GeneratePythonFromProtobufRequest(GenerateSourcesRequest):
5253
async def generate_python_from_protobuf(
5354
request: GeneratePythonFromProtobufRequest,
5455
protoc: Protoc,
56+
grpc_python_plugin: GrpcPythonPlugin,
5557
python_protobuf_subsystem: PythonProtobufSubsystem,
5658
) -> GeneratedSources:
5759
download_protoc_request = Get(
@@ -112,13 +114,25 @@ async def generate_python_from_protobuf(
112114
)
113115
extracted_mypy_wheels = await Get(ExtractedPexDistributions, Pex, mypy_pex)
114116

117+
downloaded_grpc_plugin = (
118+
await Get(
119+
DownloadedExternalTool,
120+
ExternalToolRequest,
121+
grpc_python_plugin.get_request(Platform.current),
122+
)
123+
if request.protocol_target.get(ProtobufGrcpToggle).value
124+
else None
125+
)
126+
115127
unmerged_digests = [
116128
all_sources_stripped.snapshot.digest,
117129
downloaded_protoc_binary.digest,
118130
empty_output_dir,
119131
]
120132
if extracted_mypy_wheels:
121133
unmerged_digests.append(extracted_mypy_wheels.digest)
134+
if downloaded_grpc_plugin:
135+
unmerged_digests.append(downloaded_grpc_plugin.digest)
122136
input_digest = await Get(Digest, MergeDigests(unmerged_digests))
123137

124138
argv = [downloaded_protoc_binary.exe, "--python_out", output_dir]
@@ -135,6 +149,10 @@ async def generate_python_from_protobuf(
135149
output_dir,
136150
]
137151
)
152+
if downloaded_grpc_plugin:
153+
argv.extend(
154+
[f"--plugin=protoc-gen-grpc={downloaded_grpc_plugin.exe}", "--grpc_out", output_dir]
155+
)
138156
argv.extend(target_sources_stripped.snapshot.files)
139157

140158
env = {}

src/python/pants/backend/codegen/protobuf/python/rules_integration_test.py

+36
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,39 @@ def test_mypy_plugin(rule_runner: RuleRunner) -> None:
232232
mypy=True,
233233
expected_files=["src/protobuf/dir1/f_pb2.py", "src/protobuf/dir1/f_pb2.pyi"],
234234
)
235+
236+
237+
def test_grpc(rule_runner: RuleRunner) -> None:
238+
rule_runner.create_file(
239+
"src/protobuf/dir1/f.proto",
240+
dedent(
241+
"""\
242+
syntax = "proto3";
243+
244+
package dir1;
245+
246+
// The greeter service definition.
247+
service Greeter {
248+
// Sends a greeting
249+
rpc SayHello (HelloRequest) returns (HelloReply) {}
250+
}
251+
252+
// The request message containing the user's name.
253+
message HelloRequest {
254+
string name = 1;
255+
}
256+
257+
// The response message containing the greetings
258+
message HelloReply {
259+
string message = 1;
260+
}
261+
"""
262+
),
263+
)
264+
rule_runner.add_to_build_file("src/protobuf/dir1", "protobuf_library(grpc=True)")
265+
assert_files_generated(
266+
rule_runner,
267+
"src/protobuf/dir1",
268+
source_roots=["src/protobuf"],
269+
expected_files=["src/protobuf/dir1/f_pb2.py", "src/protobuf/dir1/f_pb2_grpc.py"],
270+
)

src/python/pants/backend/codegen/protobuf/target_types.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pants.engine.rules import Get, collect_rules, rule
77
from pants.engine.target import (
88
COMMON_TARGET_FIELDS,
9+
BoolField,
910
Dependencies,
1011
InjectDependenciesRequest,
1112
InjectedDependencies,
@@ -30,14 +31,21 @@ class ProtobufSources(Sources):
3031
expected_file_extensions = (".proto",)
3132

3233

34+
class ProtobufGrcpToggle(BoolField):
35+
"""Whether to generate gRPC code or not."""
36+
37+
alias = "grpc"
38+
default = False
39+
40+
3341
class ProtobufLibrary(Target):
3442
"""Protobuf files used to generate various languages.
3543
3644
See https://www.pantsbuild.org/docs/protobuf.
3745
"""
3846

3947
alias = "protobuf_library"
40-
core_fields = (*COMMON_TARGET_FIELDS, ProtobufDependencies, ProtobufSources)
48+
core_fields = (*COMMON_TARGET_FIELDS, ProtobufDependencies, ProtobufSources, ProtobufGrcpToggle)
4149

4250

4351
class InjectProtobufDependencies(InjectDependenciesRequest):

0 commit comments

Comments
 (0)