pydantic-rpc 0.4.1__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. pydantic_rpc-0.4.1/README.md → pydantic_rpc-0.6.0/PKG-INFO +26 -4
  2. pydantic_rpc-0.4.1/PKG-INFO → pydantic_rpc-0.6.0/README.md +11 -19
  3. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/README.md +1 -1
  4. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter_connecpy.py +24 -0
  5. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/pyproject.toml +12 -2
  6. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/src/pydantic_rpc/core.py +119 -7
  7. pydantic_rpc-0.6.0/tests/asyncechoservice.proto +33 -0
  8. pydantic_rpc-0.6.0/tests/asyncechoservice_connecpy.py +101 -0
  9. pydantic_rpc-0.6.0/tests/asyncechoservice_pb2.py +40 -0
  10. pydantic_rpc-0.6.0/tests/asyncechoservice_pb2.pyi +17 -0
  11. pydantic_rpc-0.6.0/tests/asyncechoservice_pb2_grpc.py +110 -0
  12. pydantic_rpc-0.6.0/tests/echoservice.proto +33 -0
  13. pydantic_rpc-0.6.0/tests/echoservice_connecpy.py +101 -0
  14. pydantic_rpc-0.6.0/tests/echoservice_pb2.py +40 -0
  15. pydantic_rpc-0.6.0/tests/echoservice_pb2.pyi +17 -0
  16. pydantic_rpc-0.6.0/tests/echoservice_pb2_grpc.py +110 -0
  17. pydantic_rpc-0.6.0/tests/test_apps.py +190 -0
  18. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/uv.lock +55 -5
  19. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/.gitignore +0 -0
  20. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/.python-version +0 -0
  21. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/LICENSE +0 -0
  22. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/agent_aio_grpc.py +0 -0
  23. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/agent_connecpy.py +0 -0
  24. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/asyncio_greeting.py +0 -0
  25. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/barservice.proto +0 -0
  26. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/barservice_pb2.py +0 -0
  27. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/barservice_pb2.pyi +0 -0
  28. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/barservice_pb2_grpc.py +0 -0
  29. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/foobar.py +0 -0
  30. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/foobar_client.py +0 -0
  31. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/fooservice.proto +0 -0
  32. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/fooservice_pb2.py +0 -0
  33. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/fooservice_pb2.pyi +0 -0
  34. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/fooservice_pb2_grpc.py +0 -0
  35. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/google/protobuf/duration.proto +0 -0
  36. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/google/protobuf/timestamp.proto +0 -0
  37. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter.proto +0 -0
  38. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter_client.py +0 -0
  39. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter_connecpy_client.py +0 -0
  40. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter_pb2.py +0 -0
  41. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter_pb2.pyi +0 -0
  42. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter_pb2_grpc.py +0 -0
  43. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeter_sonora_client.py +0 -0
  44. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeting.py +0 -0
  45. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeting_asgi.py +0 -0
  46. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeting_connecpy.py +0 -0
  47. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeting_using_exsiting_pb2_modules.py +0 -0
  48. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/greeting_wsgi.py +0 -0
  49. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicsagent.proto +0 -0
  50. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicsagent_pb2.py +0 -0
  51. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicsagent_pb2.pyi +0 -0
  52. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicsagent_pb2_grpc.py +0 -0
  53. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicslocationagent.proto +0 -0
  54. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicslocationagent_connecpy.py +0 -0
  55. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicslocationagent_pb2.py +0 -0
  56. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicslocationagent_pb2.pyi +0 -0
  57. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/examples/olympicslocationagent_pb2_grpc.py +0 -0
  58. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/src/pydantic_rpc/__init__.py +0 -0
  59. {pydantic_rpc-0.4.1 → pydantic_rpc-0.6.0}/src/pydantic_rpc/py.typed +0 -0
@@ -1,3 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydantic-rpc
3
+ Version: 0.6.0
4
+ Summary: A Python library for building gRPC/ConnectRPC services with Pydantic models.
5
+ Author: Yasushi Itoh
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: connecpy>=1.3.2
9
+ Requires-Dist: grpcio-health-checking>=1.56.2
10
+ Requires-Dist: grpcio-reflection>=1.56.2
11
+ Requires-Dist: grpcio-tools>=1.56.2
12
+ Requires-Dist: pydantic>=2.1.1
13
+ Requires-Dist: sonora>=0.2.3
14
+ Description-Content-Type: text/markdown
15
+
1
16
  # 🚀 PydanticRPC
2
17
 
3
18
  **PydanticRPC** is a Python library that enables you to rapidly expose [Pydantic](https://docs.pydantic.dev/) models via [gRPC](https://grpc.io/)/[Connect RPC](https://connectrpc.com/docs/protocol/) services without writing any protobuf files. Instead, it automatically generates protobuf files on the fly from the method signatures of your Python objects and the type signatures of your Pydantic models.
@@ -89,6 +104,7 @@ app.mount(OlympicsLocationAgent())
89
104
  - 🌐 **WSGI/ASGI Support:** Create gRPC-Web services that can run as WSGI or ASGI applications powered by `Sonora`.
90
105
  - **For Connect-RPC:**
91
106
  - 🌐 **Connecpy Support:** Partially supports Connect-RPC via `Connecpy`.
107
+ - 🛠️ **Pre-generated Protobuf Files and Code:** Pre-generate proto files and corresponding code via the CLI. By setting the environment variable (PYDANTIC_RPC_SKIP_GENERATION), you can skip runtime generation.
92
108
 
93
109
  ## 📦 Installation
94
110
 
@@ -242,7 +258,9 @@ When this variable is set to "true", PydanticRPC will load existing pre-generate
242
258
  ## 💎 Advanced Features
243
259
 
244
260
  ### 🌊 Response Streaming
245
- PydanticRPC supports streaming for responses in asynchronous gRPC and gRPC-Web services only.
261
+ PydanticRPC supports streaming responses only for asynchronous gRPC and gRPC-Web services.
262
+ If a service class method’s return type is `typing.AsyncIterator[T]`, the method is considered a streaming method.
263
+
246
264
 
247
265
  Please see the sample code below:
248
266
 
@@ -304,6 +322,9 @@ if __name__ == "__main__":
304
322
  loop.run_until_complete(s.run(OlympicsAgent()))
305
323
  ```
306
324
 
325
+ In the example above, the `ask_stream` method returns an `AsyncIterator[StreamingResult]` object, which is considered a streaming method. The `StreamingResult` class is a Pydantic model that defines the response type of the streaming method. You can use any Pydantic model as the response type.
326
+
327
+
307
328
  ### 🔗 Multiple Services with Custom Interceptors
308
329
 
309
330
  PydanticRPC supports defining and running multiple services in a single server:
@@ -389,16 +410,17 @@ if __name__ == "__main__":
389
410
 
390
411
  TODO
391
412
 
392
- ### 🗄️ Protobuf file generation
413
+ ### 🗄️ Protobuf file and code (Python files) generation using CLI
393
414
 
394
- You can generate protobuf files for a given module and a specified class using `core.py`:
415
+ You can genereate protobuf files and code for a given module and a specified class using `pydantic-rpc` CLI command:
395
416
 
396
417
  ```bash
397
- python core.py a_module.py aClass
418
+ pydantic-rpc a_module.py aClassName
398
419
  ```
399
420
 
400
421
  Using this generated proto file and tools as `protoc`, `buf` and `BSR`, you could generate code for any desired language other than Python.
401
422
 
423
+
402
424
  ## 📖 Data Type Mapping
403
425
 
404
426
  | Python Type | Protobuf Type |
@@ -1,18 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: pydantic-rpc
3
- Version: 0.4.1
4
- Summary: A Python library for building gRPC/ConnectRPC services with Pydantic models.
5
- Author: Yasushi Itoh
6
- License-File: LICENSE
7
- Requires-Python: >=3.11
8
- Requires-Dist: connecpy>=1.2.1
9
- Requires-Dist: grpcio-health-checking>=1.56.2
10
- Requires-Dist: grpcio-reflection>=1.56.2
11
- Requires-Dist: grpcio-tools>=1.56.2
12
- Requires-Dist: pydantic>=2.1.1
13
- Requires-Dist: sonora>=0.2.3
14
- Description-Content-Type: text/markdown
15
-
16
1
  # 🚀 PydanticRPC
17
2
 
18
3
  **PydanticRPC** is a Python library that enables you to rapidly expose [Pydantic](https://docs.pydantic.dev/) models via [gRPC](https://grpc.io/)/[Connect RPC](https://connectrpc.com/docs/protocol/) services without writing any protobuf files. Instead, it automatically generates protobuf files on the fly from the method signatures of your Python objects and the type signatures of your Pydantic models.
@@ -104,6 +89,7 @@ app.mount(OlympicsLocationAgent())
104
89
  - 🌐 **WSGI/ASGI Support:** Create gRPC-Web services that can run as WSGI or ASGI applications powered by `Sonora`.
105
90
  - **For Connect-RPC:**
106
91
  - 🌐 **Connecpy Support:** Partially supports Connect-RPC via `Connecpy`.
92
+ - 🛠️ **Pre-generated Protobuf Files and Code:** Pre-generate proto files and corresponding code via the CLI. By setting the environment variable (PYDANTIC_RPC_SKIP_GENERATION), you can skip runtime generation.
107
93
 
108
94
  ## 📦 Installation
109
95
 
@@ -257,7 +243,9 @@ When this variable is set to "true", PydanticRPC will load existing pre-generate
257
243
  ## 💎 Advanced Features
258
244
 
259
245
  ### 🌊 Response Streaming
260
- PydanticRPC supports streaming for responses in asynchronous gRPC and gRPC-Web services only.
246
+ PydanticRPC supports streaming responses only for asynchronous gRPC and gRPC-Web services.
247
+ If a service class method’s return type is `typing.AsyncIterator[T]`, the method is considered a streaming method.
248
+
261
249
 
262
250
  Please see the sample code below:
263
251
 
@@ -319,6 +307,9 @@ if __name__ == "__main__":
319
307
  loop.run_until_complete(s.run(OlympicsAgent()))
320
308
  ```
321
309
 
310
+ In the example above, the `ask_stream` method returns an `AsyncIterator[StreamingResult]` object, which is considered a streaming method. The `StreamingResult` class is a Pydantic model that defines the response type of the streaming method. You can use any Pydantic model as the response type.
311
+
312
+
322
313
  ### 🔗 Multiple Services with Custom Interceptors
323
314
 
324
315
  PydanticRPC supports defining and running multiple services in a single server:
@@ -404,16 +395,17 @@ if __name__ == "__main__":
404
395
 
405
396
  TODO
406
397
 
407
- ### 🗄️ Protobuf file generation
398
+ ### 🗄️ Protobuf file and code (Python files) generation using CLI
408
399
 
409
- You can generate protobuf files for a given module and a specified class using `core.py`:
400
+ You can genereate protobuf files and code for a given module and a specified class using `pydantic-rpc` CLI command:
410
401
 
411
402
  ```bash
412
- python core.py a_module.py aClass
403
+ pydantic-rpc a_module.py aClassName
413
404
  ```
414
405
 
415
406
  Using this generated proto file and tools as `protoc`, `buf` and `BSR`, you could generate code for any desired language other than Python.
416
407
 
408
+
417
409
  ## 📖 Data Type Mapping
418
410
 
419
411
  | Python Type | Protobuf Type |
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 📝 Prerequisites
4
4
 
5
- Ensure you have [UV](https://docs.astral.sh/uv/) installed on your system. If not, you can install it using the following command:
5
+ Ensure you have [uv](https://docs.astral.sh/uv/) installed on your system. If not, you can install it using the following command:
6
6
 
7
7
  ### Linux/macOS
8
8
 
@@ -43,6 +43,30 @@ class GreeterServer(ConnecpyServer):
43
43
  return "greeter.v1.Greeter"
44
44
 
45
45
 
46
+ class GreeterSync(Protocol):
47
+ def SayHello(
48
+ self, req: _pb2.HelloRequest, ctx: ServiceContext
49
+ ) -> _pb2.HelloReply: ...
50
+
51
+
52
+ class GreeterServerSync(ConnecpyServer):
53
+ def __init__(self, *, service: GreeterSync, server_path_prefix=""):
54
+ super().__init__()
55
+ self._prefix = f"{server_path_prefix}/greeter.v1.Greeter"
56
+ self._endpoints = {
57
+ "SayHello": Endpoint[_pb2.HelloRequest, _pb2.HelloReply](
58
+ service_name="Greeter",
59
+ name="SayHello",
60
+ function=getattr(service, "SayHello"),
61
+ input=_pb2.HelloRequest,
62
+ output=_pb2.HelloReply,
63
+ ),
64
+ }
65
+
66
+ def serviceName(self):
67
+ return "greeter.v1.Greeter"
68
+
69
+
46
70
  class GreeterClient(ConnecpyClient):
47
71
  def SayHello(
48
72
  self,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pydantic-rpc"
3
- version = "0.4.1"
3
+ version = "0.6.0"
4
4
  description = "A Python library for building gRPC/ConnectRPC services with Pydantic models."
5
5
  authors = [
6
6
  { name = "Yasushi Itoh" }
@@ -11,11 +11,14 @@ dependencies = [
11
11
  "grpcio-reflection>=1.56.2",
12
12
  "grpcio-health-checking>=1.56.2",
13
13
  "sonora>=0.2.3",
14
- "connecpy>=1.2.1",
14
+ "connecpy>=1.3.2",
15
15
  ]
16
16
  readme = "README.md"
17
17
  requires-python = ">= 3.11"
18
18
 
19
+ [project.scripts]
20
+ pydantic-rpc = "pydantic_rpc.core:main"
21
+
19
22
  [build-system]
20
23
  requires = ["hatchling"]
21
24
  build-backend = "hatchling.build"
@@ -25,8 +28,15 @@ managed = true
25
28
  dev-dependencies = [
26
29
  "hypercorn>=0.17.3",
27
30
  "pydantic-ai==0.0.20",
31
+ "pytest>=8.3.4",
32
+ "pytest-asyncio>=0.20.3",
28
33
  "ruff>=0.9.4",
29
34
  ]
30
35
 
31
36
  [tool.hatch.metadata]
32
37
  allow-direct-references = true
38
+
39
+ [tool.pytest.ini_options]
40
+ markers = [
41
+ "asyncio: mark test as asyncio",
42
+ ]
@@ -31,6 +31,7 @@ from sonora.wsgi import grpcWSGI
31
31
  from sonora.asgi import grpcASGI
32
32
  from connecpy.asgi import ConnecpyASGIApp as ConnecpyASGI
33
33
  from connecpy.errors import Errors
34
+ from connecpy.wsgi import ConnecpyWSGIApp as ConnecpyWSGI
34
35
 
35
36
  # Protobuf Python modules for Timestamp, Duration (requires protobuf / grpcio)
36
37
  from google.protobuf import timestamp_pb2, duration_pb2
@@ -356,6 +357,69 @@ def connect_obj_with_stub_async(pb2_grpc_module, pb2_module, obj: object) -> typ
356
357
  return ConcreteServiceClass
357
358
 
358
359
 
360
+ def connect_obj_with_stub_connecpy(connecpy_module, pb2_module, obj: object) -> type:
361
+ """
362
+ Connect a Python service object to a Connecpy stub.
363
+ """
364
+ service_class = obj.__class__
365
+ stub_class_name = service_class.__name__
366
+ stub_class = getattr(connecpy_module, stub_class_name)
367
+
368
+ class ConcreteServiceClass(stub_class):
369
+ pass
370
+
371
+ def implement_stub_method(method):
372
+ sig = inspect.signature(method)
373
+ arg_type = get_request_arg_type(sig)
374
+ converter = generate_message_converter(arg_type)
375
+ response_type = sig.return_annotation
376
+ size_of_parameters = len(sig.parameters)
377
+
378
+ match size_of_parameters:
379
+ case 1:
380
+
381
+ def stub_method1(self, request, context, method=method):
382
+ try:
383
+ arg = converter(request)
384
+ resp_obj = method(arg)
385
+ return convert_python_message_to_proto(
386
+ resp_obj, response_type, pb2_module
387
+ )
388
+ except ValidationError as e:
389
+ return context.abort(Errors.InvalidArgument, str(e))
390
+ except Exception as e:
391
+ return context.abort(Errors.Internal, str(e))
392
+
393
+ return stub_method1
394
+
395
+ case 2:
396
+
397
+ def stub_method2(self, request, context, method=method):
398
+ try:
399
+ arg = converter(request)
400
+ resp_obj = method(arg, context)
401
+ return convert_python_message_to_proto(
402
+ resp_obj, response_type, pb2_module
403
+ )
404
+ except ValidationError as e:
405
+ return context.abort(Errors.InvalidArgument, str(e))
406
+ except Exception as e:
407
+ return context.abort(Errors.Internal, str(e))
408
+
409
+ return stub_method2
410
+
411
+ case _:
412
+ raise Exception("Method must have exactly one or two parameters")
413
+
414
+ for method_name, method in get_rpc_methods(obj):
415
+ if method.__name__.startswith("_"):
416
+ continue
417
+ a_method = implement_stub_method(method)
418
+ setattr(ConcreteServiceClass, method_name, a_method)
419
+
420
+ return ConcreteServiceClass
421
+
422
+
359
423
  def connect_obj_with_stub_async_connecpy(
360
424
  connecpy_module, pb2_module, obj: object
361
425
  ) -> type:
@@ -1330,14 +1394,62 @@ class ConnecpyASGIApp:
1330
1394
  await self._app(scope, receive, send)
1331
1395
 
1332
1396
 
1333
- if __name__ == "__main__":
1397
+ class ConnecpyWSGIApp:
1334
1398
  """
1335
- If executed as a script, generate the .proto files for a given class.
1336
- Usage: python core.py some_module.py SomeServiceClass
1399
+ A WSGI-compatible application that can serve Connect-RPC via Connecpy's ConnecpyWSGIApp.
1337
1400
  """
1338
- py_file_name = sys.argv[1]
1339
- class_name = sys.argv[2]
1340
- module_name = os.path.splitext(basename(py_file_name))[0]
1401
+
1402
+ def __init__(self):
1403
+ self._app = ConnecpyWSGI()
1404
+ self._service_names = []
1405
+ self._package_name = ""
1406
+
1407
+ def mount(self, obj: object, package_name: str = ""):
1408
+ """Generate and compile proto files, then mount the async service implementation."""
1409
+ connecpy_module, pb2_module = generate_and_compile_proto_using_connecpy(
1410
+ obj, package_name
1411
+ )
1412
+ self.mount_using_pb2_modules(connecpy_module, pb2_module, obj)
1413
+
1414
+ def mount_using_pb2_modules(self, connecpy_module, pb2_module, obj: object):
1415
+ """Connect the compiled connecpy and pb2 modules with the async service implementation."""
1416
+ concreteServiceClass = connect_obj_with_stub_connecpy(
1417
+ connecpy_module, pb2_module, obj
1418
+ )
1419
+ service_name = obj.__class__.__name__
1420
+ service_impl = concreteServiceClass()
1421
+ connecpy_server = get_connecpy_server_class(connecpy_module, service_name)
1422
+ self._app.add_service(connecpy_server(service=service_impl))
1423
+ full_service_name = pb2_module.DESCRIPTOR.services_by_name[
1424
+ service_name
1425
+ ].full_name
1426
+ self._service_names.append(full_service_name)
1427
+
1428
+ def mount_objs(self, *objs):
1429
+ """Mount multiple service objects into this WSGI app."""
1430
+ for obj in objs:
1431
+ self.mount(obj, self._package_name)
1432
+
1433
+ def __call__(self, environ, start_response):
1434
+ """WSGI entry point."""
1435
+ return self._app(environ, start_response)
1436
+
1437
+
1438
+ def main():
1439
+ import argparse
1440
+
1441
+ parser = argparse.ArgumentParser(description="Generate and compile proto files.")
1442
+ parser.add_argument(
1443
+ "py_file", type=str, help="The Python file containing the service class."
1444
+ )
1445
+ parser.add_argument("class_name", type=str, help="The name of the service class.")
1446
+ args = parser.parse_args()
1447
+
1448
+ module_name = os.path.splitext(basename(args.py_file))[0]
1341
1449
  module = importlib.import_module(module_name)
1342
- klass = getattr(module, class_name)
1450
+ klass = getattr(module, args.class_name)
1343
1451
  generate_and_compile_proto(klass())
1452
+
1453
+
1454
+ if __name__ == "__main__":
1455
+ main()
@@ -0,0 +1,33 @@
1
+ syntax = "proto3";
2
+
3
+ package asyncecho.v1;
4
+
5
+ // Echo service.
6
+ // A simple service that echoes messages back in uppercase.
7
+ service AsyncEchoService {
8
+ // Echo the message back in uppercase.
9
+ //
10
+ // Args:
11
+ // request (EchoRequest): The request message.
12
+ //
13
+ // Returns:
14
+ // EchoResponse: The response message.
15
+ rpc Echo (EchoRequest) returns (EchoResponse);
16
+ }
17
+
18
+ // Echo response message.
19
+ //
20
+ // Attributes:
21
+ // text (str): The echoed text.
22
+ message EchoResponse {
23
+ string text = 1;
24
+ }
25
+
26
+ // Echo request message.
27
+ //
28
+ // Attributes:
29
+ // text (str): The text to echo.
30
+ message EchoRequest {
31
+ string text = 1;
32
+ }
33
+
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by https://github.com/i2y/connecpy/protoc-gen-connecpy. DO NOT EDIT!
3
+ # source: asyncechoservice.proto
4
+
5
+ from typing import Any, Protocol, Union
6
+
7
+ import httpx
8
+
9
+ from connecpy.async_client import AsyncConnecpyClient
10
+ from connecpy.base import Endpoint
11
+ from connecpy.server import ConnecpyServer
12
+ from connecpy.client import ConnecpyClient
13
+ from connecpy.context import ClientContext, ServiceContext
14
+
15
+ import asyncechoservice_pb2 as _pb2
16
+
17
+ from google.protobuf import symbol_database
18
+
19
+ _sym_db = symbol_database.Default()
20
+
21
+
22
+ class AsyncEchoService(Protocol):
23
+ async def Echo(self, req: _pb2.EchoRequest, ctx: ServiceContext) -> _pb2.EchoResponse: ...
24
+
25
+
26
+ class AsyncEchoServiceServer(ConnecpyServer):
27
+ def __init__(self, *, service: AsyncEchoService, server_path_prefix=""):
28
+ super().__init__()
29
+ self._prefix = f"{server_path_prefix}/asyncecho.v1.AsyncEchoService"
30
+ self._endpoints = {
31
+ "Echo": Endpoint[_pb2.EchoRequest, _pb2.EchoResponse](
32
+ service_name="AsyncEchoService",
33
+ name="Echo",
34
+ function=getattr(service, "Echo"),
35
+ input=_pb2.EchoRequest,
36
+ output=_pb2.EchoResponse,
37
+ ),
38
+ }
39
+
40
+ def serviceName(self):
41
+ return "asyncecho.v1.AsyncEchoService"
42
+
43
+
44
+ class AsyncEchoServiceSync(Protocol):
45
+ def Echo(self, req: _pb2.EchoRequest, ctx: ServiceContext) -> _pb2.EchoResponse: ...
46
+
47
+
48
+ class AsyncEchoServiceServerSync(ConnecpyServer):
49
+ def __init__(self, *, service: AsyncEchoServiceSync, server_path_prefix=""):
50
+ super().__init__()
51
+ self._prefix = f"{server_path_prefix}/asyncecho.v1.AsyncEchoService"
52
+ self._endpoints = {
53
+ "Echo": Endpoint[_pb2.EchoRequest, _pb2.EchoResponse](
54
+ service_name="AsyncEchoService",
55
+ name="Echo",
56
+ function=getattr(service, "Echo"),
57
+ input=_pb2.EchoRequest,
58
+ output=_pb2.EchoResponse,
59
+ ),
60
+ }
61
+
62
+ def serviceName(self):
63
+ return "asyncecho.v1.AsyncEchoService"
64
+
65
+
66
+ class AsyncEchoServiceClient(ConnecpyClient):
67
+ def Echo(
68
+ self,
69
+ *,
70
+ request: _pb2.EchoRequest,
71
+ ctx: ClientContext,
72
+ server_path_prefix: str = "",
73
+ **kwargs,
74
+ ) -> _pb2.EchoResponse:
75
+ return self._make_request(
76
+ url=f"{server_path_prefix}/asyncecho.v1.AsyncEchoService/Echo",
77
+ ctx=ctx,
78
+ request=request,
79
+ response_obj=_pb2.EchoResponse,
80
+ **kwargs,
81
+ )
82
+
83
+
84
+ class AsyncAsyncEchoServiceClient(AsyncConnecpyClient):
85
+ async def Echo(
86
+ self,
87
+ *,
88
+ request: _pb2.EchoRequest,
89
+ ctx: ClientContext,
90
+ server_path_prefix: str = "",
91
+ session: Union[httpx.AsyncClient, None] = None,
92
+ **kwargs,
93
+ ) -> _pb2.EchoResponse:
94
+ return await self._make_request(
95
+ url=f"{server_path_prefix}/asyncecho.v1.AsyncEchoService/Echo",
96
+ ctx=ctx,
97
+ request=request,
98
+ response_obj=_pb2.EchoResponse,
99
+ session=session,
100
+ **kwargs,
101
+ )
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: asyncechoservice.proto
5
+ # Protobuf Python Version: 5.29.0
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 5,
15
+ 29,
16
+ 0,
17
+ '',
18
+ 'asyncechoservice.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61syncechoservice.proto\x12\x0c\x61syncecho.v1\"\x1c\n\x0c\x45\x63hoResponse\x12\x0c\n\x04text\x18\x01 \x01(\t\"\x1b\n\x0b\x45\x63hoRequest\x12\x0c\n\x04text\x18\x01 \x01(\t2Q\n\x10\x41syncEchoService\x12=\n\x04\x45\x63ho\x12\x19.asyncecho.v1.EchoRequest\x1a\x1a.asyncecho.v1.EchoResponseb\x06proto3')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'asyncechoservice_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ DESCRIPTOR._loaded_options = None
34
+ _globals['_ECHORESPONSE']._serialized_start=40
35
+ _globals['_ECHORESPONSE']._serialized_end=68
36
+ _globals['_ECHOREQUEST']._serialized_start=70
37
+ _globals['_ECHOREQUEST']._serialized_end=97
38
+ _globals['_ASYNCECHOSERVICE']._serialized_start=99
39
+ _globals['_ASYNCECHOSERVICE']._serialized_end=180
40
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,17 @@
1
+ from google.protobuf import descriptor as _descriptor
2
+ from google.protobuf import message as _message
3
+ from typing import ClassVar as _ClassVar, Optional as _Optional
4
+
5
+ DESCRIPTOR: _descriptor.FileDescriptor
6
+
7
+ class EchoResponse(_message.Message):
8
+ __slots__ = ("text",)
9
+ TEXT_FIELD_NUMBER: _ClassVar[int]
10
+ text: str
11
+ def __init__(self, text: _Optional[str] = ...) -> None: ...
12
+
13
+ class EchoRequest(_message.Message):
14
+ __slots__ = ("text",)
15
+ TEXT_FIELD_NUMBER: _ClassVar[int]
16
+ text: str
17
+ def __init__(self, text: _Optional[str] = ...) -> None: ...
@@ -0,0 +1,110 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+ import warnings
5
+
6
+ import asyncechoservice_pb2 as asyncechoservice__pb2
7
+
8
+ GRPC_GENERATED_VERSION = '1.70.0'
9
+ GRPC_VERSION = grpc.__version__
10
+ _version_not_supported = False
11
+
12
+ try:
13
+ from grpc._utilities import first_version_is_lower
14
+ _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
15
+ except ImportError:
16
+ _version_not_supported = True
17
+
18
+ if _version_not_supported:
19
+ raise RuntimeError(
20
+ f'The grpc package installed is at version {GRPC_VERSION},'
21
+ + f' but the generated code in asyncechoservice_pb2_grpc.py depends on'
22
+ + f' grpcio>={GRPC_GENERATED_VERSION}.'
23
+ + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
24
+ + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
25
+ )
26
+
27
+
28
+ class AsyncEchoServiceStub(object):
29
+ """Echo service.
30
+ A simple service that echoes messages back in uppercase.
31
+ """
32
+
33
+ def __init__(self, channel):
34
+ """Constructor.
35
+
36
+ Args:
37
+ channel: A grpc.Channel.
38
+ """
39
+ self.Echo = channel.unary_unary(
40
+ '/asyncecho.v1.AsyncEchoService/Echo',
41
+ request_serializer=asyncechoservice__pb2.EchoRequest.SerializeToString,
42
+ response_deserializer=asyncechoservice__pb2.EchoResponse.FromString,
43
+ _registered_method=True)
44
+
45
+
46
+ class AsyncEchoServiceServicer(object):
47
+ """Echo service.
48
+ A simple service that echoes messages back in uppercase.
49
+ """
50
+
51
+ def Echo(self, request, context):
52
+ """Echo the message back in uppercase.
53
+
54
+ Args:
55
+ request (EchoRequest): The request message.
56
+
57
+ Returns:
58
+ EchoResponse: The response message.
59
+ """
60
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
61
+ context.set_details('Method not implemented!')
62
+ raise NotImplementedError('Method not implemented!')
63
+
64
+
65
+ def add_AsyncEchoServiceServicer_to_server(servicer, server):
66
+ rpc_method_handlers = {
67
+ 'Echo': grpc.unary_unary_rpc_method_handler(
68
+ servicer.Echo,
69
+ request_deserializer=asyncechoservice__pb2.EchoRequest.FromString,
70
+ response_serializer=asyncechoservice__pb2.EchoResponse.SerializeToString,
71
+ ),
72
+ }
73
+ generic_handler = grpc.method_handlers_generic_handler(
74
+ 'asyncecho.v1.AsyncEchoService', rpc_method_handlers)
75
+ server.add_generic_rpc_handlers((generic_handler,))
76
+ server.add_registered_method_handlers('asyncecho.v1.AsyncEchoService', rpc_method_handlers)
77
+
78
+
79
+ # This class is part of an EXPERIMENTAL API.
80
+ class AsyncEchoService(object):
81
+ """Echo service.
82
+ A simple service that echoes messages back in uppercase.
83
+ """
84
+
85
+ @staticmethod
86
+ def Echo(request,
87
+ target,
88
+ options=(),
89
+ channel_credentials=None,
90
+ call_credentials=None,
91
+ insecure=False,
92
+ compression=None,
93
+ wait_for_ready=None,
94
+ timeout=None,
95
+ metadata=None):
96
+ return grpc.experimental.unary_unary(
97
+ request,
98
+ target,
99
+ '/asyncecho.v1.AsyncEchoService/Echo',
100
+ asyncechoservice__pb2.EchoRequest.SerializeToString,
101
+ asyncechoservice__pb2.EchoResponse.FromString,
102
+ options,
103
+ channel_credentials,
104
+ insecure,
105
+ call_credentials,
106
+ compression,
107
+ wait_for_ready,
108
+ timeout,
109
+ metadata,
110
+ _registered_method=True)