durabletask 1.3.0.dev28__tar.gz → 1.3.0.dev29__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 (44) hide show
  1. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/PKG-INFO +1 -1
  2. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/task.py +67 -6
  3. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/worker.py +105 -31
  4. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask.egg-info/PKG-INFO +1 -1
  5. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/pyproject.toml +1 -1
  6. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/LICENSE +0 -0
  7. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/README.md +0 -0
  8. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/__init__.py +0 -0
  9. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/client.py +0 -0
  10. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/entities/__init__.py +0 -0
  11. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/entities/durable_entity.py +0 -0
  12. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/entities/entity_context.py +0 -0
  13. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/entities/entity_instance_id.py +0 -0
  14. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/entities/entity_lock.py +0 -0
  15. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/entities/entity_metadata.py +0 -0
  16. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/entities/entity_operation_failed_exception.py +0 -0
  17. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/extensions/__init__.py +0 -0
  18. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/extensions/azure_blob_payloads/__init__.py +0 -0
  19. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/extensions/azure_blob_payloads/blob_payload_store.py +0 -0
  20. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/extensions/azure_blob_payloads/options.py +0 -0
  21. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/client_helpers.py +0 -0
  22. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/entity_state_shim.py +0 -0
  23. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/exceptions.py +0 -0
  24. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/grpc_interceptor.py +0 -0
  25. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/helpers.py +0 -0
  26. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/json_encode_output_exception.py +0 -0
  27. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/orchestration_entity_context.py +0 -0
  28. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/orchestrator_service_pb2.py +0 -0
  29. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/orchestrator_service_pb2.pyi +0 -0
  30. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/orchestrator_service_pb2_grpc.py +0 -0
  31. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/proto_task_hub_sidecar_service_stub.py +0 -0
  32. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/shared.py +0 -0
  33. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/internal/tracing.py +0 -0
  34. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/payload/__init__.py +0 -0
  35. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/payload/helpers.py +0 -0
  36. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/payload/store.py +0 -0
  37. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/py.typed +0 -0
  38. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/testing/__init__.py +0 -0
  39. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask/testing/in_memory_backend.py +0 -0
  40. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask.egg-info/SOURCES.txt +0 -0
  41. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask.egg-info/dependency_links.txt +0 -0
  42. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask.egg-info/requires.txt +0 -0
  43. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/durabletask.egg-info/top_level.txt +0 -0
  44. {durabletask-1.3.0.dev28 → durabletask-1.3.0.dev29}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: durabletask
3
- Version: 1.3.0.dev28
3
+ Version: 1.3.0.dev29
4
4
  Summary: A Durable Task Client SDK for Python
5
5
  License: MIT License
6
6
 
@@ -98,7 +98,7 @@ class OrchestrationContext(ABC):
98
98
  pass
99
99
 
100
100
  @abstractmethod
101
- def create_timer(self, fire_at: Union[datetime, timedelta]) -> Task:
101
+ def create_timer(self, fire_at: Union[datetime, timedelta]) -> CancellableTask:
102
102
  """Create a Timer Task to fire after at the specified deadline.
103
103
 
104
104
  Parameters
@@ -228,10 +228,10 @@ class OrchestrationContext(ABC):
228
228
  """
229
229
  pass
230
230
 
231
- # TOOD: Add a timeout parameter, which allows the task to be canceled if the event is
231
+ # TOOD: Add a timeout parameter, which allows the task to be cancelled if the event is
232
232
  # not received within the specified timeout. This requires support for task cancellation.
233
233
  @abstractmethod
234
- def wait_for_external_event(self, name: str) -> CompletableTask:
234
+ def wait_for_external_event(self, name: str) -> CancellableTask:
235
235
  """Wait asynchronously for an event to be raised with the name `name`.
236
236
 
237
237
  Parameters
@@ -324,6 +324,10 @@ class OrchestrationStateError(Exception):
324
324
  pass
325
325
 
326
326
 
327
+ class TaskCancelledError(Exception):
328
+ """Exception type for cancelled orchestration tasks."""
329
+
330
+
327
331
  class Task(ABC, Generic[T]):
328
332
  """Abstract base class for asynchronous tasks in a durable orchestration."""
329
333
  _result: T
@@ -435,6 +439,48 @@ class CompletableTask(Task[T]):
435
439
  self._parent.on_child_completed(self)
436
440
 
437
441
 
442
+ class CancellableTask(CompletableTask[T]):
443
+ """A completable task that can be cancelled before it finishes."""
444
+
445
+ def __init__(self) -> None:
446
+ super().__init__()
447
+ self._is_cancelled = False
448
+ self._cancel_handler: Optional[Callable[[], None]] = None
449
+
450
+ @property
451
+ def is_cancelled(self) -> bool:
452
+ """Returns True if the task was cancelled, False otherwise."""
453
+ return self._is_cancelled
454
+
455
+ def get_result(self) -> T:
456
+ if self._is_cancelled:
457
+ raise TaskCancelledError('The task was cancelled.')
458
+ return super().get_result()
459
+
460
+ def set_cancel_handler(self, cancel_handler: Callable[[], None]) -> None:
461
+ self._cancel_handler = cancel_handler
462
+
463
+ def cancel(self) -> bool:
464
+ """Attempts to cancel this task.
465
+
466
+ Returns
467
+ -------
468
+ bool
469
+ True if cancellation was applied, False if the task had already completed.
470
+ """
471
+ if self._is_complete:
472
+ return False
473
+
474
+ if self._cancel_handler is not None:
475
+ self._cancel_handler()
476
+
477
+ self._is_cancelled = True
478
+ self._is_complete = True
479
+ if self._parent is not None:
480
+ self._parent.on_child_completed(self)
481
+ return True
482
+
483
+
438
484
  class RetryableTask(CompletableTask[T]):
439
485
  """A task that can be retried according to a retry policy."""
440
486
 
@@ -474,14 +520,29 @@ class RetryableTask(CompletableTask[T]):
474
520
  return None
475
521
 
476
522
 
477
- class TimerTask(CompletableTask[T]):
478
-
479
- def __init__(self) -> None:
523
+ class TimerTask(CancellableTask[None]):
524
+ def __init__(self, final_fire_at: Optional[datetime] = None,
525
+ maximum_timer_interval: Optional[timedelta] = None):
480
526
  super().__init__()
527
+ self._final_fire_at = final_fire_at
528
+ self._maximum_timer_interval = maximum_timer_interval
481
529
 
482
530
  def set_retryable_parent(self, retryable_task: RetryableTask):
483
531
  self._retryable_parent = retryable_task
484
532
 
533
+ def _handle_timer_fired(self, current_utc_datetime: datetime) -> Optional[datetime]:
534
+ if (self._final_fire_at is not None
535
+ and self._maximum_timer_interval is not None
536
+ and current_utc_datetime < self._final_fire_at):
537
+ return self._get_next_fire_at(current_utc_datetime)
538
+ super().complete(None)
539
+ return None
540
+
541
+ def _get_next_fire_at(self, current_utc_datetime: datetime) -> datetime:
542
+ if current_utc_datetime + self._maximum_timer_interval < self._final_fire_at:
543
+ return current_utc_datetime + self._maximum_timer_interval
544
+ return self._final_fire_at
545
+
485
546
 
486
547
  class WhenAnyTask(CompositeTask[Task]):
487
548
  """A task that completes when any of its child tasks complete."""
@@ -43,6 +43,7 @@ from durabletask.payload.store import PayloadStore
43
43
  TInput = TypeVar("TInput")
44
44
  TOutput = TypeVar("TOutput")
45
45
  DATETIME_STRING_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
46
+ DEFAULT_MAXIMUM_TIMER_INTERVAL = timedelta(days=3)
46
47
 
47
48
 
48
49
  class ConcurrencyOptions:
@@ -415,7 +416,7 @@ class TaskHubGrpcWorker:
415
416
  activity function.
416
417
  """
417
418
 
418
- _response_stream: Optional[grpc.Future] = None
419
+ _response_stream: Optional[Any] = None
419
420
  _interceptors: Optional[list[shared.ClientInterceptor]] = None
420
421
 
421
422
  def __init__(
@@ -428,6 +429,7 @@ class TaskHubGrpcWorker:
428
429
  secure_channel: bool = False,
429
430
  interceptors: Optional[Sequence[shared.ClientInterceptor]] = None,
430
431
  concurrency_options: Optional[ConcurrencyOptions] = None,
432
+ maximum_timer_interval: Optional[timedelta] = DEFAULT_MAXIMUM_TIMER_INTERVAL,
431
433
  payload_store: Optional[PayloadStore] = None,
432
434
  ):
433
435
  self._registry = _Registry()
@@ -458,6 +460,7 @@ class TaskHubGrpcWorker:
458
460
  self._interceptors = None
459
461
 
460
462
  self._async_worker_manager = _AsyncWorkerManager(self._concurrency_options, self._logger)
463
+ self._maximum_timer_interval = maximum_timer_interval
461
464
  self._work_item_filters: Optional[WorkItemFilters] = None
462
465
  self._auto_generate_work_item_filters: bool = False
463
466
 
@@ -466,6 +469,11 @@ class TaskHubGrpcWorker:
466
469
  """Get the current concurrency options for this worker."""
467
470
  return self._concurrency_options
468
471
 
472
+ @property
473
+ def maximum_timer_interval(self) -> Optional[timedelta]:
474
+ """Get the configured maximum timer interval for long timer chunking."""
475
+ return self._maximum_timer_interval
476
+
469
477
  def __enter__(self):
470
478
  return self
471
479
 
@@ -686,7 +694,11 @@ class TaskHubGrpcWorker:
686
694
 
687
695
  def stream_reader():
688
696
  try:
689
- for work_item in self._response_stream:
697
+ response_stream = self._response_stream
698
+ if response_stream is None:
699
+ return
700
+
701
+ for work_item in response_stream:
690
702
  work_item_queue.put(work_item)
691
703
  except Exception as e:
692
704
  work_item_queue.put(e)
@@ -838,7 +850,8 @@ class TaskHubGrpcWorker:
838
850
  try:
839
851
  executor = _OrchestrationExecutor(
840
852
  self._registry, self._logger,
841
- persisted_orch_span_id=persisted_orch_span_id)
853
+ persisted_orch_span_id=persisted_orch_span_id,
854
+ maximum_timer_interval=self.maximum_timer_interval)
842
855
  result = executor.execute(instance_id, req.pastEvents, req.newEvents)
843
856
 
844
857
  # Determine completion status for span
@@ -1126,7 +1139,11 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1126
1139
  _generator: Optional[Generator[task.Task, Any, Any]]
1127
1140
  _previous_task: Optional[task.Task]
1128
1141
 
1129
- def __init__(self, instance_id: str, registry: _Registry):
1142
+ def __init__(self,
1143
+ instance_id: str,
1144
+ registry: _Registry,
1145
+ maximum_timer_interval: Optional[timedelta] = DEFAULT_MAXIMUM_TIMER_INTERVAL,
1146
+ ):
1130
1147
  self._generator = None
1131
1148
  self._is_replaying = True
1132
1149
  self._is_complete = False
@@ -1147,12 +1164,13 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1147
1164
  self._version: Optional[str] = None
1148
1165
  self._completion_status: Optional[pb.OrchestrationStatus] = None
1149
1166
  self._received_events: dict[str, list[Any]] = {}
1150
- self._pending_events: dict[str, list[task.CompletableTask]] = {}
1167
+ self._pending_events: dict[str, list[task.CancellableTask]] = {}
1151
1168
  self._new_input: Optional[Any] = None
1152
1169
  self._save_events = False
1153
1170
  self._encoded_custom_status: Optional[str] = None
1154
1171
  self._parent_trace_context: Optional[pb.TraceContext] = None
1155
1172
  self._orchestration_trace_context: Optional[pb.TraceContext] = None
1173
+ self._maximum_timer_interval = maximum_timer_interval
1156
1174
 
1157
1175
  def run(self, generator: Generator[task.Task, Any, Any]):
1158
1176
  self._generator = generator
@@ -1318,7 +1336,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1318
1336
  shared.to_json(custom_status) if custom_status is not None else None
1319
1337
  )
1320
1338
 
1321
- def create_timer(self, fire_at: Union[datetime, timedelta]) -> task.Task:
1339
+ def create_timer(self, fire_at: Union[datetime, timedelta]) -> task.CancellableTask:
1322
1340
  return self.create_timer_internal(fire_at)
1323
1341
 
1324
1342
  def create_timer_internal(
@@ -1328,11 +1346,30 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1328
1346
  ) -> task.TimerTask:
1329
1347
  id = self.next_sequence_number()
1330
1348
  if isinstance(fire_at, timedelta):
1331
- fire_at = self.current_utc_datetime + fire_at
1332
- action = ph.new_create_timer_action(id, fire_at)
1349
+ final_fire_at = self.current_utc_datetime + fire_at
1350
+ else:
1351
+ final_fire_at = fire_at
1352
+
1353
+ next_fire_at: datetime = final_fire_at
1354
+
1355
+ if (
1356
+ self._maximum_timer_interval is not None
1357
+ and self._maximum_timer_interval > timedelta(0)
1358
+ and self.current_utc_datetime + self._maximum_timer_interval < final_fire_at
1359
+ ):
1360
+ timer_task = task.TimerTask(final_fire_at, self._maximum_timer_interval)
1361
+ next_fire_at = timer_task._get_next_fire_at(self.current_utc_datetime)
1362
+ else:
1363
+ timer_task = task.TimerTask()
1364
+
1365
+ action = ph.new_create_timer_action(id, next_fire_at)
1333
1366
  self._pending_actions[id] = action
1334
1367
 
1335
- timer_task: task.TimerTask = task.TimerTask()
1368
+ def _cancel_timer() -> None:
1369
+ self._pending_actions.pop(id, None)
1370
+ self._pending_tasks.pop(id, None)
1371
+
1372
+ timer_task.set_cancel_handler(_cancel_timer)
1336
1373
  if retryable_task is not None:
1337
1374
  timer_task.set_retryable_parent(retryable_task)
1338
1375
  self._pending_tasks[id] = timer_task
@@ -1563,13 +1600,13 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1563
1600
  action = pb.OrchestratorAction(id=task_id, sendEntityMessage=entity_unlock_message)
1564
1601
  self._pending_actions[task_id] = action
1565
1602
 
1566
- def wait_for_external_event(self, name: str) -> task.CompletableTask:
1603
+ def wait_for_external_event(self, name: str) -> task.CancellableTask:
1567
1604
  # Check to see if this event has already been received, in which case we
1568
1605
  # can return it immediately. Otherwise, record out intent to receive an
1569
1606
  # event with the given name so that we can resume the generator when it
1570
1607
  # arrives. If there are multiple events with the same name, we return
1571
1608
  # them in the order they were received.
1572
- external_event_task: task.CompletableTask = task.CompletableTask()
1609
+ external_event_task: task.CancellableTask = task.CancellableTask()
1573
1610
  event_name = name.casefold()
1574
1611
  event_list = self._received_events.get(event_name, None)
1575
1612
  if event_list:
@@ -1583,6 +1620,19 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
1583
1620
  task_list = []
1584
1621
  self._pending_events[event_name] = task_list
1585
1622
  task_list.append(external_event_task)
1623
+
1624
+ def _cancel_wait() -> None:
1625
+ waiting_tasks = self._pending_events.get(event_name)
1626
+ if waiting_tasks is None:
1627
+ return
1628
+ try:
1629
+ waiting_tasks.remove(external_event_task)
1630
+ except ValueError:
1631
+ return
1632
+ if not waiting_tasks:
1633
+ del self._pending_events[event_name]
1634
+
1635
+ external_event_task.set_cancel_handler(_cancel_wait)
1586
1636
  return external_event_task
1587
1637
 
1588
1638
  def continue_as_new(self, new_input, *, save_events: bool = False) -> None:
@@ -1625,9 +1675,11 @@ class _OrchestrationExecutor:
1625
1675
  registry: _Registry,
1626
1676
  logger: logging.Logger,
1627
1677
  persisted_orch_span_id: Optional[str] = None,
1678
+ maximum_timer_interval: Optional[timedelta] = DEFAULT_MAXIMUM_TIMER_INTERVAL,
1628
1679
  ):
1629
1680
  self._registry = registry
1630
1681
  self._logger = logger
1682
+ self._maximum_timer_interval = maximum_timer_interval
1631
1683
  self._is_suspended = False
1632
1684
  self._suspended_events: list[pb.HistoryEvent] = []
1633
1685
  self._persisted_orch_span_id = persisted_orch_span_id
@@ -1661,7 +1713,11 @@ class _OrchestrationExecutor:
1661
1713
  "The new history event list must have at least one event in it."
1662
1714
  )
1663
1715
 
1664
- ctx = _RuntimeOrchestrationContext(instance_id, self._registry)
1716
+ ctx = _RuntimeOrchestrationContext(
1717
+ instance_id,
1718
+ self._registry,
1719
+ maximum_timer_interval=self._maximum_timer_interval,
1720
+ )
1665
1721
  try:
1666
1722
  # Rebuild local state by replaying old history into the orchestrator function
1667
1723
  self._logger.debug(
@@ -1818,6 +1874,12 @@ class _OrchestrationExecutor:
1818
1874
  f"{ctx.instance_id}: Ignoring unexpected timerFired event with ID = {timer_id}."
1819
1875
  )
1820
1876
  return
1877
+ if not isinstance(timer_task, task.TimerTask):
1878
+ if not ctx._is_replaying:
1879
+ self._logger.warning(
1880
+ f"{ctx.instance_id}: Ignoring timerFired event with non-timer task ID = {timer_id}."
1881
+ )
1882
+ return
1821
1883
  # Emit timer span with backdated start time (skip during replay)
1822
1884
  if not ctx.is_replaying:
1823
1885
  timer_info = self._timer_fire_at.get(timer_id)
@@ -1829,27 +1891,39 @@ class _OrchestrationExecutor:
1829
1891
  scheduled_time_ns=created_ns,
1830
1892
  parent_trace_context=ctx._orchestration_trace_context or ctx._parent_trace_context,
1831
1893
  )
1832
- timer_task.complete(None)
1833
- if timer_task._retryable_parent is not None:
1834
- activity_action = timer_task._retryable_parent._action
1894
+ next_fire_at = timer_task._handle_timer_fired(event.timerFired.fireAt.ToDatetime())
1895
+ if next_fire_at is not None:
1896
+ id = ctx.next_sequence_number()
1897
+ new_action = ph.new_create_timer_action(id, next_fire_at)
1898
+ ctx._pending_tasks[id] = timer_task
1899
+ ctx._pending_actions[id] = new_action
1900
+
1901
+ def _cancel_timer() -> None:
1902
+ ctx._pending_actions.pop(id, None)
1903
+ ctx._pending_tasks.pop(id, None)
1904
+
1905
+ timer_task.set_cancel_handler(_cancel_timer)
1906
+ else:
1907
+ if timer_task._retryable_parent is not None:
1908
+ activity_action = timer_task._retryable_parent._action
1835
1909
 
1836
- if not timer_task._retryable_parent._is_sub_orch:
1837
- cur_task = activity_action.scheduleTask
1838
- instance_id = None
1910
+ if not timer_task._retryable_parent._is_sub_orch:
1911
+ cur_task = activity_action.scheduleTask
1912
+ instance_id = None
1913
+ else:
1914
+ cur_task = activity_action.createSubOrchestration
1915
+ instance_id = cur_task.instanceId
1916
+ ctx.call_activity_function_helper(
1917
+ id=activity_action.id,
1918
+ activity_function=cur_task.name,
1919
+ input=cur_task.input.value,
1920
+ retry_policy=timer_task._retryable_parent._retry_policy,
1921
+ is_sub_orch=timer_task._retryable_parent._is_sub_orch,
1922
+ instance_id=instance_id,
1923
+ fn_task=timer_task._retryable_parent,
1924
+ )
1839
1925
  else:
1840
- cur_task = activity_action.createSubOrchestration
1841
- instance_id = cur_task.instanceId
1842
- ctx.call_activity_function_helper(
1843
- id=activity_action.id,
1844
- activity_function=cur_task.name,
1845
- input=cur_task.input.value,
1846
- retry_policy=timer_task._retryable_parent._retry_policy,
1847
- is_sub_orch=timer_task._retryable_parent._is_sub_orch,
1848
- instance_id=instance_id,
1849
- fn_task=timer_task._retryable_parent,
1850
- )
1851
- else:
1852
- ctx.resume()
1926
+ ctx.resume()
1853
1927
  elif event.HasField("taskScheduled"):
1854
1928
  # This history event confirms that the activity execution was successfully scheduled.
1855
1929
  # Remove the taskScheduled event from the pending action list so we don't schedule it again.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: durabletask
3
- Version: 1.3.0.dev28
3
+ Version: 1.3.0.dev29
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 = "1.3.0.dev28"
12
+ version = "1.3.0.dev29"
13
13
  description = "A Durable Task Client SDK for Python"
14
14
  keywords = [
15
15
  "durable",