ddeutil-workflow 0.0.37__tar.gz → 0.0.38__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.38}/PKG-INFO +8 -2
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/README.md +2 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/pyproject.toml +5 -0
- ddeutil_workflow-0.0.38/src/ddeutil/workflow/__about__.py +1 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/__init__.py +4 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/routes/job.py +3 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/routes/logs.py +12 -4
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/caller.py +6 -2
- ddeutil_workflow-0.0.38/src/ddeutil/workflow/context.py +59 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/exceptions.py +14 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/job.py +100 -121
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/logs.py +6 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/result.py +1 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/stages.py +364 -111
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/utils.py +1 -44
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/workflow.py +137 -72
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil_workflow.egg-info/PKG-INFO +8 -2
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil_workflow.egg-info/SOURCES.txt +2 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil_workflow.egg-info/requires.txt +4 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_call_tag.py +33 -2
- ddeutil_workflow-0.0.38/tests/test_context.py +136 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_job.py +4 -7
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_job_exec.py +24 -28
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_job_exec_strategy.py +2 -4
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_job_strategy.py +6 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_result.py +4 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_stage_handler_exec.py +165 -4
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_utils.py +0 -9
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_workflow.py +1 -2
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_workflow_exec.py +38 -27
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_workflow_exec_job.py +0 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_workflow_exec_poke.py +0 -1
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_workflow_exec_release.py +0 -3
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/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.38}/LICENSE +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/setup.cfg +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/__cron.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/__types.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/__init__.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/api.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/log.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/repeat.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/routes/schedules.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/audit.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/conf.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/cron.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/params.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/scheduler.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil/workflow/templates.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test__cron.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test__regex.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_audit.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_conf.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_cron_on.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_logs.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_params.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_release.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_release_queue.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_schedule.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_schedule_pending.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_schedule_tasks.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_schedule_workflow.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_scheduler_control.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_stage.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/tests/test_templates.py +0 -0
- {ddeutil_workflow-0.0.37 → ddeutil_workflow-0.0.38}/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.38
|
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
|
|
@@ -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
|
@@ -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.38"
|
@@ -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,
|
@@ -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.
|
@@ -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):
|
@@ -60,10 +61,13 @@ def tag(
|
|
60
61
|
|
61
62
|
@wraps(func)
|
62
63
|
def wrapped(*args: P.args, **kwargs: P.kwargs) -> TagFunc:
|
63
|
-
# NOTE: Able to do anything before calling the call function.
|
64
64
|
return func(*args, **kwargs)
|
65
65
|
|
66
|
-
|
66
|
+
@wraps(func)
|
67
|
+
async def awrapped(*args: P.args, **kwargs: P.kwargs) -> TagFunc:
|
68
|
+
return await func(*args, **kwargs)
|
69
|
+
|
70
|
+
return awrapped if inspect.iscoroutinefunction(func) else wrapped
|
67
71
|
|
68
72
|
return func_internal
|
69
73
|
|
@@ -0,0 +1,59 @@
|
|
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
|
+
|
22
|
+
def is_exception(self) -> bool:
|
23
|
+
return self.errors is not None
|
24
|
+
|
25
|
+
|
26
|
+
class StageContext(BaseModel): # pragma: no cov
|
27
|
+
stages: dict[str, OutputContext]
|
28
|
+
errors: Optional[ErrorContext] = Field(default=None)
|
29
|
+
|
30
|
+
def is_exception(self) -> bool:
|
31
|
+
return self.errors is not None
|
32
|
+
|
33
|
+
|
34
|
+
class MatrixContext(StageContext): # pragma: no cov
|
35
|
+
matrix: DictData = Field(default_factory=dict)
|
36
|
+
|
37
|
+
|
38
|
+
MatrixStageContext = dict[
|
39
|
+
str, Union[MatrixContext, StageContext]
|
40
|
+
] # pragma: no cov
|
41
|
+
|
42
|
+
|
43
|
+
class StrategyContext(BaseModel): # pragma: no cov
|
44
|
+
strategies: MatrixStageContext
|
45
|
+
errors: Optional[ErrorContext] = Field(default=None)
|
46
|
+
|
47
|
+
def is_exception(self) -> bool:
|
48
|
+
return self.errors is not None
|
49
|
+
|
50
|
+
|
51
|
+
StrategyMatrixContext = Union[
|
52
|
+
StrategyContext, MatrixStageContext
|
53
|
+
] # pragma: no cov
|
54
|
+
|
55
|
+
|
56
|
+
class JobContext(BaseModel): # pragma: no cov
|
57
|
+
params: DictData = Field(description="A parameterize value")
|
58
|
+
jobs: dict[str, StrategyMatrixContext]
|
59
|
+
errors: Optional[ErrorContext] = Field(default=None)
|
@@ -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): ...
|