Skip to content

Commit 86c8e66

Browse files
authored
Update pydantic converter (#31)
1 parent caa6691 commit 86c8e66

File tree

2 files changed

+80
-26
lines changed

2 files changed

+80
-26
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "temporal-boost"
3-
version = "1.1.0"
3+
version = "1.2.0"
44
description = "Small framework for Temporal development"
55
authors = ["northpowered <[email protected]>"]
66
readme = "README.md"
Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,107 @@
1-
import json
2-
from typing import Any
1+
"""A data converter for Pydantic v2.
32
4-
from temporalio.api.common.v1 import Payload
3+
To use, pass ``pydantic_data_converter`` as the ``data_converter`` argument to
4+
:py:class:`temporalio.client.Client`:
5+
6+
.. code-block:: python
7+
8+
client = Client(
9+
data_converter=pydantic_data_converter,
10+
...
11+
)
12+
13+
Pydantic v1 is not supported.
14+
"""
15+
16+
from typing import Any, Optional, Type
17+
18+
import temporalio.api.common.v1
19+
from pydantic import TypeAdapter
20+
from pydantic_core import to_json
521
from temporalio.converter import (
622
CompositePayloadConverter,
723
DataConverter,
824
DefaultPayloadConverter,
25+
EncodingPayloadConverter,
926
JSONPlainPayloadConverter,
1027
)
1128

12-
try: # noqa: SIM105
13-
from pydantic.json import pydantic_encoder
14-
except ImportError:
15-
pass
29+
# Note that in addition to the implementation in this module, _RestrictedProxy
30+
# implements __get_pydantic_core_schema__ so that pydantic unwraps proxied types.
1631

1732

18-
class PydanticJSONPayloadConverter(JSONPlainPayloadConverter):
33+
class PydanticJSONPlainPayloadConverter(EncodingPayloadConverter):
1934
"""Pydantic JSON payload converter.
2035
21-
This extends the :py:class:`JSONPlainPayloadConverter` to override
22-
:py:meth:`to_payload` using the Pydantic encoder.
36+
Supports conversion of all types supported by Pydantic to and from JSON.
37+
38+
In addition to Pydantic models, these include all `json.dump`-able types,
39+
various non-`json.dump`-able standard library types such as dataclasses,
40+
types from the datetime module, sets, UUID, etc, and custom types composed
41+
of any of these.
42+
43+
See https://docs.pydantic.dev/latest/api/standard_library_types/
2344
"""
2445

25-
def to_payload(self, value: Any) -> Payload | None:
26-
"""Convert all values with Pydantic encoder or fail.
46+
@property
47+
def encoding(self) -> str:
48+
"""See base class."""
49+
return "json/plain"
50+
51+
def to_payload(self, value: Any) -> Optional[temporalio.api.common.v1.Payload]:
52+
"""See base class.
53+
54+
Uses ``pydantic_core.to_json`` to serialize ``value`` to JSON.
2755
28-
Like the base class, we fail if we cannot convert. This payload
29-
converter is expected to be the last in the chain, so it can fail if
30-
unable to convert.
56+
See
57+
https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.to_json.
3158
"""
32-
# We let JSON conversion errors be thrown to caller
33-
return Payload(
34-
metadata={"encoding": self.encoding.encode()},
35-
data=json.dumps(value, separators=(",", ":"), sort_keys=True, default=pydantic_encoder).encode(),
36-
)
59+
return temporalio.api.common.v1.Payload(metadata={"encoding": self.encoding.encode()}, data=to_json(value))
60+
61+
def from_payload(
62+
self,
63+
payload: temporalio.api.common.v1.Payload,
64+
type_hint: Optional[Type] = None,
65+
) -> Any:
66+
"""See base class.
67+
68+
Uses ``pydantic.TypeAdapter.validate_json`` to construct an
69+
instance of the type specified by ``type_hint`` from the JSON payload.
70+
71+
See
72+
https://docs.pydantic.dev/latest/api/type_adapter/#pydantic.type_adapter.TypeAdapter.validate_json.
73+
"""
74+
_type_hint = type_hint if type_hint is not None else Any
75+
return TypeAdapter(_type_hint).validate_json(payload.data)
3776

3877

3978
class PydanticPayloadConverter(CompositePayloadConverter):
40-
"""Payload converter that replaces Temporal JSON conversion with Pydantic
41-
JSON conversion.
79+
"""Payload converter for payloads containing pydantic model instances.
80+
81+
JSON conversion is replaced with a converter that uses
82+
:py:class:`PydanticJSONPlainPayloadConverter`.
4283
"""
4384

4485
def __init__(self) -> None:
86+
"""Initialize object"""
87+
json_payload_converter = PydanticJSONPlainPayloadConverter()
4588
super().__init__(
4689
*(
47-
converter if not isinstance(converter, JSONPlainPayloadConverter) else PydanticJSONPayloadConverter()
48-
for converter in DefaultPayloadConverter.default_encoding_payload_converters
49-
),
90+
c if not isinstance(c, JSONPlainPayloadConverter) else json_payload_converter
91+
for c in DefaultPayloadConverter.default_encoding_payload_converters
92+
)
5093
)
5194

5295

5396
pydantic_data_converter = DataConverter(payload_converter_class=PydanticPayloadConverter)
97+
"""Pydantic data converter.
98+
99+
Supports conversion of all types supported by Pydantic to and from JSON.
100+
101+
In addition to Pydantic models, these include all `json.dump`-able types,
102+
various non-`json.dump`-able standard library types such as dataclasses,
103+
types from the datetime module, sets, UUID, etc, and custom types composed
104+
of any of these.
105+
106+
To use, pass as the ``data_converter`` argument of :py:class:`temporalio.client.Client`
107+
"""

0 commit comments

Comments
 (0)