pydantic-rpc 0.3.0__tar.gz → 0.4.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 (53) hide show
  1. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/PKG-INFO +29 -38
  2. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/README.md +28 -37
  3. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/agent_aio_grpc.py +10 -29
  4. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/agent_connecpy.py +10 -14
  5. pydantic_rpc-0.4.0/examples/barservice.proto +17 -0
  6. pydantic_rpc-0.4.0/examples/fooservice.proto +21 -0
  7. pydantic_rpc-0.4.0/examples/olympicsagent.proto +40 -0
  8. pydantic_rpc-0.4.0/examples/olympicslocationagent.proto +24 -0
  9. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/pyproject.toml +1 -1
  10. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/src/pydantic_rpc/core.py +1343 -1304
  11. pydantic_rpc-0.3.0/examples/barservice.proto +0 -75
  12. pydantic_rpc-0.3.0/examples/fooservice.proto +0 -79
  13. pydantic_rpc-0.3.0/examples/olympicsagent.proto +0 -144
  14. pydantic_rpc-0.3.0/examples/olympicslocationagent.proto +0 -76
  15. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/.gitignore +0 -0
  16. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/.python-version +0 -0
  17. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/LICENSE +0 -0
  18. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/README.md +0 -0
  19. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/asyncio_greeting.py +0 -0
  20. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/barservice_pb2.py +0 -0
  21. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/barservice_pb2.pyi +0 -0
  22. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/barservice_pb2_grpc.py +0 -0
  23. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/foobar.py +0 -0
  24. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/foobar_client.py +0 -0
  25. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/fooservice_pb2.py +0 -0
  26. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/fooservice_pb2.pyi +0 -0
  27. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/fooservice_pb2_grpc.py +0 -0
  28. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/google/protobuf/duration.proto +0 -0
  29. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/google/protobuf/timestamp.proto +0 -0
  30. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter.proto +0 -0
  31. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter_client.py +0 -0
  32. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter_connecpy.py +0 -0
  33. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter_connecpy_client.py +0 -0
  34. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter_pb2.py +0 -0
  35. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter_pb2.pyi +0 -0
  36. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter_pb2_grpc.py +0 -0
  37. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeter_sonora_client.py +0 -0
  38. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeting.py +0 -0
  39. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeting_asgi.py +0 -0
  40. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeting_connecpy.py +0 -0
  41. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeting_using_exsiting_pb2_modules.py +0 -0
  42. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/greeting_wsgi.py +0 -0
  43. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/olympicsagent_pb2.py +0 -0
  44. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/olympicsagent_pb2.pyi +0 -0
  45. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/olympicsagent_pb2_grpc.py +0 -0
  46. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/olympicslocationagent_connecpy.py +0 -0
  47. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/olympicslocationagent_pb2.py +0 -0
  48. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/olympicslocationagent_pb2.pyi +0 -0
  49. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/examples/olympicslocationagent_pb2_grpc.py +0 -0
  50. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/requirements-dev.lock +0 -0
  51. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/requirements.lock +0 -0
  52. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/src/pydantic_rpc/__init__.py +0 -0
  53. {pydantic_rpc-0.3.0 → pydantic_rpc-0.4.0}/src/pydantic_rpc/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pydantic-rpc
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: A Python library for building gRPC/ConnectRPC services with Pydantic models.
5
5
  Author: Yasushi Itoh
6
6
  Requires-Python: >=3.11
@@ -94,6 +94,7 @@ app.mount(OlympicsLocationAgent())
94
94
  - 🔄 **Automatic Protobuf Generation:** Automatically creates protobuf files matching the method signatures of your Python objects.
95
95
  - ⚙️ **Dynamic Code Generation:** Generates server and client stubs using `grpcio-tools`.
96
96
  - ✅ **Pydantic Integration:** Uses `pydantic` for robust type validation and serialization.
97
+ - 📄 **Pprotobuf File Export:** Exports the generated protobuf files for use in other languages.
97
98
  - **For gRPC:**
98
99
  - 💚 **Health Checking:** Built-in support for gRPC health checks using `grpc_health.v1`.
99
100
  - 🔎 **Server Reflection:** Built-in support for gRPC server reflection.
@@ -261,57 +262,38 @@ Please see the sample code below:
261
262
 
262
263
  ```python
263
264
  import asyncio
264
- from typing import AsyncIterator
265
+ from typing import Annotated, AsyncIterator
265
266
 
266
- from pydantic import field_validator
267
+ from pydantic import Field
267
268
  from pydantic_ai import Agent
268
269
  from pydantic_rpc import AsyncIOServer, Message
269
270
 
270
271
 
271
272
  # `Message` is just a pydantic BaseModel alias
272
273
  class CityLocation(Message):
273
- city: str
274
- country: str
274
+ city: Annotated[str, Field(description="The city where the Olympics were held")]
275
+ country: Annotated[
276
+ str, Field(description="The country where the Olympics were held")
277
+ ]
275
278
 
276
279
 
277
280
  class OlympicsQuery(Message):
278
- year: int
281
+ year: Annotated[int, Field(description="The year of the Olympics", ge=1896)]
279
282
 
280
283
  def prompt(self):
281
284
  return f"Where were the Olympics held in {self.year}?"
282
285
 
283
- @field_validator("year")
284
- def validate_year(cls, value):
285
- if value < 1896:
286
- raise ValueError("The first modern Olympics was held in 1896.")
287
-
288
- return value
289
-
290
286
 
291
287
  class OlympicsDurationQuery(Message):
292
- start: int
293
- end: int
288
+ start: Annotated[int, Field(description="The start year of the Olympics", ge=1896)]
289
+ end: Annotated[int, Field(description="The end year of the Olympics", ge=1896)]
294
290
 
295
291
  def prompt(self):
296
292
  return f"From {self.start} to {self.end}, how many Olympics were held? Please provide the list of countries and cities."
297
293
 
298
- @field_validator("start")
299
- def validate_start(cls, value):
300
- if value < 1896:
301
- raise ValueError("The first modern Olympics was held in 1896.")
302
-
303
- return value
304
-
305
- @field_validator("end")
306
- def validate_end(cls, value):
307
- if value < 1896:
308
- raise ValueError("The first modern Olympics was held in 1896.")
309
-
310
- return value
311
-
312
294
 
313
295
  class StreamingResult(Message):
314
- answer: str
296
+ answer: Annotated[str, Field(description="The answer to the query")]
315
297
 
316
298
 
317
299
  class OlympicsAgent:
@@ -433,18 +415,27 @@ Using this generated proto file and tools as `protoc`, `buf` and `BSR`, you coul
433
415
 
434
416
  ## 📖 Data Type Mapping
435
417
 
436
- | Python Type | Protobuf Type |
437
- |--------------------|-----------------|
438
- | str | string |
439
- | bool | bool |
440
- | int | int32, int64 |
441
- | float | float, double |
442
- | list[T], tuple[T] | repeated T |
443
- | dict[K, V] | map<K, V> |
418
+ | Python Type | Protobuf Type |
419
+ |--------------------------------|---------------------------|
420
+ | str | string |
421
+ | bytes | bytes |
422
+ | bool | bool |
423
+ | int | int32 |
424
+ | float | float, double |
425
+ | list[T], tuple[T] | repeated T |
426
+ | dict[K, V] | map<K, V> |
427
+ | datetime.datetime | google.protobuf.Timestamp |
428
+ | datetime.timedelta | google.protobuf.Duration |
429
+ | typing.Union[A, B] | oneof A, B |
430
+ | subclass of enum.Enum | enum |
431
+ | subclass of pydantic.BaseModel | message |
444
432
 
445
433
 
446
434
  ## TODO
447
435
  - [ ] Streaming Support
436
+ - [x] unary-stream
437
+ - [ ] stream-unary
438
+ - [ ] stream-stream
448
439
  - [ ] Betterproto Support
449
440
  - [ ] Sonora-connect Support
450
441
  - [ ] Custom Health Check Support
@@ -80,6 +80,7 @@ app.mount(OlympicsLocationAgent())
80
80
  - 🔄 **Automatic Protobuf Generation:** Automatically creates protobuf files matching the method signatures of your Python objects.
81
81
  - ⚙️ **Dynamic Code Generation:** Generates server and client stubs using `grpcio-tools`.
82
82
  - ✅ **Pydantic Integration:** Uses `pydantic` for robust type validation and serialization.
83
+ - 📄 **Pprotobuf File Export:** Exports the generated protobuf files for use in other languages.
83
84
  - **For gRPC:**
84
85
  - 💚 **Health Checking:** Built-in support for gRPC health checks using `grpc_health.v1`.
85
86
  - 🔎 **Server Reflection:** Built-in support for gRPC server reflection.
@@ -247,57 +248,38 @@ Please see the sample code below:
247
248
 
248
249
  ```python
249
250
  import asyncio
250
- from typing import AsyncIterator
251
+ from typing import Annotated, AsyncIterator
251
252
 
252
- from pydantic import field_validator
253
+ from pydantic import Field
253
254
  from pydantic_ai import Agent
254
255
  from pydantic_rpc import AsyncIOServer, Message
255
256
 
256
257
 
257
258
  # `Message` is just a pydantic BaseModel alias
258
259
  class CityLocation(Message):
259
- city: str
260
- country: str
260
+ city: Annotated[str, Field(description="The city where the Olympics were held")]
261
+ country: Annotated[
262
+ str, Field(description="The country where the Olympics were held")
263
+ ]
261
264
 
262
265
 
263
266
  class OlympicsQuery(Message):
264
- year: int
267
+ year: Annotated[int, Field(description="The year of the Olympics", ge=1896)]
265
268
 
266
269
  def prompt(self):
267
270
  return f"Where were the Olympics held in {self.year}?"
268
271
 
269
- @field_validator("year")
270
- def validate_year(cls, value):
271
- if value < 1896:
272
- raise ValueError("The first modern Olympics was held in 1896.")
273
-
274
- return value
275
-
276
272
 
277
273
  class OlympicsDurationQuery(Message):
278
- start: int
279
- end: int
274
+ start: Annotated[int, Field(description="The start year of the Olympics", ge=1896)]
275
+ end: Annotated[int, Field(description="The end year of the Olympics", ge=1896)]
280
276
 
281
277
  def prompt(self):
282
278
  return f"From {self.start} to {self.end}, how many Olympics were held? Please provide the list of countries and cities."
283
279
 
284
- @field_validator("start")
285
- def validate_start(cls, value):
286
- if value < 1896:
287
- raise ValueError("The first modern Olympics was held in 1896.")
288
-
289
- return value
290
-
291
- @field_validator("end")
292
- def validate_end(cls, value):
293
- if value < 1896:
294
- raise ValueError("The first modern Olympics was held in 1896.")
295
-
296
- return value
297
-
298
280
 
299
281
  class StreamingResult(Message):
300
- answer: str
282
+ answer: Annotated[str, Field(description="The answer to the query")]
301
283
 
302
284
 
303
285
  class OlympicsAgent:
@@ -419,18 +401,27 @@ Using this generated proto file and tools as `protoc`, `buf` and `BSR`, you coul
419
401
 
420
402
  ## 📖 Data Type Mapping
421
403
 
422
- | Python Type | Protobuf Type |
423
- |--------------------|-----------------|
424
- | str | string |
425
- | bool | bool |
426
- | int | int32, int64 |
427
- | float | float, double |
428
- | list[T], tuple[T] | repeated T |
429
- | dict[K, V] | map<K, V> |
404
+ | Python Type | Protobuf Type |
405
+ |--------------------------------|---------------------------|
406
+ | str | string |
407
+ | bytes | bytes |
408
+ | bool | bool |
409
+ | int | int32 |
410
+ | float | float, double |
411
+ | list[T], tuple[T] | repeated T |
412
+ | dict[K, V] | map<K, V> |
413
+ | datetime.datetime | google.protobuf.Timestamp |
414
+ | datetime.timedelta | google.protobuf.Duration |
415
+ | typing.Union[A, B] | oneof A, B |
416
+ | subclass of enum.Enum | enum |
417
+ | subclass of pydantic.BaseModel | message |
430
418
 
431
419
 
432
420
  ## TODO
433
421
  - [ ] Streaming Support
422
+ - [x] unary-stream
423
+ - [ ] stream-unary
424
+ - [ ] stream-stream
434
425
  - [ ] Betterproto Support
435
426
  - [ ] Sonora-connect Support
436
427
  - [ ] Custom Health Check Support
@@ -1,55 +1,36 @@
1
1
  import asyncio
2
- from typing import AsyncIterator
2
+ from typing import Annotated, AsyncIterator
3
3
 
4
- from pydantic import field_validator
4
+ from pydantic import Field
5
5
  from pydantic_ai import Agent
6
6
  from pydantic_rpc import AsyncIOServer, Message
7
7
 
8
8
 
9
9
  # `Message` is just a pydantic BaseModel alias
10
10
  class CityLocation(Message):
11
- city: str
12
- country: str
11
+ city: Annotated[str, Field(description="The city where the Olympics were held")]
12
+ country: Annotated[
13
+ str, Field(description="The country where the Olympics were held")
14
+ ]
13
15
 
14
16
 
15
17
  class OlympicsQuery(Message):
16
- year: int
18
+ year: Annotated[int, Field(description="The year of the Olympics", ge=1896)]
17
19
 
18
20
  def prompt(self):
19
21
  return f"Where were the Olympics held in {self.year}?"
20
22
 
21
- @field_validator("year")
22
- def validate_year(cls, value):
23
- if value < 1896:
24
- raise ValueError("The first modern Olympics was held in 1896.")
25
-
26
- return value
27
-
28
23
 
29
24
  class OlympicsDurationQuery(Message):
30
- start: int
31
- end: int
25
+ start: Annotated[int, Field(description="The start year of the Olympics", ge=1896)]
26
+ end: Annotated[int, Field(description="The end year of the Olympics", ge=1896)]
32
27
 
33
28
  def prompt(self):
34
29
  return f"From {self.start} to {self.end}, how many Olympics were held? Please provide the list of countries and cities."
35
30
 
36
- @field_validator("start")
37
- def validate_start(cls, value):
38
- if value < 1896:
39
- raise ValueError("The first modern Olympics was held in 1896.")
40
-
41
- return value
42
-
43
- @field_validator("end")
44
- def validate_end(cls, value):
45
- if value < 1896:
46
- raise ValueError("The first modern Olympics was held in 1896.")
47
-
48
- return value
49
-
50
31
 
51
32
  class StreamingResult(Message):
52
- answer: str
33
+ answer: Annotated[str, Field(description="The answer to the query")]
53
34
 
54
35
 
55
36
  class OlympicsAgent:
@@ -1,33 +1,29 @@
1
1
  import asyncio
2
2
 
3
+ from typing import Annotated
4
+
3
5
  from hypercorn.asyncio import serve
4
6
  from hypercorn.config import Config
5
- from pydantic import field_validator
7
+ from pydantic import Field
6
8
  from pydantic_ai import Agent
7
9
  from pydantic_rpc import ConnecpyASGIApp, Message
8
10
 
9
11
 
10
12
  class CityLocation(Message):
11
- city: str
12
- country: str
13
+ city: Annotated[str, Field(description="The city where the Olympics were held")]
14
+ country: Annotated[
15
+ str, Field(description="The country where the Olympics were held")
16
+ ]
13
17
 
14
18
 
15
19
  class Olympics(Message):
16
- year: int
20
+ year: Annotated[
21
+ int, Field(description="The year of the Olympics", ge=1896, multiple_of=4)
22
+ ]
17
23
 
18
24
  def prompt(self):
19
25
  return f"Where were the Olympics held in {self.year}?"
20
26
 
21
- @field_validator("year")
22
- def validate_year(cls, value):
23
- if value < 1896:
24
- raise ValueError("The first modern Olympics was held in 1896.")
25
-
26
- if value % 4 != 0:
27
- raise ValueError("The Olympics are held every 4 years.")
28
-
29
- return value
30
-
31
27
 
32
28
  class OlympicsLocationAgent:
33
29
  def __init__(self):
@@ -0,0 +1,17 @@
1
+ syntax = "proto3";
2
+
3
+ package bar.v1;
4
+
5
+
6
+ service BarService {
7
+ rpc Bar (BarRequest) returns (BarResponse);
8
+ }
9
+
10
+ message BarResponse {
11
+ repeated string names = 1;
12
+ }
13
+
14
+ message BarRequest {
15
+ repeated string names = 1;
16
+ }
17
+
@@ -0,0 +1,21 @@
1
+ syntax = "proto3";
2
+
3
+ package foo.v1;
4
+
5
+
6
+ service FooService {
7
+ rpc Foo (FooRequest) returns (FooResponse);
8
+ }
9
+
10
+ message FooResponse {
11
+ string name = 1;
12
+ int32 age = 2;
13
+ map<string, string> d = 3;
14
+ }
15
+
16
+ message FooRequest {
17
+ string name = 1;
18
+ int32 age = 2;
19
+ map<string, string> d = 3;
20
+ }
21
+
@@ -0,0 +1,40 @@
1
+ syntax = "proto3";
2
+
3
+ package olympicsagent.v1;
4
+
5
+
6
+ service OlympicsAgent {
7
+ rpc Ask (OlympicsQuery) returns (CityLocation);
8
+ rpc AskStream (OlympicsDurationQuery) returns (stream StreamingResult);
9
+ }
10
+
11
+ message CityLocation {
12
+ // The city where the Olympics were held
13
+ string city = 1;
14
+ // The country where the Olympics were held
15
+ string country = 2;
16
+ }
17
+
18
+ message OlympicsQuery {
19
+ // The year of the Olympics
20
+ // Constraint:
21
+ // greater than or equal to 1896
22
+ int32 year = 1;
23
+ }
24
+
25
+ message StreamingResult {
26
+ // The answer to the query
27
+ string answer = 1;
28
+ }
29
+
30
+ message OlympicsDurationQuery {
31
+ // The start year of the Olympics
32
+ // Constraint:
33
+ // greater than or equal to 1896
34
+ int32 start = 1;
35
+ // The end year of the Olympics
36
+ // Constraint:
37
+ // greater than or equal to 1896
38
+ int32 end = 2;
39
+ }
40
+
@@ -0,0 +1,24 @@
1
+ syntax = "proto3";
2
+
3
+ package olympicslocationagent.v1;
4
+
5
+
6
+ service OlympicsLocationAgent {
7
+ rpc Ask (Olympics) returns (CityLocation);
8
+ }
9
+
10
+ message CityLocation {
11
+ // The city where the Olympics were held
12
+ string city = 1;
13
+ // The country where the Olympics were held
14
+ string country = 2;
15
+ }
16
+
17
+ message Olympics {
18
+ // The year of the Olympics
19
+ // Constraint:
20
+ // greater than or equal to 1896
21
+ // multiple of 4
22
+ int32 year = 1;
23
+ }
24
+
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pydantic-rpc"
3
- version = "0.3.0"
3
+ version = "0.4.0"
4
4
  description = "A Python library for building gRPC/ConnectRPC services with Pydantic models."
5
5
  authors = [
6
6
  { name = "Yasushi Itoh" }