Skip to content

Commit a38aaff

Browse files
committed
Move conversion into the json converter to allow nested datetimes
1 parent e23d92d commit a38aaff

2 files changed

Lines changed: 36 additions & 32 deletions

File tree

temporalio/converter.py

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -485,35 +485,6 @@ def from_payload(
485485
raise RuntimeError("Failed parsing") from err
486486

487487

488-
class ISO8601DatetimePayloadConverter(EncodingPayloadConverter):
489-
"""Converter for 'binary/iso8601' payloads supporting datetime values."""
490-
491-
@property
492-
def encoding(self) -> str:
493-
"""See base class."""
494-
return "binary/iso8601"
495-
496-
def to_payload(self, value: Any) -> Optional[temporalio.api.common.v1.Payload]:
497-
"""See base class."""
498-
if isinstance(value, datetime):
499-
return temporalio.api.common.v1.Payload(
500-
metadata={"encoding": self.encoding.encode()},
501-
data=value.isoformat().encode(),
502-
)
503-
return None
504-
505-
def from_payload(
506-
self,
507-
payload: temporalio.api.common.v1.Payload,
508-
type_hint: Optional[Type] = None,
509-
) -> Any:
510-
"""See base class."""
511-
try:
512-
return datetime.fromisoformat(payload.data.decode())
513-
except ValueError as err:
514-
raise RuntimeError("Failed parsing ISO8601 datetime") from err
515-
516-
517488
class AdvancedJSONEncoder(json.JSONEncoder):
518489
"""Advanced JSON encoder.
519490
@@ -529,6 +500,9 @@ def default(self, o: Any) -> Any:
529500
530501
See :py:meth:`json.JSONEncoder.default`.
531502
"""
503+
# Datetime support
504+
if isinstance(o, datetime):
505+
return o.isoformat()
532506
# Dataclass support
533507
if dataclasses.is_dataclass(o):
534508
return dataclasses.asdict(o)
@@ -1161,7 +1135,6 @@ async def decode_failure(
11611135
BinaryPlainPayloadConverter(),
11621136
JSONProtoPayloadConverter(),
11631137
BinaryProtoPayloadConverter(),
1164-
ISO8601DatetimePayloadConverter(),
11651138
JSONPlainPayloadConverter(), # JSON Plain needs to remain in last because it throws on unknown types
11661139
)
11671140

@@ -1429,6 +1402,15 @@ def value_to_type(
14291402
# Any or primitives
14301403
if hint is Any:
14311404
return value
1405+
elif hint is datetime:
1406+
if isinstance(value, str):
1407+
try:
1408+
return _get_iso_datetime_parser()(value)
1409+
except ValueError as err:
1410+
raise TypeError(f"Failed parsing datetime string: {value}") from err
1411+
elif isinstance(value, datetime):
1412+
return value
1413+
raise TypeError(f"Expected datetime or ISO8601 string, got {type(value)}")
14321414
elif hint is int or hint is float:
14331415
if not isinstance(value, (int, float)):
14341416
raise TypeError(f"Expected value to be int|float, was {type(value)}")

tests/test_converter.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ class MyDataClass:
8686
baz: SerializableEnum
8787

8888

89+
@dataclass
90+
class DatetimeClass:
91+
datetime: datetime
92+
93+
8994
async def test_converter_default():
9095
async def assert_payload(
9196
input,
@@ -178,11 +183,28 @@ async def assert_payload(
178183
type_hint=RawValue,
179184
)
180185

186+
# Without type hint, it is deserialized as a str
187+
await assert_payload(
188+
datetime(2020, 1, 1, 1, 1, 1),
189+
"json/plain",
190+
'"2020-01-01T01:01:01"',
191+
expected_decoded_input="2020-01-01T01:01:01",
192+
)
193+
194+
# With type hint, it is deserialized as a datetime
181195
await assert_payload(
182-
datetime(2020, 1, 1, 1, 1, 1), "binary/iso8601", "2020-01-01T01:01:01"
196+
datetime(2020, 1, 1, 1, 1, 1, 1),
197+
"json/plain",
198+
'"2020-01-01T01:01:01.000001"',
199+
type_hint=datetime,
183200
)
201+
202+
# Data class with datetime
184203
await assert_payload(
185-
datetime(2020, 1, 1, 1, 1, 1, 1), "binary/iso8601", "2020-01-01T01:01:01.000001"
204+
DatetimeClass(datetime=datetime(2020, 1, 1, 1, 1, 1)),
205+
"json/plain",
206+
'{"datetime":"2020-01-01T01:01:01"}',
207+
type_hint=DatetimeClass,
186208
)
187209

188210

0 commit comments

Comments
 (0)