1212"""
1313
1414import inspect
15- import json
1615from typing import (
1716 Any ,
18- Optional ,
1917 Type ,
2018)
2119
2220import pydantic
2321
22+ try :
23+ from pydantic_core import to_jsonable_python
24+ except ImportError :
25+ # pydantic v1
26+ from pydantic .json import pydantic_encoder as to_jsonable_python
27+
2428import temporalio .workflow
25- from temporalio .api .common .v1 import Payload
2629from temporalio .converter import (
30+ AdvancedJSONEncoder ,
2731 CompositePayloadConverter ,
2832 DataConverter ,
2933 DefaultPayloadConverter ,
3236)
3337from temporalio .worker .workflow_sandbox ._restrictions import RestrictionContext
3438
35- try :
36- from pydantic_core import to_jsonable_python
37- except ImportError :
38- from pydantic .json import pydantic_encoder as to_jsonable_python
3939
40-
41- class _PydanticModelTypeConverter (JSONTypeConverter ):
40+ class PydanticModelTypeConverter (JSONTypeConverter ):
4241 def to_typed_value (self , hint : Type , value : Any ) -> Any :
4342 if not inspect .isclass (hint ) or not issubclass (hint , pydantic .BaseModel ):
4443 return JSONTypeConverter .Unhandled
@@ -59,55 +58,33 @@ def to_typed_value(self, hint: Type, value: Any) -> Any:
5958 if hasattr (model , "model_validate" ):
6059 return model .model_validate (value )
6160 elif hasattr (model , "parse_obj" ):
62- # Pydantic v1
61+ # pydantic v1
6362 return model .parse_obj (value )
6463 else :
6564 raise ValueError (
6665 f"{ model } is a Pydantic model but does not have a `model_validate` or `parse_obj` method"
6766 )
6867
6968
70- class _PydanticJSONPayloadConverter (JSONPlainPayloadConverter ):
71- """Pydantic JSON payload converter.
72-
73- Conversion to JSON is implemented by overriding :py:meth:`to_payload` to use the
74- Pydantic encoder.
75-
76- Conversion from JSON uses the parent implementation of :py:meth:`from_payload`, with a
77- custom type converter. The parent implementation of :py:meth:`from_payload` traverses
78- the JSON document according to the structure specified by the type annotation; the
79- custom type converter ensures that, during this traversal, Pydantic model instances
80- will be created as specified by the type annotation.
81- """
82-
83- def __init__ (self ) -> None :
84- super ().__init__ (custom_type_converters = [_PydanticModelTypeConverter ()])
85-
86- def to_payload (self , value : Any ) -> Optional [Payload ]:
87- """Convert all values with Pydantic encoder or fail.
88-
89- Like the base class, we fail if we cannot convert. This payload
90- converter is expected to be the last in the chain, so it can fail if
91- unable to convert.
92- """
93- # Let JSON conversion errors be thrown to caller
94- return Payload (
95- metadata = {"encoding" : self .encoding .encode ()},
96- data = json .dumps (
97- value , separators = ("," , ":" ), sort_keys = True , default = to_jsonable_python
98- ).encode (),
99- )
69+ class PydanticJSONEncoder (AdvancedJSONEncoder ):
70+ def default (self , o : Any ) -> Any :
71+ if isinstance (o , pydantic .BaseModel ):
72+ return to_jsonable_python (o )
73+ return super ().default (o )
10074
10175
102- class _PydanticPayloadConverter (CompositePayloadConverter ):
76+ class PydanticPayloadConverter (CompositePayloadConverter ):
10377 """Pydantic payload converter.
10478
10579 Payload converter that replaces the default JSON conversion with Pydantic
10680 JSON conversion.
10781 """
10882
10983 def __init__ (self ) -> None :
110- json_payload_converter = _PydanticJSONPayloadConverter ()
84+ json_payload_converter = JSONPlainPayloadConverter (
85+ encoder = PydanticJSONEncoder ,
86+ custom_type_converters = [PydanticModelTypeConverter ()],
87+ )
11188 super ().__init__ (
11289 * (
11390 c
@@ -119,7 +96,7 @@ def __init__(self) -> None:
11996
12097
12198pydantic_data_converter = DataConverter (
122- payload_converter_class = _PydanticPayloadConverter
99+ payload_converter_class = PydanticPayloadConverter
123100)
124101"""Data converter for Pydantic models.
125102
0 commit comments