ddeutil-workflow 0.0.78__py3-none-any.whl → 0.0.79__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 -5
- ddeutil/workflow/api/routes/job.py +2 -2
- ddeutil/workflow/audits.py +554 -112
- ddeutil/workflow/cli.py +19 -1
- ddeutil/workflow/conf.py +9 -21
- ddeutil/workflow/event.py +15 -6
- ddeutil/workflow/job.py +147 -73
- ddeutil/workflow/params.py +172 -58
- ddeutil/workflow/plugins/__init__.py +0 -0
- ddeutil/workflow/plugins/providers/__init__.py +0 -0
- ddeutil/workflow/plugins/providers/aws.py +908 -0
- ddeutil/workflow/plugins/providers/az.py +1003 -0
- ddeutil/workflow/plugins/providers/container.py +703 -0
- ddeutil/workflow/plugins/providers/gcs.py +826 -0
- ddeutil/workflow/result.py +6 -4
- ddeutil/workflow/reusables.py +151 -95
- ddeutil/workflow/stages.py +28 -28
- ddeutil/workflow/traces.py +1678 -540
- ddeutil/workflow/utils.py +109 -67
- ddeutil/workflow/workflow.py +20 -11
- {ddeutil_workflow-0.0.78.dist-info → ddeutil_workflow-0.0.79.dist-info}/METADATA +52 -19
- ddeutil_workflow-0.0.79.dist-info/RECORD +36 -0
- ddeutil_workflow-0.0.78.dist-info/RECORD +0 -30
- {ddeutil_workflow-0.0.78.dist-info → ddeutil_workflow-0.0.79.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.78.dist-info → ddeutil_workflow-0.0.79.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.78.dist-info → ddeutil_workflow-0.0.79.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.78.dist-info → ddeutil_workflow-0.0.79.dist-info}/top_level.txt +0 -0
ddeutil/workflow/result.py
CHANGED
@@ -39,7 +39,7 @@ from . import (
|
|
39
39
|
WorkflowError,
|
40
40
|
)
|
41
41
|
from .__types import DictData
|
42
|
-
from .audits import
|
42
|
+
from .audits import TraceManager, get_trace
|
43
43
|
from .errors import ResultError
|
44
44
|
from .utils import default_gen_id
|
45
45
|
|
@@ -188,7 +188,9 @@ class Result:
|
|
188
188
|
info: DictData = field(default_factory=dict)
|
189
189
|
run_id: str = field(default_factory=default_gen_id)
|
190
190
|
parent_run_id: Optional[str] = field(default=None)
|
191
|
-
trace: Optional[
|
191
|
+
trace: Optional[TraceManager] = field(
|
192
|
+
default=None, compare=False, repr=False
|
193
|
+
)
|
192
194
|
|
193
195
|
@model_validator(mode="after")
|
194
196
|
def __prepare_trace(self) -> Self:
|
@@ -197,7 +199,7 @@ class Result:
|
|
197
199
|
:rtype: Self
|
198
200
|
"""
|
199
201
|
if self.trace is None: # pragma: no cov
|
200
|
-
self.trace:
|
202
|
+
self.trace: TraceManager = get_trace(
|
201
203
|
self.run_id,
|
202
204
|
parent_run_id=self.parent_run_id,
|
203
205
|
extras=self.extras,
|
@@ -206,7 +208,7 @@ class Result:
|
|
206
208
|
return self
|
207
209
|
|
208
210
|
@classmethod
|
209
|
-
def from_trace(cls, trace:
|
211
|
+
def from_trace(cls, trace: TraceManager):
|
210
212
|
"""Construct the result model from trace for clean code objective."""
|
211
213
|
return cls(
|
212
214
|
run_id=trace.run_id,
|
ddeutil/workflow/reusables.py
CHANGED
@@ -119,12 +119,15 @@ FilterRegistry = Union[FilterFunc, Callable[[...], Any]]
|
|
119
119
|
|
120
120
|
|
121
121
|
def custom_filter(name: str) -> Callable[P, FilterFunc]:
|
122
|
-
"""Custom filter decorator function that
|
123
|
-
for making filter registries variable.
|
122
|
+
"""Custom filter decorator function that sets function attributes.
|
124
123
|
|
125
|
-
|
124
|
+
This decorator sets the `filter` attribute for making filter registries variable.
|
126
125
|
|
127
|
-
:
|
126
|
+
Args:
|
127
|
+
name: A filter name for different use-cases of a function.
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
Callable[P, FilterFunc]: Decorated function with filter attributes.
|
128
131
|
"""
|
129
132
|
|
130
133
|
def func_internal(func: Callable[[...], Any]) -> FilterFunc:
|
@@ -144,11 +147,13 @@ def custom_filter(name: str) -> Callable[P, FilterFunc]:
|
|
144
147
|
def make_filter_registry(
|
145
148
|
registers: Optional[list[str]] = None,
|
146
149
|
) -> dict[str, FilterRegistry]:
|
147
|
-
"""Return registries of all functions that
|
150
|
+
"""Return registries of all functions that can be called with tasks.
|
148
151
|
|
149
|
-
:
|
152
|
+
Args:
|
153
|
+
registers: Optional override list of registers.
|
150
154
|
|
151
|
-
:
|
155
|
+
Returns:
|
156
|
+
dict[str, FilterRegistry]: Dictionary mapping filter names to functions.
|
152
157
|
"""
|
153
158
|
rs: dict[str, FilterRegistry] = {}
|
154
159
|
for module in dynamic("registry_filter", f=registers):
|
@@ -180,9 +185,14 @@ def get_args_const(
|
|
180
185
|
) -> tuple[str, list[Constant], dict[str, Constant]]:
|
181
186
|
"""Get arguments and keyword-arguments from function calling string.
|
182
187
|
|
183
|
-
:
|
188
|
+
Args:
|
189
|
+
expr: A string expression representing a function call.
|
190
|
+
|
191
|
+
Returns:
|
192
|
+
tuple[str, list[Constant], dict[str, Constant]]: Function name, args, and kwargs.
|
184
193
|
|
185
|
-
:
|
194
|
+
Raises:
|
195
|
+
UtilError: If the expression has syntax errors or invalid format.
|
186
196
|
"""
|
187
197
|
try:
|
188
198
|
mod: Module = parse(expr)
|
@@ -223,12 +233,18 @@ def get_args_from_filter(
|
|
223
233
|
filters: dict[str, FilterRegistry],
|
224
234
|
) -> tuple[str, FilterRegistry, list[Any], dict[Any, Any]]: # pragma: no cov
|
225
235
|
"""Get arguments and keyword-arguments from filter function calling string.
|
226
|
-
and validate it with the filter functions mapping dict.
|
227
236
|
|
228
|
-
|
229
|
-
|
237
|
+
This function validates the filter function call with the filter functions mapping dict.
|
238
|
+
|
239
|
+
Args:
|
240
|
+
ft: Filter function calling string.
|
241
|
+
filters: A mapping of filter registry.
|
230
242
|
|
231
|
-
:
|
243
|
+
Returns:
|
244
|
+
tuple[str, FilterRegistry, list[Any], dict[Any, Any]]: Function name, function, args, and kwargs.
|
245
|
+
|
246
|
+
Raises:
|
247
|
+
UtilError: If the filter function is not supported or has invalid arguments.
|
232
248
|
"""
|
233
249
|
func_name, _args, _kwargs = get_args_const(ft)
|
234
250
|
args: list[Any] = [arg.value for arg in _args]
|
@@ -250,14 +266,18 @@ def map_post_filter(
|
|
250
266
|
post_filter: list[str],
|
251
267
|
filters: dict[str, FilterRegistry],
|
252
268
|
) -> T:
|
253
|
-
"""
|
254
|
-
|
269
|
+
"""Map post-filter functions to value with sequence of filter function names.
|
270
|
+
|
271
|
+
Args:
|
272
|
+
value: A value to map with filter functions.
|
273
|
+
post_filter: A list of post-filter function names.
|
274
|
+
filters: A mapping of filter registry.
|
255
275
|
|
256
|
-
:
|
257
|
-
|
258
|
-
:param filters: A mapping of filter registry.
|
276
|
+
Returns:
|
277
|
+
T: The value after applying all post-filter functions.
|
259
278
|
|
260
|
-
:
|
279
|
+
Raises:
|
280
|
+
UtilError: If a post-filter function fails or is incompatible with the value.
|
261
281
|
"""
|
262
282
|
for ft in post_filter:
|
263
283
|
func_name, f_func, args, kwargs = get_args_from_filter(ft, filters)
|
@@ -278,13 +298,15 @@ def map_post_filter(
|
|
278
298
|
|
279
299
|
|
280
300
|
def not_in_template(value: Any, *, not_in: str = "matrix.") -> bool:
|
281
|
-
"""Check value should not pass template with not_in value prefix.
|
301
|
+
"""Check if value should not pass template with not_in value prefix.
|
282
302
|
|
283
|
-
:
|
284
|
-
|
285
|
-
|
303
|
+
Args:
|
304
|
+
value: A value to check for parameter template prefix.
|
305
|
+
not_in: The not-in string to use in the `.startswith` function.
|
306
|
+
Default is `matrix.`.
|
286
307
|
|
287
|
-
:
|
308
|
+
Returns:
|
309
|
+
bool: True if value should not pass template, False otherwise.
|
288
310
|
"""
|
289
311
|
if isinstance(value, dict):
|
290
312
|
return any(not_in_template(value[k], not_in=not_in) for k in value)
|
@@ -299,11 +321,13 @@ def not_in_template(value: Any, *, not_in: str = "matrix.") -> bool:
|
|
299
321
|
|
300
322
|
|
301
323
|
def has_template(value: Any) -> bool:
|
302
|
-
"""Check value
|
324
|
+
"""Check if value includes templating string.
|
303
325
|
|
304
|
-
:
|
326
|
+
Args:
|
327
|
+
value: A value to check for parameter template.
|
305
328
|
|
306
|
-
:
|
329
|
+
Returns:
|
330
|
+
bool: True if value contains template variables, False otherwise.
|
307
331
|
"""
|
308
332
|
if isinstance(value, dict):
|
309
333
|
return any(has_template(value[k]) for k in value)
|
@@ -322,21 +346,24 @@ def str2template(
|
|
322
346
|
filters: Optional[dict[str, FilterRegistry]] = None,
|
323
347
|
registers: Optional[list[str]] = None,
|
324
348
|
) -> Optional[str]:
|
325
|
-
"""
|
326
|
-
|
349
|
+
"""Pass parameters to template string using RE_CALLER regular expression.
|
350
|
+
|
351
|
+
This is a sub-function that processes template strings. The getter value
|
352
|
+
that maps a template should have typing support aligned with workflow
|
353
|
+
parameter types: `str`, `int`, `datetime`, and `list`.
|
327
354
|
|
328
|
-
|
329
|
-
|
330
|
-
|
355
|
+
Args:
|
356
|
+
value: A string value to map with parameters.
|
357
|
+
params: Parameter values to get with matched regular expression.
|
358
|
+
context: Optional context data.
|
359
|
+
filters: Optional mapping of filter registry.
|
360
|
+
registers: Optional override list of registers.
|
331
361
|
|
332
|
-
:
|
333
|
-
|
334
|
-
regular expression.
|
335
|
-
:param context: (DictData)
|
336
|
-
:param filters: (dict[str, FilterRegistry]) A mapping of filter registry.
|
337
|
-
:param registers: (Optional[list[str]]) Override list of register.
|
362
|
+
Returns:
|
363
|
+
Optional[str]: The processed template string or None if value is "None".
|
338
364
|
|
339
|
-
:
|
365
|
+
Raises:
|
366
|
+
UtilError: If parameters cannot be retrieved or template processing fails.
|
340
367
|
"""
|
341
368
|
filters: dict[str, FilterRegistry] = filters or make_filter_registry(
|
342
369
|
registers=registers
|
@@ -395,19 +422,17 @@ def param2template(
|
|
395
422
|
*,
|
396
423
|
extras: Optional[DictData] = None,
|
397
424
|
) -> Any:
|
398
|
-
"""Pass
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
:
|
408
|
-
|
409
|
-
:rtype: Any
|
410
|
-
:returns: An any getter value from the params input.
|
425
|
+
"""Pass parameters to template string using RE_CALLER regular expression.
|
426
|
+
|
427
|
+
Args:
|
428
|
+
value: A value to map with parameters.
|
429
|
+
params: Parameter values to get with matched regular expression.
|
430
|
+
context: Optional context data.
|
431
|
+
filters: Optional filter mapping for use with `map_post_filter` function.
|
432
|
+
extras: Optional override extras.
|
433
|
+
|
434
|
+
Returns:
|
435
|
+
Any: The processed value with template variables replaced.
|
411
436
|
"""
|
412
437
|
registers: Optional[list[str]] = (
|
413
438
|
extras.get("registry_filter") if extras else None
|
@@ -437,19 +462,21 @@ def param2template(
|
|
437
462
|
|
438
463
|
@custom_filter("fmt") # pragma: no cov
|
439
464
|
def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
440
|
-
"""Format datetime object to string with the format.
|
465
|
+
"""Format datetime object to string with the specified format.
|
441
466
|
|
442
467
|
Examples:
|
443
|
-
|
444
468
|
>>> "${{ start-date | fmt('%Y%m%d') }}"
|
445
469
|
>>> "${{ start-date | fmt }}"
|
446
470
|
|
447
|
-
:
|
448
|
-
value.
|
449
|
-
|
450
|
-
method.
|
471
|
+
Args:
|
472
|
+
value: A datetime value to format to string.
|
473
|
+
fmt: A format string pattern to pass to the `dt.strftime` method.
|
451
474
|
|
452
|
-
:
|
475
|
+
Returns:
|
476
|
+
str: The formatted datetime string.
|
477
|
+
|
478
|
+
Raises:
|
479
|
+
UtilError: If the input value is not a datetime object.
|
453
480
|
"""
|
454
481
|
if isinstance(value, datetime):
|
455
482
|
return value.strftime(fmt)
|
@@ -463,12 +490,14 @@ def coalesce(value: Optional[T], default: Any) -> T:
|
|
463
490
|
"""Coalesce with default value if the main value is None.
|
464
491
|
|
465
492
|
Examples:
|
466
|
-
|
467
493
|
>>> "${{ value | coalesce('foo') }}"
|
468
494
|
|
469
|
-
:
|
470
|
-
|
471
|
-
value
|
495
|
+
Args:
|
496
|
+
value: A value to check for null.
|
497
|
+
default: A default value to return if the input value is null.
|
498
|
+
|
499
|
+
Returns:
|
500
|
+
T: The original value if not None, otherwise the default value.
|
472
501
|
"""
|
473
502
|
return default if value is None else value
|
474
503
|
|
@@ -477,13 +506,22 @@ def coalesce(value: Optional[T], default: Any) -> T:
|
|
477
506
|
def get_item(
|
478
507
|
value: DictData, key: Union[str, int], default: Optional[Any] = None
|
479
508
|
) -> Any:
|
480
|
-
"""Get a value with
|
509
|
+
"""Get a value with a specific key.
|
481
510
|
|
482
511
|
Examples:
|
483
|
-
|
484
512
|
>>> "${{ value | getitem('key') }}"
|
485
513
|
>>> "${{ value | getitem('key', 'default') }}"
|
486
514
|
|
515
|
+
Args:
|
516
|
+
value: A dictionary to get the value from.
|
517
|
+
key: The key to look up in the dictionary.
|
518
|
+
default: Optional default value if key is not found.
|
519
|
+
|
520
|
+
Returns:
|
521
|
+
Any: The value associated with the key, or the default value.
|
522
|
+
|
523
|
+
Raises:
|
524
|
+
UtilError: If the value is not a dictionary.
|
487
525
|
"""
|
488
526
|
if not isinstance(value, dict):
|
489
527
|
raise UtilError(
|
@@ -495,12 +533,20 @@ def get_item(
|
|
495
533
|
|
496
534
|
@custom_filter("getindex") # pragma: no cov
|
497
535
|
def get_index(value: list[Any], index: int) -> Any:
|
498
|
-
"""Get a value with
|
536
|
+
"""Get a value with a specific index.
|
499
537
|
|
500
538
|
Examples:
|
501
|
-
|
502
539
|
>>> "${{ value | getindex(1) }}"
|
503
540
|
|
541
|
+
Args:
|
542
|
+
value: A list to get the value from.
|
543
|
+
index: The index to access in the list.
|
544
|
+
|
545
|
+
Returns:
|
546
|
+
Any: The value at the specified index.
|
547
|
+
|
548
|
+
Raises:
|
549
|
+
UtilError: If the value is not a list or if the index is out of range.
|
504
550
|
"""
|
505
551
|
if not isinstance(value, list):
|
506
552
|
raise UtilError(
|
@@ -534,16 +580,18 @@ def tag(
|
|
534
580
|
name: Optional[str] = None,
|
535
581
|
alias: Optional[str] = None,
|
536
582
|
) -> DecoratorTagFunc: # pragma: no cov
|
537
|
-
"""Tag decorator function that
|
538
|
-
for making registries variable.
|
583
|
+
"""Tag decorator function that sets function attributes for registry.
|
539
584
|
|
540
|
-
|
541
|
-
It will use 'latest' if this tag name does not set.
|
542
|
-
:param: alias: (str) A alias function name that keeping in registries.
|
543
|
-
If this value does not supply, it will use original function name
|
544
|
-
from `__name__` argument.
|
585
|
+
This decorator sets the `tag` and `name` attributes for making registries variable.
|
545
586
|
|
546
|
-
:
|
587
|
+
Args:
|
588
|
+
name: A tag name for different use-cases of a function.
|
589
|
+
Uses 'latest' if not set.
|
590
|
+
alias: An alias function name to keep in registries.
|
591
|
+
Uses original function name from `__name__` if not supplied.
|
592
|
+
|
593
|
+
Returns:
|
594
|
+
DecoratorTagFunc: Decorated function with tag attributes.
|
547
595
|
"""
|
548
596
|
|
549
597
|
def func_internal(func: Callable[[...], Any]) -> ReturnTagFunc:
|
@@ -574,12 +622,17 @@ def make_registry(
|
|
574
622
|
*,
|
575
623
|
registries: Optional[list[str]] = None,
|
576
624
|
) -> dict[str, Registry]:
|
577
|
-
"""Return registries of all functions that
|
625
|
+
"""Return registries of all functions that can be called with tasks.
|
626
|
+
|
627
|
+
Args:
|
628
|
+
submodule: A module prefix to import registry from.
|
629
|
+
registries: Optional list of registries.
|
578
630
|
|
579
|
-
:
|
580
|
-
|
631
|
+
Returns:
|
632
|
+
dict[str, Registry]: Dictionary mapping function names to their registries.
|
581
633
|
|
582
|
-
:
|
634
|
+
Raises:
|
635
|
+
ValueError: If a tag already exists for a function name.
|
583
636
|
"""
|
584
637
|
rs: dict[str, Registry] = {}
|
585
638
|
regis_calls: list[str] = copy.deepcopy(
|
@@ -639,13 +692,9 @@ def extract_call(
|
|
639
692
|
*,
|
640
693
|
registries: Optional[list[str]] = None,
|
641
694
|
) -> Callable[[], TagFunc]:
|
642
|
-
"""Extract
|
643
|
-
does run it at runtime.
|
644
|
-
|
645
|
-
:param call: (str) A call value that able to match with Task regex.
|
646
|
-
:param registries: (Optional[list[str]]) A list of registry.
|
695
|
+
"""Extract call function from string value to call partial function at runtime.
|
647
696
|
|
648
|
-
|
697
|
+
The format of call value should contain 3 regular expression groups
|
649
698
|
which match with the below config format:
|
650
699
|
|
651
700
|
>>> "^(?P<path>[^/@]+)/(?P<func>[^@]+)@(?P<tag>.+)$"
|
@@ -656,12 +705,16 @@ def extract_call(
|
|
656
705
|
>>> extract_call("tasks/return-type-not-valid@raise")
|
657
706
|
...
|
658
707
|
|
659
|
-
:
|
660
|
-
|
661
|
-
|
662
|
-
exist in the registry with its function key.
|
708
|
+
Args:
|
709
|
+
call: A call value that matches the Task regex.
|
710
|
+
registries: Optional list of registries.
|
663
711
|
|
664
|
-
:
|
712
|
+
Returns:
|
713
|
+
Callable[[], TagFunc]: A callable function that can be executed.
|
714
|
+
|
715
|
+
Raises:
|
716
|
+
ValueError: If the call does not match the regex format.
|
717
|
+
NotImplementedError: If the function or tag does not exist in the registry.
|
665
718
|
"""
|
666
719
|
if not (found := Re.RE_TASK_FMT.search(call)):
|
667
720
|
raise ValueError(
|
@@ -697,17 +750,20 @@ class BaseCallerArgs(BaseModel): # pragma: no cov
|
|
697
750
|
|
698
751
|
|
699
752
|
def create_model_from_caller(func: Callable) -> BaseModel: # pragma: no cov
|
700
|
-
"""Create model from the caller function.
|
701
|
-
|
702
|
-
|
753
|
+
"""Create model from the caller function.
|
754
|
+
|
755
|
+
This function is used to validate the caller function argument type hints
|
756
|
+
that are valid with the args field.
|
703
757
|
|
704
758
|
Reference:
|
705
759
|
- https://github.com/lmmx/pydantic-function-models
|
706
760
|
- https://docs.pydantic.dev/1.10/usage/models/#dynamic-model-creation
|
707
761
|
|
708
|
-
:
|
762
|
+
Args:
|
763
|
+
func: A caller function to create a model from.
|
709
764
|
|
710
|
-
:
|
765
|
+
Returns:
|
766
|
+
BaseModel: A Pydantic model created from the function signature.
|
711
767
|
"""
|
712
768
|
sig: inspect.Signature = inspect.signature(func)
|
713
769
|
type_hints: dict[str, Any] = get_type_hints(func)
|