ddeutil-workflow 0.0.15__py3-none-any.whl → 0.0.16__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/__types.py +18 -6
- ddeutil/workflow/api.py +3 -4
- ddeutil/workflow/cli.py +2 -5
- ddeutil/workflow/conf.py +276 -3
- ddeutil/workflow/job.py +42 -25
- ddeutil/workflow/log.py +5 -8
- ddeutil/workflow/on.py +1 -1
- ddeutil/workflow/repeat.py +2 -5
- ddeutil/workflow/route.py +4 -11
- ddeutil/workflow/scheduler.py +64 -46
- ddeutil/workflow/stage.py +33 -28
- ddeutil/workflow/utils.py +79 -266
- {ddeutil_workflow-0.0.15.dist-info → ddeutil_workflow-0.0.16.dist-info}/METADATA +39 -23
- ddeutil_workflow-0.0.16.dist-info/RECORD +22 -0
- {ddeutil_workflow-0.0.15.dist-info → ddeutil_workflow-0.0.16.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.15.dist-info/RECORD +0 -22
- {ddeutil_workflow-0.0.15.dist-info → ddeutil_workflow-0.0.16.dist-info}/LICENSE +0 -0
- {ddeutil_workflow-0.0.15.dist-info → ddeutil_workflow-0.0.16.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.15.dist-info → ddeutil_workflow-0.0.16.dist-info}/top_level.txt +0 -0
ddeutil/workflow/repeat.py
CHANGED
@@ -6,14 +6,13 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
import asyncio
|
9
|
-
import os
|
10
9
|
from asyncio import ensure_future
|
11
10
|
from datetime import datetime
|
12
11
|
from functools import wraps
|
13
|
-
from zoneinfo import ZoneInfo
|
14
12
|
|
15
13
|
from starlette.concurrency import run_in_threadpool
|
16
14
|
|
15
|
+
from .conf import config
|
17
16
|
from .cron import CronJob
|
18
17
|
from .log import get_logger
|
19
18
|
|
@@ -24,9 +23,7 @@ def get_cronjob_delta(cron: str) -> float:
|
|
24
23
|
"""This function returns the time delta between now and the next cron
|
25
24
|
execution time.
|
26
25
|
"""
|
27
|
-
now: datetime = datetime.now(
|
28
|
-
tz=ZoneInfo(os.getenv("WORKFLOW_CORE_TIMEZONE", "UTC"))
|
29
|
-
)
|
26
|
+
now: datetime = datetime.now(tz=config.tz)
|
30
27
|
cron = CronJob(cron)
|
31
28
|
return (cron.schedule(now).next - now).total_seconds()
|
32
29
|
|
ddeutil/workflow/route.py
CHANGED
@@ -6,10 +6,8 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
import copy
|
9
|
-
import os
|
10
9
|
from datetime import datetime, timedelta
|
11
10
|
from typing import Any
|
12
|
-
from zoneinfo import ZoneInfo
|
13
11
|
|
14
12
|
from fastapi import APIRouter, HTTPException, Request
|
15
13
|
from fastapi import status as st
|
@@ -18,9 +16,10 @@ from pydantic import BaseModel
|
|
18
16
|
|
19
17
|
from . import Workflow
|
20
18
|
from .__types import DictData
|
19
|
+
from .conf import Loader, config
|
21
20
|
from .log import get_logger
|
22
21
|
from .scheduler import Schedule
|
23
|
-
from .utils import
|
22
|
+
from .utils import Result
|
24
23
|
|
25
24
|
logger = get_logger("ddeutil.workflow")
|
26
25
|
workflow = APIRouter(
|
@@ -87,12 +86,7 @@ async def execute_workflow(name: str, payload: ExecutePayload) -> DictData:
|
|
87
86
|
# NOTE: Start execute manually
|
88
87
|
rs: Result = wf.execute(params=payload.params)
|
89
88
|
|
90
|
-
return rs
|
91
|
-
by_alias=True,
|
92
|
-
exclude_none=True,
|
93
|
-
exclude_unset=True,
|
94
|
-
exclude_defaults=True,
|
95
|
-
)
|
89
|
+
return dict(rs)
|
96
90
|
|
97
91
|
|
98
92
|
@workflow.get("/{name}/logs")
|
@@ -172,8 +166,7 @@ async def add_deploy_scheduler(request: Request, name: str):
|
|
172
166
|
|
173
167
|
request.state.scheduler.append(name)
|
174
168
|
|
175
|
-
|
176
|
-
start_date: datetime = datetime.now(tz=tz)
|
169
|
+
start_date: datetime = datetime.now(tz=config.tz)
|
177
170
|
start_date_waiting: datetime = (start_date + timedelta(minutes=1)).replace(
|
178
171
|
second=0, microsecond=0
|
179
172
|
)
|
ddeutil/workflow/scheduler.py
CHANGED
@@ -3,13 +3,26 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
+
"""
|
7
|
+
The main schedule running is ``workflow_runner`` function that trigger the
|
8
|
+
multiprocess of ``workflow_control`` function for listing schedules on the
|
9
|
+
config by ``Loader.finds(Schedule)``.
|
10
|
+
|
11
|
+
The ``workflow_control`` is the scheduler function that release 2 schedule
|
12
|
+
functions; ``workflow_task``, and ``workflow_monitor``.
|
13
|
+
|
14
|
+
``workflow_control`` --- Every minute at :02 --> ``workflow_task``
|
15
|
+
--- Every 5 minutes --> ``workflow_monitor``
|
16
|
+
|
17
|
+
The ``workflow_task`` will run ``task.release`` method in threading object
|
18
|
+
for multithreading strategy. This ``release`` method will run only one crontab
|
19
|
+
value with the on field.
|
20
|
+
"""
|
6
21
|
from __future__ import annotations
|
7
22
|
|
8
23
|
import copy
|
9
24
|
import inspect
|
10
|
-
import json
|
11
25
|
import logging
|
12
|
-
import os
|
13
26
|
import time
|
14
27
|
from concurrent.futures import (
|
15
28
|
Future,
|
@@ -43,14 +56,13 @@ except ImportError:
|
|
43
56
|
CancelJob = None
|
44
57
|
|
45
58
|
from .__types import DictData, TupleStr
|
46
|
-
from .conf import config
|
59
|
+
from .conf import Loader, config
|
47
60
|
from .cron import CronRunner
|
48
61
|
from .exceptions import JobException, WorkflowException
|
49
62
|
from .job import Job
|
50
63
|
from .log import FileLog, Log, get_logger
|
51
64
|
from .on import On
|
52
65
|
from .utils import (
|
53
|
-
Loader,
|
54
66
|
Param,
|
55
67
|
Result,
|
56
68
|
batch,
|
@@ -75,7 +87,7 @@ __all__: TupleStr = (
|
|
75
87
|
"Schedule",
|
76
88
|
"ScheduleWorkflow",
|
77
89
|
"workflow_task",
|
78
|
-
"
|
90
|
+
"workflow_monitor",
|
79
91
|
"workflow_control",
|
80
92
|
"workflow_runner",
|
81
93
|
)
|
@@ -184,7 +196,7 @@ class Workflow(BaseModel):
|
|
184
196
|
return data
|
185
197
|
|
186
198
|
@model_validator(mode="before")
|
187
|
-
def
|
199
|
+
def __prepare_model_before__(cls, values: DictData) -> DictData:
|
188
200
|
"""Prepare the params key."""
|
189
201
|
# NOTE: Prepare params type if it passing with only type value.
|
190
202
|
if params := values.pop("params", {}):
|
@@ -199,9 +211,10 @@ class Workflow(BaseModel):
|
|
199
211
|
return values
|
200
212
|
|
201
213
|
@field_validator("desc", mode="after")
|
202
|
-
def
|
214
|
+
def __dedent_desc__(cls, value: str) -> str:
|
203
215
|
"""Prepare description string that was created on a template.
|
204
216
|
|
217
|
+
:param value: A description string value that want to dedent.
|
205
218
|
:rtype: str
|
206
219
|
"""
|
207
220
|
return dedent(value)
|
@@ -458,8 +471,10 @@ class Workflow(BaseModel):
|
|
458
471
|
queue: list[datetime] = []
|
459
472
|
results: list[Result] = []
|
460
473
|
|
461
|
-
|
462
|
-
|
474
|
+
with ThreadPoolExecutor(
|
475
|
+
max_workers=config.max_poking_pool_worker,
|
476
|
+
thread_name_prefix="wf_poking_",
|
477
|
+
) as executor:
|
463
478
|
futures: list[Future] = []
|
464
479
|
for on in self.on:
|
465
480
|
futures.append(
|
@@ -795,7 +810,7 @@ class ScheduleWorkflow(BaseModel):
|
|
795
810
|
)
|
796
811
|
|
797
812
|
@model_validator(mode="before")
|
798
|
-
def
|
813
|
+
def __prepare_before__(cls, values: DictData) -> DictData:
|
799
814
|
"""Prepare incoming values before validating with model fields.
|
800
815
|
|
801
816
|
:rtype: DictData
|
@@ -933,9 +948,11 @@ class Schedule(BaseModel):
|
|
933
948
|
return workflow_tasks
|
934
949
|
|
935
950
|
|
936
|
-
|
937
|
-
|
938
|
-
|
951
|
+
ReturnCancelJob = Callable[P, Optional[CancelJob]]
|
952
|
+
DecoratorCancelJob = Callable[[ReturnCancelJob], ReturnCancelJob]
|
953
|
+
|
954
|
+
|
955
|
+
def catch_exceptions(cancel_on_failure: bool = False) -> DecoratorCancelJob:
|
939
956
|
"""Catch exception error from scheduler job that running with schedule
|
940
957
|
package and return CancelJob if this function raise an error.
|
941
958
|
|
@@ -944,9 +961,7 @@ def catch_exceptions(
|
|
944
961
|
:rtype: Callable[P, Optional[CancelJob]]
|
945
962
|
"""
|
946
963
|
|
947
|
-
def decorator(
|
948
|
-
func: Callable[P, Optional[CancelJob]],
|
949
|
-
) -> Callable[P, Optional[CancelJob]]:
|
964
|
+
def decorator(func: ReturnCancelJob) -> ReturnCancelJob:
|
950
965
|
try:
|
951
966
|
# NOTE: Check the function that want to handle is method or not.
|
952
967
|
if inspect.ismethod(func):
|
@@ -981,8 +996,8 @@ class WorkflowTaskData:
|
|
981
996
|
workflow: Workflow
|
982
997
|
on: On
|
983
998
|
params: DictData = field(compare=False, hash=False)
|
984
|
-
queue: list[datetime] = field(compare=False, hash=False)
|
985
|
-
running: list[datetime] = field(compare=False, hash=False)
|
999
|
+
queue: dict[str, list[datetime]] = field(compare=False, hash=False)
|
1000
|
+
running: dict[str, list[datetime]] = field(compare=False, hash=False)
|
986
1001
|
|
987
1002
|
@catch_exceptions(cancel_on_failure=True)
|
988
1003
|
def release(
|
@@ -1062,8 +1077,9 @@ class WorkflowTaskData:
|
|
1062
1077
|
},
|
1063
1078
|
}
|
1064
1079
|
|
1065
|
-
# WARNING:
|
1066
|
-
# ID.
|
1080
|
+
# WARNING:
|
1081
|
+
# Re-create workflow object that use new running workflow ID.
|
1082
|
+
#
|
1067
1083
|
runner: Workflow = wf.get_running_id(run_id=wf.new_run_id)
|
1068
1084
|
rs: Result = runner.execute(
|
1069
1085
|
params=param2template(self.params, release_params),
|
@@ -1116,6 +1132,7 @@ class WorkflowTaskData:
|
|
1116
1132
|
self.workflow.name == other.workflow.name
|
1117
1133
|
and self.on.cronjob == other.on.cronjob
|
1118
1134
|
)
|
1135
|
+
return NotImplemented
|
1119
1136
|
|
1120
1137
|
|
1121
1138
|
@catch_exceptions(cancel_on_failure=True)
|
@@ -1127,10 +1144,10 @@ def workflow_task(
|
|
1127
1144
|
"""Workflow task generator that create release pair of workflow and on to
|
1128
1145
|
the threading in background.
|
1129
1146
|
|
1130
|
-
This workflow task will start every minute at :02 second.
|
1147
|
+
This workflow task will start every minute at ':02' second.
|
1131
1148
|
|
1132
1149
|
:param workflow_tasks:
|
1133
|
-
:param stop:
|
1150
|
+
:param stop: A stop datetime object that force stop running scheduler.
|
1134
1151
|
:param threads:
|
1135
1152
|
:rtype: CancelJob | None
|
1136
1153
|
"""
|
@@ -1145,7 +1162,7 @@ def workflow_task(
|
|
1145
1162
|
"running in background."
|
1146
1163
|
)
|
1147
1164
|
time.sleep(15)
|
1148
|
-
|
1165
|
+
workflow_monitor(threads)
|
1149
1166
|
return CancelJob
|
1150
1167
|
|
1151
1168
|
# IMPORTANT:
|
@@ -1217,7 +1234,7 @@ def workflow_task(
|
|
1217
1234
|
logger.debug(f"[WORKFLOW]: {'=' * 100}")
|
1218
1235
|
|
1219
1236
|
|
1220
|
-
def
|
1237
|
+
def workflow_monitor(threads: dict[str, Thread]) -> None:
|
1221
1238
|
"""Workflow schedule for monitoring long running thread from the schedule
|
1222
1239
|
control.
|
1223
1240
|
|
@@ -1275,30 +1292,29 @@ def workflow_control(
|
|
1275
1292
|
sch: Schedule = Schedule.from_loader(name, externals=externals)
|
1276
1293
|
workflow_tasks.extend(
|
1277
1294
|
sch.tasks(
|
1278
|
-
start_date_waiting,
|
1295
|
+
start_date_waiting,
|
1296
|
+
queue=wf_queue,
|
1297
|
+
running=wf_running,
|
1298
|
+
externals=externals,
|
1279
1299
|
),
|
1280
1300
|
)
|
1281
1301
|
|
1282
1302
|
# NOTE: This schedule job will start every minute at :02 seconds.
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
+
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
)
|
1295
|
-
),
|
1296
|
-
threads=thread_releases,
|
1297
|
-
).tag("control")
|
1303
|
+
(
|
1304
|
+
schedule.every(1)
|
1305
|
+
.minutes.at(":02")
|
1306
|
+
.do(
|
1307
|
+
workflow_task,
|
1308
|
+
workflow_tasks=workflow_tasks,
|
1309
|
+
stop=(stop or (start_date + config.stop_boundary_delta)),
|
1310
|
+
threads=thread_releases,
|
1311
|
+
)
|
1312
|
+
.tag("control")
|
1313
|
+
)
|
1298
1314
|
|
1299
1315
|
# NOTE: Checking zombie task with schedule job will start every 5 minute.
|
1300
1316
|
schedule.every(5).minutes.at(":10").do(
|
1301
|
-
|
1317
|
+
workflow_monitor,
|
1302
1318
|
threads=thread_releases,
|
1303
1319
|
).tag("monitor")
|
1304
1320
|
|
@@ -1332,14 +1348,16 @@ def workflow_runner(
|
|
1332
1348
|
"""Workflow application that running multiprocessing schedule with chunk of
|
1333
1349
|
workflows that exists in config path.
|
1334
1350
|
|
1335
|
-
:param stop:
|
1351
|
+
:param stop: A stop datetime object that force stop running scheduler.
|
1336
1352
|
:param excluded:
|
1337
1353
|
:param externals:
|
1354
|
+
|
1338
1355
|
:rtype: list[str]
|
1339
1356
|
|
1340
1357
|
This function will get all workflows that include on value that was
|
1341
|
-
created in config path and chuck it with
|
1342
|
-
|
1358
|
+
created in config path and chuck it with application config variable
|
1359
|
+
``WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS`` env var to multiprocess executor
|
1360
|
+
pool.
|
1343
1361
|
|
1344
1362
|
The current workflow logic that split to process will be below diagram:
|
1345
1363
|
|
@@ -1356,7 +1374,7 @@ def workflow_runner(
|
|
1356
1374
|
excluded: list[str] = excluded or []
|
1357
1375
|
|
1358
1376
|
with ProcessPoolExecutor(
|
1359
|
-
max_workers=
|
1377
|
+
max_workers=config.max_schedule_process,
|
1360
1378
|
) as executor:
|
1361
1379
|
futures: list[Future] = [
|
1362
1380
|
executor.submit(
|
@@ -1367,7 +1385,7 @@ def workflow_runner(
|
|
1367
1385
|
)
|
1368
1386
|
for loader in batch(
|
1369
1387
|
Loader.finds(Schedule, excluded=excluded),
|
1370
|
-
n=
|
1388
|
+
n=config.max_schedule_per_process,
|
1371
1389
|
)
|
1372
1390
|
]
|
1373
1391
|
|
ddeutil/workflow/stage.py
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
"""Stage Model that use for getting stage data template from Job Model.
|
7
|
-
The stage
|
6
|
+
"""Stage Model that use for getting stage data template from the Job Model.
|
7
|
+
The stage handle the minimize task that run in some thread (same thread at
|
8
8
|
its job owner) that mean it is the lowest executor of a workflow workflow that
|
9
9
|
can tracking logs.
|
10
10
|
|
@@ -12,11 +12,13 @@ can tracking logs.
|
|
12
12
|
handle stage error on this stage model. I think stage model should have a lot of
|
13
13
|
usecase and it does not worry when I want to create a new one.
|
14
14
|
|
15
|
-
Execution --> Ok
|
16
|
-
--> Error
|
15
|
+
Execution --> Ok --> Result with 0
|
16
|
+
--> Error --> Raise StageException
|
17
|
+
--> Result with 1 (if env var was set)
|
17
18
|
|
18
|
-
On the context I/O that pass to stage object at execute process. The
|
19
|
-
method
|
19
|
+
On the context I/O that pass to a stage object at execute process. The
|
20
|
+
execute method receives a `params={"params": {...}}` value for mapping to
|
21
|
+
template searching.
|
20
22
|
"""
|
21
23
|
from __future__ import annotations
|
22
24
|
|
@@ -88,20 +90,28 @@ def handler_result(message: str | None = None) -> DecoratorResult:
|
|
88
90
|
you force catching an output result with error message by specific
|
89
91
|
environment variable,`WORKFLOW_CORE_STAGE_RAISE_ERROR`.
|
90
92
|
|
91
|
-
Execution --> Ok --> Result
|
93
|
+
Execution --> Ok --> Result
|
94
|
+
status: 0
|
95
|
+
context:
|
96
|
+
outputs: ...
|
92
97
|
--> Error --> Raise StageException
|
93
|
-
--> Result
|
98
|
+
--> Result (if env var was set)
|
99
|
+
status: 1
|
100
|
+
context:
|
101
|
+
error: ...
|
102
|
+
error_message: ...
|
94
103
|
|
95
104
|
On the last step, it will set the running ID on a return result object
|
96
105
|
from current stage ID before release the final result.
|
97
106
|
|
98
107
|
:param message: A message that want to add at prefix of exception statement.
|
108
|
+
:type message: str | None (Default=None)
|
99
109
|
:rtype: Callable[P, Result]
|
100
110
|
"""
|
101
111
|
# NOTE: The prefix message string that want to add on the first exception
|
102
112
|
# message dialog.
|
103
113
|
#
|
104
|
-
#
|
114
|
+
# >>> ValueError: {message}
|
105
115
|
# ... raise value error from the stage execution process.
|
106
116
|
#
|
107
117
|
message: str = message or ""
|
@@ -175,7 +185,7 @@ class BaseStage(BaseModel, ABC):
|
|
175
185
|
)
|
176
186
|
|
177
187
|
@model_validator(mode="after")
|
178
|
-
def
|
188
|
+
def __prepare_running_id__(self) -> Self:
|
179
189
|
"""Prepare stage running ID that use default value of field and this
|
180
190
|
method will validate name and id fields should not contain any template
|
181
191
|
parameter (exclude matrix template).
|
@@ -235,7 +245,7 @@ class BaseStage(BaseModel, ABC):
|
|
235
245
|
:param to: A context data that want to add output result.
|
236
246
|
:rtype: DictData
|
237
247
|
"""
|
238
|
-
if
|
248
|
+
if self.id is None and not config.stage_default_id:
|
239
249
|
logger.debug(
|
240
250
|
f"({self.run_id}) [STAGE]: Output does not set because this "
|
241
251
|
f"stage does not set ID or default stage ID config flag not be "
|
@@ -255,7 +265,7 @@ class BaseStage(BaseModel, ABC):
|
|
255
265
|
)
|
256
266
|
|
257
267
|
# NOTE: Set the output to that stage generated ID with ``outputs`` key.
|
258
|
-
logger.debug(f"({self.run_id}) [STAGE]: Set outputs
|
268
|
+
logger.debug(f"({self.run_id}) [STAGE]: Set outputs to {_id!r}")
|
259
269
|
to["stages"][_id] = {"outputs": output}
|
260
270
|
return to
|
261
271
|
|
@@ -299,6 +309,7 @@ class EmptyStage(BaseStage):
|
|
299
309
|
sleep: float = Field(
|
300
310
|
default=0,
|
301
311
|
description="A second value to sleep before finish execution",
|
312
|
+
ge=0,
|
302
313
|
)
|
303
314
|
|
304
315
|
def execute(self, params: DictData) -> Result:
|
@@ -351,7 +362,7 @@ class BashStage(BaseStage):
|
|
351
362
|
)
|
352
363
|
|
353
364
|
@contextlib.contextmanager
|
354
|
-
def
|
365
|
+
def prepare_bash(self, bash: str, env: DictStr) -> Iterator[TupleStr]:
|
355
366
|
"""Return context of prepared bash statement that want to execute. This
|
356
367
|
step will write the `.sh` file before giving this file name to context.
|
357
368
|
After that, it will auto delete this file automatic.
|
@@ -394,15 +405,12 @@ class BashStage(BaseStage):
|
|
394
405
|
:rtype: Result
|
395
406
|
"""
|
396
407
|
bash: str = param2template(dedent(self.bash), params)
|
397
|
-
with self.
|
408
|
+
with self.prepare_bash(
|
398
409
|
bash=bash, env=param2template(self.env, params)
|
399
410
|
) as sh:
|
400
411
|
logger.info(f"({self.run_id}) [STAGE]: Shell-Execute: {sh}")
|
401
412
|
rs: CompletedProcess = subprocess.run(
|
402
|
-
sh,
|
403
|
-
shell=False,
|
404
|
-
capture_output=True,
|
405
|
-
text=True,
|
413
|
+
sh, shell=False, capture_output=True, text=True
|
406
414
|
)
|
407
415
|
if rs.returncode > 0:
|
408
416
|
# NOTE: Prepare stderr message that returning from subprocess.
|
@@ -419,8 +427,8 @@ class BashStage(BaseStage):
|
|
419
427
|
status=0,
|
420
428
|
context={
|
421
429
|
"return_code": rs.returncode,
|
422
|
-
"stdout": rs.stdout.rstrip("\n"),
|
423
|
-
"stderr": rs.stderr.rstrip("\n"),
|
430
|
+
"stdout": rs.stdout.rstrip("\n") or None,
|
431
|
+
"stderr": rs.stderr.rstrip("\n") or None,
|
424
432
|
},
|
425
433
|
)
|
426
434
|
|
@@ -554,14 +562,14 @@ class HookStage(BaseStage):
|
|
554
562
|
>>> stage = {
|
555
563
|
... "name": "Task stage execution",
|
556
564
|
... "uses": "tasks/function-name@tag-name",
|
557
|
-
... "args": {
|
558
|
-
... "FOO": "BAR",
|
559
|
-
... },
|
565
|
+
... "args": {"FOO": "BAR"},
|
560
566
|
... }
|
561
567
|
"""
|
562
568
|
|
563
569
|
uses: str = Field(
|
564
|
-
description=
|
570
|
+
description=(
|
571
|
+
"A pointer that want to load function from the hook registry."
|
572
|
+
),
|
565
573
|
)
|
566
574
|
args: DictData = Field(
|
567
575
|
default_factory=dict,
|
@@ -622,10 +630,7 @@ class TriggerStage(BaseStage):
|
|
622
630
|
>>> stage = {
|
623
631
|
... "name": "Trigger workflow stage execution",
|
624
632
|
... "trigger": 'workflow-name-for-loader',
|
625
|
-
... "params": {
|
626
|
-
... "run-date": "2024-08-01",
|
627
|
-
... "source": "src",
|
628
|
-
... },
|
633
|
+
... "params": {"run-date": "2024-08-01", "source": "src"},
|
629
634
|
... }
|
630
635
|
"""
|
631
636
|
|