ddeutil-workflow 0.0.28__py3-none-any.whl → 0.0.29__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 +1 -0
- ddeutil/workflow/__types.py +11 -9
- ddeutil/workflow/conf.py +38 -10
- ddeutil/workflow/hook.py +4 -2
- ddeutil/workflow/templates.py +1 -4
- ddeutil/workflow/workflow.py +4 -1
- {ddeutil_workflow-0.0.28.dist-info → ddeutil_workflow-0.0.29.dist-info}/METADATA +12 -4
- {ddeutil_workflow-0.0.28.dist-info → ddeutil_workflow-0.0.29.dist-info}/RECORD +12 -12
- {ddeutil_workflow-0.0.28.dist-info → ddeutil_workflow-0.0.29.dist-info}/LICENSE +0 -0
- {ddeutil_workflow-0.0.28.dist-info → ddeutil_workflow-0.0.29.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.28.dist-info → ddeutil_workflow-0.0.29.dist-info}/top_level.txt +0 -0
ddeutil/workflow/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__: str = "0.0.
|
1
|
+
__version__: str = "0.0.29"
|
ddeutil/workflow/__init__.py
CHANGED
ddeutil/workflow/__types.py
CHANGED
@@ -27,6 +27,8 @@ Matrix = dict[str, Union[list[str], list[int]]]
|
|
27
27
|
|
28
28
|
|
29
29
|
class Context(TypedDict):
|
30
|
+
"""TypeDict support the Context."""
|
31
|
+
|
30
32
|
params: dict[str, Any]
|
31
33
|
jobs: dict[str, Any]
|
32
34
|
|
@@ -71,14 +73,14 @@ class Re:
|
|
71
73
|
# - ${{ params.source?.schema }}
|
72
74
|
#
|
73
75
|
__re_caller: str = r"""
|
74
|
-
\$
|
75
|
-
{{
|
76
|
-
\s*
|
76
|
+
\$ # start with $
|
77
|
+
{{ # value open with {{
|
78
|
+
\s* # whitespace or not
|
77
79
|
(?P<caller>
|
78
80
|
(?P<caller_prefix>(?:[a-zA-Z_-]+\??\.)*)
|
79
81
|
(?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+\??)
|
80
82
|
)
|
81
|
-
\s*
|
83
|
+
\s* # whitespace or not
|
82
84
|
(?P<post_filters>
|
83
85
|
(?:
|
84
86
|
\|\s*
|
@@ -88,7 +90,7 @@ class Re:
|
|
88
90
|
)\s*
|
89
91
|
)*
|
90
92
|
)
|
91
|
-
}}
|
93
|
+
}} # value close with }}
|
92
94
|
"""
|
93
95
|
RE_CALLER: Pattern = re.compile(
|
94
96
|
__re_caller, MULTILINE | IGNORECASE | UNICODE | VERBOSE
|
@@ -103,13 +105,13 @@ class Re:
|
|
103
105
|
# - tasks/function@dummy
|
104
106
|
#
|
105
107
|
__re_task_fmt: str = r"""
|
106
|
-
^
|
108
|
+
^ # start task format
|
107
109
|
(?P<path>[^/@]+)
|
108
|
-
/
|
110
|
+
/ # start get function with /
|
109
111
|
(?P<func>[^@]+)
|
110
|
-
@
|
112
|
+
@ # start tag with @
|
111
113
|
(?P<tag>.+)
|
112
|
-
$
|
114
|
+
$ # end task format
|
113
115
|
"""
|
114
116
|
RE_TASK_FMT: Pattern = re.compile(
|
115
117
|
__re_task_fmt, MULTILINE | IGNORECASE | UNICODE | VERBOSE
|
ddeutil/workflow/conf.py
CHANGED
@@ -13,7 +13,7 @@ from collections.abc import Iterator
|
|
13
13
|
from datetime import datetime, timedelta
|
14
14
|
from functools import cached_property, lru_cache
|
15
15
|
from pathlib import Path
|
16
|
-
from typing import ClassVar, Optional, Union
|
16
|
+
from typing import ClassVar, Optional, TypeVar, Union
|
17
17
|
from zoneinfo import ZoneInfo
|
18
18
|
|
19
19
|
from ddeutil.core import str2bool
|
@@ -39,6 +39,7 @@ __all__: TupleStr = (
|
|
39
39
|
"env",
|
40
40
|
"get_logger",
|
41
41
|
"get_log",
|
42
|
+
"C",
|
42
43
|
"Config",
|
43
44
|
"SimLoad",
|
44
45
|
"Loader",
|
@@ -81,18 +82,42 @@ def get_logger(name: str):
|
|
81
82
|
return lg
|
82
83
|
|
83
84
|
|
84
|
-
class
|
85
|
+
class BaseConfig: # pragma: no cov
|
86
|
+
"""BaseConfig object inheritable."""
|
87
|
+
|
88
|
+
__slots__ = ()
|
89
|
+
|
90
|
+
@property
|
91
|
+
def root_path(self) -> Path:
|
92
|
+
"""Root path or the project path.
|
93
|
+
|
94
|
+
:rtype: Path
|
95
|
+
"""
|
96
|
+
return Path(os.getenv("ROOT_PATH", "."))
|
97
|
+
|
98
|
+
@property
|
99
|
+
def conf_path(self) -> Path:
|
100
|
+
"""Config path that use root_path class argument for this construction.
|
101
|
+
|
102
|
+
:rtype: Path
|
103
|
+
"""
|
104
|
+
return self.root_path / os.getenv("CONF_PATH", "conf")
|
105
|
+
|
106
|
+
|
107
|
+
class Config(BaseConfig): # pragma: no cov
|
85
108
|
"""Config object for keeping core configurations on the current session
|
86
109
|
without changing when if the application still running.
|
87
110
|
|
88
111
|
The config value can change when you call that config property again.
|
89
112
|
"""
|
90
113
|
|
91
|
-
__slots__ = ()
|
92
|
-
|
93
114
|
# NOTE: Core
|
94
115
|
@property
|
95
116
|
def root_path(self) -> Path:
|
117
|
+
"""Root path or the project path.
|
118
|
+
|
119
|
+
:rtype: Path
|
120
|
+
"""
|
96
121
|
return Path(env("CORE_ROOT_PATH", "."))
|
97
122
|
|
98
123
|
@property
|
@@ -114,7 +139,7 @@ class Config: # pragma: no cov
|
|
114
139
|
# NOTE: Register
|
115
140
|
@property
|
116
141
|
def regis_hook(self) -> list[str]:
|
117
|
-
regis_hook_str: str = env("CORE_REGISTRY", "
|
142
|
+
regis_hook_str: str = env("CORE_REGISTRY", ".")
|
118
143
|
return [r.strip() for r in regis_hook_str.split(",")]
|
119
144
|
|
120
145
|
@property
|
@@ -220,6 +245,9 @@ class Config: # pragma: no cov
|
|
220
245
|
return str2bool(env("API_ENABLE_ROUTE_SCHEDULE", "true"))
|
221
246
|
|
222
247
|
|
248
|
+
C = TypeVar("C", bound=BaseConfig)
|
249
|
+
|
250
|
+
|
223
251
|
class SimLoad:
|
224
252
|
"""Simple Load Object that will search config data by given some identity
|
225
253
|
value like name of workflow or on.
|
@@ -243,7 +271,7 @@ class SimLoad:
|
|
243
271
|
def __init__(
|
244
272
|
self,
|
245
273
|
name: str,
|
246
|
-
conf:
|
274
|
+
conf: C,
|
247
275
|
externals: DictData | None = None,
|
248
276
|
) -> None:
|
249
277
|
self.data: DictData = {}
|
@@ -256,7 +284,7 @@ class SimLoad:
|
|
256
284
|
if not self.data:
|
257
285
|
raise ValueError(f"Config {name!r} does not found on conf path")
|
258
286
|
|
259
|
-
self.conf:
|
287
|
+
self.conf: C = conf
|
260
288
|
self.externals: DictData = externals or {}
|
261
289
|
self.data.update(self.externals)
|
262
290
|
|
@@ -264,7 +292,7 @@ class SimLoad:
|
|
264
292
|
def finds(
|
265
293
|
cls,
|
266
294
|
obj: object,
|
267
|
-
conf:
|
295
|
+
conf: C,
|
268
296
|
*,
|
269
297
|
included: list[str] | None = None,
|
270
298
|
excluded: list[str] | None = None,
|
@@ -273,7 +301,7 @@ class SimLoad:
|
|
273
301
|
method can use include and exclude list of identity name for filter and
|
274
302
|
adds-on.
|
275
303
|
|
276
|
-
:param obj:
|
304
|
+
:param obj: An object that want to validate matching before return.
|
277
305
|
:param conf: A config object.
|
278
306
|
:param included:
|
279
307
|
:param excluded:
|
@@ -338,7 +366,7 @@ class Loader(SimLoad):
|
|
338
366
|
) -> Iterator[tuple[str, DictData]]:
|
339
367
|
"""Override the find class method from the Simple Loader object.
|
340
368
|
|
341
|
-
:param obj:
|
369
|
+
:param obj: An object that want to validate matching before return.
|
342
370
|
:param included:
|
343
371
|
:param excluded:
|
344
372
|
|
ddeutil/workflow/hook.py
CHANGED
@@ -50,6 +50,7 @@ def tag(
|
|
50
50
|
:param: name: A tag name for make different use-case of a function.
|
51
51
|
:param: alias: A alias function name that keeping in registries. If this
|
52
52
|
value does not supply, it will use original function name from __name__.
|
53
|
+
|
53
54
|
:rtype: Callable[P, TagFunc]
|
54
55
|
"""
|
55
56
|
|
@@ -58,7 +59,7 @@ def tag(
|
|
58
59
|
func.name = alias or func.__name__.replace("_", "-")
|
59
60
|
|
60
61
|
@wraps(func)
|
61
|
-
def wrapped(*args, **kwargs):
|
62
|
+
def wrapped(*args: P.args, **kwargs: P.kwargs) -> TagFunc:
|
62
63
|
# NOTE: Able to do anything before calling hook function.
|
63
64
|
return func(*args, **kwargs)
|
64
65
|
|
@@ -74,10 +75,11 @@ def make_registry(submodule: str) -> dict[str, Registry]:
|
|
74
75
|
"""Return registries of all functions that able to called with task.
|
75
76
|
|
76
77
|
:param submodule: A module prefix that want to import registry.
|
78
|
+
|
77
79
|
:rtype: dict[str, Registry]
|
78
80
|
"""
|
79
81
|
rs: dict[str, Registry] = {}
|
80
|
-
for module in config.regis_hook:
|
82
|
+
for module in config.regis_hook | ["ddeutil.vendors"]:
|
81
83
|
# NOTE: try to sequential import task functions
|
82
84
|
try:
|
83
85
|
importer = import_module(f"{module}.{submodule}")
|
ddeutil/workflow/templates.py
CHANGED
@@ -295,10 +295,7 @@ def str2template(
|
|
295
295
|
return search_env_replace(value)
|
296
296
|
|
297
297
|
|
298
|
-
def param2template(
|
299
|
-
value: Any,
|
300
|
-
params: DictData,
|
301
|
-
) -> Any:
|
298
|
+
def param2template(value: Any, params: DictData) -> Any:
|
302
299
|
"""Pass param to template string that can search by ``RE_CALLER`` regular
|
303
300
|
expression.
|
304
301
|
|
ddeutil/workflow/workflow.py
CHANGED
@@ -884,7 +884,9 @@ class Workflow(BaseModel):
|
|
884
884
|
:param run_id: A workflow running ID for this job execution.
|
885
885
|
:type run_id: str | None (default: None)
|
886
886
|
:param timeout: A workflow execution time out in second unit that use
|
887
|
-
for limit time of execution and waiting job dependency.
|
887
|
+
for limit time of execution and waiting job dependency. This value
|
888
|
+
does not force stop the task that still running more than this limit
|
889
|
+
time.
|
888
890
|
:type timeout: int (default: 0)
|
889
891
|
|
890
892
|
:rtype: Result
|
@@ -1220,6 +1222,7 @@ class WorkflowTask:
|
|
1220
1222
|
return queue
|
1221
1223
|
|
1222
1224
|
def __repr__(self) -> str:
|
1225
|
+
"""Override ___repr__ method."""
|
1223
1226
|
return (
|
1224
1227
|
f"{self.__class__.__name__}(alias={self.alias!r}, "
|
1225
1228
|
f"workflow={self.workflow.name!r}, runner={self.runner!r}, "
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: ddeutil-workflow
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.29
|
4
4
|
Summary: Lightweight workflow orchestration
|
5
5
|
Author-email: ddeutils <korawich.anu@gmail.com>
|
6
6
|
License: MIT
|
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Requires-Python: >=3.9.13
|
23
23
|
Description-Content-Type: text/markdown
|
24
24
|
License-File: LICENSE
|
25
|
-
Requires-Dist: ddeutil
|
25
|
+
Requires-Dist: ddeutil>=0.4.6
|
26
26
|
Requires-Dist: ddeutil-io[toml,yaml]>=0.2.3
|
27
27
|
Requires-Dist: pydantic==2.10.6
|
28
28
|
Requires-Dist: python-dotenv==1.0.1
|
@@ -55,6 +55,8 @@ the input parameters per use-case instead.
|
|
55
55
|
This way I can handle a lot of logical workflows in our orgs with only metadata
|
56
56
|
configuration. It called **Metadata Driven Data Workflow**.
|
57
57
|
|
58
|
+
---
|
59
|
+
|
58
60
|
**:pushpin: <u>Rules of This Workflow engine</u>**:
|
59
61
|
|
60
62
|
1. The Minimum frequency unit of scheduling is **1 minute** :warning:
|
@@ -62,8 +64,14 @@ configuration. It called **Metadata Driven Data Workflow**.
|
|
62
64
|
3. All parallel tasks inside workflow engine use Multi-Threading
|
63
65
|
(Python 3.13 unlock GIL :unlock:)
|
64
66
|
|
67
|
+
---
|
68
|
+
|
65
69
|
**:memo: <u>Workflow Diagrams</u>**:
|
66
70
|
|
71
|
+
This diagram show where is this application run on the production infrastructure.
|
72
|
+
You will see that this application do only running code with stress-less which mean
|
73
|
+
you should to set the data layer separate this core program before run this application.
|
74
|
+
|
67
75
|
```mermaid
|
68
76
|
flowchart LR
|
69
77
|
subgraph Interface
|
@@ -215,8 +223,8 @@ and do not raise any error to you.
|
|
215
223
|
| Name | Component | Default | Description |
|
216
224
|
|:-----------------------------|:---------:|:----------------------------------|:-------------------------------------------------------------------------------------------------------------------|
|
217
225
|
| **ROOT_PATH** | Core | `.` | The root path of the workflow application. |
|
218
|
-
| **REGISTRY** | Core |
|
219
|
-
| **REGISTRY_FILTER** | Core | `ddeutil.workflow.
|
226
|
+
| **REGISTRY** | Core | `.` | List of importable string for the hook stage. |
|
227
|
+
| **REGISTRY_FILTER** | Core | `ddeutil.workflow.templates` | List of importable string for the filter template. |
|
220
228
|
| **CONF_PATH** | Core | `conf` | The config path that keep all template `.yaml` files. |
|
221
229
|
| **TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. |
|
222
230
|
| **STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. |
|
@@ -1,25 +1,25 @@
|
|
1
|
-
ddeutil/workflow/__about__.py,sha256=
|
1
|
+
ddeutil/workflow/__about__.py,sha256=msVKiLUg4jRVo_KJlghj1cc0zwX_olhWZZkqcWYz16E,28
|
2
2
|
ddeutil/workflow/__cron.py,sha256=uA8XcbY_GwA9rJSHaHUaXaJyGDObJN0ZeYlJSinL8y8,26880
|
3
|
-
ddeutil/workflow/__init__.py,sha256=
|
4
|
-
ddeutil/workflow/__types.py,sha256=
|
5
|
-
ddeutil/workflow/conf.py,sha256=
|
3
|
+
ddeutil/workflow/__init__.py,sha256=dghn2lFl3Own4Pyq7SFHu-FMymOgLontJ6aCfxea9h4,1606
|
4
|
+
ddeutil/workflow/__types.py,sha256=CK1jfzyHP9P-MB0ElhpJZ59ZFGJC9MkQuAop5739_9k,4304
|
5
|
+
ddeutil/workflow/conf.py,sha256=7lj_Im9jsa95fWUo19Q4-ZAcHa8Pu1HW-vaLgvrjNUM,17559
|
6
6
|
ddeutil/workflow/cron.py,sha256=OLgniUxmrn65gzckk-uTmE2Pk1enJJyjYUKVeBbDQz0,7522
|
7
7
|
ddeutil/workflow/exceptions.py,sha256=XUnpJSuxOyataClP0w_gpYjzn-NIwZK2BHro-J7Yw24,895
|
8
|
-
ddeutil/workflow/hook.py,sha256
|
8
|
+
ddeutil/workflow/hook.py,sha256=vgiJVbgm4aVl-tt_HVhHn-65UXCojzGLapdOPkoX9QA,5406
|
9
9
|
ddeutil/workflow/job.py,sha256=XcewyALsLYYq94ycF6mkj3Ydr6if683z7t1oBqEVInE,24290
|
10
10
|
ddeutil/workflow/params.py,sha256=svCjmFgEhim8yFJVjZhFmKP8JqTDHQ5EPhwJHVuDGno,5289
|
11
11
|
ddeutil/workflow/result.py,sha256=k4pcj5KjbEcEPymsEUXeGY4gyLMfPkMTO6YDrAtfk7Q,3408
|
12
12
|
ddeutil/workflow/scheduler.py,sha256=OlrnBZvVttoymeY1g-on9icEMU729OWISJReeX3jAKI,20452
|
13
13
|
ddeutil/workflow/stage.py,sha256=wn8CARTvFJY4ZK1SwjzH8sKoMRz_eIeSGUMgnDWNi6g,24031
|
14
|
-
ddeutil/workflow/templates.py,sha256=
|
14
|
+
ddeutil/workflow/templates.py,sha256=bVU_8gnMQmdhhw3W28ZqwmpEaOx10Nx_aauqiLS0lqg,10807
|
15
15
|
ddeutil/workflow/utils.py,sha256=8LTqpvRPfrEYxsxhwszk6GKkyjrswxnwF3r_9vE8szw,6059
|
16
|
-
ddeutil/workflow/workflow.py,sha256=
|
16
|
+
ddeutil/workflow/workflow.py,sha256=vuy0Q3ceslBth04qbslXrp5NAQQ7XfpOochwgORzQ4Q,42349
|
17
17
|
ddeutil/workflow/api/__init__.py,sha256=F53NMBWtb9IKaDWkPU5KvybGGfKAcbehgn6TLBwHuuM,21
|
18
18
|
ddeutil/workflow/api/api.py,sha256=hmH45GtpyZ-kbiqQNmnHgjwEiCiDDXLKTGvcNa5nFos,4041
|
19
19
|
ddeutil/workflow/api/repeat.py,sha256=zyvsrXKk-3-_N8ZRZSki0Mueshugum2jtqctEOp9QSc,4927
|
20
20
|
ddeutil/workflow/api/route.py,sha256=v96jNbgjM1cJ2MpVSRWs2kgRqF8DQElEBdRZrVFEpEw,8578
|
21
|
-
ddeutil_workflow-0.0.
|
22
|
-
ddeutil_workflow-0.0.
|
23
|
-
ddeutil_workflow-0.0.
|
24
|
-
ddeutil_workflow-0.0.
|
25
|
-
ddeutil_workflow-0.0.
|
21
|
+
ddeutil_workflow-0.0.29.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
|
22
|
+
ddeutil_workflow-0.0.29.dist-info/METADATA,sha256=UXsjCGddPiksHRAByDfUcsYAsGIqAbL1qJ87uQKWCVQ,14801
|
23
|
+
ddeutil_workflow-0.0.29.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
24
|
+
ddeutil_workflow-0.0.29.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
|
25
|
+
ddeutil_workflow-0.0.29.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|