ddeutil-workflow 0.0.58__py3-none-any.whl → 0.0.60__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/__cron.py +3 -3
- ddeutil/workflow/__types.py +9 -2
- ddeutil/workflow/conf.py +6 -4
- ddeutil/workflow/event.py +17 -14
- ddeutil/workflow/exceptions.py +6 -5
- ddeutil/workflow/job.py +40 -39
- ddeutil/workflow/logs.py +171 -73
- ddeutil/workflow/params.py +9 -5
- ddeutil/workflow/result.py +19 -19
- ddeutil/workflow/reusables.py +9 -9
- ddeutil/workflow/scheduler.py +8 -8
- ddeutil/workflow/stages.py +96 -85
- ddeutil/workflow/utils.py +11 -10
- ddeutil/workflow/workflow.py +33 -32
- {ddeutil_workflow-0.0.58.dist-info → ddeutil_workflow-0.0.60.dist-info}/METADATA +8 -3
- ddeutil_workflow-0.0.60.dist-info/RECORD +31 -0
- {ddeutil_workflow-0.0.58.dist-info → ddeutil_workflow-0.0.60.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.58.dist-info/RECORD +0 -31
- {ddeutil_workflow-0.0.58.dist-info → ddeutil_workflow-0.0.60.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.58.dist-info → ddeutil_workflow-0.0.60.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.58.dist-info → ddeutil_workflow-0.0.60.dist-info}/top_level.txt +0 -0
ddeutil/workflow/reusables.py
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
"""Reusables module that keep any templating functions."""
|
8
8
|
from __future__ import annotations
|
9
9
|
|
10
|
+
import copy
|
10
11
|
import inspect
|
11
12
|
import logging
|
12
13
|
from ast import Call, Constant, Expr, Module, Name, parse
|
@@ -35,7 +36,7 @@ logger = logging.getLogger("ddeutil.workflow")
|
|
35
36
|
logging.getLogger("asyncio").setLevel(logging.INFO)
|
36
37
|
|
37
38
|
|
38
|
-
FILTERS: dict[str,
|
39
|
+
FILTERS: dict[str, Callable] = { # pragma: no cov
|
39
40
|
"abs": abs,
|
40
41
|
"str": str,
|
41
42
|
"int": int,
|
@@ -258,9 +259,9 @@ def str2template(
|
|
258
259
|
value: str,
|
259
260
|
params: DictData,
|
260
261
|
*,
|
261
|
-
filters: dict[str, FilterRegistry]
|
262
|
+
filters: Optional[dict[str, FilterRegistry]] = None,
|
262
263
|
registers: Optional[list[str]] = None,
|
263
|
-
) -> str:
|
264
|
+
) -> Optional[str]:
|
264
265
|
"""(Sub-function) Pass param to template string that can search by
|
265
266
|
``RE_CALLER`` regular expression.
|
266
267
|
|
@@ -326,7 +327,7 @@ def str2template(
|
|
326
327
|
def param2template(
|
327
328
|
value: T,
|
328
329
|
params: DictData,
|
329
|
-
filters: dict[str, FilterRegistry]
|
330
|
+
filters: Optional[dict[str, FilterRegistry]] = None,
|
330
331
|
*,
|
331
332
|
extras: Optional[DictData] = None,
|
332
333
|
) -> T:
|
@@ -381,7 +382,7 @@ def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
|
381
382
|
|
382
383
|
|
383
384
|
@custom_filter("coalesce") # pragma: no cov
|
384
|
-
def coalesce(value: T
|
385
|
+
def coalesce(value: Optional[T], default: Any) -> T:
|
385
386
|
"""Coalesce with default value if the main value is None."""
|
386
387
|
return default if value is None else value
|
387
388
|
|
@@ -450,11 +451,10 @@ def make_registry(
|
|
450
451
|
:rtype: dict[str, Registry]
|
451
452
|
"""
|
452
453
|
rs: dict[str, Registry] = {}
|
453
|
-
regis_calls: list[str] =
|
454
|
-
"registry_caller", f=registries
|
455
|
-
)
|
454
|
+
regis_calls: list[str] = copy.deepcopy(
|
455
|
+
dynamic("registry_caller", f=registries)
|
456
|
+
)
|
456
457
|
regis_calls.extend(["ddeutil.vendors"])
|
457
|
-
|
458
458
|
for module in regis_calls:
|
459
459
|
# NOTE: try to sequential import task functions
|
460
460
|
try:
|
ddeutil/workflow/scheduler.py
CHANGED
@@ -335,9 +335,9 @@ class Schedule(BaseModel):
|
|
335
335
|
def pending(
|
336
336
|
self,
|
337
337
|
*,
|
338
|
-
stop: datetime
|
338
|
+
stop: Optional[datetime] = None,
|
339
339
|
audit: type[Audit] | None = None,
|
340
|
-
parent_run_id: str
|
340
|
+
parent_run_id: Optional[str] = None,
|
341
341
|
) -> Result: # pragma: no cov
|
342
342
|
"""Pending this schedule tasks with the schedule package.
|
343
343
|
|
@@ -384,7 +384,7 @@ DecoratorCancelJob = Callable[[ReturnResultOrCancel], ReturnResultOrCancel]
|
|
384
384
|
|
385
385
|
def catch_exceptions(
|
386
386
|
cancel_on_failure: bool = False,
|
387
|
-
parent_run_id: str
|
387
|
+
parent_run_id: Optional[str] = None,
|
388
388
|
) -> DecoratorCancelJob:
|
389
389
|
"""Catch exception error from scheduler job that running with schedule
|
390
390
|
package and return CancelJob if this function raise an error.
|
@@ -440,7 +440,7 @@ def schedule_task(
|
|
440
440
|
threads: ReleaseThreads,
|
441
441
|
audit: type[Audit],
|
442
442
|
*,
|
443
|
-
parent_run_id: str
|
443
|
+
parent_run_id: Optional[str] = None,
|
444
444
|
extras: Optional[DictData] = None,
|
445
445
|
) -> ResultOrCancel:
|
446
446
|
"""Schedule task function that generate thread of workflow task release
|
@@ -558,7 +558,7 @@ def schedule_task(
|
|
558
558
|
|
559
559
|
def monitor(
|
560
560
|
threads: ReleaseThreads,
|
561
|
-
parent_run_id: str
|
561
|
+
parent_run_id: Optional[str] = None,
|
562
562
|
) -> None: # pragma: no cov
|
563
563
|
"""Monitoring function that running every five minute for track long-running
|
564
564
|
thread instance from the schedule_control function that run every minute.
|
@@ -685,11 +685,11 @@ def scheduler_pending(
|
|
685
685
|
|
686
686
|
def schedule_control(
|
687
687
|
schedules: list[str],
|
688
|
-
stop: datetime
|
688
|
+
stop: Optional[datetime] = None,
|
689
689
|
*,
|
690
690
|
extras: DictData | None = None,
|
691
691
|
audit: type[Audit] | None = None,
|
692
|
-
parent_run_id: str
|
692
|
+
parent_run_id: Optional[str] = None,
|
693
693
|
) -> Result: # pragma: no cov
|
694
694
|
"""Scheduler control function that run the chuck of schedules every minute
|
695
695
|
and this function release monitoring thread for tracking undead thread in
|
@@ -744,7 +744,7 @@ def schedule_control(
|
|
744
744
|
|
745
745
|
|
746
746
|
def schedule_runner(
|
747
|
-
stop: datetime
|
747
|
+
stop: Optional[datetime] = None,
|
748
748
|
*,
|
749
749
|
max_process: int | None = None,
|
750
750
|
extras: DictData | None = None,
|
ddeutil/workflow/stages.py
CHANGED
@@ -58,7 +58,7 @@ from pydantic import BaseModel, Field
|
|
58
58
|
from pydantic.functional_validators import model_validator
|
59
59
|
from typing_extensions import Self
|
60
60
|
|
61
|
-
from .__types import DictData, DictStr, StrOrInt, TupleStr
|
61
|
+
from .__types import DictData, DictStr, StrOrInt, StrOrNone, TupleStr
|
62
62
|
from .conf import dynamic
|
63
63
|
from .exceptions import StageException, to_dict
|
64
64
|
from .result import CANCEL, FAILED, SUCCESS, WAIT, Result, Status
|
@@ -87,7 +87,7 @@ class BaseStage(BaseModel, ABC):
|
|
87
87
|
default_factory=dict,
|
88
88
|
description="An extra parameter that override core config values.",
|
89
89
|
)
|
90
|
-
id:
|
90
|
+
id: StrOrNone = Field(
|
91
91
|
default=None,
|
92
92
|
description=(
|
93
93
|
"A stage ID that use to keep execution output or getting by job "
|
@@ -97,7 +97,7 @@ class BaseStage(BaseModel, ABC):
|
|
97
97
|
name: str = Field(
|
98
98
|
description="A stage name that want to logging when start execution.",
|
99
99
|
)
|
100
|
-
condition:
|
100
|
+
condition: StrOrNone = Field(
|
101
101
|
default=None,
|
102
102
|
description=(
|
103
103
|
"A stage condition statement to allow stage executable. This field "
|
@@ -141,8 +141,8 @@ class BaseStage(BaseModel, ABC):
|
|
141
141
|
self,
|
142
142
|
params: DictData,
|
143
143
|
*,
|
144
|
-
result: Result
|
145
|
-
event: Event
|
144
|
+
result: Optional[Result] = None,
|
145
|
+
event: Optional[Event] = None,
|
146
146
|
) -> Result:
|
147
147
|
"""Execute abstraction method that action something by sub-model class.
|
148
148
|
This is important method that make this class is able to be the stage.
|
@@ -162,12 +162,12 @@ class BaseStage(BaseModel, ABC):
|
|
162
162
|
self,
|
163
163
|
params: DictData,
|
164
164
|
*,
|
165
|
-
run_id:
|
166
|
-
parent_run_id:
|
167
|
-
result: Result
|
168
|
-
event: Event
|
169
|
-
raise_error: bool
|
170
|
-
) -> Result
|
165
|
+
run_id: StrOrNone = None,
|
166
|
+
parent_run_id: StrOrNone = None,
|
167
|
+
result: Optional[Result] = None,
|
168
|
+
event: Optional[Event] = None,
|
169
|
+
raise_error: Optional[bool] = None,
|
170
|
+
) -> Union[Result, DictData]:
|
171
171
|
"""Handler stage execution result from the stage `execute` method.
|
172
172
|
|
173
173
|
This stage exception handler still use ok-error concept, but it
|
@@ -376,8 +376,8 @@ class BaseAsyncStage(BaseStage):
|
|
376
376
|
self,
|
377
377
|
params: DictData,
|
378
378
|
*,
|
379
|
-
result: Result
|
380
|
-
event: Event
|
379
|
+
result: Optional[Result] = None,
|
380
|
+
event: Optional[Event] = None,
|
381
381
|
) -> Result:
|
382
382
|
raise NotImplementedError(
|
383
383
|
"Async Stage should implement `execute` method."
|
@@ -388,8 +388,8 @@ class BaseAsyncStage(BaseStage):
|
|
388
388
|
self,
|
389
389
|
params: DictData,
|
390
390
|
*,
|
391
|
-
result: Result
|
392
|
-
event: Event
|
391
|
+
result: Optional[Result] = None,
|
392
|
+
event: Optional[Event] = None,
|
393
393
|
) -> Result:
|
394
394
|
"""Async execution method for this Empty stage that only logging out to
|
395
395
|
stdout.
|
@@ -411,11 +411,11 @@ class BaseAsyncStage(BaseStage):
|
|
411
411
|
self,
|
412
412
|
params: DictData,
|
413
413
|
*,
|
414
|
-
run_id:
|
415
|
-
parent_run_id:
|
416
|
-
result: Result
|
417
|
-
event: Event
|
418
|
-
raise_error: bool
|
414
|
+
run_id: StrOrNone = None,
|
415
|
+
parent_run_id: StrOrNone = None,
|
416
|
+
result: Optional[Result] = None,
|
417
|
+
event: Optional[Event] = None,
|
418
|
+
raise_error: Optional[bool] = None,
|
419
419
|
) -> Result:
|
420
420
|
"""Async Handler stage execution result from the stage `execute` method.
|
421
421
|
|
@@ -469,7 +469,7 @@ class EmptyStage(BaseAsyncStage):
|
|
469
469
|
... }
|
470
470
|
"""
|
471
471
|
|
472
|
-
echo:
|
472
|
+
echo: StrOrNone = Field(
|
473
473
|
default=None,
|
474
474
|
description="A message that want to show on the stdout.",
|
475
475
|
)
|
@@ -487,8 +487,8 @@ class EmptyStage(BaseAsyncStage):
|
|
487
487
|
self,
|
488
488
|
params: DictData,
|
489
489
|
*,
|
490
|
-
result: Result
|
491
|
-
event: Event
|
490
|
+
result: Optional[Result] = None,
|
491
|
+
event: Optional[Event] = None,
|
492
492
|
) -> Result:
|
493
493
|
"""Execution method for the Empty stage that do only logging out to
|
494
494
|
stdout.
|
@@ -528,8 +528,8 @@ class EmptyStage(BaseAsyncStage):
|
|
528
528
|
self,
|
529
529
|
params: DictData,
|
530
530
|
*,
|
531
|
-
result: Result
|
532
|
-
event: Event
|
531
|
+
result: Optional[Result] = None,
|
532
|
+
event: Optional[Event] = None,
|
533
533
|
) -> Result:
|
534
534
|
"""Async execution method for this Empty stage that only logging out to
|
535
535
|
stdout.
|
@@ -598,14 +598,14 @@ class BashStage(BaseAsyncStage):
|
|
598
598
|
)
|
599
599
|
|
600
600
|
@contextlib.asynccontextmanager
|
601
|
-
async def
|
602
|
-
self, bash: str, env: DictStr, run_id:
|
601
|
+
async def async_create_sh_file(
|
602
|
+
self, bash: str, env: DictStr, run_id: StrOrNone = None
|
603
603
|
) -> AsyncIterator[TupleStr]:
|
604
604
|
"""Async create and write `.sh` file with the `aiofiles` package.
|
605
605
|
|
606
606
|
:param bash: (str) A bash statement.
|
607
607
|
:param env: (DictStr) An environment variable that set before run bash.
|
608
|
-
:param run_id: (
|
608
|
+
:param run_id: (StrOrNone) A running stage ID that use for writing sh
|
609
609
|
file instead generate by UUID4.
|
610
610
|
|
611
611
|
:rtype: AsyncIterator[TupleStr]
|
@@ -635,14 +635,14 @@ class BashStage(BaseAsyncStage):
|
|
635
635
|
|
636
636
|
@contextlib.contextmanager
|
637
637
|
def create_sh_file(
|
638
|
-
self, bash: str, env: DictStr, run_id:
|
638
|
+
self, bash: str, env: DictStr, run_id: StrOrNone = None
|
639
639
|
) -> Iterator[TupleStr]:
|
640
640
|
"""Create and write the `.sh` file before giving this file name to
|
641
641
|
context. After that, it will auto delete this file automatic.
|
642
642
|
|
643
643
|
:param bash: (str) A bash statement.
|
644
644
|
:param env: (DictStr) An environment variable that set before run bash.
|
645
|
-
:param run_id: (
|
645
|
+
:param run_id: (StrOrNone) A running stage ID that use for writing sh
|
646
646
|
file instead generate by UUID4.
|
647
647
|
|
648
648
|
:rtype: Iterator[TupleStr]
|
@@ -673,8 +673,8 @@ class BashStage(BaseAsyncStage):
|
|
673
673
|
self,
|
674
674
|
params: DictData,
|
675
675
|
*,
|
676
|
-
result: Result
|
677
|
-
event: Event
|
676
|
+
result: Optional[Result] = None,
|
677
|
+
event: Optional[Event] = None,
|
678
678
|
) -> Result:
|
679
679
|
"""Execute bash statement with the Python build-in `subprocess` package.
|
680
680
|
It will catch result from the `subprocess.run` returning output like
|
@@ -730,8 +730,8 @@ class BashStage(BaseAsyncStage):
|
|
730
730
|
self,
|
731
731
|
params: DictData,
|
732
732
|
*,
|
733
|
-
result: Result
|
734
|
-
event: Event
|
733
|
+
result: Optional[Result] = None,
|
734
|
+
event: Optional[Event] = None,
|
735
735
|
) -> Result:
|
736
736
|
"""Async execution method for this Bash stage that only logging out to
|
737
737
|
stdout.
|
@@ -752,7 +752,7 @@ class BashStage(BaseAsyncStage):
|
|
752
752
|
dedent(self.bash.strip("\n")), params, extras=self.extras
|
753
753
|
)
|
754
754
|
|
755
|
-
async with self.
|
755
|
+
async with self.async_create_sh_file(
|
756
756
|
bash=bash,
|
757
757
|
env=param2template(self.env, params, extras=self.extras),
|
758
758
|
run_id=result.run_id,
|
@@ -858,8 +858,8 @@ class PyStage(BaseAsyncStage):
|
|
858
858
|
self,
|
859
859
|
params: DictData,
|
860
860
|
*,
|
861
|
-
result: Result
|
862
|
-
event: Event
|
861
|
+
result: Optional[Result] = None,
|
862
|
+
event: Optional[Event] = None,
|
863
863
|
) -> Result:
|
864
864
|
"""Execute the Python statement that pass all globals and input params
|
865
865
|
to globals argument on `exec` build-in function.
|
@@ -916,8 +916,8 @@ class PyStage(BaseAsyncStage):
|
|
916
916
|
self,
|
917
917
|
params: DictData,
|
918
918
|
*,
|
919
|
-
result: Result
|
920
|
-
event: Event
|
919
|
+
result: Optional[Result] = None,
|
920
|
+
event: Optional[Event] = None,
|
921
921
|
) -> Result:
|
922
922
|
"""Async execution method for this Bash stage that only logging out to
|
923
923
|
stdout.
|
@@ -1018,8 +1018,8 @@ class CallStage(BaseAsyncStage):
|
|
1018
1018
|
self,
|
1019
1019
|
params: DictData,
|
1020
1020
|
*,
|
1021
|
-
result: Result
|
1022
|
-
event: Event
|
1021
|
+
result: Optional[Result] = None,
|
1022
|
+
event: Optional[Event] = None,
|
1023
1023
|
) -> Result:
|
1024
1024
|
"""Execute this caller function with its argument parameter.
|
1025
1025
|
|
@@ -1108,8 +1108,8 @@ class CallStage(BaseAsyncStage):
|
|
1108
1108
|
self,
|
1109
1109
|
params: DictData,
|
1110
1110
|
*,
|
1111
|
-
result: Result
|
1112
|
-
event: Event
|
1111
|
+
result: Optional[Result] = None,
|
1112
|
+
event: Optional[Event] = None,
|
1113
1113
|
) -> Result:
|
1114
1114
|
"""Async execution method for this Bash stage that only logging out to
|
1115
1115
|
stdout.
|
@@ -1266,8 +1266,8 @@ class TriggerStage(BaseStage):
|
|
1266
1266
|
self,
|
1267
1267
|
params: DictData,
|
1268
1268
|
*,
|
1269
|
-
result: Result
|
1270
|
-
event: Event
|
1269
|
+
result: Optional[Result] = None,
|
1270
|
+
event: Optional[Event] = None,
|
1271
1271
|
) -> Result:
|
1272
1272
|
"""Trigger another workflow execution. It will wait the trigger
|
1273
1273
|
workflow running complete before catching its result.
|
@@ -1294,11 +1294,12 @@ class TriggerStage(BaseStage):
|
|
1294
1294
|
extras=self.extras | {"stage_raise_error": True},
|
1295
1295
|
).execute(
|
1296
1296
|
params=param2template(self.params, params, extras=self.extras),
|
1297
|
-
|
1297
|
+
run_id=None,
|
1298
|
+
parent_run_id=result.parent_run_id,
|
1298
1299
|
event=event,
|
1299
1300
|
)
|
1300
1301
|
if rs.status == FAILED:
|
1301
|
-
err_msg:
|
1302
|
+
err_msg: StrOrNone = (
|
1302
1303
|
f" with:\n{msg}"
|
1303
1304
|
if (msg := rs.context.get("errors", {}).get("message"))
|
1304
1305
|
else "."
|
@@ -1319,8 +1320,8 @@ class BaseNestedStage(BaseStage):
|
|
1319
1320
|
self,
|
1320
1321
|
params: DictData,
|
1321
1322
|
*,
|
1322
|
-
result: Result
|
1323
|
-
event: Event
|
1323
|
+
result: Optional[Result] = None,
|
1324
|
+
event: Optional[Event] = None,
|
1324
1325
|
) -> Result:
|
1325
1326
|
"""Execute abstraction method that action something by sub-model class.
|
1326
1327
|
This is important method that make this class is able to be the nested
|
@@ -1390,7 +1391,7 @@ class ParallelStage(BaseNestedStage):
|
|
1390
1391
|
params: DictData,
|
1391
1392
|
result: Result,
|
1392
1393
|
*,
|
1393
|
-
event: Event
|
1394
|
+
event: Optional[Event] = None,
|
1394
1395
|
) -> Result:
|
1395
1396
|
"""Execute all stage with specific branch ID.
|
1396
1397
|
|
@@ -1487,8 +1488,8 @@ class ParallelStage(BaseNestedStage):
|
|
1487
1488
|
self,
|
1488
1489
|
params: DictData,
|
1489
1490
|
*,
|
1490
|
-
result: Result
|
1491
|
-
event: Event
|
1491
|
+
result: Optional[Result] = None,
|
1492
|
+
event: Optional[Event] = None,
|
1492
1493
|
) -> Result:
|
1493
1494
|
"""Execute parallel each branch via multi-threading pool.
|
1494
1495
|
|
@@ -1526,7 +1527,7 @@ class ParallelStage(BaseNestedStage):
|
|
1526
1527
|
context: DictData = {}
|
1527
1528
|
status: Status = SUCCESS
|
1528
1529
|
|
1529
|
-
futures: list[Future] =
|
1530
|
+
futures: list[Future] = [
|
1530
1531
|
executor.submit(
|
1531
1532
|
self.execute_branch,
|
1532
1533
|
branch=branch,
|
@@ -1535,7 +1536,7 @@ class ParallelStage(BaseNestedStage):
|
|
1535
1536
|
event=event,
|
1536
1537
|
)
|
1537
1538
|
for branch in self.parallel
|
1538
|
-
|
1539
|
+
]
|
1539
1540
|
|
1540
1541
|
for future in as_completed(futures):
|
1541
1542
|
try:
|
@@ -1605,7 +1606,7 @@ class ForEachStage(BaseNestedStage):
|
|
1605
1606
|
params: DictData,
|
1606
1607
|
result: Result,
|
1607
1608
|
*,
|
1608
|
-
event: Event
|
1609
|
+
event: Optional[Event] = None,
|
1609
1610
|
) -> Result:
|
1610
1611
|
"""Execute all nested stage that set on this stage with specific foreach
|
1611
1612
|
item parameter.
|
@@ -1709,8 +1710,8 @@ class ForEachStage(BaseNestedStage):
|
|
1709
1710
|
self,
|
1710
1711
|
params: DictData,
|
1711
1712
|
*,
|
1712
|
-
result: Result
|
1713
|
-
event: Event
|
1713
|
+
result: Optional[Result] = None,
|
1714
|
+
event: Optional[Event] = None,
|
1714
1715
|
) -> Result:
|
1715
1716
|
"""Execute the stages that pass each item form the foreach field.
|
1716
1717
|
|
@@ -1775,7 +1776,7 @@ class ForEachStage(BaseNestedStage):
|
|
1775
1776
|
status: Status = SUCCESS
|
1776
1777
|
|
1777
1778
|
done, not_done = wait(futures, return_when=FIRST_EXCEPTION)
|
1778
|
-
if len(done) != len(futures):
|
1779
|
+
if len(list(done)) != len(futures):
|
1779
1780
|
result.trace.warning(
|
1780
1781
|
"[STAGE]: Set event for stop pending for-each stage."
|
1781
1782
|
)
|
@@ -1795,7 +1796,7 @@ class ForEachStage(BaseNestedStage):
|
|
1795
1796
|
result.trace.debug(
|
1796
1797
|
f"[STAGE]: ... Foreach-Stage set failed event{nd}"
|
1797
1798
|
)
|
1798
|
-
done:
|
1799
|
+
done: Iterator[Future] = as_completed(futures)
|
1799
1800
|
|
1800
1801
|
for future in done:
|
1801
1802
|
try:
|
@@ -1826,7 +1827,10 @@ class UntilStage(BaseNestedStage):
|
|
1826
1827
|
... "stages": [
|
1827
1828
|
... {
|
1828
1829
|
... "name": "Start increase item value.",
|
1829
|
-
... "run":
|
1830
|
+
... "run": (
|
1831
|
+
... "item = ${{ item }}\\n"
|
1832
|
+
... "item += 1\\n"
|
1833
|
+
... )
|
1830
1834
|
... },
|
1831
1835
|
... ],
|
1832
1836
|
... }
|
@@ -1862,7 +1866,7 @@ class UntilStage(BaseNestedStage):
|
|
1862
1866
|
loop: int,
|
1863
1867
|
params: DictData,
|
1864
1868
|
result: Result,
|
1865
|
-
event: Event
|
1869
|
+
event: Optional[Event] = None,
|
1866
1870
|
) -> tuple[Result, T]:
|
1867
1871
|
"""Execute all stage with specific loop and item.
|
1868
1872
|
|
@@ -1975,8 +1979,8 @@ class UntilStage(BaseNestedStage):
|
|
1975
1979
|
self,
|
1976
1980
|
params: DictData,
|
1977
1981
|
*,
|
1978
|
-
result: Result
|
1979
|
-
event: Event
|
1982
|
+
result: Optional[Result] = None,
|
1983
|
+
event: Optional[Event] = None,
|
1980
1984
|
) -> Result:
|
1981
1985
|
"""Execute until loop with checking until condition.
|
1982
1986
|
|
@@ -2113,7 +2117,7 @@ class CaseStage(BaseNestedStage):
|
|
2113
2117
|
params: DictData,
|
2114
2118
|
result: Result,
|
2115
2119
|
*,
|
2116
|
-
event: Event
|
2120
|
+
event: Optional[Event] = None,
|
2117
2121
|
) -> Result:
|
2118
2122
|
"""Execute case.
|
2119
2123
|
|
@@ -2198,8 +2202,8 @@ class CaseStage(BaseNestedStage):
|
|
2198
2202
|
self,
|
2199
2203
|
params: DictData,
|
2200
2204
|
*,
|
2201
|
-
result: Result
|
2202
|
-
event: Event
|
2205
|
+
result: Optional[Result] = None,
|
2206
|
+
event: Optional[Event] = None,
|
2203
2207
|
) -> Result:
|
2204
2208
|
"""Execute case-match condition that pass to the case field.
|
2205
2209
|
|
@@ -2215,9 +2219,7 @@ class CaseStage(BaseNestedStage):
|
|
2215
2219
|
extras=self.extras,
|
2216
2220
|
)
|
2217
2221
|
|
2218
|
-
_case:
|
2219
|
-
self.case, params, extras=self.extras
|
2220
|
-
)
|
2222
|
+
_case: StrOrNone = param2template(self.case, params, extras=self.extras)
|
2221
2223
|
|
2222
2224
|
result.trace.info(f"[STAGE]: Execute Case-Stage: {_case!r}.")
|
2223
2225
|
_else: Optional[Match] = None
|
@@ -2291,8 +2293,8 @@ class RaiseStage(BaseAsyncStage):
|
|
2291
2293
|
self,
|
2292
2294
|
params: DictData,
|
2293
2295
|
*,
|
2294
|
-
result: Result
|
2295
|
-
event: Event
|
2296
|
+
result: Optional[Result] = None,
|
2297
|
+
event: Optional[Event] = None,
|
2296
2298
|
) -> Result:
|
2297
2299
|
"""Raise the StageException object with the message field execution.
|
2298
2300
|
|
@@ -2313,8 +2315,8 @@ class RaiseStage(BaseAsyncStage):
|
|
2313
2315
|
self,
|
2314
2316
|
params: DictData,
|
2315
2317
|
*,
|
2316
|
-
result: Result
|
2317
|
-
event: Event
|
2318
|
+
result: Optional[Result] = None,
|
2319
|
+
event: Optional[Event] = None,
|
2318
2320
|
) -> Result:
|
2319
2321
|
"""Async execution method for this Empty stage that only logging out to
|
2320
2322
|
stdout.
|
@@ -2367,7 +2369,7 @@ class DockerStage(BaseStage): # pragma: no cov
|
|
2367
2369
|
env: DictData = Field(
|
2368
2370
|
default_factory=dict,
|
2369
2371
|
description=(
|
2370
|
-
"An environment variable that want pass to Docker container."
|
2372
|
+
"An environment variable that want pass to Docker container."
|
2371
2373
|
),
|
2372
2374
|
)
|
2373
2375
|
volume: DictData = Field(
|
@@ -2385,7 +2387,7 @@ class DockerStage(BaseStage): # pragma: no cov
|
|
2385
2387
|
self,
|
2386
2388
|
params: DictData,
|
2387
2389
|
result: Result,
|
2388
|
-
event: Event
|
2390
|
+
event: Optional[Event] = None,
|
2389
2391
|
) -> Result:
|
2390
2392
|
"""Execute Docker container task.
|
2391
2393
|
|
@@ -2396,8 +2398,14 @@ class DockerStage(BaseStage): # pragma: no cov
|
|
2396
2398
|
|
2397
2399
|
:rtype: Result
|
2398
2400
|
"""
|
2399
|
-
|
2400
|
-
|
2401
|
+
try:
|
2402
|
+
from docker import DockerClient
|
2403
|
+
from docker.errors import ContainerError
|
2404
|
+
except ImportError:
|
2405
|
+
raise ImportError(
|
2406
|
+
"Docker stage need the docker package, you should install it "
|
2407
|
+
"by `pip install docker` first."
|
2408
|
+
) from None
|
2401
2409
|
|
2402
2410
|
client = DockerClient(
|
2403
2411
|
base_url="unix://var/run/docker.sock", version="auto"
|
@@ -2459,7 +2467,7 @@ class DockerStage(BaseStage): # pragma: no cov
|
|
2459
2467
|
exit_status,
|
2460
2468
|
None,
|
2461
2469
|
f"{self.image}:{self.tag}",
|
2462
|
-
out,
|
2470
|
+
out.decode("utf-8"),
|
2463
2471
|
)
|
2464
2472
|
output_file: Path = Path(f".docker.{result.run_id}.logs/outputs.json")
|
2465
2473
|
if not output_file.exists():
|
@@ -2473,8 +2481,8 @@ class DockerStage(BaseStage): # pragma: no cov
|
|
2473
2481
|
self,
|
2474
2482
|
params: DictData,
|
2475
2483
|
*,
|
2476
|
-
result: Result
|
2477
|
-
event: Event
|
2484
|
+
result: Optional[Result] = None,
|
2485
|
+
event: Optional[Event] = None,
|
2478
2486
|
) -> Result:
|
2479
2487
|
"""Execute the Docker image via Python API.
|
2480
2488
|
|
@@ -2518,7 +2526,7 @@ class VirtualPyStage(PyStage): # pragma: no cov
|
|
2518
2526
|
py: str,
|
2519
2527
|
values: DictData,
|
2520
2528
|
deps: list[str],
|
2521
|
-
run_id:
|
2529
|
+
run_id: StrOrNone = None,
|
2522
2530
|
) -> Iterator[str]:
|
2523
2531
|
"""Create the .py file with an input Python string statement.
|
2524
2532
|
|
@@ -2526,7 +2534,7 @@ class VirtualPyStage(PyStage): # pragma: no cov
|
|
2526
2534
|
:param values: A variable that want to set before running this
|
2527
2535
|
:param deps: An additional Python dependencies that want install before
|
2528
2536
|
run this python stage.
|
2529
|
-
:param run_id: (
|
2537
|
+
:param run_id: (StrOrNone) A running ID of this stage execution.
|
2530
2538
|
"""
|
2531
2539
|
run_id: str = run_id or uuid.uuid4()
|
2532
2540
|
f_name: str = f"{run_id}.py"
|
@@ -2565,8 +2573,8 @@ class VirtualPyStage(PyStage): # pragma: no cov
|
|
2565
2573
|
self,
|
2566
2574
|
params: DictData,
|
2567
2575
|
*,
|
2568
|
-
result: Result
|
2569
|
-
event: Event
|
2576
|
+
result: Optional[Result] = None,
|
2577
|
+
event: Optional[Event] = None,
|
2570
2578
|
) -> Result:
|
2571
2579
|
"""Execute the Python statement via Python virtual environment.
|
2572
2580
|
|
@@ -2644,5 +2652,8 @@ Stage = Annotated[
|
|
2644
2652
|
RaiseStage,
|
2645
2653
|
EmptyStage,
|
2646
2654
|
],
|
2647
|
-
Field(
|
2655
|
+
Field(
|
2656
|
+
union_mode="smart",
|
2657
|
+
description="A stage models that already implemented on this package.",
|
2658
|
+
),
|
2648
2659
|
] # pragma: no cov
|