ddeutil-workflow 0.0.37__py3-none-any.whl → 0.0.39__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 +4 -1
- ddeutil/workflow/__types.py +2 -0
- ddeutil/workflow/api/routes/job.py +3 -1
- ddeutil/workflow/api/routes/logs.py +12 -4
- ddeutil/workflow/audit.py +7 -5
- ddeutil/workflow/caller.py +17 -12
- ddeutil/workflow/context.py +61 -0
- ddeutil/workflow/exceptions.py +14 -1
- ddeutil/workflow/job.py +224 -135
- ddeutil/workflow/logs.py +6 -1
- ddeutil/workflow/result.py +1 -1
- ddeutil/workflow/stages.py +403 -133
- ddeutil/workflow/templates.py +39 -20
- ddeutil/workflow/utils.py +1 -44
- ddeutil/workflow/workflow.py +168 -84
- {ddeutil_workflow-0.0.37.dist-info → ddeutil_workflow-0.0.39.dist-info}/METADATA +9 -3
- ddeutil_workflow-0.0.39.dist-info/RECORD +33 -0
- {ddeutil_workflow-0.0.37.dist-info → ddeutil_workflow-0.0.39.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.37.dist-info/RECORD +0 -32
- {ddeutil_workflow-0.0.37.dist-info → ddeutil_workflow-0.0.39.dist-info/licenses}/LICENSE +0 -0
- {ddeutil_workflow-0.0.37.dist-info → ddeutil_workflow-0.0.39.dist-info}/top_level.txt +0 -0
ddeutil/workflow/templates.py
CHANGED
@@ -18,7 +18,7 @@ try:
|
|
18
18
|
except ImportError:
|
19
19
|
from typing_extensions import ParamSpec
|
20
20
|
|
21
|
-
from ddeutil.core import getdot,
|
21
|
+
from ddeutil.core import getdot, import_string
|
22
22
|
from ddeutil.io import search_env_replace
|
23
23
|
|
24
24
|
from .__types import DictData, Re
|
@@ -59,7 +59,8 @@ def custom_filter(name: str) -> Callable[P, FilterFunc]:
|
|
59
59
|
"""Custom filter decorator function that set function attributes, ``filter``
|
60
60
|
for making filter registries variable.
|
61
61
|
|
62
|
-
:param: name: A filter name for make different use-case of a function.
|
62
|
+
:param: name: (str) A filter name for make different use-case of a function.
|
63
|
+
|
63
64
|
:rtype: Callable[P, FilterFunc]
|
64
65
|
"""
|
65
66
|
|
@@ -108,7 +109,7 @@ def get_args_const(
|
|
108
109
|
) -> tuple[str, list[Constant], dict[str, Constant]]:
|
109
110
|
"""Get arguments and keyword-arguments from function calling string.
|
110
111
|
|
111
|
-
:param expr: An expr string value.
|
112
|
+
:param expr: (str) An expr string value.
|
112
113
|
|
113
114
|
:rtype: tuple[str, list[Constant], dict[str, Constant]]
|
114
115
|
"""
|
@@ -154,7 +155,7 @@ def get_args_from_filter(
|
|
154
155
|
and validate it with the filter functions mapping dict.
|
155
156
|
|
156
157
|
:param ft:
|
157
|
-
:param filters:
|
158
|
+
:param filters: A mapping of filter registry.
|
158
159
|
|
159
160
|
:rtype: tuple[str, FilterRegistry, list[Any], dict[Any, Any]]
|
160
161
|
"""
|
@@ -185,7 +186,7 @@ def map_post_filter(
|
|
185
186
|
|
186
187
|
:param value: A string value that want to map with filter function.
|
187
188
|
:param post_filter: A list of post-filter function name.
|
188
|
-
:param filters: A filter registry.
|
189
|
+
:param filters: A mapping of filter registry.
|
189
190
|
|
190
191
|
:rtype: T
|
191
192
|
"""
|
@@ -203,8 +204,8 @@ def map_post_filter(
|
|
203
204
|
except Exception as err:
|
204
205
|
logger.warning(str(err))
|
205
206
|
raise UtilException(
|
206
|
-
f"The post-filter
|
207
|
-
f"
|
207
|
+
f"The post-filter: {func_name!r} does not fit with {value!r} "
|
208
|
+
f"(type: {type(value).__name__})."
|
208
209
|
) from None
|
209
210
|
return value
|
210
211
|
|
@@ -258,10 +259,10 @@ def str2template(
|
|
258
259
|
with the workflow parameter types that is `str`, `int`, `datetime`, and
|
259
260
|
`list`.
|
260
261
|
|
261
|
-
:param value: A string value that want to map with params
|
262
|
-
:param params: A parameter value that getting with matched
|
263
|
-
expression.
|
264
|
-
:param filters:
|
262
|
+
:param value: (str) A string value that want to map with params.
|
263
|
+
:param params: (DictData) A parameter value that getting with matched
|
264
|
+
regular expression.
|
265
|
+
:param filters: A mapping of filter registry.
|
265
266
|
|
266
267
|
:rtype: str
|
267
268
|
"""
|
@@ -281,11 +282,14 @@ def str2template(
|
|
281
282
|
for i in (found.post_filters.strip().removeprefix("|").split("|"))
|
282
283
|
if i != ""
|
283
284
|
]
|
284
|
-
if not hasdot(caller, params):
|
285
|
-
raise UtilException(f"The params does not set caller: {caller!r}.")
|
286
285
|
|
287
286
|
# NOTE: from validate step, it guarantees that caller exists in params.
|
288
|
-
|
287
|
+
try:
|
288
|
+
getter: Any = getdot(caller, params)
|
289
|
+
except ValueError as err:
|
290
|
+
raise UtilException(
|
291
|
+
f"Params does not set caller: {caller!r}."
|
292
|
+
) from err
|
289
293
|
|
290
294
|
# NOTE:
|
291
295
|
# If type of getter caller is not string type, and it does not use to
|
@@ -301,25 +305,33 @@ def str2template(
|
|
301
305
|
|
302
306
|
value: str = value.replace(found.full, getter, 1)
|
303
307
|
|
308
|
+
if value == "None":
|
309
|
+
return None
|
310
|
+
|
304
311
|
return search_env_replace(value)
|
305
312
|
|
306
313
|
|
307
|
-
def param2template(
|
314
|
+
def param2template(
|
315
|
+
value: T,
|
316
|
+
params: DictData,
|
317
|
+
filters: dict[str, FilterRegistry] | None = None,
|
318
|
+
) -> T:
|
308
319
|
"""Pass param to template string that can search by ``RE_CALLER`` regular
|
309
320
|
expression.
|
310
321
|
|
311
322
|
:param value: A value that want to map with params
|
312
323
|
:param params: A parameter value that getting with matched regular
|
313
324
|
expression.
|
325
|
+
:param filters: A filter mapping for mapping with `map_post_filter` func.
|
314
326
|
|
315
327
|
:rtype: T
|
316
328
|
:returns: An any getter value from the params input.
|
317
329
|
"""
|
318
|
-
filters: dict[str, FilterRegistry] = make_filter_registry()
|
330
|
+
filters: dict[str, FilterRegistry] = filters or make_filter_registry()
|
319
331
|
if isinstance(value, dict):
|
320
|
-
return {k: param2template(value[k], params) for k in value}
|
332
|
+
return {k: param2template(value[k], params, filters) for k in value}
|
321
333
|
elif isinstance(value, (list, tuple, set)):
|
322
|
-
return type(value)([param2template(i, params) for i in value])
|
334
|
+
return type(value)([param2template(i, params, filters) for i in value])
|
323
335
|
elif not isinstance(value, str):
|
324
336
|
return value
|
325
337
|
return str2template(value, params, filters=filters)
|
@@ -329,8 +341,9 @@ def param2template(value: T, params: DictData) -> T:
|
|
329
341
|
def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
330
342
|
"""Format datetime object to string with the format.
|
331
343
|
|
332
|
-
:param value: A datetime value that want to format to string
|
333
|
-
|
344
|
+
:param value: (datetime) A datetime value that want to format to string
|
345
|
+
value.
|
346
|
+
:param fmt: (str) A format string pattern that passing to the `dt.strftime`
|
334
347
|
method.
|
335
348
|
|
336
349
|
:rtype: str
|
@@ -340,3 +353,9 @@ def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
|
340
353
|
raise UtilException(
|
341
354
|
"This custom function should pass input value with datetime type."
|
342
355
|
)
|
356
|
+
|
357
|
+
|
358
|
+
@custom_filter("coalesce") # pragma: no cov
|
359
|
+
def coalesce(value: T | None, default: Any) -> T:
|
360
|
+
"""Coalesce with default value if the main value is None."""
|
361
|
+
return default if value is None else value
|
ddeutil/workflow/utils.py
CHANGED
@@ -5,10 +5,9 @@
|
|
5
5
|
# ------------------------------------------------------------------------------
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
|
-
import logging
|
9
8
|
import stat
|
10
9
|
import time
|
11
|
-
from collections.abc import Iterator
|
10
|
+
from collections.abc import Iterator
|
12
11
|
from datetime import date, datetime, timedelta
|
13
12
|
from hashlib import md5
|
14
13
|
from inspect import isfunction
|
@@ -24,7 +23,6 @@ from .__types import DictData, Matrix
|
|
24
23
|
|
25
24
|
T = TypeVar("T")
|
26
25
|
UTC = ZoneInfo("UTC")
|
27
|
-
logger = logging.getLogger("ddeutil.workflow")
|
28
26
|
|
29
27
|
|
30
28
|
def get_dt_now(
|
@@ -188,29 +186,6 @@ def filter_func(value: T) -> T:
|
|
188
186
|
return value
|
189
187
|
|
190
188
|
|
191
|
-
def dash2underscore(
|
192
|
-
key: str,
|
193
|
-
values: DictData,
|
194
|
-
*,
|
195
|
-
fixed: str | None = None,
|
196
|
-
) -> DictData:
|
197
|
-
"""Change key name that has dash to underscore.
|
198
|
-
|
199
|
-
:param key:
|
200
|
-
:param values:
|
201
|
-
:param fixed:
|
202
|
-
|
203
|
-
Examples:
|
204
|
-
>>> dash2underscore('foo-bar', {"foo-bar": "demo"})
|
205
|
-
{'foo_bar': 'demo'}
|
206
|
-
|
207
|
-
:rtype: DictData
|
208
|
-
"""
|
209
|
-
if key in values:
|
210
|
-
values[(fixed or key.replace("-", "_"))] = values.pop(key)
|
211
|
-
return values
|
212
|
-
|
213
|
-
|
214
189
|
def cross_product(matrix: Matrix) -> Iterator[DictData]:
|
215
190
|
"""Iterator of products value from matrix.
|
216
191
|
|
@@ -267,21 +242,3 @@ def cut_id(run_id: str, *, num: int = 6) -> str:
|
|
267
242
|
:rtype: str
|
268
243
|
"""
|
269
244
|
return run_id[-num:]
|
270
|
-
|
271
|
-
|
272
|
-
def deep_update(origin: DictData, u: Mapping) -> DictData:
|
273
|
-
"""Deep update dict.
|
274
|
-
|
275
|
-
Example:
|
276
|
-
>>> deep_update(
|
277
|
-
... origin={"jobs": {"job01": "foo"}},
|
278
|
-
... u={"jobs": {"job02": "bar"}},
|
279
|
-
... )
|
280
|
-
{"jobs": {"job01": "foo", "job02": "bar"}}
|
281
|
-
"""
|
282
|
-
for k, value in u.items():
|
283
|
-
if isinstance(value, Mapping) and value:
|
284
|
-
origin[k] = deep_update(origin.get(k, {}), value)
|
285
|
-
else:
|
286
|
-
origin[k] = value
|
287
|
-
return origin
|