27
27
Mapping ,
28
28
Optional ,
29
29
Sequence ,
30
+ Text ,
30
31
Tuple ,
31
32
Type ,
32
33
Union ,
37
38
import google .protobuf .duration_pb2
38
39
import google .protobuf .json_format
39
40
import google .protobuf .timestamp_pb2
41
+ from google .protobuf .internal .containers import MessageMap
40
42
from typing_extensions import Concatenate , Required , TypedDict
41
43
42
44
import temporalio .api .common .v1
66
68
TLSConfig ,
67
69
)
68
70
71
+ from .common import HeaderCodecBehavior
69
72
from .types import (
70
73
AnyType ,
71
74
LocalReturnType ,
@@ -116,6 +119,7 @@ async def connect(
116
119
lazy : bool = False ,
117
120
runtime : Optional [temporalio .runtime .Runtime ] = None ,
118
121
http_connect_proxy_config : Optional [HttpConnectProxyConfig ] = None ,
122
+ header_codec_behavior : HeaderCodecBehavior = HeaderCodecBehavior .NO_CODEC ,
119
123
) -> Client :
120
124
"""Connect to a Temporal server.
121
125
@@ -160,6 +164,7 @@ async def connect(
160
164
used for workers.
161
165
runtime: The runtime for this client, or the default if unset.
162
166
http_connect_proxy_config: Configuration for HTTP CONNECT proxy.
167
+ header_codec_behavior: Encoding behavior for headers sent by the client.
163
168
"""
164
169
connect_config = temporalio .service .ConnectConfig (
165
170
target_host = target_host ,
@@ -179,6 +184,7 @@ async def connect(
179
184
data_converter = data_converter ,
180
185
interceptors = interceptors ,
181
186
default_workflow_query_reject_condition = default_workflow_query_reject_condition ,
187
+ header_codec_behavior = header_codec_behavior ,
182
188
)
183
189
184
190
def __init__ (
@@ -191,6 +197,7 @@ def __init__(
191
197
default_workflow_query_reject_condition : Optional [
192
198
temporalio .common .QueryRejectCondition
193
199
] = None ,
200
+ header_codec_behavior : HeaderCodecBehavior = HeaderCodecBehavior .NO_CODEC ,
194
201
):
195
202
"""Create a Temporal client from a service client.
196
203
@@ -208,6 +215,7 @@ def __init__(
208
215
data_converter = data_converter ,
209
216
interceptors = interceptors ,
210
217
default_workflow_query_reject_condition = default_workflow_query_reject_condition ,
218
+ header_codec_behavior = header_codec_behavior ,
211
219
)
212
220
213
221
def config (self ) -> ClientConfig :
@@ -1501,6 +1509,7 @@ class ClientConfig(TypedDict, total=False):
1501
1509
default_workflow_query_reject_condition : Required [
1502
1510
Optional [temporalio .common .QueryRejectCondition ]
1503
1511
]
1512
+ header_codec_behavior : Required [HeaderCodecBehavior ]
1504
1513
1505
1514
1506
1515
class WorkflowHistoryEventFilterType (IntEnum ):
@@ -3859,6 +3868,10 @@ class ScheduleActionStartWorkflow(ScheduleAction):
3859
3868
priority : temporalio .common .Priority
3860
3869
3861
3870
headers : Optional [Mapping [str , temporalio .api .common .v1 .Payload ]]
3871
+ """
3872
+ Headers may still be encoded by the payload codec if present.
3873
+ """
3874
+ _from_raw : bool = dataclasses .field (compare = False , init = False )
3862
3875
3863
3876
@staticmethod
3864
3877
def _from_proto ( # pyright: ignore
@@ -3985,6 +3998,7 @@ def __init__(
3985
3998
"""
3986
3999
super ().__init__ ()
3987
4000
if raw_info :
4001
+ self ._from_raw = True
3988
4002
# Ignore other fields
3989
4003
self .workflow = raw_info .workflow_type .name
3990
4004
self .args = raw_info .input .payloads if raw_info .input else []
@@ -4044,6 +4058,7 @@ def __init__(
4044
4058
else temporalio .common .Priority .default
4045
4059
)
4046
4060
else :
4061
+ self ._from_raw = False
4047
4062
if not id :
4048
4063
raise ValueError ("ID required" )
4049
4064
if not task_queue :
@@ -4067,7 +4082,7 @@ def __init__(
4067
4082
self .memo = memo
4068
4083
self .typed_search_attributes = typed_search_attributes
4069
4084
self .untyped_search_attributes = untyped_search_attributes
4070
- self .headers = headers
4085
+ self .headers = headers # encode here
4071
4086
self .static_summary = static_summary
4072
4087
self .static_details = static_details
4073
4088
self .priority = priority
@@ -4145,8 +4160,12 @@ async def _to_proto(
4145
4160
self .typed_search_attributes , action .start_workflow .search_attributes
4146
4161
)
4147
4162
if self .headers :
4148
- temporalio .common ._apply_headers (
4149
- self .headers , action .start_workflow .header .fields
4163
+ await _apply_headers (
4164
+ self .headers ,
4165
+ action .start_workflow .header .fields ,
4166
+ client .config ()["header_codec_behavior" ] == HeaderCodecBehavior .CODEC
4167
+ and not self ._from_raw ,
4168
+ client .data_converter .payload_codec ,
4150
4169
)
4151
4170
return action
4152
4171
@@ -5920,7 +5939,7 @@ async def _populate_start_workflow_execution_request(
5920
5939
if input .start_delay is not None :
5921
5940
req .workflow_start_delay .FromTimedelta (input .start_delay )
5922
5941
if input .headers is not None :
5923
- temporalio . common ._apply_headers (input .headers , req .header .fields )
5942
+ await self ._apply_headers (input .headers , req .header .fields )
5924
5943
if input .priority is not None :
5925
5944
req .priority .CopyFrom (input .priority ._to_proto ())
5926
5945
if input .versioning_override is not None :
@@ -6006,7 +6025,7 @@ async def query_workflow(self, input: QueryWorkflowInput) -> Any:
6006
6025
await self ._client .data_converter .encode (input .args )
6007
6026
)
6008
6027
if input .headers is not None :
6009
- temporalio . common ._apply_headers (input .headers , req .query .header .fields )
6028
+ await self ._apply_headers (input .headers , req .query .header .fields )
6010
6029
try :
6011
6030
resp = await self ._client .workflow_service .query_workflow (
6012
6031
req , retry = True , metadata = input .rpc_metadata , timeout = input .rpc_timeout
@@ -6052,7 +6071,7 @@ async def signal_workflow(self, input: SignalWorkflowInput) -> None:
6052
6071
await self ._client .data_converter .encode (input .args )
6053
6072
)
6054
6073
if input .headers is not None :
6055
- temporalio . common ._apply_headers (input .headers , req .header .fields )
6074
+ await self ._apply_headers (input .headers , req .header .fields )
6056
6075
await self ._client .workflow_service .signal_workflow_execution (
6057
6076
req , retry = True , metadata = input .rpc_metadata , timeout = input .rpc_timeout
6058
6077
)
@@ -6163,9 +6182,7 @@ async def _build_update_workflow_execution_request(
6163
6182
await self ._client .data_converter .encode (input .args )
6164
6183
)
6165
6184
if input .headers is not None :
6166
- temporalio .common ._apply_headers (
6167
- input .headers , req .request .input .header .fields
6168
- )
6185
+ await self ._apply_headers (input .headers , req .request .input .header .fields )
6169
6186
return req
6170
6187
6171
6188
async def start_update_with_start_workflow (
@@ -6721,6 +6738,33 @@ async def get_worker_task_reachability(
6721
6738
)
6722
6739
return WorkerTaskReachability ._from_proto (resp )
6723
6740
6741
+ async def _apply_headers (
6742
+ self ,
6743
+ source : Optional [Mapping [str , temporalio .api .common .v1 .Payload ]],
6744
+ dest : MessageMap [Text , temporalio .api .common .v1 .Payload ],
6745
+ ) -> None :
6746
+ await _apply_headers (
6747
+ source ,
6748
+ dest ,
6749
+ self ._client .config ()["header_codec_behavior" ] == HeaderCodecBehavior .CODEC ,
6750
+ self ._client .data_converter .payload_codec ,
6751
+ )
6752
+
6753
+
6754
+ async def _apply_headers (
6755
+ source : Optional [Mapping [str , temporalio .api .common .v1 .Payload ]],
6756
+ dest : MessageMap [Text , temporalio .api .common .v1 .Payload ],
6757
+ encode_headers : bool ,
6758
+ codec : Optional [temporalio .converter .PayloadCodec ],
6759
+ ) -> None :
6760
+ if source is None :
6761
+ return
6762
+ if encode_headers and codec is not None :
6763
+ for payload in source .values ():
6764
+ new_payload = (await codec .encode ([payload ]))[0 ]
6765
+ payload .CopyFrom (new_payload )
6766
+ temporalio .common ._apply_headers (source , dest )
6767
+
6724
6768
6725
6769
def _history_from_json (
6726
6770
history : Union [str , Dict [str , Any ]],
0 commit comments