durabletask 0.1.1a1__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 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, List, Tuple, TypeVar, Union
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: Union[str, None]
46
- serialized_output: Union[str, None]
47
- serialized_custom_status: Union[str, None]
48
- failure_details: Union[task.FailureDetails, None]
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) -> Union[OrchestrationState, None]:
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: Union[str, None] = None,
96
- metadata: Union[List[Tuple[str, str]], None] = None,
97
- log_handler = None,
98
- log_formatter: Union[logging.Formatter, None] = None,
99
- secure_channel: bool = False):
100
- channel = shared.get_grpc_channel(host_address, metadata, secure_channel=secure_channel)
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: Union[TInput, None] = None,
106
- instance_id: Union[str, None] = None,
107
- start_at: Union[datetime, None] = None) -> str:
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) -> Union[OrchestrationState, None]:
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) -> Union[OrchestrationState, None]:
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) -> Union[OrchestrationState, None]:
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: Union[Any, None] = None):
191
+ data: Optional[Any] = None):
171
192
  req = pb.RaiseEventRequest(
172
193
  instanceId=instance_id,
173
194
  name=event_name,
@@ -177,7 +198,7 @@ class TaskHubGrpcClient:
177
198
  self._stub.RaiseEvent(req)
178
199
 
179
200
  def terminate_orchestration(self, instance_id: str, *,
180
- output: Union[Any, None] = None,
201
+ output: Optional[Any] = None,
181
202
  recursive: bool = True):
182
203
  req = pb.TerminateRequest(
183
204
  instanceId=instance_id,
@@ -196,3 +217,8 @@ class TaskHubGrpcClient:
196
217
  req = pb.ResumeRequest(instanceId=instance_id)
197
218
  self._logger.info(f"Resuming instance '{instance_id}'.")
198
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
- grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor,
24
- grpc.StreamUnaryClientInterceptor, grpc.StreamStreamClientInterceptor):
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: List[Tuple[str, str]]):
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
- self, client_call_details: _ClientCallDetails) -> grpc.ClientCallDetails:
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,
@@ -3,7 +3,7 @@
3
3
 
4
4
  import traceback
5
5
  from datetime import datetime
6
- from typing import List, Union
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: Union[datetime, None] = None) -> pb.HistoryEvent:
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: Union[str, None] = None) -> pb.HistoryEvent:
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: Union[str, None] = None) -> pb.HistoryEvent:
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: Union[str, None] = None) -> pb.HistoryEvent:
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: Union[str, None] = None) -> pb.HistoryEvent:
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: Union[str, None] = None) -> pb.HistoryEvent:
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: Union[str, None] = None) -> pb.HistoryEvent:
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: Union[str, None] = None) -> pb.HistoryEvent:
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: Union[str, None]) -> Union[wrappers_pb2.StringValue, None]:
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: Union[str, None] = None,
164
- failure_details: Union[pb.TaskFailureDetails, None] = None,
165
- carryover_events: Union[List[pb.HistoryEvent], None] = None) -> pb.OrchestratorAction:
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: Union[str, None]) -> pb.OrchestratorAction:
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: Union[str, None],
198
- encoded_input: Union[str, None]) -> pb.OrchestratorAction:
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,