durable-workflow 0.1.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.
- durable_workflow/__init__.py +75 -0
- durable_workflow/activity.py +83 -0
- durable_workflow/client.py +854 -0
- durable_workflow/errors.py +142 -0
- durable_workflow/py.typed +0 -0
- durable_workflow/retry_policy.py +85 -0
- durable_workflow/serializer.py +54 -0
- durable_workflow/sync.py +337 -0
- durable_workflow/worker.py +329 -0
- durable_workflow/workflow.py +419 -0
- durable_workflow-0.1.0.dist-info/METADATA +130 -0
- durable_workflow-0.1.0.dist-info/RECORD +15 -0
- durable_workflow-0.1.0.dist-info/WHEEL +5 -0
- durable_workflow-0.1.0.dist-info/licenses/LICENSE +21 -0
- durable_workflow-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from . import activity, sync, workflow
|
|
2
|
+
from .activity import ActivityContext, ActivityInfo
|
|
3
|
+
from .client import (
|
|
4
|
+
Client,
|
|
5
|
+
ScheduleAction,
|
|
6
|
+
ScheduleBackfillResult,
|
|
7
|
+
ScheduleDescription,
|
|
8
|
+
ScheduleHandle,
|
|
9
|
+
ScheduleList,
|
|
10
|
+
ScheduleSpec,
|
|
11
|
+
ScheduleTriggerResult,
|
|
12
|
+
WorkflowExecution,
|
|
13
|
+
WorkflowHandle,
|
|
14
|
+
WorkflowList,
|
|
15
|
+
)
|
|
16
|
+
from .errors import (
|
|
17
|
+
ActivityCancelled,
|
|
18
|
+
ChildWorkflowFailed,
|
|
19
|
+
DurableWorkflowError,
|
|
20
|
+
InvalidArgument,
|
|
21
|
+
NamespaceNotFound,
|
|
22
|
+
NonRetryableError,
|
|
23
|
+
QueryFailed,
|
|
24
|
+
ScheduleAlreadyExists,
|
|
25
|
+
ScheduleNotFound,
|
|
26
|
+
ServerError,
|
|
27
|
+
Unauthorized,
|
|
28
|
+
UpdateRejected,
|
|
29
|
+
WorkflowAlreadyStarted,
|
|
30
|
+
WorkflowCancelled,
|
|
31
|
+
WorkflowFailed,
|
|
32
|
+
WorkflowNotFound,
|
|
33
|
+
WorkflowTerminated,
|
|
34
|
+
)
|
|
35
|
+
from .worker import Worker
|
|
36
|
+
from .workflow import ContinueAsNew, StartChildWorkflow
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"ActivityCancelled",
|
|
40
|
+
"ActivityContext",
|
|
41
|
+
"ActivityInfo",
|
|
42
|
+
"ChildWorkflowFailed",
|
|
43
|
+
"Client",
|
|
44
|
+
"ContinueAsNew",
|
|
45
|
+
"NonRetryableError",
|
|
46
|
+
"ScheduleAction",
|
|
47
|
+
"ScheduleAlreadyExists",
|
|
48
|
+
"ScheduleBackfillResult",
|
|
49
|
+
"ScheduleDescription",
|
|
50
|
+
"ScheduleHandle",
|
|
51
|
+
"ScheduleList",
|
|
52
|
+
"ScheduleNotFound",
|
|
53
|
+
"ScheduleSpec",
|
|
54
|
+
"ScheduleTriggerResult",
|
|
55
|
+
"StartChildWorkflow",
|
|
56
|
+
"Worker",
|
|
57
|
+
"WorkflowExecution",
|
|
58
|
+
"WorkflowHandle",
|
|
59
|
+
"WorkflowList",
|
|
60
|
+
"activity",
|
|
61
|
+
"sync",
|
|
62
|
+
"workflow",
|
|
63
|
+
"DurableWorkflowError",
|
|
64
|
+
"InvalidArgument",
|
|
65
|
+
"NamespaceNotFound",
|
|
66
|
+
"QueryFailed",
|
|
67
|
+
"ServerError",
|
|
68
|
+
"Unauthorized",
|
|
69
|
+
"UpdateRejected",
|
|
70
|
+
"WorkflowAlreadyStarted",
|
|
71
|
+
"WorkflowCancelled",
|
|
72
|
+
"WorkflowFailed",
|
|
73
|
+
"WorkflowNotFound",
|
|
74
|
+
"WorkflowTerminated",
|
|
75
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Activity decorator, registry, and execution context."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import contextvars
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from .errors import ActivityCancelled
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .client import Client
|
|
13
|
+
|
|
14
|
+
_REGISTRY: dict[str, Callable[..., Any]] = {}
|
|
15
|
+
|
|
16
|
+
_current_context: contextvars.ContextVar[ActivityContext | None] = contextvars.ContextVar(
|
|
17
|
+
"activity_context", default=None
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class ActivityInfo:
|
|
23
|
+
task_id: str
|
|
24
|
+
activity_type: str
|
|
25
|
+
activity_attempt_id: str
|
|
26
|
+
attempt_number: int
|
|
27
|
+
task_queue: str
|
|
28
|
+
worker_id: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ActivityContext:
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
*,
|
|
35
|
+
info: ActivityInfo,
|
|
36
|
+
client: Client,
|
|
37
|
+
) -> None:
|
|
38
|
+
self._info = info
|
|
39
|
+
self._client = client
|
|
40
|
+
self._cancel_requested = False
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def info(self) -> ActivityInfo:
|
|
44
|
+
return self._info
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def is_cancelled(self) -> bool:
|
|
48
|
+
return self._cancel_requested
|
|
49
|
+
|
|
50
|
+
async def heartbeat(self, details: dict[str, Any] | None = None) -> None:
|
|
51
|
+
resp = await self._client.heartbeat_activity_task(
|
|
52
|
+
task_id=self._info.task_id,
|
|
53
|
+
activity_attempt_id=self._info.activity_attempt_id,
|
|
54
|
+
lease_owner=self._info.worker_id,
|
|
55
|
+
details=details,
|
|
56
|
+
)
|
|
57
|
+
if isinstance(resp, dict) and resp.get("cancel_requested"):
|
|
58
|
+
self._cancel_requested = True
|
|
59
|
+
raise ActivityCancelled()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def context() -> ActivityContext:
|
|
63
|
+
ctx = _current_context.get()
|
|
64
|
+
if ctx is None:
|
|
65
|
+
raise RuntimeError("activity.context() called outside of an activity execution")
|
|
66
|
+
return ctx
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _set_context(ctx: ActivityContext | None) -> contextvars.Token[ActivityContext | None]:
|
|
70
|
+
return _current_context.set(ctx)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def defn(*, name: str) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
74
|
+
def wrap(fn: Callable[..., Any]) -> Callable[..., Any]:
|
|
75
|
+
fn.__activity_name__ = name # type: ignore[attr-defined]
|
|
76
|
+
_REGISTRY[name] = fn
|
|
77
|
+
return fn
|
|
78
|
+
|
|
79
|
+
return wrap
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def registry() -> dict[str, Callable[..., Any]]:
|
|
83
|
+
return dict(_REGISTRY)
|