ddeutil-workflow 0.0.37__tar.gz → 0.0.39__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.
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/PKG-INFO +9 -3
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/README.md +3 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/pyproject.toml +5 -0
- ddeutil_workflow-0.0.39/src/ddeutil/workflow/__about__.py +1 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/__init__.py +4 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/__types.py +2 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/routes/job.py +3 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/routes/logs.py +12 -4
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/audit.py +7 -5
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/caller.py +17 -12
- ddeutil_workflow-0.0.39/src/ddeutil/workflow/context.py +61 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/exceptions.py +14 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/job.py +224 -135
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/logs.py +6 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/result.py +1 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/stages.py +403 -133
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/templates.py +39 -20
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/utils.py +1 -44
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/workflow.py +168 -84
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil_workflow.egg-info/PKG-INFO +9 -3
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil_workflow.egg-info/SOURCES.txt +2 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil_workflow.egg-info/requires.txt +4 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_call_tag.py +33 -2
- ddeutil_workflow-0.0.39/tests/test_context.py +136 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_job.py +19 -9
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_job_exec.py +24 -28
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_job_exec_strategy.py +20 -4
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_job_strategy.py +6 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_result.py +4 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_stage.py +9 -27
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_stage_handler_exec.py +165 -4
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_templates.py +23 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_utils.py +0 -9
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_workflow.py +7 -2
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_workflow_exec.py +58 -28
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_workflow_exec_job.py +0 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_workflow_exec_poke.py +1 -2
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_workflow_exec_release.py +0 -3
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_workflow_task.py +0 -1
- ddeutil_workflow-0.0.37/src/ddeutil/workflow/__about__.py +0 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/LICENSE +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/setup.cfg +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/__cron.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/__init__.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/api.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/log.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/repeat.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/routes/schedules.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/conf.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/cron.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/params.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil/workflow/scheduler.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test__cron.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test__regex.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_audit.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_conf.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_cron_on.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_logs.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_params.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_release.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_release_queue.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_schedule.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_schedule_pending.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_schedule_tasks.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_schedule_workflow.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_scheduler_control.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.39}/tests/test_templates_filter.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: ddeutil-workflow
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.39
|
4
4
|
Summary: Lightweight workflow orchestration
|
5
5
|
Author-email: ddeutils <korawich.anu@gmail.com>
|
6
6
|
License: MIT
|
@@ -31,6 +31,10 @@ Provides-Extra: api
|
|
31
31
|
Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "api"
|
32
32
|
Requires-Dist: httpx; extra == "api"
|
33
33
|
Requires-Dist: ujson; extra == "api"
|
34
|
+
Provides-Extra: async
|
35
|
+
Requires-Dist: aiofiles; extra == "async"
|
36
|
+
Requires-Dist: aiohttp; extra == "async"
|
37
|
+
Dynamic: license-file
|
34
38
|
|
35
39
|
# Workflow Orchestration
|
36
40
|
|
@@ -126,7 +130,7 @@ flowchart LR
|
|
126
130
|
> - [Google **Workflows**](https://cloud.google.com/workflows)
|
127
131
|
> - [AWS **Step Functions**](https://aws.amazon.com/step-functions/)
|
128
132
|
|
129
|
-
##
|
133
|
+
## 📦 Installation
|
130
134
|
|
131
135
|
This project need `ddeutil` and `ddeutil-io` extension namespace packages.
|
132
136
|
If you want to install this package with application add-ons, you should add
|
@@ -165,6 +169,8 @@ run-py-local:
|
|
165
169
|
run-date: datetime
|
166
170
|
jobs:
|
167
171
|
getting-api-data:
|
172
|
+
runs-on:
|
173
|
+
type: local
|
168
174
|
stages:
|
169
175
|
- name: "Retrieve API Data"
|
170
176
|
id: retrieve-api
|
@@ -92,7 +92,7 @@ flowchart LR
|
|
92
92
|
> - [Google **Workflows**](https://cloud.google.com/workflows)
|
93
93
|
> - [AWS **Step Functions**](https://aws.amazon.com/step-functions/)
|
94
94
|
|
95
|
-
##
|
95
|
+
## 📦 Installation
|
96
96
|
|
97
97
|
This project need `ddeutil` and `ddeutil-io` extension namespace packages.
|
98
98
|
If you want to install this package with application add-ons, you should add
|
@@ -131,6 +131,8 @@ run-py-local:
|
|
131
131
|
run-date: datetime
|
132
132
|
jobs:
|
133
133
|
getting-api-data:
|
134
|
+
runs-on:
|
135
|
+
type: local
|
134
136
|
stages:
|
135
137
|
- name: "Retrieve API Data"
|
136
138
|
id: retrieve-api
|
@@ -40,6 +40,10 @@ api = [
|
|
40
40
|
"httpx",
|
41
41
|
"ujson",
|
42
42
|
]
|
43
|
+
async = [
|
44
|
+
"aiofiles",
|
45
|
+
"aiohttp",
|
46
|
+
]
|
43
47
|
|
44
48
|
[project.urls]
|
45
49
|
Homepage = "https://github.com/ddeutils/ddeutil-workflow/"
|
@@ -67,6 +71,7 @@ source = ["src.ddeutil.workflow"]
|
|
67
71
|
omit = [
|
68
72
|
"src/ddeutil/workflow/__about__.py",
|
69
73
|
"src/ddeutil/workflow/__cron.py",
|
74
|
+
"src/ddeutil/workflow/context.py",
|
70
75
|
"src/ddeutil/workflow/api/__init__.py",
|
71
76
|
"src/ddeutil/workflow/api/api.py",
|
72
77
|
"src/ddeutil/workflow/api/log.py",
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__: str = "0.0.39"
|
@@ -39,6 +39,8 @@ from .job import (
|
|
39
39
|
Job,
|
40
40
|
RunsOn,
|
41
41
|
Strategy,
|
42
|
+
local_execute,
|
43
|
+
local_execute_strategy,
|
42
44
|
)
|
43
45
|
from .logs import (
|
44
46
|
TraceData,
|
@@ -69,6 +71,8 @@ from .stages import (
|
|
69
71
|
BashStage,
|
70
72
|
CallStage,
|
71
73
|
EmptyStage,
|
74
|
+
ForEachStage,
|
75
|
+
ParallelStage,
|
72
76
|
PyStage,
|
73
77
|
Stage,
|
74
78
|
TriggerStage,
|
@@ -89,7 +93,6 @@ from .templates import (
|
|
89
93
|
from .utils import (
|
90
94
|
batch,
|
91
95
|
cross_product,
|
92
|
-
dash2underscore,
|
93
96
|
delay,
|
94
97
|
filter_func,
|
95
98
|
gen_id,
|
@@ -61,8 +61,10 @@ class Re:
|
|
61
61
|
# Regular expression:
|
62
62
|
# - Version 1:
|
63
63
|
# \${{\s*(?P<caller>[a-zA-Z0-9_.\s'\"\[\]\(\)\-\{}]+?)\s*(?P<post_filters>(?:\|\s*(?:[a-zA-Z0-9_]{3,}[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]+)\s*)*)}}
|
64
|
+
#
|
64
65
|
# - Version 2: (2024-09-30):
|
65
66
|
# \${{\s*(?P<caller>(?P<caller_prefix>(?:[a-zA-Z_-]+\.)*)(?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+))\s*(?P<post_filters>(?:\|\s*(?:[a-zA-Z0-9_]{3,}[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]+)\s*)*)}}
|
67
|
+
#
|
66
68
|
# - Version 3: (2024-10-05):
|
67
69
|
# \${{\s*(?P<caller>(?P<caller_prefix>(?:[a-zA-Z_-]+\??\.)*)(?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+\??))\s*(?P<post_filters>(?:\|\s*(?:[a-zA-Z0-9_]{3,}[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]+)\s*)*)}}
|
68
70
|
#
|
@@ -45,6 +45,7 @@ async def job_execute(
|
|
45
45
|
run_id=result.run_id,
|
46
46
|
parent_run_id=result.parent_run_id,
|
47
47
|
)
|
48
|
+
context: DictData = {}
|
48
49
|
try:
|
49
50
|
job.set_outputs(
|
50
51
|
job.execute(
|
@@ -52,7 +53,7 @@ async def job_execute(
|
|
52
53
|
run_id=rs.run_id,
|
53
54
|
parent_run_id=rs.parent_run_id,
|
54
55
|
).context,
|
55
|
-
to=
|
56
|
+
to=context,
|
56
57
|
)
|
57
58
|
except JobException as err:
|
58
59
|
rs.trace.error(f"[WORKFLOW]: {err.__class__.__name__}: {err}")
|
@@ -70,4 +71,5 @@ async def job_execute(
|
|
70
71
|
exclude_defaults=True,
|
71
72
|
),
|
72
73
|
"params": params,
|
74
|
+
"context": context,
|
73
75
|
}
|
@@ -6,7 +6,7 @@
|
|
6
6
|
"""This route include audit and trace log paths."""
|
7
7
|
from __future__ import annotations
|
8
8
|
|
9
|
-
from fastapi import APIRouter
|
9
|
+
from fastapi import APIRouter, Path, Query
|
10
10
|
from fastapi import status as st
|
11
11
|
from fastapi.responses import UJSONResponse
|
12
12
|
|
@@ -27,12 +27,17 @@ log_route = APIRouter(
|
|
27
27
|
summary="Read all trace logs.",
|
28
28
|
tags=["trace"],
|
29
29
|
)
|
30
|
-
async def get_traces(
|
30
|
+
async def get_traces(
|
31
|
+
offset: int = Query(default=0, gt=0),
|
32
|
+
limit: int = Query(default=100, gt=0),
|
33
|
+
):
|
31
34
|
"""Return all trace logs from the current trace log path that config with
|
32
35
|
`WORKFLOW_LOG_PATH` environment variable name.
|
33
36
|
"""
|
34
37
|
return {
|
35
|
-
"message":
|
38
|
+
"message": (
|
39
|
+
f"Getting trace logs with offset: {offset} and limit: {limit}"
|
40
|
+
),
|
36
41
|
"traces": [
|
37
42
|
trace.model_dump(
|
38
43
|
by_alias=True,
|
@@ -117,7 +122,10 @@ async def get_audit_with_workflow(workflow: str):
|
|
117
122
|
summary="Read all audit logs with specific workflow name and release date.",
|
118
123
|
tags=["audit"],
|
119
124
|
)
|
120
|
-
async def get_audit_with_workflow_release(
|
125
|
+
async def get_audit_with_workflow_release(
|
126
|
+
workflow: str = Path(...),
|
127
|
+
release: str = Path(...),
|
128
|
+
):
|
121
129
|
"""Return all audit logs with specific workflow name and release date from
|
122
130
|
the current audit log path that config with `WORKFLOW_AUDIT_PATH`
|
123
131
|
environment variable name.
|
@@ -20,7 +20,7 @@ from typing_extensions import Self
|
|
20
20
|
|
21
21
|
from .__types import DictData, TupleStr
|
22
22
|
from .conf import config
|
23
|
-
from .logs import TraceLog, get_trace
|
23
|
+
from .logs import TraceLog, get_dt_tznow, get_trace
|
24
24
|
|
25
25
|
__all__: TupleStr = (
|
26
26
|
"get_audit",
|
@@ -43,10 +43,12 @@ class BaseAudit(BaseModel, ABC):
|
|
43
43
|
default_factory=dict,
|
44
44
|
description="A context that receive from a workflow execution result.",
|
45
45
|
)
|
46
|
-
parent_run_id: Optional[str] = Field(
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
parent_run_id: Optional[str] = Field(
|
47
|
+
default=None, description="A parent running ID."
|
48
|
+
)
|
49
|
+
run_id: str = Field(description="A running ID")
|
50
|
+
update: datetime = Field(default_factory=get_dt_tznow)
|
51
|
+
execution_time: float = Field(default=0, description="An execution time.")
|
50
52
|
|
51
53
|
@model_validator(mode="after")
|
52
54
|
def __model_action(self) -> Self:
|
@@ -26,6 +26,7 @@ T = TypeVar("T")
|
|
26
26
|
P = ParamSpec("P")
|
27
27
|
|
28
28
|
logger = logging.getLogger("ddeutil.workflow")
|
29
|
+
logging.getLogger("asyncio").setLevel(logging.INFO)
|
29
30
|
|
30
31
|
|
31
32
|
class TagFunc(Protocol):
|
@@ -47,9 +48,10 @@ def tag(
|
|
47
48
|
"""Tag decorator function that set function attributes, ``tag`` and ``name``
|
48
49
|
for making registries variable.
|
49
50
|
|
50
|
-
:param: name: A tag name for make different use-case of a function.
|
51
|
-
:param: alias: A alias function name that keeping in registries.
|
52
|
-
value does not supply, it will use original function name
|
51
|
+
:param: name: (str) A tag name for make different use-case of a function.
|
52
|
+
:param: alias: (str) A alias function name that keeping in registries.
|
53
|
+
If this value does not supply, it will use original function name
|
54
|
+
from `__name__` argument.
|
53
55
|
|
54
56
|
:rtype: Callable[P, TagFunc]
|
55
57
|
"""
|
@@ -60,10 +62,13 @@ def tag(
|
|
60
62
|
|
61
63
|
@wraps(func)
|
62
64
|
def wrapped(*args: P.args, **kwargs: P.kwargs) -> TagFunc:
|
63
|
-
# NOTE: Able to do anything before calling the call function.
|
64
65
|
return func(*args, **kwargs)
|
65
66
|
|
66
|
-
|
67
|
+
@wraps(func)
|
68
|
+
async def awrapped(*args: P.args, **kwargs: P.kwargs) -> TagFunc:
|
69
|
+
return await func(*args, **kwargs)
|
70
|
+
|
71
|
+
return awrapped if inspect.iscoroutinefunction(func) else wrapped
|
67
72
|
|
68
73
|
return func_internal
|
69
74
|
|
@@ -74,7 +79,7 @@ Registry = dict[str, Callable[[], TagFunc]]
|
|
74
79
|
def make_registry(submodule: str) -> dict[str, Registry]:
|
75
80
|
"""Return registries of all functions that able to called with task.
|
76
81
|
|
77
|
-
:param submodule: A module prefix that want to import registry.
|
82
|
+
:param submodule: (str) A module prefix that want to import registry.
|
78
83
|
|
79
84
|
:rtype: dict[str, Registry]
|
80
85
|
"""
|
@@ -130,12 +135,7 @@ def extract_call(call: str) -> Callable[[], TagFunc]:
|
|
130
135
|
"""Extract Call function from string value to call partial function that
|
131
136
|
does run it at runtime.
|
132
137
|
|
133
|
-
:
|
134
|
-
not exist in the registry.
|
135
|
-
:raise NotImplementedError: When the searching call's tag result does not
|
136
|
-
exist in the registry with its function key.
|
137
|
-
|
138
|
-
:param call: A call value that able to match with Task regex.
|
138
|
+
:param call: (str) A call value that able to match with Task regex.
|
139
139
|
|
140
140
|
The format of call value should contain 3 regular expression groups
|
141
141
|
which match with the below config format:
|
@@ -148,6 +148,11 @@ def extract_call(call: str) -> Callable[[], TagFunc]:
|
|
148
148
|
>>> extract_call("tasks/return-type-not-valid@raise")
|
149
149
|
...
|
150
150
|
|
151
|
+
:raise NotImplementedError: When the searching call's function result does
|
152
|
+
not exist in the registry.
|
153
|
+
:raise NotImplementedError: When the searching call's tag result does not
|
154
|
+
exist in the registry with its function key.
|
155
|
+
|
151
156
|
:rtype: Callable[[], TagFunc]
|
152
157
|
"""
|
153
158
|
if not (found := Re.RE_TASK_FMT.search(call)):
|
@@ -0,0 +1,61 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Optional, Union
|
4
|
+
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
6
|
+
|
7
|
+
from .__types import DictData
|
8
|
+
|
9
|
+
|
10
|
+
class ErrorContext(BaseModel): # pragma: no cov
|
11
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
12
|
+
|
13
|
+
obj: Exception = Field(alias="class")
|
14
|
+
name: str = Field(description="A name of exception class.")
|
15
|
+
message: str = Field(description="A exception message.")
|
16
|
+
|
17
|
+
|
18
|
+
class OutputContext(BaseModel): # pragma: no cov
|
19
|
+
outputs: DictData = Field(default_factory=dict)
|
20
|
+
errors: Optional[ErrorContext] = Field(default=None)
|
21
|
+
skipped: bool = Field(default=False)
|
22
|
+
|
23
|
+
def is_exception(self) -> bool:
|
24
|
+
return self.errors is not None
|
25
|
+
|
26
|
+
|
27
|
+
class StageContext(BaseModel): # pragma: no cov
|
28
|
+
stages: dict[str, OutputContext]
|
29
|
+
errors: Optional[ErrorContext] = Field(default=None)
|
30
|
+
|
31
|
+
def is_exception(self) -> bool:
|
32
|
+
return self.errors is not None
|
33
|
+
|
34
|
+
|
35
|
+
class MatrixContext(StageContext): # pragma: no cov
|
36
|
+
matrix: DictData = Field(default_factory=dict)
|
37
|
+
|
38
|
+
|
39
|
+
MatrixStageContext = dict[
|
40
|
+
str, Union[MatrixContext, StageContext]
|
41
|
+
] # pragma: no cov
|
42
|
+
|
43
|
+
|
44
|
+
class StrategyContext(BaseModel): # pragma: no cov
|
45
|
+
strategies: MatrixStageContext
|
46
|
+
errors: Optional[ErrorContext] = Field(default=None)
|
47
|
+
|
48
|
+
def is_exception(self) -> bool:
|
49
|
+
return self.errors is not None
|
50
|
+
|
51
|
+
|
52
|
+
StrategyMatrixContext = Union[
|
53
|
+
StrategyContext, MatrixStageContext
|
54
|
+
] # pragma: no cov
|
55
|
+
|
56
|
+
|
57
|
+
class JobContext(BaseModel): # pragma: no cov
|
58
|
+
params: DictData = Field(description="A parameterize value")
|
59
|
+
jobs: dict[str, StrategyMatrixContext]
|
60
|
+
errors: Optional[ErrorContext] = Field(default=None)
|
61
|
+
skipped: bool = Field(default=False)
|
@@ -9,8 +9,21 @@ annotate for handle error only.
|
|
9
9
|
"""
|
10
10
|
from __future__ import annotations
|
11
11
|
|
12
|
+
from typing import Any
|
12
13
|
|
13
|
-
|
14
|
+
|
15
|
+
def to_dict(exception: Exception) -> dict[str, Any]: # pragma: no cov
|
16
|
+
return {
|
17
|
+
"class": exception,
|
18
|
+
"name": exception.__class__.__name__,
|
19
|
+
"message": str(exception),
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
class BaseWorkflowException(Exception):
|
24
|
+
|
25
|
+
def to_dict(self) -> dict[str, Any]:
|
26
|
+
return to_dict(self)
|
14
27
|
|
15
28
|
|
16
29
|
class UtilException(BaseWorkflowException): ...
|