durabletask 0.3.0__py3-none-any.whl → 0.4.1__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/__init__.py +2 -2
- durabletask/client.py +8 -3
- durabletask/internal/exceptions.py +11 -0
- durabletask/internal/helpers.py +12 -6
- durabletask/internal/orchestrator_service_pb2.py +243 -186
- durabletask/internal/orchestrator_service_pb2.pyi +305 -51
- durabletask/internal/orchestrator_service_pb2_grpc.py +510 -88
- durabletask/task.py +21 -2
- durabletask/worker.py +153 -7
- durabletask-0.4.1.dist-info/METADATA +64 -0
- durabletask-0.4.1.dist-info/RECORD +16 -0
- durabletask-0.3.0.dist-info/METADATA +0 -252
- durabletask-0.3.0.dist-info/RECORD +0 -15
- {durabletask-0.3.0.dist-info → durabletask-0.4.1.dist-info}/WHEEL +0 -0
- {durabletask-0.3.0.dist-info → durabletask-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {durabletask-0.3.0.dist-info → durabletask-0.4.1.dist-info}/top_level.txt +0 -0
durabletask/task.py
CHANGED
|
@@ -35,6 +35,21 @@ class OrchestrationContext(ABC):
|
|
|
35
35
|
"""
|
|
36
36
|
pass
|
|
37
37
|
|
|
38
|
+
@property
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def version(self) -> Optional[str]:
|
|
41
|
+
"""Get the version of the orchestration instance.
|
|
42
|
+
|
|
43
|
+
This version is set when the orchestration is scheduled and can be used
|
|
44
|
+
to determine which version of the orchestrator function is being executed.
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
Optional[str]
|
|
49
|
+
The version of the orchestration instance, or None if not set.
|
|
50
|
+
"""
|
|
51
|
+
pass
|
|
52
|
+
|
|
38
53
|
@property
|
|
39
54
|
@abstractmethod
|
|
40
55
|
def current_utc_datetime(self) -> datetime:
|
|
@@ -100,7 +115,8 @@ class OrchestrationContext(ABC):
|
|
|
100
115
|
@abstractmethod
|
|
101
116
|
def call_activity(self, activity: Union[Activity[TInput, TOutput], str], *,
|
|
102
117
|
input: Optional[TInput] = None,
|
|
103
|
-
retry_policy: Optional[RetryPolicy] = None
|
|
118
|
+
retry_policy: Optional[RetryPolicy] = None,
|
|
119
|
+
tags: Optional[dict[str, str]] = None) -> Task[TOutput]:
|
|
104
120
|
"""Schedule an activity for execution.
|
|
105
121
|
|
|
106
122
|
Parameters
|
|
@@ -111,6 +127,8 @@ class OrchestrationContext(ABC):
|
|
|
111
127
|
The JSON-serializable input (or None) to pass to the activity.
|
|
112
128
|
retry_policy: Optional[RetryPolicy]
|
|
113
129
|
The retry policy to use for this activity call.
|
|
130
|
+
tags: Optional[dict[str, str]]
|
|
131
|
+
Optional tags to associate with the activity invocation.
|
|
114
132
|
|
|
115
133
|
Returns
|
|
116
134
|
-------
|
|
@@ -123,7 +141,8 @@ class OrchestrationContext(ABC):
|
|
|
123
141
|
def call_sub_orchestrator(self, orchestrator: Orchestrator[TInput, TOutput], *,
|
|
124
142
|
input: Optional[TInput] = None,
|
|
125
143
|
instance_id: Optional[str] = None,
|
|
126
|
-
retry_policy: Optional[RetryPolicy] = None
|
|
144
|
+
retry_policy: Optional[RetryPolicy] = None,
|
|
145
|
+
version: Optional[str] = None) -> Task[TOutput]:
|
|
127
146
|
"""Schedule sub-orchestrator function for execution.
|
|
128
147
|
|
|
129
148
|
Parameters
|
durabletask/worker.py
CHANGED
|
@@ -10,12 +10,15 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
10
10
|
from datetime import datetime, timedelta
|
|
11
11
|
from threading import Event, Thread
|
|
12
12
|
from types import GeneratorType
|
|
13
|
+
from enum import Enum
|
|
13
14
|
from typing import Any, Generator, Optional, Sequence, TypeVar, Union
|
|
15
|
+
from packaging.version import InvalidVersion, parse
|
|
14
16
|
|
|
15
17
|
import grpc
|
|
16
18
|
from google.protobuf import empty_pb2
|
|
17
19
|
|
|
18
20
|
import durabletask.internal.helpers as ph
|
|
21
|
+
import durabletask.internal.exceptions as pe
|
|
19
22
|
import durabletask.internal.orchestrator_service_pb2 as pb
|
|
20
23
|
import durabletask.internal.orchestrator_service_pb2_grpc as stubs
|
|
21
24
|
import durabletask.internal.shared as shared
|
|
@@ -72,9 +75,56 @@ class ConcurrencyOptions:
|
|
|
72
75
|
)
|
|
73
76
|
|
|
74
77
|
|
|
78
|
+
class VersionMatchStrategy(Enum):
|
|
79
|
+
"""Enumeration for version matching strategies."""
|
|
80
|
+
|
|
81
|
+
NONE = 1
|
|
82
|
+
STRICT = 2
|
|
83
|
+
CURRENT_OR_OLDER = 3
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class VersionFailureStrategy(Enum):
|
|
87
|
+
"""Enumeration for version failure strategies."""
|
|
88
|
+
|
|
89
|
+
REJECT = 1
|
|
90
|
+
FAIL = 2
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class VersioningOptions:
|
|
94
|
+
"""Configuration options for orchestrator and activity versioning.
|
|
95
|
+
|
|
96
|
+
This class provides options to control how versioning is handled for orchestrators
|
|
97
|
+
and activities, including whether to use the default version and how to compare versions.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
version: Optional[str] = None
|
|
101
|
+
default_version: Optional[str] = None
|
|
102
|
+
match_strategy: Optional[VersionMatchStrategy] = None
|
|
103
|
+
failure_strategy: Optional[VersionFailureStrategy] = None
|
|
104
|
+
|
|
105
|
+
def __init__(self, version: Optional[str] = None,
|
|
106
|
+
default_version: Optional[str] = None,
|
|
107
|
+
match_strategy: Optional[VersionMatchStrategy] = None,
|
|
108
|
+
failure_strategy: Optional[VersionFailureStrategy] = None
|
|
109
|
+
):
|
|
110
|
+
"""Initialize versioning options.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
version: The version of orchestrations that the worker can work on.
|
|
114
|
+
default_version: The default version that will be used for starting new sub-orchestrations.
|
|
115
|
+
match_strategy: The versioning strategy for the Durable Task worker.
|
|
116
|
+
failure_strategy: The versioning failure strategy for the Durable Task worker.
|
|
117
|
+
"""
|
|
118
|
+
self.version = version
|
|
119
|
+
self.default_version = default_version
|
|
120
|
+
self.match_strategy = match_strategy
|
|
121
|
+
self.failure_strategy = failure_strategy
|
|
122
|
+
|
|
123
|
+
|
|
75
124
|
class _Registry:
|
|
76
125
|
orchestrators: dict[str, task.Orchestrator]
|
|
77
126
|
activities: dict[str, task.Activity]
|
|
127
|
+
versioning: Optional[VersioningOptions] = None
|
|
78
128
|
|
|
79
129
|
def __init__(self):
|
|
80
130
|
self.orchestrators = {}
|
|
@@ -279,6 +329,12 @@ class TaskHubGrpcWorker:
|
|
|
279
329
|
)
|
|
280
330
|
return self._registry.add_activity(fn)
|
|
281
331
|
|
|
332
|
+
def use_versioning(self, version: VersioningOptions) -> None:
|
|
333
|
+
"""Initializes versioning options for sub-orchestrators and activities."""
|
|
334
|
+
if self._is_running:
|
|
335
|
+
raise RuntimeError("Cannot set default version while the worker is running.")
|
|
336
|
+
self._registry.versioning = version
|
|
337
|
+
|
|
282
338
|
def start(self):
|
|
283
339
|
"""Starts the worker on a background thread and begins listening for work items."""
|
|
284
340
|
if self._is_running:
|
|
@@ -513,6 +569,16 @@ class TaskHubGrpcWorker:
|
|
|
513
569
|
customStatus=ph.get_string_value(result.encoded_custom_status),
|
|
514
570
|
completionToken=completionToken,
|
|
515
571
|
)
|
|
572
|
+
except pe.AbandonOrchestrationError:
|
|
573
|
+
self._logger.info(
|
|
574
|
+
f"Abandoning orchestration. InstanceId = '{req.instanceId}'. Completion token = '{completionToken}'"
|
|
575
|
+
)
|
|
576
|
+
stub.AbandonTaskOrchestratorWorkItem(
|
|
577
|
+
pb.AbandonOrchestrationTaskRequest(
|
|
578
|
+
completionToken=completionToken
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
return
|
|
516
582
|
except Exception as ex:
|
|
517
583
|
self._logger.exception(
|
|
518
584
|
f"An error occurred while trying to execute instance '{req.instanceId}': {ex}"
|
|
@@ -574,7 +640,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
574
640
|
_generator: Optional[Generator[task.Task, Any, Any]]
|
|
575
641
|
_previous_task: Optional[task.Task]
|
|
576
642
|
|
|
577
|
-
def __init__(self, instance_id: str):
|
|
643
|
+
def __init__(self, instance_id: str, registry: _Registry):
|
|
578
644
|
self._generator = None
|
|
579
645
|
self._is_replaying = True
|
|
580
646
|
self._is_complete = False
|
|
@@ -584,6 +650,8 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
584
650
|
self._sequence_number = 0
|
|
585
651
|
self._current_utc_datetime = datetime(1000, 1, 1)
|
|
586
652
|
self._instance_id = instance_id
|
|
653
|
+
self._registry = registry
|
|
654
|
+
self._version: Optional[str] = None
|
|
587
655
|
self._completion_status: Optional[pb.OrchestrationStatus] = None
|
|
588
656
|
self._received_events: dict[str, list[Any]] = {}
|
|
589
657
|
self._pending_events: dict[str, list[task.CompletableTask]] = {}
|
|
@@ -646,7 +714,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
646
714
|
)
|
|
647
715
|
self._pending_actions[action.id] = action
|
|
648
716
|
|
|
649
|
-
def set_failed(self, ex: Exception):
|
|
717
|
+
def set_failed(self, ex: Union[Exception, pb.TaskFailureDetails]):
|
|
650
718
|
if self._is_complete:
|
|
651
719
|
return
|
|
652
720
|
|
|
@@ -658,7 +726,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
658
726
|
self.next_sequence_number(),
|
|
659
727
|
pb.ORCHESTRATION_STATUS_FAILED,
|
|
660
728
|
None,
|
|
661
|
-
ph.new_failure_details(ex),
|
|
729
|
+
ph.new_failure_details(ex) if isinstance(ex, Exception) else ex,
|
|
662
730
|
)
|
|
663
731
|
self._pending_actions[action.id] = action
|
|
664
732
|
|
|
@@ -709,6 +777,10 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
709
777
|
def instance_id(self) -> str:
|
|
710
778
|
return self._instance_id
|
|
711
779
|
|
|
780
|
+
@property
|
|
781
|
+
def version(self) -> Optional[str]:
|
|
782
|
+
return self._version
|
|
783
|
+
|
|
712
784
|
@property
|
|
713
785
|
def current_utc_datetime(self) -> datetime:
|
|
714
786
|
return self._current_utc_datetime
|
|
@@ -752,11 +824,12 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
752
824
|
*,
|
|
753
825
|
input: Optional[TInput] = None,
|
|
754
826
|
retry_policy: Optional[task.RetryPolicy] = None,
|
|
827
|
+
tags: Optional[dict[str, str]] = None,
|
|
755
828
|
) -> task.Task[TOutput]:
|
|
756
829
|
id = self.next_sequence_number()
|
|
757
830
|
|
|
758
831
|
self.call_activity_function_helper(
|
|
759
|
-
id, activity, input=input, retry_policy=retry_policy, is_sub_orch=False
|
|
832
|
+
id, activity, input=input, retry_policy=retry_policy, is_sub_orch=False, tags=tags
|
|
760
833
|
)
|
|
761
834
|
return self._pending_tasks.get(id, task.CompletableTask())
|
|
762
835
|
|
|
@@ -767,9 +840,12 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
767
840
|
input: Optional[TInput] = None,
|
|
768
841
|
instance_id: Optional[str] = None,
|
|
769
842
|
retry_policy: Optional[task.RetryPolicy] = None,
|
|
843
|
+
version: Optional[str] = None,
|
|
770
844
|
) -> task.Task[TOutput]:
|
|
771
845
|
id = self.next_sequence_number()
|
|
772
846
|
orchestrator_name = task.get_name(orchestrator)
|
|
847
|
+
default_version = self._registry.versioning.default_version if self._registry.versioning else None
|
|
848
|
+
orchestrator_version = version if version else default_version
|
|
773
849
|
self.call_activity_function_helper(
|
|
774
850
|
id,
|
|
775
851
|
orchestrator_name,
|
|
@@ -777,6 +853,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
777
853
|
retry_policy=retry_policy,
|
|
778
854
|
is_sub_orch=True,
|
|
779
855
|
instance_id=instance_id,
|
|
856
|
+
version=orchestrator_version
|
|
780
857
|
)
|
|
781
858
|
return self._pending_tasks.get(id, task.CompletableTask())
|
|
782
859
|
|
|
@@ -787,9 +864,11 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
787
864
|
*,
|
|
788
865
|
input: Optional[TInput] = None,
|
|
789
866
|
retry_policy: Optional[task.RetryPolicy] = None,
|
|
867
|
+
tags: Optional[dict[str, str]] = None,
|
|
790
868
|
is_sub_orch: bool = False,
|
|
791
869
|
instance_id: Optional[str] = None,
|
|
792
870
|
fn_task: Optional[task.CompletableTask[TOutput]] = None,
|
|
871
|
+
version: Optional[str] = None,
|
|
793
872
|
):
|
|
794
873
|
if id is None:
|
|
795
874
|
id = self.next_sequence_number()
|
|
@@ -806,7 +885,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
806
885
|
if isinstance(activity_function, str)
|
|
807
886
|
else task.get_name(activity_function)
|
|
808
887
|
)
|
|
809
|
-
action = ph.new_schedule_task_action(id, name, encoded_input)
|
|
888
|
+
action = ph.new_schedule_task_action(id, name, encoded_input, tags)
|
|
810
889
|
else:
|
|
811
890
|
if instance_id is None:
|
|
812
891
|
# Create a deteministic instance ID based on the parent instance ID
|
|
@@ -814,7 +893,7 @@ class _RuntimeOrchestrationContext(task.OrchestrationContext):
|
|
|
814
893
|
if not isinstance(activity_function, str):
|
|
815
894
|
raise ValueError("Orchestrator function name must be a string")
|
|
816
895
|
action = ph.new_create_sub_orchestration_action(
|
|
817
|
-
id, activity_function, instance_id, encoded_input
|
|
896
|
+
id, activity_function, instance_id, encoded_input, version
|
|
818
897
|
)
|
|
819
898
|
self._pending_actions[id] = action
|
|
820
899
|
|
|
@@ -890,7 +969,7 @@ class _OrchestrationExecutor:
|
|
|
890
969
|
"The new history event list must have at least one event in it."
|
|
891
970
|
)
|
|
892
971
|
|
|
893
|
-
ctx = _RuntimeOrchestrationContext(instance_id)
|
|
972
|
+
ctx = _RuntimeOrchestrationContext(instance_id, self._registry)
|
|
894
973
|
try:
|
|
895
974
|
# Rebuild local state by replaying old history into the orchestrator function
|
|
896
975
|
self._logger.debug(
|
|
@@ -910,6 +989,15 @@ class _OrchestrationExecutor:
|
|
|
910
989
|
for new_event in new_events:
|
|
911
990
|
self.process_event(ctx, new_event)
|
|
912
991
|
|
|
992
|
+
except pe.VersionFailureException as ex:
|
|
993
|
+
if self._registry.versioning and self._registry.versioning.failure_strategy == VersionFailureStrategy.FAIL:
|
|
994
|
+
if ex.error_details:
|
|
995
|
+
ctx.set_failed(ex.error_details)
|
|
996
|
+
else:
|
|
997
|
+
ctx.set_failed(ex)
|
|
998
|
+
elif self._registry.versioning and self._registry.versioning.failure_strategy == VersionFailureStrategy.REJECT:
|
|
999
|
+
raise pe.AbandonOrchestrationError
|
|
1000
|
+
|
|
913
1001
|
except Exception as ex:
|
|
914
1002
|
# Unhandled exceptions fail the orchestration
|
|
915
1003
|
ctx.set_failed(ex)
|
|
@@ -959,6 +1047,22 @@ class _OrchestrationExecutor:
|
|
|
959
1047
|
f"A '{event.executionStarted.name}' orchestrator was not registered."
|
|
960
1048
|
)
|
|
961
1049
|
|
|
1050
|
+
if event.executionStarted.version:
|
|
1051
|
+
ctx._version = event.executionStarted.version.value
|
|
1052
|
+
|
|
1053
|
+
if self._registry.versioning:
|
|
1054
|
+
version_failure = self.evaluate_orchestration_versioning(
|
|
1055
|
+
self._registry.versioning,
|
|
1056
|
+
ctx.version
|
|
1057
|
+
)
|
|
1058
|
+
if version_failure:
|
|
1059
|
+
self._logger.warning(
|
|
1060
|
+
f"Orchestration version did not meet worker versioning requirements. "
|
|
1061
|
+
f"Error action = '{self._registry.versioning.failure_strategy}'. "
|
|
1062
|
+
f"Version error = '{version_failure}'"
|
|
1063
|
+
)
|
|
1064
|
+
raise pe.VersionFailureException(version_failure)
|
|
1065
|
+
|
|
962
1066
|
# deserialize the input, if any
|
|
963
1067
|
input = None
|
|
964
1068
|
if (
|
|
@@ -1221,6 +1325,48 @@ class _OrchestrationExecutor:
|
|
|
1221
1325
|
# The orchestrator generator function completed
|
|
1222
1326
|
ctx.set_complete(generatorStopped.value, pb.ORCHESTRATION_STATUS_COMPLETED)
|
|
1223
1327
|
|
|
1328
|
+
def evaluate_orchestration_versioning(self, versioning: Optional[VersioningOptions], orchestration_version: Optional[str]) -> Optional[pb.TaskFailureDetails]:
|
|
1329
|
+
if versioning is None:
|
|
1330
|
+
return None
|
|
1331
|
+
version_comparison = self.compare_versions(orchestration_version, versioning.version)
|
|
1332
|
+
if versioning.match_strategy == VersionMatchStrategy.NONE:
|
|
1333
|
+
return None
|
|
1334
|
+
elif versioning.match_strategy == VersionMatchStrategy.STRICT:
|
|
1335
|
+
if version_comparison != 0:
|
|
1336
|
+
return pb.TaskFailureDetails(
|
|
1337
|
+
errorType="VersionMismatch",
|
|
1338
|
+
errorMessage=f"The orchestration version '{orchestration_version}' does not match the worker version '{versioning.version}'.",
|
|
1339
|
+
isNonRetriable=True,
|
|
1340
|
+
)
|
|
1341
|
+
elif versioning.match_strategy == VersionMatchStrategy.CURRENT_OR_OLDER:
|
|
1342
|
+
if version_comparison > 0:
|
|
1343
|
+
return pb.TaskFailureDetails(
|
|
1344
|
+
errorType="VersionMismatch",
|
|
1345
|
+
errorMessage=f"The orchestration version '{orchestration_version}' is greater than the worker version '{versioning.version}'.",
|
|
1346
|
+
isNonRetriable=True,
|
|
1347
|
+
)
|
|
1348
|
+
else:
|
|
1349
|
+
# If there is a type of versioning we don't understand, it is better to treat it as a versioning failure.
|
|
1350
|
+
return pb.TaskFailureDetails(
|
|
1351
|
+
errorType="VersionMismatch",
|
|
1352
|
+
errorMessage=f"The version match strategy '{versioning.match_strategy}' is unknown.",
|
|
1353
|
+
isNonRetriable=True,
|
|
1354
|
+
)
|
|
1355
|
+
|
|
1356
|
+
def compare_versions(self, source_version: Optional[str], default_version: Optional[str]) -> int:
|
|
1357
|
+
if not source_version and not default_version:
|
|
1358
|
+
return 0
|
|
1359
|
+
if not source_version:
|
|
1360
|
+
return -1
|
|
1361
|
+
if not default_version:
|
|
1362
|
+
return 1
|
|
1363
|
+
try:
|
|
1364
|
+
source_version_parsed = parse(source_version)
|
|
1365
|
+
default_version_parsed = parse(default_version)
|
|
1366
|
+
return (source_version_parsed > default_version_parsed) - (source_version_parsed < default_version_parsed)
|
|
1367
|
+
except InvalidVersion:
|
|
1368
|
+
return (source_version > default_version) - (source_version < default_version)
|
|
1369
|
+
|
|
1224
1370
|
|
|
1225
1371
|
class _ActivityExecutor:
|
|
1226
1372
|
def __init__(self, registry: _Registry, logger: logging.Logger):
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: durabletask
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: A Durable Task Client SDK for Python
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) Microsoft Corporation.
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE
|
|
26
|
+
|
|
27
|
+
Project-URL: repository, https://github.com/microsoft/durabletask-python
|
|
28
|
+
Project-URL: changelog, https://github.com/microsoft/durabletask-python/blob/main/CHANGELOG.md
|
|
29
|
+
Keywords: durable,task,workflow
|
|
30
|
+
Classifier: Development Status :: 3 - Alpha
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Requires-Python: >=3.9
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Requires-Dist: grpcio
|
|
37
|
+
Requires-Dist: protobuf
|
|
38
|
+
Requires-Dist: asyncio
|
|
39
|
+
Requires-Dist: packaging
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# Durable Task SDK for Python
|
|
43
|
+
|
|
44
|
+
[](https://opensource.org/licenses/MIT)
|
|
45
|
+
[](https://github.com/microsoft/durabletask-python/actions/workflows/pr-validation.yml)
|
|
46
|
+
[](https://badge.fury.io/py/durabletask)
|
|
47
|
+
|
|
48
|
+
This repo contains a Python SDK for use with the [Azure Durable Task Scheduler](https://github.com/Azure/Durable-Task-Scheduler). With this SDK, you can define, schedule, and manage durable orchestrations using ordinary Python code.
|
|
49
|
+
|
|
50
|
+
> Note that this SDK is **not** currently compatible with [Azure Durable Functions](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview). If you are looking for a Python SDK for Azure Durable Functions, please see [this repo](https://github.com/Azure/azure-functions-durable-python).
|
|
51
|
+
|
|
52
|
+
# References
|
|
53
|
+
- [Supported Patterns](./docs/supported-patterns.md)
|
|
54
|
+
- [Available Features](./docs/features.md)
|
|
55
|
+
- [Getting Started](./docs/getting-started.md)
|
|
56
|
+
- [Development Guide](./docs/development.md)
|
|
57
|
+
- [Contributing Guide](./CONTRIBUTING.md)
|
|
58
|
+
|
|
59
|
+
## Trademarks
|
|
60
|
+
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
|
|
61
|
+
trademarks or logos is subject to and must follow
|
|
62
|
+
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
|
63
|
+
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
|
64
|
+
Any use of third-party trademarks or logos are subject to those third-party's policies.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
durabletask/__init__.py,sha256=1PTnFPvigLCs2apa7ITASeqqFOS09Zn-rrllyWeoDJE,263
|
|
2
|
+
durabletask/client.py,sha256=VrHXntWfSk4xYAv7JkOOXgNoFzefYWcD7UPGgn2zuWM,10312
|
|
3
|
+
durabletask/task.py,sha256=LCtKrDh_Yb94Zjxx8fUtXt3z5GwSaV-EieIFXKeZNOI,18404
|
|
4
|
+
durabletask/worker.py,sha256=B0cUfzbgQW2WlYzixvQqRZkWRtvsETdD3jDLz64RwjU,73279
|
|
5
|
+
durabletask/internal/exceptions.py,sha256=G1qnH5KReJk04_eewMIr2TTXvdeWDriq-SiAwSahMMs,298
|
|
6
|
+
durabletask/internal/grpc_interceptor.py,sha256=KGl8GGIbNdiEnWVLwQwkOemWvIlcEO0dh-_Tg20h5XA,2834
|
|
7
|
+
durabletask/internal/helpers.py,sha256=7A1Bb-KNuAovQTOh9mxeTFahCPtl45C5j5tUJ43BXuo,7538
|
|
8
|
+
durabletask/internal/orchestrator_service_pb2.py,sha256=q4elBQnofrZ4eYVCVnmed4vsa-FLNFLeBRtFOZYhTv8,44631
|
|
9
|
+
durabletask/internal/orchestrator_service_pb2.pyi,sha256=FFxZhxdV7SX0pKxwKPHTGaAdXKowXnib4WvjhGFw2eo,66480
|
|
10
|
+
durabletask/internal/orchestrator_service_pb2_grpc.py,sha256=5xhDLJ73Ipsp1tjwWhUqVqEm30MjWHlj71zfPNBehWc,54366
|
|
11
|
+
durabletask/internal/shared.py,sha256=dKRGU8z1EQM4_YA6zkKeKfiaWbiZ6-B8lP-wHy7Q_jI,4379
|
|
12
|
+
durabletask-0.4.1.dist-info/licenses/LICENSE,sha256=ws_MuBL-SCEBqPBFl9_FqZkaaydIJmxHrJG2parhU4M,1141
|
|
13
|
+
durabletask-0.4.1.dist-info/METADATA,sha256=NMPUDyoxbMC33_X068dk80Ml3p3LKmXuTDV4VQlusw4,3724
|
|
14
|
+
durabletask-0.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
durabletask-0.4.1.dist-info/top_level.txt,sha256=EBVyuKWnjOwq8bJI1Uvb9U3c4fzQxACWj9p83he6fik,12
|
|
16
|
+
durabletask-0.4.1.dist-info/RECORD,,
|