ddeutil-workflow 0.0.51__tar.gz → 0.0.52__tar.gz
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-0.0.51/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.52}/PKG-INFO +1 -1
- ddeutil_workflow-0.0.52/src/ddeutil/workflow/__about__.py +1 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__init__.py +3 -30
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/job.py +19 -11
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/stages.py +15 -12
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/workflow.py +7 -2
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52/src/ddeutil_workflow.egg-info}/PKG-INFO +1 -1
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_stage_handler_exec.py +162 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec.py +61 -1
- ddeutil_workflow-0.0.51/src/ddeutil/workflow/__about__.py +0 -1
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/LICENSE +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/README.md +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/pyproject.toml +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/setup.cfg +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__cron.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__main__.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__types.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/__init__.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/api.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/log.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/repeat.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/job.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/logs.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/schedules.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/conf.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/cron.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/exceptions.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/logs.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/params.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/result.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/reusables.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/scheduler.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/utils.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/SOURCES.txt +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test__cron.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test__regex.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_conf.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_cron_on.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_job.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_job_exec.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_job_exec_strategy.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_job_strategy.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_logs_audit.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_logs_trace.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_params.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_release.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_release_queue.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_result.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_reusables_call_tag.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_reusables_template.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_reusables_template_filter.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_schedule.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_schedule_pending.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_schedule_tasks.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_schedule_workflow.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_scheduler_control.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_stage.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_utils.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_workflow.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec_job.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec_poke.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec_release.py +0 -0
- {ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/tests/test_workflow_task.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__version__: str = "0.0.52"
|
@@ -60,33 +60,6 @@ from .scheduler import (
|
|
60
60
|
schedule_runner,
|
61
61
|
schedule_task,
|
62
62
|
)
|
63
|
-
from .stages import
|
64
|
-
|
65
|
-
|
66
|
-
EmptyStage,
|
67
|
-
ForEachStage,
|
68
|
-
ParallelStage,
|
69
|
-
PyStage,
|
70
|
-
Stage,
|
71
|
-
TriggerStage,
|
72
|
-
)
|
73
|
-
from .utils import (
|
74
|
-
batch,
|
75
|
-
cross_product,
|
76
|
-
default_gen_id,
|
77
|
-
delay,
|
78
|
-
filter_func,
|
79
|
-
gen_id,
|
80
|
-
get_diff_sec,
|
81
|
-
get_dt_now,
|
82
|
-
make_exec,
|
83
|
-
reach_next_minute,
|
84
|
-
replace_sec,
|
85
|
-
wait_to_next_minute,
|
86
|
-
)
|
87
|
-
from .workflow import (
|
88
|
-
Release,
|
89
|
-
ReleaseQueue,
|
90
|
-
Workflow,
|
91
|
-
WorkflowTask,
|
92
|
-
)
|
63
|
+
from .stages import *
|
64
|
+
from .utils import *
|
65
|
+
from .workflow import *
|
@@ -27,7 +27,7 @@ from threading import Event
|
|
27
27
|
from typing import Annotated, Any, Literal, Optional, Union
|
28
28
|
|
29
29
|
from ddeutil.core import freeze_args
|
30
|
-
from pydantic import BaseModel,
|
30
|
+
from pydantic import BaseModel, Discriminator, Field, SecretStr, Tag
|
31
31
|
from pydantic.functional_validators import field_validator, model_validator
|
32
32
|
from typing_extensions import Self
|
33
33
|
|
@@ -178,7 +178,7 @@ class Strategy(BaseModel):
|
|
178
178
|
|
179
179
|
|
180
180
|
class Rule(str, Enum):
|
181
|
-
"""
|
181
|
+
"""Rule enum object for assign trigger option."""
|
182
182
|
|
183
183
|
ALL_SUCCESS: str = "all_success"
|
184
184
|
ALL_FAILED: str = "all_failed"
|
@@ -203,8 +203,6 @@ class BaseRunsOn(BaseModel): # pragma: no cov
|
|
203
203
|
object and override execute method.
|
204
204
|
"""
|
205
205
|
|
206
|
-
model_config = ConfigDict(use_enum_values=True)
|
207
|
-
|
208
206
|
type: RunsOn = Field(description="A runs-on type.")
|
209
207
|
args: DictData = Field(
|
210
208
|
default_factory=dict,
|
@@ -219,7 +217,9 @@ class BaseRunsOn(BaseModel): # pragma: no cov
|
|
219
217
|
class OnLocal(BaseRunsOn): # pragma: no cov
|
220
218
|
"""Runs-on local."""
|
221
219
|
|
222
|
-
type: Literal[RunsOn.LOCAL] = Field(
|
220
|
+
type: Literal[RunsOn.LOCAL] = Field(
|
221
|
+
default=RunsOn.LOCAL, validate_default=True
|
222
|
+
)
|
223
223
|
|
224
224
|
|
225
225
|
class SelfHostedArgs(BaseModel):
|
@@ -231,7 +231,9 @@ class SelfHostedArgs(BaseModel):
|
|
231
231
|
class OnSelfHosted(BaseRunsOn): # pragma: no cov
|
232
232
|
"""Runs-on self-hosted."""
|
233
233
|
|
234
|
-
type: Literal[RunsOn.SELF_HOSTED] = Field(
|
234
|
+
type: Literal[RunsOn.SELF_HOSTED] = Field(
|
235
|
+
default=RunsOn.SELF_HOSTED, validate_default=True
|
236
|
+
)
|
235
237
|
args: SelfHostedArgs = Field(alias="with")
|
236
238
|
|
237
239
|
|
@@ -245,7 +247,9 @@ class AzBatchArgs(BaseModel):
|
|
245
247
|
|
246
248
|
class OnAzBatch(BaseRunsOn): # pragma: no cov
|
247
249
|
|
248
|
-
type: Literal[RunsOn.AZ_BATCH] = Field(
|
250
|
+
type: Literal[RunsOn.AZ_BATCH] = Field(
|
251
|
+
default=RunsOn.AZ_BATCH, validate_default=True
|
252
|
+
)
|
249
253
|
args: AzBatchArgs = Field(alias="with")
|
250
254
|
|
251
255
|
|
@@ -264,13 +268,16 @@ class DockerArgs(BaseModel):
|
|
264
268
|
class OnDocker(BaseRunsOn): # pragma: no cov
|
265
269
|
"""Runs-on Docker container."""
|
266
270
|
|
267
|
-
type: Literal[RunsOn.DOCKER] = Field(
|
271
|
+
type: Literal[RunsOn.DOCKER] = Field(
|
272
|
+
default=RunsOn.DOCKER, validate_default=True
|
273
|
+
)
|
268
274
|
args: DockerArgs = Field(alias="with", default_factory=DockerArgs)
|
269
275
|
|
270
276
|
|
271
|
-
def get_discriminator_runs_on(model: dict[str, Any]) ->
|
277
|
+
def get_discriminator_runs_on(model: dict[str, Any]) -> RunsOn:
|
272
278
|
"""Get discriminator of the RunsOn models."""
|
273
|
-
|
279
|
+
t = model.get("type")
|
280
|
+
return RunsOn(t) if t else RunsOn.LOCAL
|
274
281
|
|
275
282
|
|
276
283
|
RunsOnModel = Annotated[
|
@@ -336,6 +343,7 @@ class Job(BaseModel):
|
|
336
343
|
)
|
337
344
|
trigger_rule: Rule = Field(
|
338
345
|
default=Rule.ALL_SUCCESS,
|
346
|
+
validate_default=True,
|
339
347
|
description=(
|
340
348
|
"A trigger rule of tracking needed jobs if feature will use when "
|
341
349
|
"the `raise_error` did not set from job and stage executions."
|
@@ -609,7 +617,7 @@ class Job(BaseModel):
|
|
609
617
|
)
|
610
618
|
|
611
619
|
result.trace.info(
|
612
|
-
f"[JOB]: Execute: {self.id!r} on {self.runs_on.type!r}"
|
620
|
+
f"[JOB]: Execute: {self.id!r} on {self.runs_on.type.value!r}"
|
613
621
|
)
|
614
622
|
if self.runs_on.type == RunsOn.LOCAL:
|
615
623
|
return local_execute(
|
@@ -1099,14 +1099,15 @@ class ParallelStage(BaseStage): # pragma: no cov
|
|
1099
1099
|
:rtype: DictData
|
1100
1100
|
"""
|
1101
1101
|
result.trace.debug(f"... Execute branch: {branch!r}")
|
1102
|
-
|
1103
|
-
|
1102
|
+
_params: DictData = copy.deepcopy(params)
|
1103
|
+
_params.update({"branch": branch})
|
1104
|
+
context: DictData = {"branch": branch, "stages": {}}
|
1104
1105
|
for stage in self.parallel[branch]:
|
1105
1106
|
|
1106
1107
|
if extras:
|
1107
1108
|
stage.extras = extras
|
1108
1109
|
|
1109
|
-
if stage.is_skipped(params=
|
1110
|
+
if stage.is_skipped(params=_params):
|
1110
1111
|
result.trace.info(f"... Skip stage: {stage.iden!r}")
|
1111
1112
|
stage.set_outputs(output={"skipped": True}, to=context)
|
1112
1113
|
continue
|
@@ -1129,7 +1130,7 @@ class ParallelStage(BaseStage): # pragma: no cov
|
|
1129
1130
|
|
1130
1131
|
try:
|
1131
1132
|
rs: Result = stage.handler_execute(
|
1132
|
-
params=
|
1133
|
+
params=_params,
|
1133
1134
|
run_id=result.run_id,
|
1134
1135
|
parent_run_id=result.parent_run_id,
|
1135
1136
|
raise_error=True,
|
@@ -1294,14 +1295,15 @@ class ForEachStage(BaseStage):
|
|
1294
1295
|
:rtype: Result
|
1295
1296
|
"""
|
1296
1297
|
result.trace.debug(f"... Execute item: {item!r}")
|
1297
|
-
|
1298
|
-
|
1298
|
+
_params: DictData = copy.deepcopy(params)
|
1299
|
+
_params.update({"item": item})
|
1300
|
+
context: DictData = {"item": item, "stages": {}}
|
1299
1301
|
for stage in self.stages:
|
1300
1302
|
|
1301
1303
|
if self.extras:
|
1302
1304
|
stage.extras = self.extras
|
1303
1305
|
|
1304
|
-
if stage.is_skipped(params=
|
1306
|
+
if stage.is_skipped(params=_params):
|
1305
1307
|
result.trace.info(f"... Skip stage: {stage.iden!r}")
|
1306
1308
|
stage.set_outputs(output={"skipped": True}, to=context)
|
1307
1309
|
continue
|
@@ -1324,7 +1326,7 @@ class ForEachStage(BaseStage):
|
|
1324
1326
|
|
1325
1327
|
try:
|
1326
1328
|
rs: Result = stage.handler_execute(
|
1327
|
-
params=
|
1329
|
+
params=_params,
|
1328
1330
|
run_id=result.run_id,
|
1329
1331
|
parent_run_id=result.parent_run_id,
|
1330
1332
|
raise_error=True,
|
@@ -1513,15 +1515,16 @@ class UntilStage(BaseStage): # pragma: no cov
|
|
1513
1515
|
:rtype: tuple[Result, T]
|
1514
1516
|
"""
|
1515
1517
|
result.trace.debug(f"... Execute until item: {item!r}")
|
1516
|
-
|
1517
|
-
|
1518
|
+
_params: DictData = copy.deepcopy(params)
|
1519
|
+
_params.update({"item": item})
|
1520
|
+
context: DictData = {"loop": loop, "item": item, "stages": {}}
|
1518
1521
|
next_item: T = None
|
1519
1522
|
for stage in self.stages:
|
1520
1523
|
|
1521
1524
|
if self.extras:
|
1522
1525
|
stage.extras = self.extras
|
1523
1526
|
|
1524
|
-
if stage.is_skipped(params=
|
1527
|
+
if stage.is_skipped(params=_params):
|
1525
1528
|
result.trace.info(f"... Skip stage: {stage.iden!r}")
|
1526
1529
|
stage.set_outputs(output={"skipped": True}, to=context)
|
1527
1530
|
continue
|
@@ -1550,7 +1553,7 @@ class UntilStage(BaseStage): # pragma: no cov
|
|
1550
1553
|
|
1551
1554
|
try:
|
1552
1555
|
rs: Result = stage.handler_execute(
|
1553
|
-
params=
|
1556
|
+
params=_params,
|
1554
1557
|
run_id=result.run_id,
|
1555
1558
|
parent_run_id=result.parent_run_id,
|
1556
1559
|
raise_error=True,
|
@@ -371,7 +371,9 @@ class Workflow(BaseModel):
|
|
371
371
|
def from_conf(
|
372
372
|
cls,
|
373
373
|
name: str,
|
374
|
+
*,
|
374
375
|
extras: DictData | None = None,
|
376
|
+
loader: type[Loader] = None,
|
375
377
|
) -> Self:
|
376
378
|
"""Create Workflow instance from the Loader object that only receive
|
377
379
|
an input workflow name. The loader object will use this workflow name to
|
@@ -380,12 +382,13 @@ class Workflow(BaseModel):
|
|
380
382
|
:param name: A workflow name that want to pass to Loader object.
|
381
383
|
:param extras: An extra parameters that want to pass to Loader
|
382
384
|
object.
|
385
|
+
:param loader: A loader class for override default loader object.
|
383
386
|
|
384
387
|
:raise ValueError: If the type does not match with current object.
|
385
388
|
|
386
389
|
:rtype: Self
|
387
390
|
"""
|
388
|
-
loader: Loader = Loader(name, externals=(extras or {}))
|
391
|
+
loader: Loader = (loader or Loader)(name, externals=(extras or {}))
|
389
392
|
|
390
393
|
# NOTE: Validate the config type match with current connection model
|
391
394
|
if loader.type != cls.__name__:
|
@@ -407,6 +410,7 @@ class Workflow(BaseModel):
|
|
407
410
|
path: Path,
|
408
411
|
*,
|
409
412
|
extras: DictData | None = None,
|
413
|
+
loader: type[Loader] = None,
|
410
414
|
) -> Self:
|
411
415
|
"""Create Workflow instance from the specific path. The loader object
|
412
416
|
will use this workflow name and path to searching configuration data of
|
@@ -416,12 +420,13 @@ class Workflow(BaseModel):
|
|
416
420
|
:param path: (Path) A config path that want to search.
|
417
421
|
:param extras: (DictData) An extra parameters that want to override core
|
418
422
|
config values.
|
423
|
+
:param loader: A loader class for override default loader object.
|
419
424
|
|
420
425
|
:raise ValueError: If the type does not match with current object.
|
421
426
|
|
422
427
|
:rtype: Self
|
423
428
|
"""
|
424
|
-
loader: SimLoad = SimLoad(
|
429
|
+
loader: SimLoad = (loader or SimLoad)(
|
425
430
|
name, conf_path=path, externals=(extras or {})
|
426
431
|
)
|
427
432
|
# NOTE: Validate the config type match with current connection model
|
@@ -680,6 +680,168 @@ def test_stage_exec_foreach_with_trigger(test_path):
|
|
680
680
|
}
|
681
681
|
|
682
682
|
|
683
|
+
def test_stage_exec_multi_foreach_nested_with_trigger(test_path):
|
684
|
+
with dump_yaml_context(
|
685
|
+
test_path / "conf/demo/01_99_wf_test_wf_foreach_with_trigger.yml",
|
686
|
+
data="""
|
687
|
+
tmp-wf-foreach-nested-trigger-task:
|
688
|
+
type: Workflow
|
689
|
+
params:
|
690
|
+
item: int
|
691
|
+
jobs:
|
692
|
+
first-job:
|
693
|
+
stages:
|
694
|
+
- name: "Echo"
|
695
|
+
id: hello
|
696
|
+
echo: "Run trigger with item: ${{ params.item }}"
|
697
|
+
|
698
|
+
tmp-wf-foreach-nested-trigger:
|
699
|
+
type: Workflow
|
700
|
+
jobs:
|
701
|
+
first-job:
|
702
|
+
stages:
|
703
|
+
- name: "Start run for-each stage"
|
704
|
+
id: foreach-stage
|
705
|
+
foreach: [1, 2]
|
706
|
+
stages:
|
707
|
+
|
708
|
+
- name: "Start run for-each stage inside foreach"
|
709
|
+
id: foreach-nested
|
710
|
+
foreach: [3, 4]
|
711
|
+
stages:
|
712
|
+
- name: "Stage trigger"
|
713
|
+
trigger: tmp-wf-foreach-nested-trigger-task
|
714
|
+
params:
|
715
|
+
item: ${{ item }}
|
716
|
+
""",
|
717
|
+
):
|
718
|
+
workflow = Workflow.from_conf(
|
719
|
+
name="tmp-wf-foreach-nested-trigger",
|
720
|
+
extras={"test": "demo"},
|
721
|
+
)
|
722
|
+
stage: Stage = workflow.job("first-job").stage("foreach-stage")
|
723
|
+
rs = stage.set_outputs(stage.handler_execute({}).context, to={})
|
724
|
+
assert rs == {
|
725
|
+
"stages": {
|
726
|
+
"foreach-stage": {
|
727
|
+
"outputs": {
|
728
|
+
"items": [1, 2],
|
729
|
+
"foreach": {
|
730
|
+
1: {
|
731
|
+
"item": 1,
|
732
|
+
"stages": {
|
733
|
+
"foreach-nested": {
|
734
|
+
"outputs": {
|
735
|
+
"items": [3, 4],
|
736
|
+
"foreach": {
|
737
|
+
3: {
|
738
|
+
"item": 3,
|
739
|
+
"stages": {
|
740
|
+
"8713259197": {
|
741
|
+
"outputs": {
|
742
|
+
"params": {
|
743
|
+
"item": 3
|
744
|
+
},
|
745
|
+
"jobs": {
|
746
|
+
"first-job": {
|
747
|
+
"stages": {
|
748
|
+
"hello": {
|
749
|
+
"outputs": {}
|
750
|
+
}
|
751
|
+
}
|
752
|
+
}
|
753
|
+
},
|
754
|
+
}
|
755
|
+
}
|
756
|
+
},
|
757
|
+
},
|
758
|
+
4: {
|
759
|
+
"item": 4,
|
760
|
+
"stages": {
|
761
|
+
"8713259197": {
|
762
|
+
"outputs": {
|
763
|
+
"params": {
|
764
|
+
"item": 4
|
765
|
+
},
|
766
|
+
"jobs": {
|
767
|
+
"first-job": {
|
768
|
+
"stages": {
|
769
|
+
"hello": {
|
770
|
+
"outputs": {}
|
771
|
+
}
|
772
|
+
}
|
773
|
+
}
|
774
|
+
},
|
775
|
+
}
|
776
|
+
}
|
777
|
+
},
|
778
|
+
},
|
779
|
+
},
|
780
|
+
}
|
781
|
+
}
|
782
|
+
},
|
783
|
+
},
|
784
|
+
2: {
|
785
|
+
"item": 2,
|
786
|
+
"stages": {
|
787
|
+
"foreach-nested": {
|
788
|
+
"outputs": {
|
789
|
+
"items": [3, 4],
|
790
|
+
"foreach": {
|
791
|
+
3: {
|
792
|
+
"item": 3,
|
793
|
+
"stages": {
|
794
|
+
"8713259197": {
|
795
|
+
"outputs": {
|
796
|
+
"params": {
|
797
|
+
"item": 3
|
798
|
+
},
|
799
|
+
"jobs": {
|
800
|
+
"first-job": {
|
801
|
+
"stages": {
|
802
|
+
"hello": {
|
803
|
+
"outputs": {}
|
804
|
+
}
|
805
|
+
}
|
806
|
+
}
|
807
|
+
},
|
808
|
+
}
|
809
|
+
}
|
810
|
+
},
|
811
|
+
},
|
812
|
+
4: {
|
813
|
+
"item": 4,
|
814
|
+
"stages": {
|
815
|
+
"8713259197": {
|
816
|
+
"outputs": {
|
817
|
+
"params": {
|
818
|
+
"item": 4
|
819
|
+
},
|
820
|
+
"jobs": {
|
821
|
+
"first-job": {
|
822
|
+
"stages": {
|
823
|
+
"hello": {
|
824
|
+
"outputs": {}
|
825
|
+
}
|
826
|
+
}
|
827
|
+
}
|
828
|
+
},
|
829
|
+
}
|
830
|
+
}
|
831
|
+
},
|
832
|
+
},
|
833
|
+
},
|
834
|
+
}
|
835
|
+
}
|
836
|
+
},
|
837
|
+
},
|
838
|
+
},
|
839
|
+
}
|
840
|
+
}
|
841
|
+
}
|
842
|
+
}
|
843
|
+
|
844
|
+
|
683
845
|
def test_stage_exec_parallel(test_path):
|
684
846
|
with dump_yaml_context(
|
685
847
|
test_path / "conf/demo/01_99_wf_test_wf_parallel.yml",
|
@@ -599,6 +599,10 @@ def test_workflow_exec_foreach(test_path):
|
|
599
599
|
- name: "Get Items before run foreach"
|
600
600
|
id: get-items
|
601
601
|
uses: tasks/get-items@demo
|
602
|
+
- name: "Create variable"
|
603
|
+
id: create-variable
|
604
|
+
run: |
|
605
|
+
foo: str = "bar"
|
602
606
|
- name: "For-each item"
|
603
607
|
id: foreach-stage
|
604
608
|
foreach: ${{ stages.get-items.outputs.items }}
|
@@ -606,6 +610,7 @@ def test_workflow_exec_foreach(test_path):
|
|
606
610
|
- name: "Echo stage"
|
607
611
|
echo: |
|
608
612
|
Start run with item ${{ item }}
|
613
|
+
Import variable ${{ stages.create-variable.outputs.foo }}
|
609
614
|
- name: "Final Echo"
|
610
615
|
if: ${{ item }} == 4
|
611
616
|
echo: |
|
@@ -614,7 +619,62 @@ def test_workflow_exec_foreach(test_path):
|
|
614
619
|
):
|
615
620
|
workflow = Workflow.from_conf(name="tmp-wf-foreach")
|
616
621
|
rs = workflow.execute(params={})
|
617
|
-
|
622
|
+
assert rs.status == SUCCESS
|
623
|
+
assert rs.context == {
|
624
|
+
"params": {},
|
625
|
+
"jobs": {
|
626
|
+
"transform": {
|
627
|
+
"stages": {
|
628
|
+
"get-items": {"outputs": {"items": [1, 2, 3, 4]}},
|
629
|
+
"create-variable": {"outputs": {"foo": "bar"}},
|
630
|
+
"foreach-stage": {
|
631
|
+
"outputs": {
|
632
|
+
"items": [1, 2, 3, 4],
|
633
|
+
"foreach": {
|
634
|
+
1: {
|
635
|
+
"item": 1,
|
636
|
+
"stages": {
|
637
|
+
"2709471980": {"outputs": {}},
|
638
|
+
"9263488742": {
|
639
|
+
"outputs": {},
|
640
|
+
"skipped": True,
|
641
|
+
},
|
642
|
+
},
|
643
|
+
},
|
644
|
+
2: {
|
645
|
+
"item": 2,
|
646
|
+
"stages": {
|
647
|
+
"2709471980": {"outputs": {}},
|
648
|
+
"9263488742": {
|
649
|
+
"outputs": {},
|
650
|
+
"skipped": True,
|
651
|
+
},
|
652
|
+
},
|
653
|
+
},
|
654
|
+
3: {
|
655
|
+
"item": 3,
|
656
|
+
"stages": {
|
657
|
+
"2709471980": {"outputs": {}},
|
658
|
+
"9263488742": {
|
659
|
+
"outputs": {},
|
660
|
+
"skipped": True,
|
661
|
+
},
|
662
|
+
},
|
663
|
+
},
|
664
|
+
4: {
|
665
|
+
"item": 4,
|
666
|
+
"stages": {
|
667
|
+
"2709471980": {"outputs": {}},
|
668
|
+
"9263488742": {"outputs": {}},
|
669
|
+
},
|
670
|
+
},
|
671
|
+
},
|
672
|
+
}
|
673
|
+
},
|
674
|
+
}
|
675
|
+
}
|
676
|
+
},
|
677
|
+
}
|
618
678
|
|
619
679
|
|
620
680
|
@mock.patch.object(Config, "stage_raise_error", False)
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__: str = "0.0.51"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/schedules.py
RENAMED
File without changes
|
{ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/workflows.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/requires.txt
RENAMED
File without changes
|
{ddeutil_workflow-0.0.51 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|