ddeutil-workflow 0.0.8__py3-none-any.whl → 0.0.10__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 +3 -14
- ddeutil/workflow/api.py +44 -75
- ddeutil/workflow/cli.py +134 -0
- ddeutil/workflow/cron.py +803 -0
- ddeutil/workflow/exceptions.py +3 -0
- ddeutil/workflow/log.py +152 -47
- ddeutil/workflow/on.py +27 -18
- ddeutil/workflow/pipeline.py +527 -234
- ddeutil/workflow/repeat.py +71 -40
- ddeutil/workflow/route.py +77 -63
- ddeutil/workflow/scheduler.py +523 -616
- ddeutil/workflow/stage.py +158 -82
- ddeutil/workflow/utils.py +273 -46
- ddeutil_workflow-0.0.10.dist-info/METADATA +182 -0
- ddeutil_workflow-0.0.10.dist-info/RECORD +21 -0
- {ddeutil_workflow-0.0.8.dist-info → ddeutil_workflow-0.0.10.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.10.dist-info/entry_points.txt +2 -0
- ddeutil/workflow/app.py +0 -45
- ddeutil/workflow/loader.py +0 -80
- ddeutil_workflow-0.0.8.dist-info/METADATA +0 -266
- ddeutil_workflow-0.0.8.dist-info/RECORD +0 -20
- {ddeutil_workflow-0.0.8.dist-info → ddeutil_workflow-0.0.10.dist-info}/LICENSE +0 -0
- {ddeutil_workflow-0.0.8.dist-info → ddeutil_workflow-0.0.10.dist-info}/top_level.txt +0 -0
ddeutil/workflow/stage.py
CHANGED
@@ -19,7 +19,6 @@ from __future__ import annotations
|
|
19
19
|
|
20
20
|
import contextlib
|
21
21
|
import inspect
|
22
|
-
import logging
|
23
22
|
import os
|
24
23
|
import subprocess
|
25
24
|
import sys
|
@@ -31,14 +30,22 @@ from functools import wraps
|
|
31
30
|
from inspect import Parameter
|
32
31
|
from pathlib import Path
|
33
32
|
from subprocess import CompletedProcess
|
33
|
+
from textwrap import dedent
|
34
34
|
from typing import Callable, Optional, Union
|
35
35
|
|
36
|
+
try:
|
37
|
+
from typing import ParamSpec
|
38
|
+
except ImportError:
|
39
|
+
from typing_extensions import ParamSpec
|
40
|
+
|
36
41
|
from ddeutil.core import str2bool
|
37
42
|
from pydantic import BaseModel, Field
|
38
43
|
from pydantic.functional_validators import model_validator
|
44
|
+
from typing_extensions import Self
|
39
45
|
|
40
46
|
from .__types import DictData, DictStr, Re, TupleStr
|
41
47
|
from .exceptions import StageException
|
48
|
+
from .log import get_logger
|
42
49
|
from .utils import (
|
43
50
|
Registry,
|
44
51
|
Result,
|
@@ -46,33 +53,66 @@ from .utils import (
|
|
46
53
|
gen_id,
|
47
54
|
make_exec,
|
48
55
|
make_registry,
|
56
|
+
not_in_template,
|
49
57
|
param2template,
|
50
58
|
)
|
51
59
|
|
60
|
+
P = ParamSpec("P")
|
61
|
+
logger = get_logger("ddeutil.workflow")
|
62
|
+
|
63
|
+
|
64
|
+
__all__: TupleStr = (
|
65
|
+
"Stage",
|
66
|
+
"EmptyStage",
|
67
|
+
"BashStage",
|
68
|
+
"PyStage",
|
69
|
+
"HookStage",
|
70
|
+
"TriggerStage",
|
71
|
+
"handler_result",
|
72
|
+
)
|
73
|
+
|
52
74
|
|
53
|
-
def handler_result(message: str | None = None):
|
54
|
-
"""Decorator function for handler result from the stage execution.
|
75
|
+
def handler_result(message: str | None = None) -> Callable[P, Result]:
|
76
|
+
"""Decorator function for handler result from the stage execution. This
|
77
|
+
function should to use with execution method only.
|
78
|
+
|
79
|
+
:param message: A message that want to add at prefix of exception statement.
|
80
|
+
"""
|
55
81
|
message: str = message or ""
|
56
82
|
|
57
|
-
def decorator(func):
|
83
|
+
def decorator(func: Callable[P, Result]) -> Callable[P, Result]:
|
58
84
|
|
59
85
|
@wraps(func)
|
60
|
-
def wrapped(self:
|
86
|
+
def wrapped(self: Stage, *args, **kwargs):
|
61
87
|
try:
|
62
|
-
|
63
|
-
return
|
88
|
+
# NOTE: Start calling origin function with a passing args.
|
89
|
+
return func(self, *args, **kwargs).set_run_id(self.run_id)
|
64
90
|
except Exception as err:
|
65
|
-
|
91
|
+
# NOTE: Start catching error from the stage execution.
|
92
|
+
logger.error(
|
66
93
|
f"({self.run_id}) [STAGE]: {err.__class__.__name__}: {err}"
|
67
94
|
)
|
68
|
-
if
|
95
|
+
if str2bool(
|
96
|
+
os.getenv("WORKFLOW_CORE_STAGE_RAISE_ERROR", "true")
|
97
|
+
):
|
98
|
+
# NOTE: If error that raise from stage execution course by
|
99
|
+
# itself, it will return that error with previous
|
100
|
+
# dependency.
|
101
|
+
if isinstance(err, StageException):
|
102
|
+
raise StageException(
|
103
|
+
f"{self.__class__.__name__}: {message}\n\t{err}"
|
104
|
+
) from err
|
69
105
|
raise StageException(
|
70
|
-
f"{self.__class__.__name__}: {message}\n
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
106
|
+
f"{self.__class__.__name__}: {message}\n\t"
|
107
|
+
f"{err.__class__.__name__}: {err}"
|
108
|
+
) from None
|
109
|
+
rs: Result = Result(
|
110
|
+
status=1,
|
111
|
+
context={
|
112
|
+
"error_message": f"{err.__class__.__name__}: {err}",
|
113
|
+
},
|
114
|
+
)
|
115
|
+
return rs.set_run_id(self.run_id)
|
76
116
|
|
77
117
|
return wrapped
|
78
118
|
|
@@ -93,24 +133,47 @@ class BaseStage(BaseModel, ABC):
|
|
93
133
|
),
|
94
134
|
)
|
95
135
|
name: str = Field(
|
96
|
-
description="A stage name that want to logging when start execution."
|
136
|
+
description="A stage name that want to logging when start execution.",
|
97
137
|
)
|
98
138
|
condition: Optional[str] = Field(
|
99
139
|
default=None,
|
140
|
+
description="A stage condition statement to allow stage executable.",
|
100
141
|
alias="if",
|
101
142
|
)
|
102
143
|
run_id: Optional[str] = Field(
|
103
144
|
default=None,
|
104
145
|
description="A running stage ID.",
|
105
146
|
repr=False,
|
147
|
+
exclude=True,
|
106
148
|
)
|
107
149
|
|
108
150
|
@model_validator(mode="after")
|
109
151
|
def __prepare_running_id(self):
|
152
|
+
"""Prepare stage running ID that use default value of field and this
|
153
|
+
method will validate name and id fields should not contain any template
|
154
|
+
parameter (exclude matrix template).
|
155
|
+
"""
|
110
156
|
if self.run_id is None:
|
111
157
|
self.run_id = gen_id(self.name + (self.id or ""), unique=True)
|
158
|
+
|
159
|
+
# VALIDATE: Validate stage id and name should not dynamic with params
|
160
|
+
# template. (allow only matrix)
|
161
|
+
if not_in_template(self.id) or not_in_template(self.name):
|
162
|
+
raise ValueError(
|
163
|
+
"Stage name and ID should only template with matrix."
|
164
|
+
)
|
165
|
+
|
112
166
|
return self
|
113
167
|
|
168
|
+
def get_running_id(self, run_id: str) -> Self:
|
169
|
+
"""Return Stage model object that changing stage running ID with an
|
170
|
+
input running ID.
|
171
|
+
|
172
|
+
:param run_id: A replace stage running ID.
|
173
|
+
:rtype: Self
|
174
|
+
"""
|
175
|
+
return self.model_copy(update={"run_id": run_id})
|
176
|
+
|
114
177
|
@abstractmethod
|
115
178
|
def execute(self, params: DictData) -> Result:
|
116
179
|
"""Execute abstraction method that action something by sub-model class.
|
@@ -121,41 +184,39 @@ class BaseStage(BaseModel, ABC):
|
|
121
184
|
"""
|
122
185
|
raise NotImplementedError("Stage should implement ``execute`` method.")
|
123
186
|
|
124
|
-
def set_outputs(self, output: DictData,
|
187
|
+
def set_outputs(self, output: DictData, to: DictData) -> DictData:
|
125
188
|
"""Set an outputs from execution process to an input params.
|
126
189
|
|
127
190
|
:param output: A output data that want to extract to an output key.
|
128
|
-
:param
|
191
|
+
:param to: A context data that want to add output result.
|
129
192
|
:rtype: DictData
|
130
193
|
"""
|
131
194
|
if not (
|
132
195
|
self.id
|
133
|
-
or str2bool(os.getenv("
|
196
|
+
or str2bool(os.getenv("WORKFLOW_CORE_STAGE_DEFAULT_ID", "false"))
|
134
197
|
):
|
135
|
-
|
198
|
+
logger.debug(
|
136
199
|
f"({self.run_id}) [STAGE]: Output does not set because this "
|
137
200
|
f"stage does not set ID or default stage ID config flag not be "
|
138
201
|
f"True."
|
139
202
|
)
|
140
|
-
return
|
203
|
+
return to
|
141
204
|
|
142
205
|
# NOTE: Create stages key to receive an output from the stage execution.
|
143
|
-
if "stages" not in
|
144
|
-
|
206
|
+
if "stages" not in to:
|
207
|
+
to["stages"] = {}
|
145
208
|
|
146
|
-
# TODO: Validate stage id and name should not dynamic with params
|
147
|
-
# template. (allow only matrix)
|
148
209
|
if self.id:
|
149
|
-
_id: str = param2template(self.id, params=
|
210
|
+
_id: str = param2template(self.id, params=to)
|
150
211
|
else:
|
151
|
-
_id: str = gen_id(param2template(self.name, params=
|
212
|
+
_id: str = gen_id(param2template(self.name, params=to))
|
152
213
|
|
153
214
|
# NOTE: Set the output to that stage generated ID.
|
154
|
-
|
155
|
-
logging.debug(
|
215
|
+
logger.debug(
|
156
216
|
f"({self.run_id}) [STAGE]: Set output complete with stage ID: {_id}"
|
157
217
|
)
|
158
|
-
|
218
|
+
to["stages"][_id] = {"outputs": output}
|
219
|
+
return to
|
159
220
|
|
160
221
|
def is_skipped(self, params: DictData | None = None) -> bool:
|
161
222
|
"""Return true if condition of this stage do not correct.
|
@@ -174,8 +235,8 @@ class BaseStage(BaseModel, ABC):
|
|
174
235
|
raise TypeError("Return type of condition does not be boolean")
|
175
236
|
return not rs
|
176
237
|
except Exception as err:
|
177
|
-
|
178
|
-
raise StageException(
|
238
|
+
logger.error(f"({self.run_id}) [STAGE]: {err}")
|
239
|
+
raise StageException(f"{err.__class__.__name__}: {err}") from err
|
179
240
|
|
180
241
|
|
181
242
|
class EmptyStage(BaseStage):
|
@@ -201,7 +262,7 @@ class EmptyStage(BaseStage):
|
|
201
262
|
:param params: A context data that want to add output result. But this
|
202
263
|
stage does not pass any output.
|
203
264
|
"""
|
204
|
-
|
265
|
+
logger.info(
|
205
266
|
f"({self.run_id}) [STAGE]: Empty-Execute: {self.name!r}: "
|
206
267
|
f"( {param2template(self.echo, params=params) or '...'} )"
|
207
268
|
)
|
@@ -246,37 +307,40 @@ class BashStage(BaseStage):
|
|
246
307
|
f_shebang: str = "bash" if sys.platform.startswith("win") else "sh"
|
247
308
|
with open(f"./{f_name}", mode="w", newline="\n") as f:
|
248
309
|
# NOTE: write header of `.sh` file
|
249
|
-
f.write(f"#!/bin/{f_shebang}\n")
|
310
|
+
f.write(f"#!/bin/{f_shebang}\n\n")
|
250
311
|
|
251
312
|
# NOTE: add setting environment variable before bash skip statement.
|
252
313
|
f.writelines([f"{k}='{env[k]}';\n" for k in env])
|
253
314
|
|
254
315
|
# NOTE: make sure that shell script file does not have `\r` char.
|
255
|
-
f.write(bash.replace("\r\n", "\n"))
|
316
|
+
f.write("\n" + bash.replace("\r\n", "\n"))
|
256
317
|
|
318
|
+
# NOTE: Make this .sh file able to executable.
|
257
319
|
make_exec(f"./{f_name}")
|
258
|
-
|
320
|
+
|
321
|
+
logger.debug(
|
259
322
|
f"({self.run_id}) [STAGE]: Start create `.sh` file and running a "
|
260
323
|
f"bash statement."
|
261
324
|
)
|
262
325
|
|
263
326
|
yield [f_shebang, f_name]
|
264
327
|
|
328
|
+
# Note: Remove .sh file that use to run bash.
|
265
329
|
Path(f"./{f_name}").unlink()
|
266
330
|
|
267
331
|
@handler_result()
|
268
|
-
def execute(self, params: DictData) ->
|
332
|
+
def execute(self, params: DictData) -> Result:
|
269
333
|
"""Execute the Bash statement with the Python build-in ``subprocess``
|
270
334
|
package.
|
271
335
|
|
272
336
|
:param params: A parameter data that want to use in this execution.
|
273
337
|
:rtype: Result
|
274
338
|
"""
|
275
|
-
bash: str = param2template(self.bash, params)
|
339
|
+
bash: str = param2template(dedent(self.bash), params)
|
276
340
|
with self.__prepare_bash(
|
277
341
|
bash=bash, env=param2template(self.env, params)
|
278
342
|
) as sh:
|
279
|
-
|
343
|
+
logger.info(f"({self.run_id}) [STAGE]: Shell-Execute: {sh}")
|
280
344
|
rs: CompletedProcess = subprocess.run(
|
281
345
|
sh,
|
282
346
|
shell=False,
|
@@ -288,19 +352,19 @@ class BashStage(BaseStage):
|
|
288
352
|
rs.stderr.encode("utf-8").decode("utf-16")
|
289
353
|
if "\\x00" in rs.stderr
|
290
354
|
else rs.stderr
|
291
|
-
)
|
292
|
-
logging.error(
|
293
|
-
f"({self.run_id}) [STAGE]: {err}\n\n```bash\n{bash}```"
|
294
|
-
)
|
355
|
+
).removesuffix("\n")
|
295
356
|
raise StageException(
|
296
|
-
f"
|
297
|
-
f"
|
357
|
+
f"Subprocess: {err}\nRunning Statement:\n---\n"
|
358
|
+
f"```bash\n{bash}\n```"
|
298
359
|
)
|
299
|
-
return
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
360
|
+
return Result(
|
361
|
+
status=0,
|
362
|
+
context={
|
363
|
+
"return_code": rs.returncode,
|
364
|
+
"stdout": rs.stdout.rstrip("\n"),
|
365
|
+
"stderr": rs.stderr.rstrip("\n"),
|
366
|
+
},
|
367
|
+
)
|
304
368
|
|
305
369
|
|
306
370
|
class PyStage(BaseStage):
|
@@ -327,48 +391,56 @@ class PyStage(BaseStage):
|
|
327
391
|
),
|
328
392
|
)
|
329
393
|
|
330
|
-
def set_outputs(self, output: DictData,
|
394
|
+
def set_outputs(self, output: DictData, to: DictData) -> DictData:
|
331
395
|
"""Set an outputs from the Python execution process to an input params.
|
332
396
|
|
333
397
|
:param output: A output data that want to extract to an output key.
|
334
|
-
:param
|
398
|
+
:param to: A context data that want to add output result.
|
335
399
|
:rtype: DictData
|
336
400
|
"""
|
337
401
|
# NOTE: The output will fileter unnecessary keys from locals.
|
338
402
|
_locals: DictData = output["locals"]
|
339
403
|
super().set_outputs(
|
340
|
-
{k: _locals[k] for k in _locals if k != "__annotations__"},
|
341
|
-
params=params,
|
404
|
+
{k: _locals[k] for k in _locals if k != "__annotations__"}, to=to
|
342
405
|
)
|
343
406
|
|
344
407
|
# NOTE:
|
345
408
|
# Override value that changing from the globals that pass via exec.
|
346
409
|
_globals: DictData = output["globals"]
|
347
|
-
|
348
|
-
return
|
410
|
+
to.update({k: _globals[k] for k in to if k in _globals})
|
411
|
+
return to
|
349
412
|
|
350
413
|
@handler_result()
|
351
|
-
def execute(self, params: DictData) ->
|
414
|
+
def execute(self, params: DictData) -> Result:
|
352
415
|
"""Execute the Python statement that pass all globals and input params
|
353
416
|
to globals argument on ``exec`` build-in function.
|
354
417
|
|
355
418
|
:param params: A parameter that want to pass before run any statement.
|
356
419
|
:rtype: Result
|
357
420
|
"""
|
421
|
+
# NOTE: Replace the run statement that has templating value.
|
422
|
+
run: str = param2template(dedent(self.run), params)
|
423
|
+
|
358
424
|
# NOTE: create custom globals value that will pass to exec function.
|
359
425
|
_globals: DictData = (
|
360
426
|
globals() | params | param2template(self.vars, params)
|
361
427
|
)
|
362
428
|
_locals: DictData = {}
|
363
|
-
|
364
|
-
|
429
|
+
|
430
|
+
# NOTE: Start exec the run statement.
|
431
|
+
logger.info(f"({self.run_id}) [STAGE]: Py-Execute: {self.name}")
|
365
432
|
exec(run, _globals, _locals)
|
366
|
-
|
433
|
+
|
434
|
+
return Result(
|
435
|
+
status=0, context={"locals": _locals, "globals": _globals}
|
436
|
+
)
|
367
437
|
|
368
438
|
|
369
439
|
@dataclass
|
370
440
|
class HookSearch:
|
371
|
-
"""Hook Search dataclass
|
441
|
+
"""Hook Search dataclass that use for receive regular expression grouping
|
442
|
+
dict from searching hook string value.
|
443
|
+
"""
|
372
444
|
|
373
445
|
path: str
|
374
446
|
func: str
|
@@ -376,13 +448,16 @@ class HookSearch:
|
|
376
448
|
|
377
449
|
|
378
450
|
def extract_hook(hook: str) -> Callable[[], TagFunc]:
|
379
|
-
"""Extract Hook string value to hook function
|
451
|
+
"""Extract Hook function from string value to hook partial function that
|
452
|
+
does run it at runtime.
|
380
453
|
|
381
454
|
:param hook: A hook value that able to match with Task regex.
|
382
455
|
:rtype: Callable[[], TagFunc]
|
383
456
|
"""
|
384
457
|
if not (found := Re.RE_TASK_FMT.search(hook)):
|
385
|
-
raise ValueError(
|
458
|
+
raise ValueError(
|
459
|
+
f"Hook {hook!r} does not match with hook format regex."
|
460
|
+
)
|
386
461
|
|
387
462
|
# NOTE: Pass the searching hook string to `path`, `func`, and `tag`.
|
388
463
|
hook: HookSearch = HookSearch(**found.groupdict())
|
@@ -415,7 +490,7 @@ class HookStage(BaseStage):
|
|
415
490
|
Data Validate:
|
416
491
|
>>> stage = {
|
417
492
|
... "name": "Task stage execution",
|
418
|
-
... "
|
493
|
+
... "uses": "tasks/function-name@tag-name",
|
419
494
|
... "args": {
|
420
495
|
... "FOO": "BAR",
|
421
496
|
... },
|
@@ -426,12 +501,13 @@ class HookStage(BaseStage):
|
|
426
501
|
description="A pointer that want to load function from registry.",
|
427
502
|
)
|
428
503
|
args: DictData = Field(
|
504
|
+
default_factory=dict,
|
429
505
|
description="An arguments that want to pass to the hook function.",
|
430
506
|
alias="with",
|
431
507
|
)
|
432
508
|
|
433
509
|
@handler_result()
|
434
|
-
def execute(self, params: DictData) ->
|
510
|
+
def execute(self, params: DictData) -> Result:
|
435
511
|
"""Execute the Hook function that already in the hook registry.
|
436
512
|
|
437
513
|
:param params: A parameter that want to pass before run any statement.
|
@@ -440,10 +516,7 @@ class HookStage(BaseStage):
|
|
440
516
|
"""
|
441
517
|
t_func_hook: str = param2template(self.uses, params)
|
442
518
|
t_func: TagFunc = extract_hook(t_func_hook)()
|
443
|
-
|
444
|
-
raise ImportError(
|
445
|
-
f"Hook caller {t_func_hook!r} function does not callable."
|
446
|
-
)
|
519
|
+
|
447
520
|
# VALIDATE: check input task caller parameters that exists before
|
448
521
|
# calling.
|
449
522
|
args: DictData = param2template(self.args, params)
|
@@ -454,7 +527,7 @@ class HookStage(BaseStage):
|
|
454
527
|
if ips.parameters[k].default == Parameter.empty
|
455
528
|
):
|
456
529
|
raise ValueError(
|
457
|
-
f"Necessary params, ({', '.join(ips.parameters.keys())}), "
|
530
|
+
f"Necessary params, ({', '.join(ips.parameters.keys())}, ), "
|
458
531
|
f"does not set to args"
|
459
532
|
)
|
460
533
|
# NOTE: add '_' prefix if it want to use.
|
@@ -462,9 +535,8 @@ class HookStage(BaseStage):
|
|
462
535
|
if k.removeprefix("_") in args:
|
463
536
|
args[k] = args.pop(k.removeprefix("_"))
|
464
537
|
|
465
|
-
|
466
|
-
f"({self.run_id}) [STAGE]: Hook-Execute: "
|
467
|
-
f"{t_func.name}@{t_func.tag}"
|
538
|
+
logger.info(
|
539
|
+
f"({self.run_id}) [STAGE]: Hook-Execute: {t_func.name}@{t_func.tag}"
|
468
540
|
)
|
469
541
|
rs: DictData = t_func(**param2template(args, params))
|
470
542
|
|
@@ -472,11 +544,10 @@ class HookStage(BaseStage):
|
|
472
544
|
# Check the result type from hook function, it should be dict.
|
473
545
|
if not isinstance(rs, dict):
|
474
546
|
raise TypeError(
|
475
|
-
f"Return
|
476
|
-
f"
|
477
|
-
f"`dict` type."
|
547
|
+
f"Return type: '{t_func.name}@{t_func.tag}' does not serialize "
|
548
|
+
f"to result model, you change return type to `dict`."
|
478
549
|
)
|
479
|
-
return rs
|
550
|
+
return Result(status=0, context=rs)
|
480
551
|
|
481
552
|
|
482
553
|
class TriggerStage(BaseStage):
|
@@ -499,8 +570,8 @@ class TriggerStage(BaseStage):
|
|
499
570
|
description="A parameter that want to pass to pipeline execution.",
|
500
571
|
)
|
501
572
|
|
502
|
-
@handler_result("Raise from
|
503
|
-
def execute(self, params: DictData) ->
|
573
|
+
@handler_result("Raise from TriggerStage")
|
574
|
+
def execute(self, params: DictData) -> Result:
|
504
575
|
"""Trigger pipeline execution.
|
505
576
|
|
506
577
|
:param params: A parameter data that want to use in this execution.
|
@@ -510,9 +581,14 @@ class TriggerStage(BaseStage):
|
|
510
581
|
|
511
582
|
# NOTE: Loading pipeline object from trigger name.
|
512
583
|
_trigger: str = param2template(self.trigger, params=params)
|
513
|
-
|
514
|
-
|
515
|
-
|
584
|
+
|
585
|
+
# NOTE: Set running pipeline ID from running stage ID to external
|
586
|
+
# params on Loader object.
|
587
|
+
pipe: Pipeline = Pipeline.from_loader(
|
588
|
+
name=_trigger, externals={"run_id": self.run_id}
|
589
|
+
)
|
590
|
+
logger.info(f"({self.run_id}) [STAGE]: Trigger-Execute: {_trigger!r}")
|
591
|
+
return pipe.execute(params=param2template(self.params, params))
|
516
592
|
|
517
593
|
|
518
594
|
# NOTE: Order of parsing stage data
|