durabletask 0.0.0.dev21__tar.gz → 0.0.0.dev22__tar.gz

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.
Files changed (32) hide show
  1. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/PKG-INFO +1 -1
  2. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/client.py +6 -4
  3. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/entities/durable_entity.py +5 -1
  4. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/entities/entity_context.py +2 -1
  5. durabletask-0.0.0.dev22/durabletask/entities/entity_instance_id.py +89 -0
  6. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/entities/entity_metadata.py +1 -1
  7. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/helpers.py +9 -2
  8. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/task.py +21 -11
  9. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/worker.py +23 -25
  10. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask.egg-info/PKG-INFO +1 -1
  11. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/pyproject.toml +1 -1
  12. durabletask-0.0.0.dev21/durabletask/entities/entity_instance_id.py +0 -42
  13. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/LICENSE +0 -0
  14. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/README.md +0 -0
  15. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/__init__.py +0 -0
  16. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/entities/__init__.py +0 -0
  17. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/entities/entity_lock.py +0 -0
  18. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/entity_state_shim.py +0 -0
  19. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/exceptions.py +0 -0
  20. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/grpc_interceptor.py +0 -0
  21. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/orchestration_entity_context.py +0 -0
  22. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/orchestrator_service_pb2.py +0 -0
  23. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/orchestrator_service_pb2.pyi +0 -0
  24. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/orchestrator_service_pb2_grpc.py +0 -0
  25. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/proto_task_hub_sidecar_service_stub.py +0 -0
  26. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/internal/shared.py +0 -0
  27. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask/py.typed +0 -0
  28. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask.egg-info/SOURCES.txt +0 -0
  29. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask.egg-info/dependency_links.txt +0 -0
  30. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask.egg-info/requires.txt +0 -0
  31. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/durabletask.egg-info/top_level.txt +0 -0
  32. {durabletask-0.0.0.dev21 → durabletask-0.0.0.dev22}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: durabletask
3
- Version: 0.0.0.dev21
3
+ Version: 0.0.0.dev22
4
4
  Summary: A Durable Task Client SDK for Python
5
5
  License: MIT License
6
6
 
@@ -11,8 +11,7 @@ from typing import Any, Optional, Sequence, TypeVar, Union
11
11
  import grpc
12
12
  from google.protobuf import wrappers_pb2
13
13
 
14
- from durabletask.entities import EntityInstanceId
15
- from durabletask.entities.entity_metadata import EntityMetadata
14
+ from durabletask.entities import EntityInstanceId, EntityMetadata
16
15
  import durabletask.internal.helpers as helpers
17
16
  import durabletask.internal.orchestrator_service_pb2 as pb
18
17
  import durabletask.internal.orchestrator_service_pb2_grpc as stubs
@@ -230,7 +229,10 @@ class TaskHubGrpcClient:
230
229
  self._logger.info(f"Purging instance '{instance_id}'.")
231
230
  self._stub.PurgeInstances(req)
232
231
 
233
- def signal_entity(self, entity_instance_id: EntityInstanceId, operation_name: str, input: Optional[Any] = None):
232
+ def signal_entity(self,
233
+ entity_instance_id: EntityInstanceId[TInput, TOutput],
234
+ operation_name: str,
235
+ input: Optional[TInput] = None):
234
236
  req = pb.SignalEntityRequest(
235
237
  instanceId=str(entity_instance_id),
236
238
  name=operation_name,
@@ -244,7 +246,7 @@ class TaskHubGrpcClient:
244
246
  self._stub.SignalEntity(req, None) # TODO: Cancellation timeout?
245
247
 
246
248
  def get_entity(self,
247
- entity_instance_id: EntityInstanceId,
249
+ entity_instance_id: EntityInstanceId[Any, Any],
248
250
  include_state: bool = True
249
251
  ) -> Optional[EntityMetadata]:
250
252
  req = pb.GetEntityRequest(instanceId=str(entity_instance_id), includeState=include_state)
@@ -4,6 +4,7 @@ from durabletask.entities.entity_context import EntityContext
4
4
  from durabletask.entities.entity_instance_id import EntityInstanceId
5
5
 
6
6
  TState = TypeVar("TState")
7
+ TInput = TypeVar("TInput")
7
8
 
8
9
 
9
10
  class DurableEntity:
@@ -49,7 +50,10 @@ class DurableEntity:
49
50
  """
50
51
  self.entity_context.set_state(state)
51
52
 
52
- def signal_entity(self, entity_instance_id: EntityInstanceId, operation: str, input: Optional[Any] = None) -> None:
53
+ def signal_entity(self,
54
+ entity_instance_id: EntityInstanceId[TInput, Any],
55
+ operation: str,
56
+ input: Optional[TInput] = None) -> None:
53
57
  """Signal another entity to perform an operation.
54
58
 
55
59
  Parameters
@@ -7,6 +7,7 @@ from durabletask.internal.entity_state_shim import StateShim
7
7
  import durabletask.internal.orchestrator_service_pb2 as pb
8
8
 
9
9
  TState = TypeVar("TState")
10
+ TInput = TypeVar("TInput")
10
11
 
11
12
 
12
13
  class EntityContext:
@@ -81,7 +82,7 @@ class EntityContext:
81
82
  """
82
83
  self._state.set_state(new_state)
83
84
 
84
- def signal_entity(self, entity_instance_id: EntityInstanceId, operation: str, input: Optional[Any] = None) -> None:
85
+ def signal_entity(self, entity_instance_id: EntityInstanceId[TInput, Any], operation: str, input: Optional[TInput] = None) -> None:
85
86
  """Signal another entity to perform an operation.
86
87
 
87
88
  Parameters
@@ -0,0 +1,89 @@
1
+ from typing import Any, Callable, TypeVar, Union, overload, TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from durabletask import task
5
+ from durabletask.entities.durable_entity import DurableEntity
6
+ from durabletask.entities.entity_context import EntityContext
7
+
8
+
9
+ TInput = TypeVar('TInput')
10
+ TOutput = TypeVar('TOutput')
11
+
12
+
13
+ class EntityInstanceId[TInput, TOutput]:
14
+ @overload
15
+ def __new__(
16
+ cls,
17
+ entity: Callable[[EntityContext, TInput], TOutput],
18
+ key: str
19
+ ) -> "EntityInstanceId[TInput, TOutput]": ...
20
+
21
+ @overload
22
+ def __new__(
23
+ cls,
24
+ entity: type[DurableEntity],
25
+ key: str
26
+ ) -> "EntityInstanceId[Any, Any]": ...
27
+
28
+ @overload
29
+ def __new__(
30
+ cls,
31
+ entity: str,
32
+ key: str
33
+ ) -> "EntityInstanceId[Any, Any]": ...
34
+
35
+ def __new__(
36
+ cls,
37
+ entity: Union[task.Entity[TInput, TOutput], str],
38
+ key: str
39
+ ) -> "EntityInstanceId[Any, Any]":
40
+ return super().__new__(cls)
41
+
42
+ def __init__(
43
+ self,
44
+ entity: Union[task.Entity[TInput, TOutput], str],
45
+ key: str
46
+ ):
47
+ if not isinstance(entity, str):
48
+ from durabletask import task
49
+ entity = task.get_entity_name(entity)
50
+ self.entity: str = entity
51
+ self.key: str = key
52
+
53
+ def __str__(self) -> str:
54
+ return f"@{self.entity}@{self.key}"
55
+
56
+ def __eq__(self, other):
57
+ if not isinstance(other, EntityInstanceId):
58
+ return False
59
+ return self.entity == other.entity and self.key == other.key
60
+
61
+ def __lt__(self, other):
62
+ if not isinstance(other, EntityInstanceId):
63
+ return self < other
64
+ return str(self) < str(other)
65
+
66
+ @staticmethod
67
+ def parse(entity_id: str) -> "EntityInstanceId[Any, Any]":
68
+ """Parse a string representation of an entity ID into an EntityInstanceId object.
69
+
70
+ Parameters
71
+ ----------
72
+ entity_id : str
73
+ The string representation of the entity ID, in the format '@entity@key'.
74
+
75
+ Returns
76
+ -------
77
+ EntityInstanceId
78
+ The parsed EntityInstanceId object.
79
+
80
+ Raises
81
+ ------
82
+ ValueError
83
+ If the input string is not in the correct format.
84
+ """
85
+ try:
86
+ _, entity, key = entity_id.split("@", 2)
87
+ return EntityInstanceId(entity=entity, key=key)
88
+ except ValueError as ex:
89
+ raise ValueError(f"Invalid entity ID format: {entity_id}", ex)
@@ -24,7 +24,7 @@ class EntityMetadata:
24
24
  """
25
25
 
26
26
  def __init__(self,
27
- id: EntityInstanceId,
27
+ id: EntityInstanceId[Any, Any],
28
28
  last_modified: datetime,
29
29
  backlog_queue_size: int,
30
30
  locked_by: str,
@@ -116,11 +116,18 @@ def new_sub_orchestration_failed_event(event_id: int, ex: Exception) -> pb.Histo
116
116
  )
117
117
 
118
118
 
119
- def new_failure_details(ex: Exception) -> pb.TaskFailureDetails:
119
+ def new_failure_details(ex: Exception, _visited: Optional[set[int]] = None) -> pb.TaskFailureDetails:
120
+ if _visited is None:
121
+ _visited = set()
122
+ _visited.add(id(ex))
123
+ inner: Optional[BaseException] = ex.__cause__ or ex.__context__
124
+ if len(_visited) > 10 or (inner and id(inner) in _visited) or not isinstance(inner, Exception):
125
+ inner = None
120
126
  return pb.TaskFailureDetails(
121
127
  errorType=type(ex).__name__,
122
128
  errorMessage=str(ex),
123
- stackTrace=wrappers_pb2.StringValue(value=''.join(traceback.format_tb(ex.__traceback__)))
129
+ stackTrace=wrappers_pb2.StringValue(value=''.join(traceback.format_tb(ex.__traceback__))),
130
+ innerFailure=new_failure_details(inner, _visited) if inner else None
124
131
  )
125
132
 
126
133
 
@@ -140,14 +140,14 @@ class OrchestrationContext(ABC):
140
140
 
141
141
  @abstractmethod
142
142
  def call_entity(self,
143
- entity: EntityInstanceId,
143
+ entity: EntityInstanceId[TInput, TOutput],
144
144
  operation: str,
145
- input: Optional[TInput] = None) -> CompletableTask:
145
+ input: Optional[TInput] = None) -> CompletableTask[TOutput]:
146
146
  """Schedule entity function for execution.
147
147
 
148
148
  Parameters
149
149
  ----------
150
- entity: EntityInstanceId
150
+ entity: EntityInstanceId[TInput, TOutput]
151
151
  The ID of the entity instance to call.
152
152
  operation: str
153
153
  The name of the operation to invoke on the entity.
@@ -164,7 +164,7 @@ class OrchestrationContext(ABC):
164
164
  @abstractmethod
165
165
  def signal_entity(
166
166
  self,
167
- entity_id: EntityInstanceId,
167
+ entity_id: EntityInstanceId[TInput, TOutput],
168
168
  operation_name: str,
169
169
  input: Optional[TInput] = None
170
170
  ) -> None:
@@ -172,7 +172,7 @@ class OrchestrationContext(ABC):
172
172
 
173
173
  Parameters
174
174
  ----------
175
- entity_id: EntityInstanceId
175
+ entity_id: EntityInstanceId[TInput, TOutput]
176
176
  The ID of the entity instance to signal.
177
177
  operation_name: str
178
178
  The name of the operation to invoke on the entity.
@@ -182,7 +182,7 @@ class OrchestrationContext(ABC):
182
182
  pass
183
183
 
184
184
  @abstractmethod
185
- def lock_entities(self, entities: list[EntityInstanceId]) -> CompletableTask[EntityLock]:
185
+ def lock_entities(self, entities: list[EntityInstanceId[Any, Any]]) -> CompletableTask[EntityLock]:
186
186
  """Creates a Task object that locks the specified entity instances.
187
187
 
188
188
  The locks will be acquired the next time the orchestrator yields.
@@ -191,7 +191,7 @@ class OrchestrationContext(ABC):
191
191
 
192
192
  Parameters
193
193
  ----------
194
- entities: list[EntityInstanceId]
194
+ entities: list[EntityInstanceId[Any, Any]]
195
195
  The list of entity instance IDs to lock.
196
196
 
197
197
  Returns
@@ -302,8 +302,10 @@ class FailureDetails:
302
302
  class TaskFailedError(Exception):
303
303
  """Exception type for all orchestration task failures."""
304
304
 
305
- def __init__(self, message: str, details: pb.TaskFailureDetails):
305
+ def __init__(self, message: str, details: Union[pb.TaskFailureDetails, Exception]):
306
306
  super().__init__(message)
307
+ if isinstance(details, Exception):
308
+ details = pbh.new_failure_details(details)
307
309
  self._details = FailureDetails(
308
310
  details.errorMessage,
309
311
  details.errorType,
@@ -424,7 +426,7 @@ class CompletableTask(Task[T]):
424
426
  if self._parent is not None:
425
427
  self._parent.on_child_completed(self)
426
428
 
427
- def fail(self, message: str, details: pb.TaskFailureDetails):
429
+ def fail(self, message: str, details: Union[Exception, pb.TaskFailureDetails]):
428
430
  if self._is_complete:
429
431
  raise ValueError('The task has already completed.')
430
432
  self._exception = TaskFailedError(message, details)
@@ -536,8 +538,8 @@ class ActivityContext:
536
538
  return self._task_id
537
539
 
538
540
 
539
- # Orchestrators are generators that yield tasks and receive/return any type
540
- Orchestrator = Callable[[OrchestrationContext, TInput], Union[Generator[Task, Any, Any], TOutput]]
541
+ # Orchestrators are generators that yield tasks, recieve any type, and return TOutput
542
+ Orchestrator = Callable[[OrchestrationContext, TInput], Union[Generator[Task[Any], Any, TOutput], TOutput]]
541
543
 
542
544
  # Activities are simple functions that can be scheduled by orchestrators
543
545
  Activity = Callable[[ActivityContext, TInput], TOutput]
@@ -613,6 +615,14 @@ class RetryPolicy:
613
615
  return self._retry_timeout
614
616
 
615
617
 
618
+ def get_entity_name(fn: Entity) -> str:
619
+ if hasattr(fn, "__durable_entity_name__"):
620
+ return getattr(fn, "__durable_entity_name__")
621
+ if isinstance(fn, type) and issubclass(fn, DurableEntity):
622
+ return fn.__name__
623
+ return get_name(fn)
624
+
625
+
616
626
  def get_name(fn: Callable) -> str:
617
627
  """Returns the name of the provided function"""
618
628
  name = fn.__name__
@@ -150,7 +150,7 @@ class _Registry:
150
150
  self.entities = {}
151
151
  self.entity_instances = {}
152
152
 
153
- def add_orchestrator(self, fn: task.Orchestrator) -> str:
153
+ def add_orchestrator(self, fn: task.Orchestrator[TInput, TOutput]) -> str:
154
154
  if fn is None:
155
155
  raise ValueError("An orchestrator function argument is required.")
156
156
 
@@ -158,7 +158,7 @@ class _Registry:
158
158
  self.add_named_orchestrator(name, fn)
159
159
  return name
160
160
 
161
- def add_named_orchestrator(self, name: str, fn: task.Orchestrator) -> None:
161
+ def add_named_orchestrator(self, name: str, fn: task.Orchestrator[TInput, TOutput]) -> None:
162
162
  if not name:
163
163
  raise ValueError("A non-empty orchestrator name is required.")
164
164
  if name in self.orchestrators:
@@ -166,7 +166,7 @@ class _Registry:
166
166
 
167
167
  self.orchestrators[name] = fn
168
168
 
169
- def get_orchestrator(self, name: str) -> Optional[task.Orchestrator]:
169
+ def get_orchestrator(self, name: str) -> Optional[task.Orchestrator[Any, Any]]:
170
170
  return self.orchestrators.get(name)
171
171
 
172
172
  def add_activity(self, fn: task.Activity) -> str:
@@ -188,16 +188,13 @@ class _Registry:
188
188
  def get_activity(self, name: str) -> Optional[task.Activity]:
189
189
  return self.activities.get(name)
190
190
 
191
- def add_entity(self, fn: task.Entity) -> str:
191
+ def add_entity(self, fn: task.Entity, name: Optional[str] = None) -> str:
192
192
  if fn is None:
193
193
  raise ValueError("An entity function argument is required.")
194
194
 
195
- if isinstance(fn, type) and issubclass(fn, DurableEntity):
196
- name = fn.__name__
197
- self.add_named_entity(name, fn)
198
- else:
199
- name = task.get_name(fn)
200
- self.add_named_entity(name, fn)
195
+ if name is None:
196
+ name = task.get_entity_name(fn)
197
+ self.add_named_entity(name, fn)
201
198
  return name
202
199
 
203
200
  def add_named_entity(self, name: str, fn: task.Entity) -> None:
@@ -207,6 +204,7 @@ class _Registry:
207
204
  raise ValueError(f"A '{name}' entity already exists.")
208
205
 
209
206
  self.entities[name] = fn
207
+ setattr(fn, "__durable_entity_name__", name)
210
208
 
211
209
  def get_entity(self, name: str) -> Optional[task.Entity]:
212
210
  return self.entities.get(name)
@@ -362,7 +360,7 @@ class TaskHubGrpcWorker:
362
360
  def __exit__(self, type, value, traceback):
363
361
  self.stop()
364
362
 
365
- def add_orchestrator(self, fn: task.Orchestrator) -> str:
363
+ def add_orchestrator(self, fn: task.Orchestrator[TInput, TOutput]) -> str:
366
364
  """Registers an orchestrator function with the worker."""
367
365
  if self._is_running:
368
366
  raise RuntimeError(
@@ -378,13 +376,13 @@ class TaskHubGrpcWorker:
378
376
  )
379
377
  return self._registry.add_activity(fn)
380
378
 
381
- def add_entity(self, fn: task.Entity) -> str:
379
+ def add_entity(self, fn: task.Entity, name: Optional[str] = None) -> str:
382
380
  """Registers an entity function with the worker."""
383
381
  if self._is_running:
384
382
  raise RuntimeError(
385
383
  "Entities cannot be added while the worker is running."
386
384
  )
387
- return self._registry.add_entity(fn)
385
+ return self._registry.add_entity(fn, name)
388
386
 
389
387
  def use_versioning(self, version: VersioningOptions) -> None:
390
388
  """Initializes versioning options for sub-orchestrators and activities."""
@@ -1044,21 +1042,21 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1044
1042
 
1045
1043
  def call_entity(
1046
1044
  self,
1047
- entity: EntityInstanceId,
1045
+ entity: EntityInstanceId[TInput, TOutput],
1048
1046
  operation: str,
1049
1047
  input: Optional[TInput] = None,
1050
- ) -> task.CompletableTask:
1048
+ ) -> task.CompletableTask[TOutput]:
1051
1049
  id = self.next_sequence_number()
1052
1050
 
1053
1051
  self.call_entity_function_helper(
1054
1052
  id, entity, operation, input=input
1055
1053
  )
1056
1054
 
1057
- return self._pending_tasks.get(id, task.CompletableTask())
1055
+ return self._pending_tasks.get(id, task.CompletableTask[TOutput]())
1058
1056
 
1059
1057
  def signal_entity(
1060
1058
  self,
1061
- entity_id: EntityInstanceId,
1059
+ entity_id: EntityInstanceId[TInput, TOutput],
1062
1060
  operation_name: str,
1063
1061
  input: Optional[TInput] = None
1064
1062
  ) -> None:
@@ -1068,7 +1066,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1068
1066
  id, entity_id, operation_name, input
1069
1067
  )
1070
1068
 
1071
- def lock_entities(self, entities: list[EntityInstanceId]) -> task.CompletableTask[EntityLock]:
1069
+ def lock_entities(self, entities: list[EntityInstanceId[Any, Any]]) -> task.CompletableTask[EntityLock]:
1072
1070
  id = self.next_sequence_number()
1073
1071
 
1074
1072
  self.lock_entities_function_helper(
@@ -1158,11 +1156,11 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1158
1156
  def call_entity_function_helper(
1159
1157
  self,
1160
1158
  id: Optional[int],
1161
- entity_id: EntityInstanceId,
1159
+ entity_id: EntityInstanceId[TInput, TOutput],
1162
1160
  operation: str,
1163
1161
  *,
1164
1162
  input: Optional[TInput] = None,
1165
- ):
1163
+ ) -> None:
1166
1164
  if id is None:
1167
1165
  id = self.next_sequence_number()
1168
1166
 
@@ -1180,7 +1178,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1180
1178
  def signal_entity_function_helper(
1181
1179
  self,
1182
1180
  id: Optional[int],
1183
- entity_id: EntityInstanceId,
1181
+ entity_id: EntityInstanceId[TInput, TOutput],
1184
1182
  operation: str,
1185
1183
  input: Optional[TInput]
1186
1184
  ) -> None:
@@ -1197,7 +1195,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1197
1195
  action = ph.new_signal_entity_action(id, entity_id, operation, encoded_input, self.new_uuid())
1198
1196
  self._pending_actions[id] = action
1199
1197
 
1200
- def lock_entities_function_helper(self, id: int, entities: list[EntityInstanceId]) -> None:
1198
+ def lock_entities_function_helper(self, id: int, entities: list[EntityInstanceId[Any, Any]]) -> None:
1201
1199
  if id is None:
1202
1200
  id = self.next_sequence_number()
1203
1201
 
@@ -1792,7 +1790,7 @@ class _OrchestrationExecutor:
1792
1790
  # The orchestrator generator function completed
1793
1791
  ctx.set_complete(generatorStopped.value, pb.ORCHESTRATION_STATUS_COMPLETED)
1794
1792
 
1795
- def _parse_entity_event_sent_input(self, event: pb.HistoryEvent) -> Tuple[EntityInstanceId, str]:
1793
+ def _parse_entity_event_sent_input(self, event: pb.HistoryEvent) -> Tuple[EntityInstanceId[Any, Any], str]:
1796
1794
  try:
1797
1795
  entity_id = EntityInstanceId.parse(event.eventSent.instanceId)
1798
1796
  except ValueError:
@@ -1806,7 +1804,7 @@ class _OrchestrationExecutor:
1806
1804
  def _handle_entity_event_raised(self,
1807
1805
  ctx: _RuntimeOrchestrationContext,
1808
1806
  event: pb.HistoryEvent,
1809
- entity_id: Optional[EntityInstanceId],
1807
+ entity_id: Optional[EntityInstanceId[Any, Any]],
1810
1808
  task_id: Optional[int],
1811
1809
  is_lock_event: bool):
1812
1810
  # This eventRaised represents the result of an entity operation after being translated to the old
@@ -1919,7 +1917,7 @@ class _EntityExecutor:
1919
1917
  def execute(
1920
1918
  self,
1921
1919
  orchestration_id: str,
1922
- entity_id: EntityInstanceId,
1920
+ entity_id: EntityInstanceId[TInput, TOutput],
1923
1921
  operation: str,
1924
1922
  state: StateShim,
1925
1923
  encoded_input: Optional[str],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: durabletask
3
- Version: 0.0.0.dev21
3
+ Version: 0.0.0.dev22
4
4
  Summary: A Durable Task Client SDK for Python
5
5
  License: MIT License
6
6
 
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "durabletask"
12
- version = "0.0.0.dev21"
12
+ version = "0.0.0.dev22"
13
13
  description = "A Durable Task Client SDK for Python"
14
14
  keywords = [
15
15
  "durable",
@@ -1,42 +0,0 @@
1
- class EntityInstanceId:
2
- def __init__(self, entity: str, key: str):
3
- self.entity = entity
4
- self.key = key
5
-
6
- def __str__(self) -> str:
7
- return f"@{self.entity}@{self.key}"
8
-
9
- def __eq__(self, other):
10
- if not isinstance(other, EntityInstanceId):
11
- return False
12
- return self.entity == other.entity and self.key == other.key
13
-
14
- def __lt__(self, other):
15
- if not isinstance(other, EntityInstanceId):
16
- return self < other
17
- return str(self) < str(other)
18
-
19
- @staticmethod
20
- def parse(entity_id: str) -> "EntityInstanceId":
21
- """Parse a string representation of an entity ID into an EntityInstanceId object.
22
-
23
- Parameters
24
- ----------
25
- entity_id : str
26
- The string representation of the entity ID, in the format '@entity@key'.
27
-
28
- Returns
29
- -------
30
- EntityInstanceId
31
- The parsed EntityInstanceId object.
32
-
33
- Raises
34
- ------
35
- ValueError
36
- If the input string is not in the correct format.
37
- """
38
- try:
39
- _, entity, key = entity_id.split("@", 2)
40
- return EntityInstanceId(entity=entity, key=key)
41
- except ValueError as ex:
42
- raise ValueError(f"Invalid entity ID format: {entity_id}", ex)