ddeutil-workflow 0.0.41__py3-none-any.whl → 0.0.42__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/api/api.py +7 -7
- ddeutil/workflow/api/routes/schedules.py +5 -5
- ddeutil/workflow/api/routes/workflows.py +2 -2
- ddeutil/workflow/conf.py +39 -28
- ddeutil/workflow/cron.py +12 -13
- ddeutil/workflow/job.py +18 -10
- ddeutil/workflow/logs.py +33 -6
- ddeutil/workflow/reusables.py +16 -13
- ddeutil/workflow/scheduler.py +26 -28
- ddeutil/workflow/stages.py +257 -62
- ddeutil/workflow/utils.py +0 -1
- ddeutil/workflow/workflow.py +111 -74
- {ddeutil_workflow-0.0.41.dist-info → ddeutil_workflow-0.0.42.dist-info}/METADATA +6 -9
- ddeutil_workflow-0.0.42.dist-info/RECORD +30 -0
- ddeutil/workflow/context.py +0 -61
- ddeutil_workflow-0.0.41.dist-info/RECORD +0 -31
- {ddeutil_workflow-0.0.41.dist-info → ddeutil_workflow-0.0.42.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.41.dist-info → ddeutil_workflow-0.0.42.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.41.dist-info → ddeutil_workflow-0.0.42.dist-info}/top_level.txt +0 -0
ddeutil/workflow/stages.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
# [x] Use config
|
6
|
+
# [x] Use dynamic config
|
7
7
|
"""Stage Model that use for getting stage data template from the Job Model.
|
8
8
|
The stage handle the minimize task that run in some thread (same thread at
|
9
9
|
its job owner) that mean it is the lowest executor of a workflow that can
|
@@ -50,7 +50,7 @@ from pydantic.functional_validators import model_validator
|
|
50
50
|
from typing_extensions import Self
|
51
51
|
|
52
52
|
from .__types import DictData, DictStr, TupleStr
|
53
|
-
from .conf import
|
53
|
+
from .conf import dynamic
|
54
54
|
from .exceptions import StageException, to_dict
|
55
55
|
from .result import Result, Status
|
56
56
|
from .reusables import TagFunc, extract_call, not_in_template, param2template
|
@@ -67,6 +67,7 @@ __all__: TupleStr = (
|
|
67
67
|
"TriggerStage",
|
68
68
|
"ForEachStage",
|
69
69
|
"ParallelStage",
|
70
|
+
"RaiseStage",
|
70
71
|
"Stage",
|
71
72
|
)
|
72
73
|
|
@@ -96,7 +97,7 @@ class BaseStage(BaseModel, ABC):
|
|
96
97
|
)
|
97
98
|
extras: DictData = Field(
|
98
99
|
default_factory=dict,
|
99
|
-
description="An extra override values.",
|
100
|
+
description="An extra override config values.",
|
100
101
|
)
|
101
102
|
|
102
103
|
@property
|
@@ -151,15 +152,6 @@ class BaseStage(BaseModel, ABC):
|
|
151
152
|
"""
|
152
153
|
raise NotImplementedError("Stage should implement `execute` method.")
|
153
154
|
|
154
|
-
async def axecute(
|
155
|
-
self,
|
156
|
-
params: DictData,
|
157
|
-
*,
|
158
|
-
result: Result | None = None,
|
159
|
-
event: Event | None,
|
160
|
-
) -> Result: # pragma: no cov
|
161
|
-
...
|
162
|
-
|
163
155
|
def handler_execute(
|
164
156
|
self,
|
165
157
|
params: DictData,
|
@@ -167,7 +159,7 @@ class BaseStage(BaseModel, ABC):
|
|
167
159
|
run_id: str | None = None,
|
168
160
|
parent_run_id: str | None = None,
|
169
161
|
result: Result | None = None,
|
170
|
-
raise_error: bool =
|
162
|
+
raise_error: bool | None = None,
|
171
163
|
to: DictData | None = None,
|
172
164
|
event: Event | None = None,
|
173
165
|
) -> Result:
|
@@ -223,7 +215,7 @@ class BaseStage(BaseModel, ABC):
|
|
223
215
|
except Exception as err:
|
224
216
|
result.trace.error(f"[STAGE]: {err.__class__.__name__}: {err}")
|
225
217
|
|
226
|
-
if raise_error
|
218
|
+
if dynamic("stage_raise_error", f=raise_error, extras=self.extras):
|
227
219
|
if isinstance(err, StageException):
|
228
220
|
raise
|
229
221
|
|
@@ -268,13 +260,17 @@ class BaseStage(BaseModel, ABC):
|
|
268
260
|
if "stages" not in to:
|
269
261
|
to["stages"] = {}
|
270
262
|
|
271
|
-
if self.id is None and not
|
263
|
+
if self.id is None and not dynamic(
|
264
|
+
"stage_default_id", extras=self.extras
|
265
|
+
):
|
272
266
|
return to
|
273
267
|
|
274
268
|
_id: str = (
|
275
|
-
param2template(self.id, params=to)
|
269
|
+
param2template(self.id, params=to, extras=self.extras)
|
276
270
|
if self.id
|
277
|
-
else gen_id(
|
271
|
+
else gen_id(
|
272
|
+
param2template(self.name, params=to, extras=self.extras)
|
273
|
+
)
|
278
274
|
)
|
279
275
|
|
280
276
|
errors: DictData = (
|
@@ -312,7 +308,9 @@ class BaseStage(BaseModel, ABC):
|
|
312
308
|
# should use the `re` module to validate eval-string before
|
313
309
|
# running.
|
314
310
|
rs: bool = eval(
|
315
|
-
param2template(self.condition, params
|
311
|
+
param2template(self.condition, params, extras=self.extras),
|
312
|
+
globals() | params,
|
313
|
+
{},
|
316
314
|
)
|
317
315
|
if not isinstance(rs, bool):
|
318
316
|
raise TypeError("Return type of condition does not be boolean")
|
@@ -321,7 +319,102 @@ class BaseStage(BaseModel, ABC):
|
|
321
319
|
raise StageException(f"{err.__class__.__name__}: {err}") from err
|
322
320
|
|
323
321
|
|
324
|
-
class
|
322
|
+
class BaseAsyncStage(BaseStage):
|
323
|
+
|
324
|
+
@abstractmethod
|
325
|
+
def execute(
|
326
|
+
self,
|
327
|
+
params: DictData,
|
328
|
+
*,
|
329
|
+
result: Result | None = None,
|
330
|
+
event: Event | None = None,
|
331
|
+
) -> Result: ...
|
332
|
+
|
333
|
+
@abstractmethod
|
334
|
+
async def axecute(
|
335
|
+
self,
|
336
|
+
params: DictData,
|
337
|
+
*,
|
338
|
+
result: Result | None = None,
|
339
|
+
event: Event | None = None,
|
340
|
+
) -> Result:
|
341
|
+
"""Async execution method for this Empty stage that only logging out to
|
342
|
+
stdout.
|
343
|
+
|
344
|
+
:param params: (DictData) A context data that want to add output result.
|
345
|
+
But this stage does not pass any output.
|
346
|
+
:param result: (Result) A result object for keeping context and status
|
347
|
+
data.
|
348
|
+
:param event: (Event) An event manager that use to track parent execute
|
349
|
+
was not force stopped.
|
350
|
+
|
351
|
+
:rtype: Result
|
352
|
+
"""
|
353
|
+
raise NotImplementedError(
|
354
|
+
"Async Stage should implement `axecute` method."
|
355
|
+
)
|
356
|
+
|
357
|
+
async def handler_axecute(
|
358
|
+
self,
|
359
|
+
params: DictData,
|
360
|
+
*,
|
361
|
+
run_id: str | None = None,
|
362
|
+
parent_run_id: str | None = None,
|
363
|
+
result: Result | None = None,
|
364
|
+
raise_error: bool | None = None,
|
365
|
+
to: DictData | None = None,
|
366
|
+
event: Event | None = None,
|
367
|
+
) -> Result:
|
368
|
+
"""Async Handler stage execution result from the stage `execute` method.
|
369
|
+
|
370
|
+
:param params: (DictData) A parameterize value data that use in this
|
371
|
+
stage execution.
|
372
|
+
:param run_id: (str) A running stage ID for this execution.
|
373
|
+
:param parent_run_id: (str) A parent workflow running ID for this
|
374
|
+
execution.
|
375
|
+
:param result: (Result) A result object for keeping context and status
|
376
|
+
data before execution.
|
377
|
+
:param raise_error: (bool) A flag that all this method raise error
|
378
|
+
:param to: (DictData) A target object for auto set the return output
|
379
|
+
after execution.
|
380
|
+
:param event: (Event) An event manager that pass to the stage execution.
|
381
|
+
|
382
|
+
:rtype: Result
|
383
|
+
"""
|
384
|
+
result: Result = Result.construct_with_rs_or_id(
|
385
|
+
result,
|
386
|
+
run_id=run_id,
|
387
|
+
parent_run_id=parent_run_id,
|
388
|
+
id_logic=self.iden,
|
389
|
+
)
|
390
|
+
|
391
|
+
try:
|
392
|
+
rs: Result = await self.axecute(params, result=result, event=event)
|
393
|
+
if to is not None:
|
394
|
+
return self.set_outputs(rs.context, to=to)
|
395
|
+
return rs
|
396
|
+
except Exception as err:
|
397
|
+
await result.trace.aerror(
|
398
|
+
f"[STAGE]: {err.__class__.__name__}: {err}"
|
399
|
+
)
|
400
|
+
|
401
|
+
if dynamic("stage_raise_error", f=raise_error, extras=self.extras):
|
402
|
+
if isinstance(err, StageException):
|
403
|
+
raise
|
404
|
+
|
405
|
+
raise StageException(
|
406
|
+
f"{self.__class__.__name__}: \n\t"
|
407
|
+
f"{err.__class__.__name__}: {err}"
|
408
|
+
) from None
|
409
|
+
|
410
|
+
errors: DictData = {"errors": to_dict(err)}
|
411
|
+
if to is not None:
|
412
|
+
return self.set_outputs(errors, to=to)
|
413
|
+
|
414
|
+
return result.catch(status=Status.FAILED, context=errors)
|
415
|
+
|
416
|
+
|
417
|
+
class EmptyStage(BaseAsyncStage):
|
325
418
|
"""Empty stage that do nothing (context equal empty stage) and logging the
|
326
419
|
name of stage only to stdout.
|
327
420
|
|
@@ -372,7 +465,7 @@ class EmptyStage(BaseStage):
|
|
372
465
|
|
373
466
|
result.trace.info(
|
374
467
|
f"[STAGE]: Empty-Execute: {self.name!r}: "
|
375
|
-
f"( {param2template(self.echo, params=
|
468
|
+
f"( {param2template(self.echo, params, extras=self.extras) or '...'} )"
|
376
469
|
)
|
377
470
|
if self.sleep > 0:
|
378
471
|
if self.sleep > 5:
|
@@ -381,14 +474,13 @@ class EmptyStage(BaseStage):
|
|
381
474
|
|
382
475
|
return result.catch(status=Status.SUCCESS)
|
383
476
|
|
384
|
-
# TODO: Draft async execute method for the perf improvement.
|
385
477
|
async def axecute(
|
386
478
|
self,
|
387
479
|
params: DictData,
|
388
480
|
*,
|
389
481
|
result: Result | None = None,
|
390
|
-
event: Event | None,
|
391
|
-
) -> Result:
|
482
|
+
event: Event | None = None,
|
483
|
+
) -> Result:
|
392
484
|
"""Async execution method for this Empty stage that only logging out to
|
393
485
|
stdout.
|
394
486
|
|
@@ -406,14 +498,16 @@ class EmptyStage(BaseStage):
|
|
406
498
|
run_id=gen_id(self.name + (self.id or ""), unique=True)
|
407
499
|
)
|
408
500
|
|
409
|
-
result.trace.
|
501
|
+
await result.trace.ainfo(
|
410
502
|
f"[STAGE]: Empty-Execute: {self.name!r}: "
|
411
|
-
f"( {param2template(self.echo, params=
|
503
|
+
f"( {param2template(self.echo, params, extras=self.extras) or '...'} )"
|
412
504
|
)
|
413
505
|
|
414
506
|
if self.sleep > 0:
|
415
507
|
if self.sleep > 5:
|
416
|
-
result.trace.
|
508
|
+
await result.trace.ainfo(
|
509
|
+
f"[STAGE]: ... sleep ({self.sleep} seconds)"
|
510
|
+
)
|
417
511
|
await asyncio.sleep(self.sleep)
|
418
512
|
|
419
513
|
return result.catch(status=Status.SUCCESS)
|
@@ -508,12 +602,14 @@ class BashStage(BaseStage):
|
|
508
602
|
run_id=gen_id(self.name + (self.id or ""), unique=True)
|
509
603
|
)
|
510
604
|
|
511
|
-
bash: str = param2template(
|
605
|
+
bash: str = param2template(
|
606
|
+
dedent(self.bash), params, extras=self.extras
|
607
|
+
)
|
512
608
|
|
513
609
|
result.trace.info(f"[STAGE]: Shell-Execute: {self.name}")
|
514
610
|
with self.create_sh_file(
|
515
611
|
bash=bash,
|
516
|
-
env=param2template(self.env, params),
|
612
|
+
env=param2template(self.env, params, extras=self.extras),
|
517
613
|
run_id=result.run_id,
|
518
614
|
) as sh:
|
519
615
|
result.trace.debug(f"... Start create `{sh[1]}` file.")
|
@@ -635,7 +731,7 @@ class PyStage(BaseStage):
|
|
635
731
|
gb: DictData = (
|
636
732
|
globals()
|
637
733
|
| params
|
638
|
-
| param2template(self.vars, params)
|
734
|
+
| param2template(self.vars, params, extras=self.extras)
|
639
735
|
| {"result": result}
|
640
736
|
)
|
641
737
|
|
@@ -648,7 +744,9 @@ class PyStage(BaseStage):
|
|
648
744
|
|
649
745
|
# WARNING: The exec build-in function is very dangerous. So, it
|
650
746
|
# should use the re module to validate exec-string before running.
|
651
|
-
exec(
|
747
|
+
exec(
|
748
|
+
param2template(dedent(self.run), params, extras=self.extras), gb, lc
|
749
|
+
)
|
652
750
|
|
653
751
|
return result.catch(
|
654
752
|
status=Status.SUCCESS, context={"locals": lc, "globals": gb}
|
@@ -716,11 +814,16 @@ class CallStage(BaseStage):
|
|
716
814
|
run_id=gen_id(self.name + (self.id or ""), unique=True)
|
717
815
|
)
|
718
816
|
|
719
|
-
t_func: TagFunc = extract_call(
|
817
|
+
t_func: TagFunc = extract_call(
|
818
|
+
param2template(self.uses, params, extras=self.extras),
|
819
|
+
registries=self.extras.get("regis_call"),
|
820
|
+
)()
|
720
821
|
|
721
822
|
# VALIDATE: check input task caller parameters that exists before
|
722
823
|
# calling.
|
723
|
-
args: DictData = {"result": result} | param2template(
|
824
|
+
args: DictData = {"result": result} | param2template(
|
825
|
+
self.args, params, extras=self.extras
|
826
|
+
)
|
724
827
|
ips = inspect.signature(t_func)
|
725
828
|
necessary_params: list[str] = [
|
726
829
|
k
|
@@ -754,10 +857,12 @@ class CallStage(BaseStage):
|
|
754
857
|
if inspect.iscoroutinefunction(t_func): # pragma: no cov
|
755
858
|
loop = asyncio.get_event_loop()
|
756
859
|
rs: DictData = loop.run_until_complete(
|
757
|
-
t_func(**param2template(args, params))
|
860
|
+
t_func(**param2template(args, params, extras=self.extras))
|
758
861
|
)
|
759
862
|
else:
|
760
|
-
rs: DictData = t_func(
|
863
|
+
rs: DictData = t_func(
|
864
|
+
**param2template(args, params, extras=self.extras)
|
865
|
+
)
|
761
866
|
|
762
867
|
# VALIDATE:
|
763
868
|
# Check the result type from call function, it should be dict.
|
@@ -819,15 +924,16 @@ class TriggerStage(BaseStage):
|
|
819
924
|
)
|
820
925
|
|
821
926
|
# NOTE: Loading workflow object from trigger name.
|
822
|
-
_trigger: str = param2template(self.trigger, params=
|
927
|
+
_trigger: str = param2template(self.trigger, params, extras=self.extras)
|
823
928
|
|
824
929
|
# NOTE: Set running workflow ID from running stage ID to external
|
825
930
|
# params on Loader object.
|
826
|
-
workflow: Workflow = Workflow.
|
931
|
+
workflow: Workflow = Workflow.from_conf(_trigger, extras=self.extras)
|
827
932
|
result.trace.info(f"[STAGE]: Trigger-Execute: {_trigger!r}")
|
828
933
|
return workflow.execute(
|
829
|
-
params=param2template(self.params, params),
|
934
|
+
params=param2template(self.params, params, extras=self.extras),
|
830
935
|
result=result,
|
936
|
+
event=event,
|
831
937
|
)
|
832
938
|
|
833
939
|
|
@@ -931,6 +1037,9 @@ class ParallelStage(BaseStage): # pragma: no cov
|
|
931
1037
|
run_id=gen_id(self.name + (self.id or ""), unique=True)
|
932
1038
|
)
|
933
1039
|
|
1040
|
+
result.trace.info(
|
1041
|
+
f"[STAGE]: Parallel-Execute with {self.max_parallel_core} cores."
|
1042
|
+
)
|
934
1043
|
rs: DictData = {"parallel": {}}
|
935
1044
|
status = Status.SUCCESS
|
936
1045
|
with ThreadPoolExecutor(
|
@@ -981,7 +1090,7 @@ class ForEachStage(BaseStage):
|
|
981
1090
|
... }
|
982
1091
|
"""
|
983
1092
|
|
984
|
-
foreach: Union[list[str], list[int]] = Field(
|
1093
|
+
foreach: Union[list[str], list[int], str] = Field(
|
985
1094
|
description=(
|
986
1095
|
"A items for passing to each stages via ${{ item }} template."
|
987
1096
|
),
|
@@ -1023,10 +1132,21 @@ class ForEachStage(BaseStage):
|
|
1023
1132
|
run_id=gen_id(self.name + (self.id or ""), unique=True)
|
1024
1133
|
)
|
1025
1134
|
|
1026
|
-
|
1135
|
+
foreach: Union[list[str], list[int]] = (
|
1136
|
+
param2template(self.foreach, params, extras=self.extras)
|
1137
|
+
if isinstance(self.foreach, str)
|
1138
|
+
else self.foreach
|
1139
|
+
)
|
1140
|
+
if not isinstance(foreach, list):
|
1141
|
+
raise StageException(
|
1142
|
+
f"Foreach does not support foreach value: {foreach!r}"
|
1143
|
+
)
|
1144
|
+
|
1145
|
+
result.trace.info(f"[STAGE]: Foreach-Execute: {foreach!r}.")
|
1146
|
+
rs: DictData = {"items": foreach, "foreach": {}}
|
1027
1147
|
status = Status.SUCCESS
|
1028
1148
|
# TODO: Implement concurrent more than 1.
|
1029
|
-
for item in
|
1149
|
+
for item in foreach:
|
1030
1150
|
result.trace.debug(f"[STAGE]: Execute foreach item: {item!r}")
|
1031
1151
|
params["item"] = item
|
1032
1152
|
context = {"stages": {}}
|
@@ -1064,10 +1184,24 @@ class ForEachStage(BaseStage):
|
|
1064
1184
|
|
1065
1185
|
# TODO: Not implement this stages yet
|
1066
1186
|
class UntilStage(BaseStage): # pragma: no cov
|
1067
|
-
"""Until execution stage.
|
1187
|
+
"""Until execution stage.
|
1188
|
+
|
1189
|
+
Data Validate:
|
1190
|
+
>>> stage = {
|
1191
|
+
... "name": "Until stage execution",
|
1192
|
+
... "item": 1,
|
1193
|
+
... "until": "${{ item }} > 3"
|
1194
|
+
... "stages": [
|
1195
|
+
... {
|
1196
|
+
... "name": "Start increase item value.",
|
1197
|
+
... "run": "item = ${{ item }}\\nitem += 1\\n"
|
1198
|
+
... },
|
1199
|
+
... ],
|
1200
|
+
... }
|
1201
|
+
"""
|
1068
1202
|
|
1069
|
-
until: str = Field(description="A until condition.")
|
1070
1203
|
item: Union[str, int, bool] = Field(description="An initial value.")
|
1204
|
+
until: str = Field(description="A until condition.")
|
1071
1205
|
stages: list[Stage] = Field(
|
1072
1206
|
default_factory=list,
|
1073
1207
|
description=(
|
@@ -1090,8 +1224,14 @@ class UntilStage(BaseStage): # pragma: no cov
|
|
1090
1224
|
|
1091
1225
|
|
1092
1226
|
# TODO: Not implement this stages yet
|
1093
|
-
class
|
1094
|
-
|
1227
|
+
class Match(BaseModel):
|
1228
|
+
case: Union[str, int]
|
1229
|
+
stage: Stage
|
1230
|
+
|
1231
|
+
|
1232
|
+
# TODO: Not implement this stages yet
|
1233
|
+
class CaseStage(BaseStage): # pragma: no cov
|
1234
|
+
"""Case execution stage.
|
1095
1235
|
|
1096
1236
|
Data Validate:
|
1097
1237
|
>>> stage = {
|
@@ -1125,7 +1265,7 @@ class IfStage(BaseStage): # pragma: no cov
|
|
1125
1265
|
"""
|
1126
1266
|
|
1127
1267
|
case: str = Field(description="A case condition for routing.")
|
1128
|
-
match: list[
|
1268
|
+
match: list[Match]
|
1129
1269
|
|
1130
1270
|
def execute(
|
1131
1271
|
self,
|
@@ -1133,7 +1273,60 @@ class IfStage(BaseStage): # pragma: no cov
|
|
1133
1273
|
*,
|
1134
1274
|
result: Result | None = None,
|
1135
1275
|
event: Event | None = None,
|
1136
|
-
) -> Result:
|
1276
|
+
) -> Result:
|
1277
|
+
"""Execute case-match condition that pass to the case field.
|
1278
|
+
|
1279
|
+
:param params: A parameter that want to pass before run any statement.
|
1280
|
+
:param result: (Result) A result object for keeping context and status
|
1281
|
+
data.
|
1282
|
+
:param event: (Event) An event manager that use to track parent execute
|
1283
|
+
was not force stopped.
|
1284
|
+
|
1285
|
+
:rtype: Result
|
1286
|
+
"""
|
1287
|
+
if result is None: # pragma: no cov
|
1288
|
+
result: Result = Result(
|
1289
|
+
run_id=gen_id(self.name + (self.id or ""), unique=True)
|
1290
|
+
)
|
1291
|
+
status = Status.SUCCESS
|
1292
|
+
_case = param2template(self.case, params, extras=self.extras)
|
1293
|
+
_else = None
|
1294
|
+
context = {}
|
1295
|
+
for match in self.match:
|
1296
|
+
if (c := match.case) != "_":
|
1297
|
+
_condition = param2template(c, params, extras=self.extras)
|
1298
|
+
else:
|
1299
|
+
_else = match
|
1300
|
+
continue
|
1301
|
+
|
1302
|
+
if match == _condition:
|
1303
|
+
stage: Stage = match.stage
|
1304
|
+
try:
|
1305
|
+
stage.set_outputs(
|
1306
|
+
stage.handler_execute(
|
1307
|
+
params=params,
|
1308
|
+
run_id=result.run_id,
|
1309
|
+
parent_run_id=result.parent_run_id,
|
1310
|
+
).context,
|
1311
|
+
to=context,
|
1312
|
+
)
|
1313
|
+
except StageException as err: # pragma: no cov
|
1314
|
+
status = Status.FAILED
|
1315
|
+
result.trace.error(
|
1316
|
+
f"[STAGE]: Catch:\n\t{err.__class__.__name__}:"
|
1317
|
+
f"\n\t{err}"
|
1318
|
+
)
|
1319
|
+
context.update(
|
1320
|
+
{
|
1321
|
+
"errors": {
|
1322
|
+
"class": err,
|
1323
|
+
"name": err.__class__.__name__,
|
1324
|
+
"message": f"{err.__class__.__name__}: {err}",
|
1325
|
+
},
|
1326
|
+
},
|
1327
|
+
)
|
1328
|
+
|
1329
|
+
return result.catch(status=status, context=context)
|
1137
1330
|
|
1138
1331
|
|
1139
1332
|
class RaiseStage(BaseStage): # pragma: no cov
|
@@ -1165,13 +1358,14 @@ class RaiseStage(BaseStage): # pragma: no cov
|
|
1165
1358
|
result: Result = Result(
|
1166
1359
|
run_id=gen_id(self.name + (self.id or ""), unique=True)
|
1167
1360
|
)
|
1168
|
-
|
1169
|
-
result.trace.error(f"[STAGE]: ... raise ( {self.message} )")
|
1361
|
+
result.trace.info(f"[STAGE]: Raise-Execute: {self.message!r}.")
|
1170
1362
|
raise StageException(self.message)
|
1171
1363
|
|
1172
1364
|
|
1173
1365
|
# TODO: Not implement this stages yet
|
1174
1366
|
class HookStage(BaseStage): # pragma: no cov
|
1367
|
+
"""Hook stage execution."""
|
1368
|
+
|
1175
1369
|
hook: str
|
1176
1370
|
args: DictData
|
1177
1371
|
callback: str
|
@@ -1187,7 +1381,20 @@ class HookStage(BaseStage): # pragma: no cov
|
|
1187
1381
|
|
1188
1382
|
# TODO: Not implement this stages yet
|
1189
1383
|
class DockerStage(BaseStage): # pragma: no cov
|
1190
|
-
"""Docker container stage execution.
|
1384
|
+
"""Docker container stage execution.
|
1385
|
+
|
1386
|
+
Data Validate:
|
1387
|
+
>>> stage = {
|
1388
|
+
... "name": "Docker stage execution",
|
1389
|
+
... "image": "image-name.pkg.com",
|
1390
|
+
... "env": {
|
1391
|
+
... "ENV": "dev",
|
1392
|
+
... },
|
1393
|
+
... "volume": {
|
1394
|
+
... "secrets": "/secrets",
|
1395
|
+
... },
|
1396
|
+
... }
|
1397
|
+
"""
|
1191
1398
|
|
1192
1399
|
image: str = Field(
|
1193
1400
|
description="A Docker image url with tag that want to run.",
|
@@ -1224,18 +1431,6 @@ class VirtualPyStage(PyStage): # pragma: no cov
|
|
1224
1431
|
return super().execute(params, result=result)
|
1225
1432
|
|
1226
1433
|
|
1227
|
-
# TODO: Not implement this stages yet
|
1228
|
-
class SensorStage(BaseStage): # pragma: no cov
|
1229
|
-
|
1230
|
-
def execute(
|
1231
|
-
self,
|
1232
|
-
params: DictData,
|
1233
|
-
*,
|
1234
|
-
result: Result | None = None,
|
1235
|
-
event: Event | None = None,
|
1236
|
-
) -> Result: ...
|
1237
|
-
|
1238
|
-
|
1239
1434
|
# NOTE:
|
1240
1435
|
# An order of parsing stage model on the Job model with ``stages`` field.
|
1241
1436
|
# From the current build-in stages, they do not have stage that have the same
|
@@ -1243,7 +1438,6 @@ class SensorStage(BaseStage): # pragma: no cov
|
|
1243
1438
|
#
|
1244
1439
|
Stage = Annotated[
|
1245
1440
|
Union[
|
1246
|
-
EmptyStage,
|
1247
1441
|
BashStage,
|
1248
1442
|
CallStage,
|
1249
1443
|
TriggerStage,
|
@@ -1251,6 +1445,7 @@ Stage = Annotated[
|
|
1251
1445
|
ParallelStage,
|
1252
1446
|
PyStage,
|
1253
1447
|
RaiseStage,
|
1448
|
+
EmptyStage,
|
1254
1449
|
],
|
1255
1450
|
Field(union_mode="smart"),
|
1256
1451
|
]
|
ddeutil/workflow/utils.py
CHANGED