@@ -15,6 +15,7 @@ from typing import {% for i in output_file.typing_imports|sort %}{{ i }}{% if no
1515{% endif %}
1616
1717import betterproto
18+ from betterproto.grpc.grpclib_server import ServiceBase
1819{% if output_file .services %}
1920import grpclib
2021{% endif %}
@@ -82,7 +83,7 @@ class {{ service.py_name }}Stub(betterproto.ServiceStub):
8283 Optional[{{ field.annotation }}]
8384 {% - else -%}
8485 {{ field.annotation }}
85- {% - endif -%} =
86+ {% - endif -%} =
8687 {% - if field .py_name not in method .mutable_default_args -%}
8788 {{ field.default_value_string }}
8889 {% - else -%}
@@ -154,6 +155,89 @@ class {{ service.py_name }}Stub(betterproto.ServiceStub):
154155 {% endfor %}
155156{% endfor %}
156157
158+ {% for service in output_file .services %}
159+ class {{ service.py_name }}Base(ServiceBase):
160+ {% if service .comment %}
161+ {{ service.comment }}
162+
163+ {% endif %}
164+
165+ {% for method in service .methods %}
166+ async def {{ method.py_name }}(self
167+ {% - if not method .client_streaming -%}
168+ {% - if method .py_input_message and method .py_input_message .fields -%} ,
169+ {% - for field in method .py_input_message .fields -%}
170+ {{ field.py_name }}: {% if field .py_name in method .mutable_default_args and not field .annotation .startswith ("Optional[" ) -%}
171+ Optional[{{ field.annotation }}]
172+ {% - else -%}
173+ {{ field.annotation }}
174+ {% - endif -%}
175+ {% - if not loop .last %} , {% endif -%}
176+ {% - endfor -%}
177+ {% - endif -%}
178+ {% - else -%}
179+ {# Client streaming: need a request iterator instead #}
180+ , request_iterator: AsyncIterator["{{ method.py_input_message_type }}"]
181+ {% - endif -%}
182+ ) -> {% if method .server_streaming %} AsyncIterator["{{ method.py_output_message_type }}"]{% else %} "{{ method.py_output_message_type }}"{% endif %} :
183+ {% if method .comment %}
184+ {{ method.comment }}
185+
186+ {% endif %}
187+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
188+
189+ {% endfor %}
190+
191+ {% for method in service .methods %}
192+ async def __rpc_{{ method.py_name }}(self, stream: grpclib.server.Stream) -> None:
193+ {% if not method .client_streaming %}
194+ request = await stream.recv_message()
195+
196+ request_kwargs = {
197+ {% for field in method .py_input_message .fields %}
198+ "{{ field.py_name }}": request.{{ field.py_name }},
199+ {% endfor %}
200+ }
201+
202+ {% else %}
203+ request_kwargs = {"request_iterator": stream.__aiter__()}
204+ {% endif %}
205+
206+ {% if not method .server_streaming %}
207+ response = await self.{{ method.py_name }}(**request_kwargs)
208+ await stream.send_message(response)
209+ {% else %}
210+ await self._call_rpc_handler_server_stream(
211+ self.{{ method.py_name }},
212+ stream,
213+ request_kwargs,
214+ )
215+ {% endif %}
216+
217+ {% endfor %}
218+
219+ def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
220+ return {
221+ {% for method in service .methods %}
222+ "{{ method.route }}": grpclib.const.Handler(
223+ self.__rpc_{{ method.py_name }},
224+ {% if not method .client_streaming and not method .server_streaming %}
225+ grpclib.const.Cardinality.UNARY_UNARY,
226+ {% elif not method .client_streaming and method .server_streaming %}
227+ grpclib.const.Cardinality.UNARY_STREAM,
228+ {% elif method .client_streaming and not method .server_streaming %}
229+ grpclib.const.Cardinality.STREAM_UNARY,
230+ {% else %}
231+ grpclib.const.Cardinality.STREAM_STREAM,
232+ {% endif %}
233+ {{ method.py_input_message_type }},
234+ {{ method.py_output_message_type }},
235+ ),
236+ {% endfor %}
237+ }
238+
239+ {% endfor %}
240+
157241{% for i in output_file .imports |sort %}
158242{{ i }}
159243{% endfor %}
0 commit comments