33
33
34
34
import temporalio .api .common .v1
35
35
import temporalio .common
36
- import temporalio .converter
36
+ from temporalio .api . common . v1 import Payload
37
37
from temporalio .api .common .v1 import Payload as AnotherNameForPayload
38
+ from temporalio .api .common .v1 import Payloads
38
39
from temporalio .api .failure .v1 import Failure
40
+ from temporalio .converter import (
41
+ BinaryProtoPayloadConverter ,
42
+ DataConverter ,
43
+ DefaultFailureConverterWithEncodedAttributes ,
44
+ JSONPlainPayloadConverter ,
45
+ PayloadCodec ,
46
+ decode_search_attributes ,
47
+ encode_search_attribute_values ,
48
+ )
39
49
from temporalio .exceptions import ApplicationError , FailureError
40
50
41
51
# StrEnum is available in 3.11+
@@ -77,7 +87,7 @@ async def assert_payload(
77
87
expected_decoded_input = None ,
78
88
type_hint = None ,
79
89
):
80
- payloads = await temporalio . converter . DataConverter ().encode ([input ])
90
+ payloads = await DataConverter ().encode ([input ])
81
91
# Check encoding and data
82
92
assert len (payloads ) == 1
83
93
if isinstance (expected_encoding , str ):
@@ -87,9 +97,7 @@ async def assert_payload(
87
97
expected_data = expected_data .encode ()
88
98
assert payloads [0 ].data == expected_data
89
99
# Decode and check
90
- actual_inputs = await temporalio .converter .DataConverter ().decode (
91
- payloads , [type_hint ]
92
- )
100
+ actual_inputs = await DataConverter ().decode (payloads , [type_hint ])
93
101
assert len (actual_inputs ) == 1
94
102
if expected_decoded_input is None :
95
103
expected_decoded_input = input
@@ -158,7 +166,7 @@ async def assert_payload(
158
166
def test_binary_proto ():
159
167
# We have to test this separately because by default it never encodes
160
168
# anything since JSON proto takes precedence
161
- conv = temporalio . converter . BinaryProtoPayloadConverter ()
169
+ conv = BinaryProtoPayloadConverter ()
162
170
proto = temporalio .api .common .v1 .WorkflowExecution (workflow_id = "id1" , run_id = "id2" )
163
171
payload = conv .to_payload (proto )
164
172
assert payload .metadata ["encoding" ] == b"binary/protobuf"
@@ -172,11 +180,11 @@ def test_binary_proto():
172
180
173
181
def test_encode_search_attribute_values ():
174
182
with pytest .raises (TypeError , match = "of type tuple not one of" ):
175
- temporalio . converter . encode_search_attribute_values ([("bad type" ,)])
183
+ encode_search_attribute_values ([("bad type" ,)])
176
184
with pytest .raises (ValueError , match = "Timezone must be present" ):
177
- temporalio . converter . encode_search_attribute_values ([datetime .utcnow ()])
185
+ encode_search_attribute_values ([datetime .utcnow ()])
178
186
with pytest .raises (TypeError , match = "must have the same type" ):
179
- temporalio . converter . encode_search_attribute_values (["foo" , 123 ])
187
+ encode_search_attribute_values (["foo" , 123 ])
180
188
181
189
182
190
def test_decode_search_attributes ():
@@ -192,25 +200,23 @@ def payload(key, dtype, data, encoding=None):
192
200
return temporalio .api .common .v1 .SearchAttributes (indexed_fields = {key : check })
193
201
194
202
# Check basic keyword parsing works
195
- kw_check = temporalio .converter .decode_search_attributes (
196
- payload ("kw" , "Keyword" , '"test-id"' )
197
- )
203
+ kw_check = decode_search_attributes (payload ("kw" , "Keyword" , '"test-id"' ))
198
204
assert kw_check ["kw" ][0 ] == "test-id"
199
205
200
206
# Ensure original DT functionality works
201
- dt_check = temporalio . converter . decode_search_attributes (
207
+ dt_check = decode_search_attributes (
202
208
payload ("dt" , "Datetime" , '"2020-01-01T00:00:00"' )
203
209
)
204
210
assert dt_check ["dt" ][0 ] == datetime (2020 , 1 , 1 , 0 , 0 , 0 )
205
211
206
212
# Check timezone aware works as server is using ISO 8601
207
- dttz_check = temporalio . converter . decode_search_attributes (
213
+ dttz_check = decode_search_attributes (
208
214
payload ("dt" , "Datetime" , '"2020-01-01T00:00:00Z"' )
209
215
)
210
216
assert dttz_check ["dt" ][0 ] == datetime (2020 , 1 , 1 , 0 , 0 , 0 , tzinfo = timezone .utc )
211
217
212
218
# Check timezone aware, hour offset
213
- dttz_check = temporalio . converter . decode_search_attributes (
219
+ dttz_check = decode_search_attributes (
214
220
payload ("dt" , "Datetime" , '"2020-01-01T00:00:00+00:00"' )
215
221
)
216
222
assert dttz_check ["dt" ][0 ] == datetime (2020 , 1 , 1 , 0 , 0 , 0 , tzinfo = timezone .utc )
@@ -245,7 +251,7 @@ class MyPydanticClass(pydantic.BaseModel):
245
251
246
252
247
253
def test_json_type_hints ():
248
- converter = temporalio . converter . JSONPlainPayloadConverter ()
254
+ converter = JSONPlainPayloadConverter ()
249
255
250
256
def ok (
251
257
hint : Type , value : Any , expected_result : Any = temporalio .common ._arg_unset
@@ -415,10 +421,8 @@ async def test_exception_format():
415
421
416
422
# Convert to failure and back
417
423
failure = Failure ()
418
- await temporalio .converter .DataConverter .default .encode_failure (actual_err , failure )
419
- failure_error = await temporalio .converter .DataConverter .default .decode_failure (
420
- failure
421
- )
424
+ await DataConverter .default .encode_failure (actual_err , failure )
425
+ failure_error = await DataConverter .default .decode_failure (failure )
422
426
# Confirm type is prepended
423
427
assert isinstance (failure_error , ApplicationError )
424
428
assert "RuntimeError: error2" == str (failure_error )
@@ -440,3 +444,72 @@ async def test_exception_format():
440
444
logging .getLogger (__name__ ).debug (
441
445
"Showing appended exception" , exc_info = failure_error
442
446
)
447
+
448
+
449
+ # Just serializes in a "payloads" wrapper
450
+ class SimpleCodec (PayloadCodec ):
451
+ async def encode (self , payloads : Sequence [Payload ]) -> List [Payload ]:
452
+ wrapper = Payloads (payloads = payloads )
453
+ return [
454
+ Payload (
455
+ metadata = {"simple-codec" : b"true" }, data = wrapper .SerializeToString ()
456
+ )
457
+ ]
458
+
459
+ async def decode (self , payloads : Sequence [Payload ]) -> List [Payload ]:
460
+ payloads = list (payloads )
461
+ if len (payloads ) != 1 :
462
+ raise RuntimeError ("Expected only a single payload" )
463
+ elif payloads [0 ].metadata .get ("simple-codec" ) != b"true" :
464
+ raise RuntimeError ("Not encoded with this codec" )
465
+ wrapper = Payloads ()
466
+ wrapper .ParseFromString (payloads [0 ].data )
467
+ return list (wrapper .payloads )
468
+
469
+
470
+ async def test_failure_encoded_attributes ():
471
+ try :
472
+ raise ApplicationError ("some message" , "some detail" )
473
+ except ApplicationError as err :
474
+ some_err = err
475
+
476
+ conv = DataConverter (
477
+ failure_converter_class = DefaultFailureConverterWithEncodedAttributes ,
478
+ payload_codec = SimpleCodec (),
479
+ )
480
+
481
+ # Check failure
482
+ failure = Failure ()
483
+ conv .failure_converter .to_failure (some_err , conv .payload_converter , failure )
484
+ assert failure .message == "Encoded failure"
485
+ assert failure .stack_trace == ""
486
+ assert conv .payload_converter .from_payloads (
487
+ failure .application_failure_info .details .payloads
488
+ ) == ["some detail" ]
489
+ encoded_attr = conv .payload_converter .from_payloads ([failure .encoded_attributes ])[0 ]
490
+ assert encoded_attr ["message" ] == "some message"
491
+ assert "test_converter" in encoded_attr ["stack_trace" ]
492
+
493
+ # Encode it and check encoded
494
+ orig_failure = Failure ()
495
+ orig_failure .CopyFrom (failure )
496
+ await conv .payload_codec .encode_failure (failure )
497
+ assert "encoding" not in failure .encoded_attributes .metadata
498
+ assert "simple-codec" in failure .encoded_attributes .metadata
499
+ assert (
500
+ "encoding" not in failure .application_failure_info .details .payloads [0 ].metadata
501
+ )
502
+ assert (
503
+ "simple-codec" in failure .application_failure_info .details .payloads [0 ].metadata
504
+ )
505
+
506
+ # Decode and check
507
+ await conv .payload_codec .decode_failure (failure )
508
+ assert "encoding" in failure .encoded_attributes .metadata
509
+ assert "simple-codec" not in failure .encoded_attributes .metadata
510
+ assert "encoding" in failure .application_failure_info .details .payloads [0 ].metadata
511
+ assert (
512
+ "simple-codec"
513
+ not in failure .application_failure_info .details .payloads [0 ].metadata
514
+ )
515
+ assert failure == orig_failure
0 commit comments