From 7fe52aab952c2dc1caac161e72a3f88b89763e10 Mon Sep 17 00:00:00 2001 From: "Mathias L. Baumann" Date: Mon, 15 Dec 2025 19:01:02 +0100 Subject: [PATCH] Add grpc_stubs config option to control stub generation Add a new 'grpc_stubs' configuration option that allows users to choose which gRPC stubs to generate: - sync_and_async: Generate both sync and async stubs (default) - sync_only: Generate only synchronous stubs - async_only: Generate only asynchronous stubs This uses the new only_sync/only_async flags in mypy-protobuf >= 3.8.0. Fixes: https://github.com/frequenz-floss/frequenz-repo-config-python/issues/485 Signed-off-by: Mathias L. Baumann --- RELEASE_NOTES.md | 1 + pyproject.toml | 4 ++- src/frequenz/repo/config/protobuf.py | 9 ++++++ .../repo/config/setuptools/grpc_tools.py | 30 +++++++++++++++++-- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c0c115cb..18cfad0c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -23,6 +23,7 @@ But you might still need to adapt your code: ## New Features * `mkdocsstrings-python` v2 is now supported. +* Add `grpc_stubs` config option to control which gRPC stubs are generated (`sync_and_async`, `sync_only`, or `async_only`). ### Cookiecutter template diff --git a/pyproject.toml b/pyproject.toml index 3406a157..2261627c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,9 @@ compile_proto = "frequenz.repo.config.setuptools.grpc_tools:CompileProto" actor = [] api = [ "grpcio-tools >= 1.47.0, < 2", - "mypy-protobuf >= 3.0.0, < 4", + # Requires mypy-protobuf >= 3.8.0 for only_async/only_sync support + # See: https://github.com/nipunn1313/mypy-protobuf/pull/694 + "mypy-protobuf >= 3.8.0, < 4", "setuptools >= 67.6.0, < 81", ] app = [] diff --git a/src/frequenz/repo/config/protobuf.py b/src/frequenz/repo/config/protobuf.py index 83d31c63..49c1726f 100644 --- a/src/frequenz/repo/config/protobuf.py +++ b/src/frequenz/repo/config/protobuf.py @@ -36,6 +36,15 @@ class ProtobufConfig: docs_path: str = "protobuf-reference" """The path of the root directory where the documentation files will be generated.""" + grpc_stubs: str = "sync_and_async" + """The type of gRPC stubs to generate. + + Possible values: + - "sync_and_async": Generate both sync and async stubs (default). + - "sync_only": Generate only synchronous stubs. + - "async_only": Generate only asynchronous stubs. + """ + @classmethod def from_pyproject_toml( cls, path: str = "pyproject.toml", /, **defaults: Any diff --git a/src/frequenz/repo/config/setuptools/grpc_tools.py b/src/frequenz/repo/config/setuptools/grpc_tools.py index d0da3362..345247df 100644 --- a/src/frequenz/repo/config/setuptools/grpc_tools.py +++ b/src/frequenz/repo/config/setuptools/grpc_tools.py @@ -37,6 +37,9 @@ class CompileProto(_setuptools.Command): py_path: str """The path of the root directory where the Python files will be generated.""" + grpc_stubs: str + """The type of gRPC stubs to generate (sync_and_async, sync_only, async_only).""" + description: str = "compile protobuf files" """Description of the command.""" @@ -63,6 +66,11 @@ class CompileProto(_setuptools.Command): None, "path of the root directory where the Python files will be generated", ), + ( + "grpc-stubs=", + None, + "type of gRPC stubs to generate (sync_and_async, sync_only, async_only)", + ), ], ) """Options of the command.""" @@ -75,6 +83,7 @@ def initialize_options(self) -> None: self.proto_glob = config.proto_glob self.include_paths = config.include_paths self.py_path = config.py_path + self.grpc_stubs = config.grpc_stubs def finalize_options(self) -> None: """Finalize options.""" @@ -103,12 +112,29 @@ def run(self) -> None: ) return + # Build the mypy_grpc_out option based on grpc_stubs setting + mypy_grpc_out_opt = f"--mypy_grpc_out={self.py_path}" + match self.grpc_stubs: + case "sync_only": + mypy_grpc_out_opt = f"--mypy_grpc_out=only_sync:{self.py_path}" + case "async_only": + mypy_grpc_out_opt = f"--mypy_grpc_out=only_async:{self.py_path}" + case "sync_and_async": + pass # Default, no extra options needed + case _: + print( + f"WARNING: Unknown grpc_stubs value '{self.grpc_stubs}', " + "using default 'sync_and_async'" + ) + protoc_cmd = ( [_sys.executable, "-m", "grpc_tools.protoc"] + [f"-I{p}" for p in [*include_paths, self.proto_path]] + [ - f"--{opt}={self.py_path}" - for opt in "python_out grpc_python_out mypy_out mypy_grpc_out".split() + f"--python_out={self.py_path}", + f"--grpc_python_out={self.py_path}", + f"--mypy_out={self.py_path}", + mypy_grpc_out_opt, ] + proto_files )