ddeutil-workflow 0.0.48__py3-none-any.whl → 0.0.50__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.
- ddeutil/workflow/__about__.py +1 -1
- ddeutil/workflow/__init__.py +8 -1
- ddeutil/workflow/api/routes/logs.py +6 -5
- ddeutil/workflow/conf.py +40 -40
- ddeutil/workflow/exceptions.py +3 -3
- ddeutil/workflow/job.py +132 -76
- ddeutil/workflow/logs.py +145 -81
- ddeutil/workflow/result.py +20 -10
- ddeutil/workflow/reusables.py +3 -3
- ddeutil/workflow/scheduler.py +54 -44
- ddeutil/workflow/stages.py +514 -114
- ddeutil/workflow/utils.py +44 -40
- ddeutil/workflow/workflow.py +125 -112
- {ddeutil_workflow-0.0.48.dist-info → ddeutil_workflow-0.0.50.dist-info}/METADATA +5 -6
- ddeutil_workflow-0.0.50.dist-info/RECORD +31 -0
- ddeutil_workflow-0.0.48.dist-info/RECORD +0 -31
- {ddeutil_workflow-0.0.48.dist-info → ddeutil_workflow-0.0.50.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.48.dist-info → ddeutil_workflow-0.0.50.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.48.dist-info → ddeutil_workflow-0.0.50.dist-info}/top_level.txt +0 -0
ddeutil/workflow/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__: str = "0.0.
|
1
|
+
__version__: str = "0.0.50"
|
ddeutil/workflow/__init__.py
CHANGED
@@ -32,8 +32,11 @@ from .job import (
|
|
32
32
|
)
|
33
33
|
from .logs import (
|
34
34
|
Audit,
|
35
|
+
AuditModel,
|
36
|
+
Trace,
|
35
37
|
TraceData,
|
36
|
-
|
38
|
+
TraceMeta,
|
39
|
+
TraceModel,
|
37
40
|
get_audit,
|
38
41
|
get_dt_tznow,
|
39
42
|
get_trace,
|
@@ -46,6 +49,7 @@ from .params import (
|
|
46
49
|
StrParam,
|
47
50
|
)
|
48
51
|
from .result import (
|
52
|
+
CANCEL,
|
49
53
|
FAILED,
|
50
54
|
SKIP,
|
51
55
|
SUCCESS,
|
@@ -98,6 +102,9 @@ from .utils import (
|
|
98
102
|
get_diff_sec,
|
99
103
|
get_dt_now,
|
100
104
|
make_exec,
|
105
|
+
reach_next_minute,
|
106
|
+
replace_sec,
|
107
|
+
wait_to_next_minute,
|
101
108
|
)
|
102
109
|
from .workflow import (
|
103
110
|
Release,
|
@@ -10,7 +10,8 @@ from fastapi import APIRouter, Path, Query
|
|
10
10
|
from fastapi import status as st
|
11
11
|
from fastapi.responses import UJSONResponse
|
12
12
|
|
13
|
-
from ...logs import get_audit
|
13
|
+
from ...logs import get_audit
|
14
|
+
from ...result import Result
|
14
15
|
|
15
16
|
log_route = APIRouter(
|
16
17
|
prefix="/logs",
|
@@ -33,6 +34,7 @@ async def get_traces(
|
|
33
34
|
"""Return all trace logs from the current trace log path that config with
|
34
35
|
`WORKFLOW_LOG_PATH` environment variable name.
|
35
36
|
"""
|
37
|
+
result = Result()
|
36
38
|
return {
|
37
39
|
"message": (
|
38
40
|
f"Getting trace logs with offset: {offset} and limit: {limit}"
|
@@ -44,7 +46,7 @@ async def get_traces(
|
|
44
46
|
exclude_unset=True,
|
45
47
|
exclude_defaults=True,
|
46
48
|
)
|
47
|
-
for trace in
|
49
|
+
for trace in result.trace.find_traces()
|
48
50
|
],
|
49
51
|
}
|
50
52
|
|
@@ -63,12 +65,11 @@ async def get_trace_with_id(run_id: str):
|
|
63
65
|
- **run_id**: A running ID that want to search a trace log from the log
|
64
66
|
path.
|
65
67
|
"""
|
68
|
+
result = Result()
|
66
69
|
return {
|
67
70
|
"message": f"Getting trace log with specific running ID: {run_id}",
|
68
71
|
"trace": (
|
69
|
-
|
70
|
-
.find_log_with_id(run_id)
|
71
|
-
.model_dump(
|
72
|
+
result.trace.find_trace_with_id(run_id).model_dump(
|
72
73
|
by_alias=True,
|
73
74
|
exclude_none=True,
|
74
75
|
exclude_unset=True,
|
ddeutil/workflow/conf.py
CHANGED
@@ -11,7 +11,7 @@ from collections.abc import Iterator
|
|
11
11
|
from datetime import timedelta
|
12
12
|
from functools import cached_property
|
13
13
|
from pathlib import Path
|
14
|
-
from typing import Optional, TypeVar
|
14
|
+
from typing import Final, Optional, TypeVar
|
15
15
|
from zoneinfo import ZoneInfo
|
16
16
|
|
17
17
|
from ddeutil.core import str2bool
|
@@ -21,12 +21,15 @@ from ddeutil.io.paths import glob_files, is_ignored, read_ignore
|
|
21
21
|
from .__types import DictData, TupleStr
|
22
22
|
|
23
23
|
T = TypeVar("T")
|
24
|
-
PREFIX: str = "WORKFLOW"
|
24
|
+
PREFIX: Final[str] = "WORKFLOW"
|
25
25
|
|
26
26
|
|
27
27
|
def env(var: str, default: str | None = None) -> str | None: # pragma: no cov
|
28
28
|
"""Get environment variable with uppercase and adding prefix string.
|
29
29
|
|
30
|
+
:param var: (str) A env variable name.
|
31
|
+
:param default: (str | None) A default value if an env var does not set.
|
32
|
+
|
30
33
|
:rtype: str | None
|
31
34
|
"""
|
32
35
|
return os.getenv(f"{PREFIX}_{var.upper().replace(' ', '_')}", default)
|
@@ -50,23 +53,13 @@ class Config: # pragma: no cov
|
|
50
53
|
The config value can change when you call that config property again.
|
51
54
|
"""
|
52
55
|
|
53
|
-
# NOTE: Core
|
54
|
-
@property
|
55
|
-
def root_path(self) -> Path:
|
56
|
-
"""Root path or the project path for this workflow engine that use for
|
57
|
-
combine with `conf_path` value.
|
58
|
-
|
59
|
-
:rtype: Path
|
60
|
-
"""
|
61
|
-
return Path(env("CORE_ROOT_PATH", "."))
|
62
|
-
|
63
56
|
@property
|
64
57
|
def conf_path(self) -> Path:
|
65
58
|
"""Config path that keep all workflow template YAML files.
|
66
59
|
|
67
60
|
:rtype: Path
|
68
61
|
"""
|
69
|
-
return
|
62
|
+
return Path(env("CORE_CONF_PATH", "./conf"))
|
70
63
|
|
71
64
|
@property
|
72
65
|
def tz(self) -> ZoneInfo:
|
@@ -78,12 +71,16 @@ class Config: # pragma: no cov
|
|
78
71
|
return ZoneInfo(env("CORE_TIMEZONE", "UTC"))
|
79
72
|
|
80
73
|
@property
|
81
|
-
def
|
74
|
+
def generate_id_simple_mode(self) -> bool:
|
75
|
+
"""Flag for generate running ID with simple mode. That does not use
|
76
|
+
`md5` function after generate simple mode.
|
77
|
+
|
78
|
+
:rtype: bool
|
79
|
+
"""
|
82
80
|
return str2bool(env("CORE_GENERATE_ID_SIMPLE_MODE", "true"))
|
83
81
|
|
84
|
-
# NOTE: Register
|
85
82
|
@property
|
86
|
-
def
|
83
|
+
def registry_caller(self) -> list[str]:
|
87
84
|
"""Register Caller that is a list of importable string for the call
|
88
85
|
stage model can get.
|
89
86
|
|
@@ -93,7 +90,7 @@ class Config: # pragma: no cov
|
|
93
90
|
return [r.strip() for r in regis_call_str.split(",")]
|
94
91
|
|
95
92
|
@property
|
96
|
-
def
|
93
|
+
def registry_filter(self) -> list[str]:
|
97
94
|
"""Register Filter that is a list of importable string for the filter
|
98
95
|
template.
|
99
96
|
|
@@ -104,13 +101,16 @@ class Config: # pragma: no cov
|
|
104
101
|
)
|
105
102
|
return [r.strip() for r in regis_filter_str.split(",")]
|
106
103
|
|
107
|
-
# NOTE: Log
|
108
104
|
@property
|
109
|
-
def
|
105
|
+
def trace_path(self) -> Path:
|
110
106
|
return Path(env("LOG_TRACE_PATH", "./logs"))
|
111
107
|
|
112
108
|
@property
|
113
109
|
def debug(self) -> bool:
|
110
|
+
"""Debug flag for echo log that use DEBUG mode.
|
111
|
+
|
112
|
+
:rtype: bool
|
113
|
+
"""
|
114
114
|
return str2bool(env("LOG_DEBUG_MODE", "true"))
|
115
115
|
|
116
116
|
@property
|
@@ -150,7 +150,6 @@ class Config: # pragma: no cov
|
|
150
150
|
def log_datetime_format(self) -> str:
|
151
151
|
return env("LOG_DATETIME_FORMAT", "%Y-%m-%d %H:%M:%S")
|
152
152
|
|
153
|
-
# NOTE: Stage
|
154
153
|
@property
|
155
154
|
def stage_raise_error(self) -> bool:
|
156
155
|
return str2bool(env("CORE_STAGE_RAISE_ERROR", "false"))
|
@@ -159,17 +158,8 @@ class Config: # pragma: no cov
|
|
159
158
|
def stage_default_id(self) -> bool:
|
160
159
|
return str2bool(env("CORE_STAGE_DEFAULT_ID", "false"))
|
161
160
|
|
162
|
-
# NOTE: Job
|
163
|
-
@property
|
164
|
-
def job_raise_error(self) -> bool:
|
165
|
-
return str2bool(env("CORE_JOB_RAISE_ERROR", "true"))
|
166
|
-
|
167
161
|
@property
|
168
|
-
def
|
169
|
-
return str2bool(env("CORE_JOB_DEFAULT_ID", "false"))
|
170
|
-
|
171
|
-
@property
|
172
|
-
def max_on_per_workflow(self) -> int:
|
162
|
+
def max_cron_per_workflow(self) -> int:
|
173
163
|
"""The maximum on value that store in workflow model.
|
174
164
|
|
175
165
|
:rtype: int
|
@@ -305,23 +295,31 @@ class SimLoad:
|
|
305
295
|
)
|
306
296
|
|
307
297
|
@classmethod
|
308
|
-
def is_ignore(
|
298
|
+
def is_ignore(
|
299
|
+
cls,
|
300
|
+
file: Path,
|
301
|
+
conf_path: Path,
|
302
|
+
*,
|
303
|
+
ignore_filename: Optional[str] = None,
|
304
|
+
) -> bool:
|
309
305
|
"""Check this file was ignored.
|
310
306
|
|
311
307
|
:param file: (Path) A file path that want to check.
|
312
308
|
:param conf_path: (Path) A config path that want to read the config
|
313
309
|
ignore file.
|
310
|
+
:param ignore_filename: (str) An ignore filename.
|
314
311
|
|
315
312
|
:rtype: bool
|
316
313
|
"""
|
317
|
-
|
314
|
+
ignore_filename: str = ignore_filename or ".confignore"
|
315
|
+
return is_ignored(file, read_ignore(conf_path / ignore_filename))
|
318
316
|
|
319
317
|
@classmethod
|
320
318
|
def filter_yaml(cls, file: Path, name: str | None = None) -> DictData:
|
321
319
|
"""Read a YAML file context from an input file path and specific name.
|
322
320
|
|
323
|
-
:param file: (Path)
|
324
|
-
:param name: (str)
|
321
|
+
:param file: (Path) A file path that want to extract YAML context.
|
322
|
+
:param name: (str) A key name that search on a YAML context.
|
325
323
|
|
326
324
|
:rtype: DictData
|
327
325
|
"""
|
@@ -374,8 +372,8 @@ def dynamic(
|
|
374
372
|
class Loader(SimLoad):
|
375
373
|
"""Loader Object that get the config `yaml` file from current path.
|
376
374
|
|
377
|
-
:param name: A name of config data that will read by Yaml Loader object.
|
378
|
-
:param externals: An external parameters
|
375
|
+
:param name: (str) A name of config data that will read by Yaml Loader object.
|
376
|
+
:param externals: (DictData) An external parameters
|
379
377
|
"""
|
380
378
|
|
381
379
|
@classmethod
|
@@ -383,17 +381,19 @@ class Loader(SimLoad):
|
|
383
381
|
cls,
|
384
382
|
obj: object,
|
385
383
|
*,
|
384
|
+
path: Path | None = None,
|
386
385
|
included: list[str] | None = None,
|
387
386
|
excluded: list[str] | None = None,
|
388
|
-
path: Path | None = None,
|
389
387
|
**kwargs,
|
390
388
|
) -> Iterator[tuple[str, DictData]]:
|
391
389
|
"""Override the find class method from the Simple Loader object.
|
392
390
|
|
393
391
|
:param obj: An object that want to validate matching before return.
|
394
|
-
:param
|
395
|
-
:param excluded
|
396
|
-
|
392
|
+
:param path: (Path) A override config path.
|
393
|
+
:param included: An excluded list of data key that want to reject this
|
394
|
+
data if any key exist.
|
395
|
+
:param excluded: An included list of data key that want to filter from
|
396
|
+
data.
|
397
397
|
|
398
398
|
:rtype: Iterator[tuple[str, DictData]]
|
399
399
|
"""
|
ddeutil/workflow/exceptions.py
CHANGED
@@ -39,6 +39,9 @@ class BaseWorkflowException(Exception):
|
|
39
39
|
class UtilException(BaseWorkflowException): ...
|
40
40
|
|
41
41
|
|
42
|
+
class ResultException(UtilException): ...
|
43
|
+
|
44
|
+
|
42
45
|
class StageException(BaseWorkflowException): ...
|
43
46
|
|
44
47
|
|
@@ -48,9 +51,6 @@ class JobException(BaseWorkflowException): ...
|
|
48
51
|
class WorkflowException(BaseWorkflowException): ...
|
49
52
|
|
50
53
|
|
51
|
-
class WorkflowFailException(WorkflowException): ...
|
52
|
-
|
53
|
-
|
54
54
|
class ParamValueException(WorkflowException): ...
|
55
55
|
|
56
56
|
|