durabletask 0.1.0a5__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of durabletask might be problematic. Click here for more details.
- durabletask/client.py +50 -22
- durabletask/internal/grpc_interceptor.py +7 -8
- durabletask/internal/helpers.py +16 -16
- durabletask/internal/orchestrator_service_pb2.py +194 -136
- durabletask/internal/orchestrator_service_pb2.pyi +890 -0
- durabletask/internal/orchestrator_service_pb2_grpc.py +547 -380
- durabletask/internal/shared.py +37 -9
- durabletask/task.py +174 -42
- durabletask/worker.py +198 -89
- {durabletask-0.1.0a5.dist-info → durabletask-0.2.0.dist-info}/METADATA +11 -18
- durabletask-0.2.0.dist-info/RECORD +14 -0
- {durabletask-0.1.0a5.dist-info → durabletask-0.2.0.dist-info}/WHEEL +1 -1
- durabletask/internal/__init__.py +0 -0
- durabletask-0.1.0a5.dist-info/LICENSE +0 -21
- durabletask-0.1.0a5.dist-info/RECORD +0 -15
- {durabletask-0.1.0a5.dist-info → durabletask-0.2.0.dist-info}/top_level.txt +0 -0
durabletask/client.py
CHANGED
|
@@ -6,7 +6,7 @@ import uuid
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from enum import Enum
|
|
9
|
-
from typing import Any,
|
|
9
|
+
from typing import Any, Optional, Sequence, TypeVar, Union
|
|
10
10
|
|
|
11
11
|
import grpc
|
|
12
12
|
from google.protobuf import wrappers_pb2
|
|
@@ -16,6 +16,7 @@ import durabletask.internal.orchestrator_service_pb2 as pb
|
|
|
16
16
|
import durabletask.internal.orchestrator_service_pb2_grpc as stubs
|
|
17
17
|
import durabletask.internal.shared as shared
|
|
18
18
|
from durabletask import task
|
|
19
|
+
from durabletask.internal.grpc_interceptor import DefaultClientInterceptorImpl
|
|
19
20
|
|
|
20
21
|
TInput = TypeVar('TInput')
|
|
21
22
|
TOutput = TypeVar('TOutput')
|
|
@@ -42,10 +43,10 @@ class OrchestrationState:
|
|
|
42
43
|
runtime_status: OrchestrationStatus
|
|
43
44
|
created_at: datetime
|
|
44
45
|
last_updated_at: datetime
|
|
45
|
-
serialized_input:
|
|
46
|
-
serialized_output:
|
|
47
|
-
serialized_custom_status:
|
|
48
|
-
failure_details:
|
|
46
|
+
serialized_input: Optional[str]
|
|
47
|
+
serialized_output: Optional[str]
|
|
48
|
+
serialized_custom_status: Optional[str]
|
|
49
|
+
failure_details: Optional[task.FailureDetails]
|
|
49
50
|
|
|
50
51
|
def raise_if_failed(self):
|
|
51
52
|
if self.failure_details is not None:
|
|
@@ -64,7 +65,7 @@ class OrchestrationFailedError(Exception):
|
|
|
64
65
|
return self._failure_details
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
def new_orchestration_state(instance_id: str, res: pb.GetInstanceResponse) ->
|
|
68
|
+
def new_orchestration_state(instance_id: str, res: pb.GetInstanceResponse) -> Optional[OrchestrationState]:
|
|
68
69
|
if not res.exists:
|
|
69
70
|
return None
|
|
70
71
|
|
|
@@ -92,19 +93,37 @@ def new_orchestration_state(instance_id: str, res: pb.GetInstanceResponse) -> Un
|
|
|
92
93
|
class TaskHubGrpcClient:
|
|
93
94
|
|
|
94
95
|
def __init__(self, *,
|
|
95
|
-
host_address:
|
|
96
|
-
metadata:
|
|
97
|
-
log_handler = None,
|
|
98
|
-
log_formatter:
|
|
99
|
-
secure_channel: bool = False
|
|
100
|
-
|
|
96
|
+
host_address: Optional[str] = None,
|
|
97
|
+
metadata: Optional[list[tuple[str, str]]] = None,
|
|
98
|
+
log_handler: Optional[logging.Handler] = None,
|
|
99
|
+
log_formatter: Optional[logging.Formatter] = None,
|
|
100
|
+
secure_channel: bool = False,
|
|
101
|
+
interceptors: Optional[Sequence[shared.ClientInterceptor]] = None):
|
|
102
|
+
|
|
103
|
+
# If the caller provided metadata, we need to create a new interceptor for it and
|
|
104
|
+
# add it to the list of interceptors.
|
|
105
|
+
if interceptors is not None:
|
|
106
|
+
interceptors = list(interceptors)
|
|
107
|
+
if metadata is not None:
|
|
108
|
+
interceptors.append(DefaultClientInterceptorImpl(metadata))
|
|
109
|
+
elif metadata is not None:
|
|
110
|
+
interceptors = [DefaultClientInterceptorImpl(metadata)]
|
|
111
|
+
else:
|
|
112
|
+
interceptors = None
|
|
113
|
+
|
|
114
|
+
channel = shared.get_grpc_channel(
|
|
115
|
+
host_address=host_address,
|
|
116
|
+
secure_channel=secure_channel,
|
|
117
|
+
interceptors=interceptors
|
|
118
|
+
)
|
|
101
119
|
self._stub = stubs.TaskHubSidecarServiceStub(channel)
|
|
102
120
|
self._logger = shared.get_logger("client", log_handler, log_formatter)
|
|
103
121
|
|
|
104
122
|
def schedule_new_orchestration(self, orchestrator: Union[task.Orchestrator[TInput, TOutput], str], *,
|
|
105
|
-
input:
|
|
106
|
-
instance_id:
|
|
107
|
-
start_at:
|
|
123
|
+
input: Optional[TInput] = None,
|
|
124
|
+
instance_id: Optional[str] = None,
|
|
125
|
+
start_at: Optional[datetime] = None,
|
|
126
|
+
reuse_id_policy: Optional[pb.OrchestrationIdReusePolicy] = None) -> str:
|
|
108
127
|
|
|
109
128
|
name = orchestrator if isinstance(orchestrator, str) else task.get_name(orchestrator)
|
|
110
129
|
|
|
@@ -113,20 +132,22 @@ class TaskHubGrpcClient:
|
|
|
113
132
|
instanceId=instance_id if instance_id else uuid.uuid4().hex,
|
|
114
133
|
input=wrappers_pb2.StringValue(value=shared.to_json(input)) if input is not None else None,
|
|
115
134
|
scheduledStartTimestamp=helpers.new_timestamp(start_at) if start_at else None,
|
|
116
|
-
version=wrappers_pb2.StringValue(value="")
|
|
135
|
+
version=wrappers_pb2.StringValue(value=""),
|
|
136
|
+
orchestrationIdReusePolicy=reuse_id_policy,
|
|
137
|
+
)
|
|
117
138
|
|
|
118
139
|
self._logger.info(f"Starting new '{name}' instance with ID = '{req.instanceId}'.")
|
|
119
140
|
res: pb.CreateInstanceResponse = self._stub.StartInstance(req)
|
|
120
141
|
return res.instanceId
|
|
121
142
|
|
|
122
|
-
def get_orchestration_state(self, instance_id: str, *, fetch_payloads: bool = True) ->
|
|
143
|
+
def get_orchestration_state(self, instance_id: str, *, fetch_payloads: bool = True) -> Optional[OrchestrationState]:
|
|
123
144
|
req = pb.GetInstanceRequest(instanceId=instance_id, getInputsAndOutputs=fetch_payloads)
|
|
124
145
|
res: pb.GetInstanceResponse = self._stub.GetInstance(req)
|
|
125
146
|
return new_orchestration_state(req.instanceId, res)
|
|
126
147
|
|
|
127
148
|
def wait_for_orchestration_start(self, instance_id: str, *,
|
|
128
149
|
fetch_payloads: bool = False,
|
|
129
|
-
timeout: int = 60) ->
|
|
150
|
+
timeout: int = 60) -> Optional[OrchestrationState]:
|
|
130
151
|
req = pb.GetInstanceRequest(instanceId=instance_id, getInputsAndOutputs=fetch_payloads)
|
|
131
152
|
try:
|
|
132
153
|
self._logger.info(f"Waiting up to {timeout}s for instance '{instance_id}' to start.")
|
|
@@ -141,7 +162,7 @@ class TaskHubGrpcClient:
|
|
|
141
162
|
|
|
142
163
|
def wait_for_orchestration_completion(self, instance_id: str, *,
|
|
143
164
|
fetch_payloads: bool = True,
|
|
144
|
-
timeout: int = 60) ->
|
|
165
|
+
timeout: int = 60) -> Optional[OrchestrationState]:
|
|
145
166
|
req = pb.GetInstanceRequest(instanceId=instance_id, getInputsAndOutputs=fetch_payloads)
|
|
146
167
|
try:
|
|
147
168
|
self._logger.info(f"Waiting {timeout}s for instance '{instance_id}' to complete.")
|
|
@@ -167,7 +188,7 @@ class TaskHubGrpcClient:
|
|
|
167
188
|
raise
|
|
168
189
|
|
|
169
190
|
def raise_orchestration_event(self, instance_id: str, event_name: str, *,
|
|
170
|
-
data:
|
|
191
|
+
data: Optional[Any] = None):
|
|
171
192
|
req = pb.RaiseEventRequest(
|
|
172
193
|
instanceId=instance_id,
|
|
173
194
|
name=event_name,
|
|
@@ -177,10 +198,12 @@ class TaskHubGrpcClient:
|
|
|
177
198
|
self._stub.RaiseEvent(req)
|
|
178
199
|
|
|
179
200
|
def terminate_orchestration(self, instance_id: str, *,
|
|
180
|
-
output:
|
|
201
|
+
output: Optional[Any] = None,
|
|
202
|
+
recursive: bool = True):
|
|
181
203
|
req = pb.TerminateRequest(
|
|
182
204
|
instanceId=instance_id,
|
|
183
|
-
output=wrappers_pb2.StringValue(value=shared.to_json(output)) if output else None
|
|
205
|
+
output=wrappers_pb2.StringValue(value=shared.to_json(output)) if output else None,
|
|
206
|
+
recursive=recursive)
|
|
184
207
|
|
|
185
208
|
self._logger.info(f"Terminating instance '{instance_id}'.")
|
|
186
209
|
self._stub.TerminateInstance(req)
|
|
@@ -194,3 +217,8 @@ class TaskHubGrpcClient:
|
|
|
194
217
|
req = pb.ResumeRequest(instanceId=instance_id)
|
|
195
218
|
self._logger.info(f"Resuming instance '{instance_id}'.")
|
|
196
219
|
self._stub.ResumeInstance(req)
|
|
220
|
+
|
|
221
|
+
def purge_orchestration(self, instance_id: str, recursive: bool = True):
|
|
222
|
+
req = pb.PurgeInstancesRequest(instanceId=instance_id, recursive=recursive)
|
|
223
|
+
self._logger.info(f"Purging instance '{instance_id}'.")
|
|
224
|
+
self._stub.PurgeInstances(req)
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
# Licensed under the MIT License.
|
|
3
3
|
|
|
4
4
|
from collections import namedtuple
|
|
5
|
-
from typing import List, Tuple
|
|
6
5
|
|
|
7
6
|
import grpc
|
|
8
7
|
|
|
@@ -20,28 +19,28 @@ class _ClientCallDetails(
|
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
class DefaultClientInterceptorImpl (
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor,
|
|
23
|
+
grpc.StreamUnaryClientInterceptor, grpc.StreamStreamClientInterceptor):
|
|
25
24
|
"""The class implements a UnaryUnaryClientInterceptor, UnaryStreamClientInterceptor,
|
|
26
|
-
StreamUnaryClientInterceptor and StreamStreamClientInterceptor from grpc to add an
|
|
25
|
+
StreamUnaryClientInterceptor and StreamStreamClientInterceptor from grpc to add an
|
|
27
26
|
interceptor to add additional headers to all calls as needed."""
|
|
28
27
|
|
|
29
|
-
def __init__(self, metadata:
|
|
28
|
+
def __init__(self, metadata: list[tuple[str, str]]):
|
|
30
29
|
super().__init__()
|
|
31
30
|
self._metadata = metadata
|
|
32
31
|
|
|
33
32
|
def _intercept_call(
|
|
34
|
-
|
|
33
|
+
self, client_call_details: _ClientCallDetails) -> grpc.ClientCallDetails:
|
|
35
34
|
"""Internal intercept_call implementation which adds metadata to grpc metadata in the RPC
|
|
36
35
|
call details."""
|
|
37
36
|
if self._metadata is None:
|
|
38
37
|
return client_call_details
|
|
39
|
-
|
|
38
|
+
|
|
40
39
|
if client_call_details.metadata is not None:
|
|
41
40
|
metadata = list(client_call_details.metadata)
|
|
42
41
|
else:
|
|
43
42
|
metadata = []
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
metadata.extend(self._metadata)
|
|
46
45
|
client_call_details = _ClientCallDetails(
|
|
47
46
|
client_call_details.method, client_call_details.timeout, metadata,
|
durabletask/internal/helpers.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import traceback
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Optional
|
|
7
7
|
|
|
8
8
|
from google.protobuf import timestamp_pb2, wrappers_pb2
|
|
9
9
|
|
|
@@ -12,14 +12,14 @@ import durabletask.internal.orchestrator_service_pb2 as pb
|
|
|
12
12
|
# TODO: The new_xxx_event methods are only used by test code and should be moved elsewhere
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def new_orchestrator_started_event(timestamp:
|
|
15
|
+
def new_orchestrator_started_event(timestamp: Optional[datetime] = None) -> pb.HistoryEvent:
|
|
16
16
|
ts = timestamp_pb2.Timestamp()
|
|
17
17
|
if timestamp is not None:
|
|
18
18
|
ts.FromDatetime(timestamp)
|
|
19
19
|
return pb.HistoryEvent(eventId=-1, timestamp=ts, orchestratorStarted=pb.OrchestratorStartedEvent())
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def new_execution_started_event(name: str, instance_id: str, encoded_input:
|
|
22
|
+
def new_execution_started_event(name: str, instance_id: str, encoded_input: Optional[str] = None) -> pb.HistoryEvent:
|
|
23
23
|
return pb.HistoryEvent(
|
|
24
24
|
eventId=-1,
|
|
25
25
|
timestamp=timestamp_pb2.Timestamp(),
|
|
@@ -49,7 +49,7 @@ def new_timer_fired_event(timer_id: int, fire_at: datetime) -> pb.HistoryEvent:
|
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def new_task_scheduled_event(event_id: int, name: str, encoded_input:
|
|
52
|
+
def new_task_scheduled_event(event_id: int, name: str, encoded_input: Optional[str] = None) -> pb.HistoryEvent:
|
|
53
53
|
return pb.HistoryEvent(
|
|
54
54
|
eventId=event_id,
|
|
55
55
|
timestamp=timestamp_pb2.Timestamp(),
|
|
@@ -57,7 +57,7 @@ def new_task_scheduled_event(event_id: int, name: str, encoded_input: Union[str,
|
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def new_task_completed_event(event_id: int, encoded_output:
|
|
60
|
+
def new_task_completed_event(event_id: int, encoded_output: Optional[str] = None) -> pb.HistoryEvent:
|
|
61
61
|
return pb.HistoryEvent(
|
|
62
62
|
eventId=-1,
|
|
63
63
|
timestamp=timestamp_pb2.Timestamp(),
|
|
@@ -77,7 +77,7 @@ def new_sub_orchestration_created_event(
|
|
|
77
77
|
event_id: int,
|
|
78
78
|
name: str,
|
|
79
79
|
instance_id: str,
|
|
80
|
-
encoded_input:
|
|
80
|
+
encoded_input: Optional[str] = None) -> pb.HistoryEvent:
|
|
81
81
|
return pb.HistoryEvent(
|
|
82
82
|
eventId=event_id,
|
|
83
83
|
timestamp=timestamp_pb2.Timestamp(),
|
|
@@ -88,7 +88,7 @@ def new_sub_orchestration_created_event(
|
|
|
88
88
|
)
|
|
89
89
|
|
|
90
90
|
|
|
91
|
-
def new_sub_orchestration_completed_event(event_id: int, encoded_output:
|
|
91
|
+
def new_sub_orchestration_completed_event(event_id: int, encoded_output: Optional[str] = None) -> pb.HistoryEvent:
|
|
92
92
|
return pb.HistoryEvent(
|
|
93
93
|
eventId=-1,
|
|
94
94
|
timestamp=timestamp_pb2.Timestamp(),
|
|
@@ -116,7 +116,7 @@ def new_failure_details(ex: Exception) -> pb.TaskFailureDetails:
|
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
def new_event_raised_event(name: str, encoded_input:
|
|
119
|
+
def new_event_raised_event(name: str, encoded_input: Optional[str] = None) -> pb.HistoryEvent:
|
|
120
120
|
return pb.HistoryEvent(
|
|
121
121
|
eventId=-1,
|
|
122
122
|
timestamp=timestamp_pb2.Timestamp(),
|
|
@@ -140,7 +140,7 @@ def new_resume_event() -> pb.HistoryEvent:
|
|
|
140
140
|
)
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
def new_terminated_event(*, encoded_output:
|
|
143
|
+
def new_terminated_event(*, encoded_output: Optional[str] = None) -> pb.HistoryEvent:
|
|
144
144
|
return pb.HistoryEvent(
|
|
145
145
|
eventId=-1,
|
|
146
146
|
timestamp=timestamp_pb2.Timestamp(),
|
|
@@ -150,7 +150,7 @@ def new_terminated_event(*, encoded_output: Union[str, None] = None) -> pb.Histo
|
|
|
150
150
|
)
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
def get_string_value(val:
|
|
153
|
+
def get_string_value(val: Optional[str]) -> Optional[wrappers_pb2.StringValue]:
|
|
154
154
|
if val is None:
|
|
155
155
|
return None
|
|
156
156
|
else:
|
|
@@ -160,9 +160,9 @@ def get_string_value(val: Union[str, None]) -> Union[wrappers_pb2.StringValue, N
|
|
|
160
160
|
def new_complete_orchestration_action(
|
|
161
161
|
id: int,
|
|
162
162
|
status: pb.OrchestrationStatus,
|
|
163
|
-
result:
|
|
164
|
-
failure_details:
|
|
165
|
-
carryover_events:
|
|
163
|
+
result: Optional[str] = None,
|
|
164
|
+
failure_details: Optional[pb.TaskFailureDetails] = None,
|
|
165
|
+
carryover_events: Optional[list[pb.HistoryEvent]] = None) -> pb.OrchestratorAction:
|
|
166
166
|
completeOrchestrationAction = pb.CompleteOrchestrationAction(
|
|
167
167
|
orchestrationStatus=status,
|
|
168
168
|
result=get_string_value(result),
|
|
@@ -178,7 +178,7 @@ def new_create_timer_action(id: int, fire_at: datetime) -> pb.OrchestratorAction
|
|
|
178
178
|
return pb.OrchestratorAction(id=id, createTimer=pb.CreateTimerAction(fireAt=timestamp))
|
|
179
179
|
|
|
180
180
|
|
|
181
|
-
def new_schedule_task_action(id: int, name: str, encoded_input:
|
|
181
|
+
def new_schedule_task_action(id: int, name: str, encoded_input: Optional[str]) -> pb.OrchestratorAction:
|
|
182
182
|
return pb.OrchestratorAction(id=id, scheduleTask=pb.ScheduleTaskAction(
|
|
183
183
|
name=name,
|
|
184
184
|
input=get_string_value(encoded_input)
|
|
@@ -194,8 +194,8 @@ def new_timestamp(dt: datetime) -> timestamp_pb2.Timestamp:
|
|
|
194
194
|
def new_create_sub_orchestration_action(
|
|
195
195
|
id: int,
|
|
196
196
|
name: str,
|
|
197
|
-
instance_id:
|
|
198
|
-
encoded_input:
|
|
197
|
+
instance_id: Optional[str],
|
|
198
|
+
encoded_input: Optional[str]) -> pb.OrchestratorAction:
|
|
199
199
|
return pb.OrchestratorAction(id=id, createSubOrchestration=pb.CreateSubOrchestrationAction(
|
|
200
200
|
name=name,
|
|
201
201
|
instanceId=instance_id,
|