ddeutil-workflow 0.0.53__py3-none-any.whl → 0.0.54__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 +1 -1
- ddeutil/workflow/api/routes/job.py +2 -2
- ddeutil/workflow/job.py +112 -161
- ddeutil/workflow/stages.py +491 -302
- ddeutil/workflow/utils.py +5 -4
- ddeutil/workflow/workflow.py +105 -230
- {ddeutil_workflow-0.0.53.dist-info → ddeutil_workflow-0.0.54.dist-info}/METADATA +1 -7
- {ddeutil_workflow-0.0.53.dist-info → ddeutil_workflow-0.0.54.dist-info}/RECORD +14 -14
- /ddeutil/workflow/api/{log.py → logs.py} +0 -0
- /ddeutil/workflow/api/{repeat.py → utils.py} +0 -0
- {ddeutil_workflow-0.0.53.dist-info → ddeutil_workflow-0.0.54.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.53.dist-info → ddeutil_workflow-0.0.54.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.53.dist-info → ddeutil_workflow-0.0.54.dist-info}/top_level.txt +0 -0
ddeutil/workflow/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__: str = "0.0.
|
1
|
+
__version__: str = "0.0.54"
|
ddeutil/workflow/api/api.py
CHANGED
@@ -24,8 +24,8 @@ from ..conf import api_config, config
|
|
24
24
|
from ..logs import get_logger
|
25
25
|
from ..scheduler import ReleaseThread, ReleaseThreads
|
26
26
|
from ..workflow import ReleaseQueue, WorkflowTask
|
27
|
-
from .repeat import repeat_at
|
28
27
|
from .routes import job, log
|
28
|
+
from .utils import repeat_at
|
29
29
|
|
30
30
|
load_dotenv()
|
31
31
|
logger = get_logger("uvicorn.error")
|
@@ -39,7 +39,7 @@ async def job_execute(
|
|
39
39
|
job: Job,
|
40
40
|
params: dict[str, Any],
|
41
41
|
):
|
42
|
-
"""Execute job via
|
42
|
+
"""Execute job via RestAPI."""
|
43
43
|
rs: Result = Result(
|
44
44
|
context=result.context,
|
45
45
|
run_id=result.run_id,
|
@@ -56,7 +56,7 @@ async def job_execute(
|
|
56
56
|
to=context,
|
57
57
|
)
|
58
58
|
except JobException as err:
|
59
|
-
rs.trace.error(f"[
|
59
|
+
rs.trace.error(f"[JOB]: {err.__class__.__name__}: {err}")
|
60
60
|
|
61
61
|
return {
|
62
62
|
"message": "Start execute job via API.",
|
ddeutil/workflow/job.py
CHANGED
@@ -3,12 +3,17 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
"""Job
|
7
|
-
The job handle the lineage of stages and location of
|
8
|
-
|
9
|
-
|
6
|
+
"""Job model that use for store Stage models and node parameter that use for
|
7
|
+
running these stages. The job model handle the lineage of stages and location of
|
8
|
+
execution that mean you can define `runs-on` field with the Self-Hosted mode
|
9
|
+
for execute on target machine instead of the current local machine.
|
10
10
|
|
11
|
-
This module include Strategy
|
11
|
+
This module include Strategy model that use on the job `strategy` field for
|
12
|
+
making matrix values before execution parallelism stage execution.
|
13
|
+
|
14
|
+
The Job model does not implement `handler_execute` same as Stage model
|
15
|
+
because the job should raise only `JobException` class from the execution
|
16
|
+
method.
|
12
17
|
"""
|
13
18
|
from __future__ import annotations
|
14
19
|
|
@@ -41,7 +46,7 @@ from .exceptions import (
|
|
41
46
|
from .result import CANCEL, FAILED, SKIP, SUCCESS, WAIT, Result, Status
|
42
47
|
from .reusables import has_template, param2template
|
43
48
|
from .stages import Stage
|
44
|
-
from .utils import cross_product, filter_func, gen_id
|
49
|
+
from .utils import NEWLINE, cross_product, filter_func, gen_id
|
45
50
|
|
46
51
|
MatrixFilter = list[dict[str, Union[str, int]]]
|
47
52
|
|
@@ -59,10 +64,10 @@ def make(
|
|
59
64
|
This function use the `lru_cache` decorator function increase
|
60
65
|
performance for duplicate matrix value scenario.
|
61
66
|
|
62
|
-
:param matrix: A matrix values that want to cross product to
|
63
|
-
parallelism values.
|
64
|
-
:param include: A list of additional matrix that want to adds-in.
|
65
|
-
:param exclude: A list of exclude matrix that want to filter-out.
|
67
|
+
:param matrix: (Matrix) A matrix values that want to cross product to
|
68
|
+
possible parallelism values.
|
69
|
+
:param include: (A list of additional matrix that want to adds-in.
|
70
|
+
:param exclude: (A list of exclude matrix that want to filter-out.
|
66
71
|
|
67
72
|
:rtype: list[DictStr]
|
68
73
|
"""
|
@@ -320,12 +325,13 @@ class Job(BaseModel):
|
|
320
325
|
id: Optional[str] = Field(
|
321
326
|
default=None,
|
322
327
|
description=(
|
323
|
-
"A job ID that
|
328
|
+
"A job ID that was set from Workflow model after initialize step. "
|
329
|
+
"If this model create standalone, it will be None."
|
324
330
|
),
|
325
331
|
)
|
326
332
|
desc: Optional[str] = Field(
|
327
333
|
default=None,
|
328
|
-
description="A job description that can be
|
334
|
+
description="A job description that can be markdown syntax.",
|
329
335
|
)
|
330
336
|
runs_on: RunsOnModel = Field(
|
331
337
|
default_factory=OnLocal,
|
@@ -339,7 +345,7 @@ class Job(BaseModel):
|
|
339
345
|
)
|
340
346
|
stages: list[Stage] = Field(
|
341
347
|
default_factory=list,
|
342
|
-
description="A list of Stage of this job.",
|
348
|
+
description="A list of Stage model of this job.",
|
343
349
|
)
|
344
350
|
trigger_rule: Rule = Field(
|
345
351
|
default=Rule.ALL_SUCCESS,
|
@@ -373,7 +379,7 @@ class Job(BaseModel):
|
|
373
379
|
|
374
380
|
@field_validator("stages", mode="after")
|
375
381
|
def __validate_stage_id__(cls, value: list[Stage]) -> list[Stage]:
|
376
|
-
"""Validate
|
382
|
+
"""Validate stage ID of each stage in the `stages` field should not be
|
377
383
|
duplicate.
|
378
384
|
|
379
385
|
:rtype: list[Stage]
|
@@ -391,11 +397,10 @@ class Job(BaseModel):
|
|
391
397
|
|
392
398
|
@model_validator(mode="after")
|
393
399
|
def __validate_job_id__(self) -> Self:
|
394
|
-
"""Validate job id should not
|
400
|
+
"""Validate job id should not dynamic with params template.
|
395
401
|
|
396
402
|
:rtype: Self
|
397
403
|
"""
|
398
|
-
# VALIDATE: Validate job id should not dynamic with params template.
|
399
404
|
if has_template(self.id):
|
400
405
|
raise ValueError(
|
401
406
|
f"Job ID, {self.id!r}, should not has any template."
|
@@ -419,14 +424,11 @@ class Job(BaseModel):
|
|
419
424
|
return stage
|
420
425
|
raise ValueError(f"Stage {stage_id!r} does not exists in this job.")
|
421
426
|
|
422
|
-
def check_needs(
|
423
|
-
|
424
|
-
|
425
|
-
) -> Status: # pragma: no cov
|
426
|
-
"""Return Status enum for checking job's need trigger logic in an
|
427
|
-
input list of job's ID.
|
427
|
+
def check_needs(self, jobs: dict[str, Any]) -> Status: # pragma: no cov
|
428
|
+
"""Return trigger status from checking job's need trigger rule logic was
|
429
|
+
valid. The return status should be SUCCESS, FAILED, WAIT, or SKIP.
|
428
430
|
|
429
|
-
:param jobs: A mapping of job ID and
|
431
|
+
:param jobs: A mapping of job ID and its context data.
|
430
432
|
|
431
433
|
:raise NotImplementedError: If the job trigger rule out of scope.
|
432
434
|
|
@@ -441,7 +443,6 @@ class Job(BaseModel):
|
|
441
443
|
need_exist: dict[str, Any] = {
|
442
444
|
need: jobs[need] for need in self.needs if need in jobs
|
443
445
|
}
|
444
|
-
|
445
446
|
if len(need_exist) != len(self.needs):
|
446
447
|
return WAIT
|
447
448
|
elif all("skipped" in need_exist[job] for job in need_exist):
|
@@ -469,30 +470,26 @@ class Job(BaseModel):
|
|
469
470
|
elif self.trigger_rule == Rule.NONE_FAILED:
|
470
471
|
rs = all("errors" not in need_exist[job] for job in need_exist)
|
471
472
|
else: # pragma: no cov
|
472
|
-
|
473
|
-
f"Trigger rule: {self.trigger_rule} does not support yet."
|
474
|
-
)
|
473
|
+
return FAILED
|
475
474
|
return make_return(rs)
|
476
475
|
|
477
|
-
def is_skipped(self, params: DictData
|
476
|
+
def is_skipped(self, params: DictData) -> bool:
|
478
477
|
"""Return true if condition of this job do not correct. This process
|
479
478
|
use build-in eval function to execute the if-condition.
|
480
479
|
|
480
|
+
:param params: (DictData) A parameter value that want to pass to condition
|
481
|
+
template.
|
482
|
+
|
481
483
|
:raise JobException: When it has any error raise from the eval
|
482
484
|
condition statement.
|
483
485
|
:raise JobException: When return type of the eval condition statement
|
484
486
|
does not return with boolean type.
|
485
487
|
|
486
|
-
:param params: (DictData) A parameters that want to pass to condition
|
487
|
-
template.
|
488
|
-
|
489
488
|
:rtype: bool
|
490
489
|
"""
|
491
490
|
if self.condition is None:
|
492
491
|
return False
|
493
492
|
|
494
|
-
params: DictData = {} if params is None else params
|
495
|
-
|
496
493
|
try:
|
497
494
|
# WARNING: The eval build-in function is very dangerous. So, it
|
498
495
|
# should use the `re` module to validate eval-string before
|
@@ -513,15 +510,20 @@ class Job(BaseModel):
|
|
513
510
|
output: DictData,
|
514
511
|
to: DictData,
|
515
512
|
*,
|
516
|
-
job_id: Optional[
|
513
|
+
job_id: Optional[str] = None,
|
517
514
|
) -> DictData:
|
518
|
-
"""Set an outputs from execution
|
519
|
-
|
515
|
+
"""Set an outputs from execution result context to the received context
|
516
|
+
with a `to` input parameter. The result context from job strategy
|
517
|
+
execution will be set with `strategies` key in this job ID key.
|
520
518
|
|
521
519
|
For example of setting output method, If you receive execute output
|
522
520
|
and want to set on the `to` like;
|
523
521
|
|
524
|
-
... (i) output: {
|
522
|
+
... (i) output: {
|
523
|
+
'strategy-01': 'foo',
|
524
|
+
'strategy-02': 'bar',
|
525
|
+
'skipped': True,
|
526
|
+
}
|
525
527
|
... (ii) to: {'jobs': {}}
|
526
528
|
|
527
529
|
The result of the `to` argument will be;
|
@@ -530,9 +532,10 @@ class Job(BaseModel):
|
|
530
532
|
'jobs': {
|
531
533
|
'<job-id>': {
|
532
534
|
'strategies': {
|
533
|
-
'strategy-01':
|
534
|
-
'strategy-02': bar,
|
535
|
-
}
|
535
|
+
'strategy-01': 'foo',
|
536
|
+
'strategy-02': 'bar',
|
537
|
+
},
|
538
|
+
'skipped': True,
|
536
539
|
}
|
537
540
|
}
|
538
541
|
}
|
@@ -540,9 +543,10 @@ class Job(BaseModel):
|
|
540
543
|
:raise JobException: If the job's ID does not set and the setting
|
541
544
|
default job ID flag does not set.
|
542
545
|
|
543
|
-
:param output:
|
544
|
-
|
545
|
-
:param
|
546
|
+
:param output: (DictData) A result data context that want to extract
|
547
|
+
and transfer to the `strategies` key in receive context.
|
548
|
+
:param to: (DictData) A received context data.
|
549
|
+
:param job_id: (str | None) A job ID if the `id` field does not set.
|
546
550
|
|
547
551
|
:rtype: DictData
|
548
552
|
"""
|
@@ -555,7 +559,7 @@ class Job(BaseModel):
|
|
555
559
|
)
|
556
560
|
|
557
561
|
_id: str = self.id or job_id
|
558
|
-
output: DictData = copy
|
562
|
+
output: DictData = output.copy()
|
559
563
|
errors: DictData = (
|
560
564
|
{"errors": output.pop("errors", {})} if "errors" in output else {}
|
561
565
|
)
|
@@ -584,9 +588,7 @@ class Job(BaseModel):
|
|
584
588
|
*,
|
585
589
|
run_id: str | None = None,
|
586
590
|
parent_run_id: str | None = None,
|
587
|
-
result: Result | None = None,
|
588
591
|
event: Event | None = None,
|
589
|
-
raise_error: bool = True,
|
590
592
|
) -> Result:
|
591
593
|
"""Job execution with passing dynamic parameters from the workflow
|
592
594
|
execution. It will generate matrix values at the first step and run
|
@@ -595,22 +597,18 @@ class Job(BaseModel):
|
|
595
597
|
This method be execution routing for call dynamic execution function
|
596
598
|
with specific target `runs-on` value.
|
597
599
|
|
598
|
-
:param params:
|
600
|
+
:param params: (DictData) A parameter data.
|
599
601
|
:param run_id: (str) A job running ID.
|
600
602
|
:param parent_run_id: (str) A parent workflow running ID.
|
601
|
-
:param
|
602
|
-
|
603
|
-
:param event: (Event) An event manager that pass to the
|
604
|
-
PoolThreadExecutor.
|
605
|
-
:param raise_error: (bool) A flag that all this method raise error to
|
606
|
-
the strategy execution. Default is `True`.
|
603
|
+
:param event: (Event) An Event manager instance that use to cancel this
|
604
|
+
execution if it forces stopped by parent execution.
|
607
605
|
|
608
|
-
:raise NotImplementedError: If the `runs-on` value does not implement
|
606
|
+
:raise NotImplementedError: If the `runs-on` value does not implement on
|
607
|
+
this execution.
|
609
608
|
|
610
609
|
:rtype: Result
|
611
610
|
"""
|
612
611
|
result: Result = Result.construct_with_rs_or_id(
|
613
|
-
result,
|
614
612
|
run_id=run_id,
|
615
613
|
parent_run_id=parent_run_id,
|
616
614
|
id_logic=(self.id or "not-set"),
|
@@ -627,7 +625,6 @@ class Job(BaseModel):
|
|
627
625
|
run_id=run_id,
|
628
626
|
parent_run_id=parent_run_id,
|
629
627
|
event=event,
|
630
|
-
raise_error=raise_error,
|
631
628
|
)
|
632
629
|
elif self.runs_on.type == RunsOn.SELF_HOSTED: # pragma: no cov
|
633
630
|
pass
|
@@ -638,15 +635,14 @@ class Job(BaseModel):
|
|
638
635
|
run_id=run_id,
|
639
636
|
parent_run_id=parent_run_id,
|
640
637
|
event=event,
|
641
|
-
raise_error=raise_error,
|
642
638
|
)
|
643
639
|
|
644
640
|
# pragma: no cov
|
645
641
|
result.trace.error(
|
646
|
-
f"[JOB]:
|
642
|
+
f"[JOB]: Execute not support runs-on: {self.runs_on.type!r} yet."
|
647
643
|
)
|
648
644
|
raise NotImplementedError(
|
649
|
-
f"
|
645
|
+
f"Execute runs-on type: {self.runs_on.type} does not support yet."
|
650
646
|
)
|
651
647
|
|
652
648
|
|
@@ -657,7 +653,6 @@ def local_execute_strategy(
|
|
657
653
|
*,
|
658
654
|
result: Result | None = None,
|
659
655
|
event: Event | None = None,
|
660
|
-
raise_error: bool = True,
|
661
656
|
) -> Result:
|
662
657
|
"""Local job strategy execution with passing dynamic parameters from the
|
663
658
|
workflow execution to strategy matrix.
|
@@ -671,32 +666,33 @@ def local_execute_strategy(
|
|
671
666
|
For each stage that execution with this strategy metrix, it will use the
|
672
667
|
`set_outputs` method for reconstruct result context data.
|
673
668
|
|
674
|
-
:raise JobException: If it has any error from `StageException` or
|
675
|
-
`UtilException`.
|
676
|
-
|
677
669
|
:param job: (Job) A job model that want to execute.
|
678
670
|
:param strategy: A strategy metrix value that use on this execution.
|
679
671
|
This value will pass to the `matrix` key for templating.
|
680
|
-
:param params: A
|
681
|
-
:param result: (Result) A
|
682
|
-
|
683
|
-
|
684
|
-
|
672
|
+
:param params: (DictData) A parameter data.
|
673
|
+
:param result: (Result) A Result instance for return context and status.
|
674
|
+
:param event: (Event) An Event manager instance that use to cancel this
|
675
|
+
execution if it forces stopped by parent execution.
|
676
|
+
|
677
|
+
:raise JobException: If it has any error from `StageException` or
|
678
|
+
`UtilException`.
|
685
679
|
|
686
680
|
:rtype: Result
|
687
681
|
"""
|
688
|
-
|
689
|
-
|
682
|
+
result: Result = result or Result(
|
683
|
+
run_id=gen_id(job.id or "not-set", unique=True),
|
684
|
+
extras=job.extras,
|
685
|
+
)
|
690
686
|
|
691
687
|
strategy_id: str = gen_id(strategy)
|
692
688
|
context: DictData = copy.deepcopy(params)
|
693
689
|
context.update({"matrix": strategy, "stages": {}})
|
694
690
|
|
695
691
|
if strategy:
|
696
|
-
result.trace.info(f"[JOB]:
|
692
|
+
result.trace.info(f"[JOB]: Start Strategy: {strategy_id!r}")
|
697
693
|
result.trace.info(f"[JOB]: ... matrix: {strategy!r}")
|
698
694
|
else:
|
699
|
-
result.trace.info("[JOB]:
|
695
|
+
result.trace.info("[JOB]: Start Strategy: EMPTY")
|
700
696
|
|
701
697
|
for stage in job.stages:
|
702
698
|
|
@@ -735,12 +731,7 @@ def local_execute_strategy(
|
|
735
731
|
stage.set_outputs(rs.context, to=context)
|
736
732
|
except (StageException, UtilException) as e:
|
737
733
|
result.trace.error(f"[JOB]: {e.__class__.__name__}: {e}")
|
738
|
-
|
739
|
-
raise JobException(
|
740
|
-
f"Stage execution error: {e.__class__.__name__}: {e}"
|
741
|
-
) from None
|
742
|
-
|
743
|
-
return result.catch(
|
734
|
+
result.catch(
|
744
735
|
status=FAILED,
|
745
736
|
context={
|
746
737
|
strategy_id: {
|
@@ -750,13 +741,17 @@ def local_execute_strategy(
|
|
750
741
|
},
|
751
742
|
},
|
752
743
|
)
|
744
|
+
raise JobException(
|
745
|
+
f"Stage raise: {e.__class__.__name__}: {e}"
|
746
|
+
) from e
|
753
747
|
|
754
748
|
if rs.status == FAILED:
|
755
749
|
error_msg: str = (
|
756
|
-
f"
|
757
|
-
f"
|
750
|
+
f"Strategy break because stage, {stage.iden!r}, return FAILED "
|
751
|
+
f"status."
|
758
752
|
)
|
759
|
-
|
753
|
+
result.trace.warning(f"[JOB]: {error_msg}")
|
754
|
+
result.catch(
|
760
755
|
status=FAILED,
|
761
756
|
context={
|
762
757
|
strategy_id: {
|
@@ -766,6 +761,7 @@ def local_execute_strategy(
|
|
766
761
|
},
|
767
762
|
},
|
768
763
|
)
|
764
|
+
raise JobException(error_msg)
|
769
765
|
|
770
766
|
return result.catch(
|
771
767
|
status=SUCCESS,
|
@@ -785,22 +781,20 @@ def local_execute(
|
|
785
781
|
run_id: str | None = None,
|
786
782
|
parent_run_id: str | None = None,
|
787
783
|
event: Event | None = None,
|
788
|
-
raise_error: bool = True,
|
789
784
|
) -> Result:
|
790
785
|
"""Local job execution with passing dynamic parameters from the workflow
|
791
786
|
execution or itself execution. It will generate matrix values at the first
|
792
787
|
step and run multithread on this metrics to the `stages` field of this job.
|
793
788
|
|
794
|
-
This method does not raise any JobException if it runs with
|
789
|
+
This method does not raise any `JobException` if it runs with
|
795
790
|
multi-threading strategy.
|
796
791
|
|
797
|
-
:param job: (Job) A job model
|
798
|
-
:param params: (DictData)
|
799
|
-
:param run_id: (str) A job running ID
|
800
|
-
:param parent_run_id: (str) A parent workflow running ID
|
801
|
-
:param event: (Event) An
|
802
|
-
|
803
|
-
strategy execution. Default is `True`.
|
792
|
+
:param job: (Job) A job model.
|
793
|
+
:param params: (DictData) A parameter data.
|
794
|
+
:param run_id: (str) A job running ID.
|
795
|
+
:param parent_run_id: (str) A parent workflow running ID.
|
796
|
+
:param event: (Event) An Event manager instance that use to cancel this
|
797
|
+
execution if it forces stopped by parent execution.
|
804
798
|
|
805
799
|
:rtype: Result
|
806
800
|
"""
|
@@ -812,40 +806,12 @@ def local_execute(
|
|
812
806
|
)
|
813
807
|
|
814
808
|
event: Event = Event() if event is None else event
|
815
|
-
|
816
|
-
# NOTE: Normal Job execution without parallel strategy matrix. It uses
|
817
|
-
# for-loop to control strategy execution sequentially.
|
818
|
-
if (not job.strategy.is_set()) or job.strategy.max_parallel == 1:
|
819
|
-
|
820
|
-
for strategy in job.strategy.make():
|
821
|
-
|
822
|
-
if event and event.is_set(): # pragma: no cov
|
823
|
-
return result.catch(
|
824
|
-
status=CANCEL,
|
825
|
-
context={
|
826
|
-
"errors": JobException(
|
827
|
-
"Job strategy was canceled from event that had set "
|
828
|
-
"before strategy execution."
|
829
|
-
).to_dict()
|
830
|
-
},
|
831
|
-
)
|
832
|
-
|
833
|
-
local_execute_strategy(
|
834
|
-
job,
|
835
|
-
strategy,
|
836
|
-
params,
|
837
|
-
result=result,
|
838
|
-
event=event,
|
839
|
-
raise_error=raise_error,
|
840
|
-
)
|
841
|
-
|
842
|
-
return result
|
843
|
-
|
844
809
|
fail_fast_flag: bool = job.strategy.fail_fast
|
845
810
|
ls: str = "Fail-Fast" if fail_fast_flag else "All-Completed"
|
811
|
+
workers: int = job.strategy.max_parallel
|
846
812
|
result.trace.info(
|
847
|
-
f"[JOB]:
|
848
|
-
f"
|
813
|
+
f"[JOB]: {ls}-Execute: {job.id} with {workers} "
|
814
|
+
f"worker{'s' if workers > 1 else ''}."
|
849
815
|
)
|
850
816
|
|
851
817
|
if event and event.is_set(): # pragma: no cov
|
@@ -853,15 +819,14 @@ def local_execute(
|
|
853
819
|
status=CANCEL,
|
854
820
|
context={
|
855
821
|
"errors": JobException(
|
856
|
-
"Job
|
857
|
-
"
|
822
|
+
"Job was canceled from event that had set before "
|
823
|
+
"local execution."
|
858
824
|
).to_dict()
|
859
825
|
},
|
860
826
|
)
|
861
827
|
|
862
828
|
with ThreadPoolExecutor(
|
863
|
-
max_workers=
|
864
|
-
thread_name_prefix="job_strategy_exec_",
|
829
|
+
max_workers=workers, thread_name_prefix="job_strategy_exec_"
|
865
830
|
) as executor:
|
866
831
|
|
867
832
|
futures: list[Future] = [
|
@@ -872,7 +837,6 @@ def local_execute(
|
|
872
837
|
params=params,
|
873
838
|
result=result,
|
874
839
|
event=event,
|
875
|
-
raise_error=raise_error,
|
876
840
|
)
|
877
841
|
for strategy in job.strategy.make()
|
878
842
|
]
|
@@ -881,12 +845,9 @@ def local_execute(
|
|
881
845
|
status: Status = SUCCESS
|
882
846
|
|
883
847
|
if not fail_fast_flag:
|
884
|
-
done = as_completed(futures
|
848
|
+
done: list[Future] = as_completed(futures)
|
885
849
|
else:
|
886
|
-
done, not_done = wait(
|
887
|
-
futures, timeout=1800, return_when=FIRST_EXCEPTION
|
888
|
-
)
|
889
|
-
|
850
|
+
done, not_done = wait(futures, return_when=FIRST_EXCEPTION)
|
890
851
|
if len(done) != len(futures):
|
891
852
|
result.trace.warning(
|
892
853
|
"[JOB]: Set event for stop pending stage future."
|
@@ -895,10 +856,8 @@ def local_execute(
|
|
895
856
|
for future in not_done:
|
896
857
|
future.cancel()
|
897
858
|
|
898
|
-
nd: str =
|
899
|
-
|
900
|
-
)
|
901
|
-
result.trace.debug(f"[JOB]: Strategy set Fail-Fast{nd}")
|
859
|
+
nd: str = f", strategies not run: {not_done}" if not_done else ""
|
860
|
+
result.trace.debug(f"... Strategy set Fail-Fast{nd}")
|
902
861
|
|
903
862
|
for future in done:
|
904
863
|
try:
|
@@ -906,10 +865,12 @@ def local_execute(
|
|
906
865
|
except JobException as e:
|
907
866
|
status = FAILED
|
908
867
|
result.trace.error(
|
909
|
-
f"[JOB]: {ls}
|
868
|
+
f"[JOB]: {ls}: {e.__class__.__name__}:{NEWLINE}{e}"
|
910
869
|
)
|
911
|
-
|
912
|
-
|
870
|
+
if "errors" in context:
|
871
|
+
context["errors"].append(e.to_dict())
|
872
|
+
else:
|
873
|
+
context["errors"] = [e.to_dict()]
|
913
874
|
return result.catch(status=status, context=context)
|
914
875
|
|
915
876
|
|
@@ -920,19 +881,17 @@ def self_hosted_execute(
|
|
920
881
|
run_id: str | None = None,
|
921
882
|
parent_run_id: str | None = None,
|
922
883
|
event: Event | None = None,
|
923
|
-
raise_error: bool = True,
|
924
884
|
) -> Result: # pragma: no cov
|
925
885
|
"""Self-Hosted job execution with passing dynamic parameters from the
|
926
886
|
workflow execution or itself execution. It will make request to the
|
927
887
|
self-hosted host url.
|
928
888
|
|
929
889
|
:param job: (Job) A job model that want to execute.
|
930
|
-
:param params: (DictData)
|
931
|
-
:param run_id: (str) A job running ID
|
932
|
-
:param parent_run_id: (str) A parent workflow running ID
|
933
|
-
:param event: (Event) An
|
934
|
-
|
935
|
-
strategy execution.
|
890
|
+
:param params: (DictData) A parameter data.
|
891
|
+
:param run_id: (str) A job running ID.
|
892
|
+
:param parent_run_id: (str) A parent workflow running ID.
|
893
|
+
:param event: (Event) An Event manager instance that use to cancel this
|
894
|
+
execution if it forces stopped by parent execution.
|
936
895
|
|
937
896
|
:rtype: Result
|
938
897
|
"""
|
@@ -948,8 +907,8 @@ def self_hosted_execute(
|
|
948
907
|
status=CANCEL,
|
949
908
|
context={
|
950
909
|
"errors": JobException(
|
951
|
-
"Job
|
952
|
-
"
|
910
|
+
"Job was canceled from event that had set before start "
|
911
|
+
"self-hosted execution."
|
953
912
|
).to_dict()
|
954
913
|
},
|
955
914
|
)
|
@@ -964,20 +923,17 @@ def self_hosted_execute(
|
|
964
923
|
"job": job.model_dump(),
|
965
924
|
"params": params,
|
966
925
|
"result": result.__dict__,
|
967
|
-
"raise_error": raise_error,
|
968
926
|
},
|
969
927
|
)
|
970
928
|
except requests.exceptions.RequestException as e:
|
971
929
|
return result.catch(status=FAILED, context={"errors": to_dict(e)})
|
972
930
|
|
973
931
|
if resp.status_code != 200:
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
)
|
932
|
+
raise JobException(
|
933
|
+
f"Job execution error from request to self-hosted: "
|
934
|
+
f"{job.runs_on.args.host!r}"
|
935
|
+
)
|
979
936
|
|
980
|
-
return result.catch(status=FAILED)
|
981
937
|
return result.catch(status=SUCCESS)
|
982
938
|
|
983
939
|
|
@@ -988,7 +944,6 @@ def azure_batch_execute(
|
|
988
944
|
run_id: str | None = None,
|
989
945
|
parent_run_id: str | None = None,
|
990
946
|
event: Event | None = None,
|
991
|
-
raise_error: bool | None = None,
|
992
947
|
) -> Result: # pragma no cov
|
993
948
|
"""Azure Batch job execution that will run all job's stages on the Azure
|
994
949
|
Batch Node and extract the result file to be returning context result.
|
@@ -1013,7 +968,6 @@ def azure_batch_execute(
|
|
1013
968
|
:param run_id:
|
1014
969
|
:param parent_run_id:
|
1015
970
|
:param event:
|
1016
|
-
:param raise_error:
|
1017
971
|
|
1018
972
|
:rtype: Result
|
1019
973
|
"""
|
@@ -1028,13 +982,12 @@ def azure_batch_execute(
|
|
1028
982
|
status=CANCEL,
|
1029
983
|
context={
|
1030
984
|
"errors": JobException(
|
1031
|
-
"Job
|
1032
|
-
"
|
985
|
+
"Job was canceled from event that had set before start "
|
986
|
+
"azure-batch execution."
|
1033
987
|
).to_dict()
|
1034
988
|
},
|
1035
989
|
)
|
1036
990
|
print(params)
|
1037
|
-
print(raise_error)
|
1038
991
|
return result.catch(status=SUCCESS)
|
1039
992
|
|
1040
993
|
|
@@ -1045,7 +998,6 @@ def docker_execution(
|
|
1045
998
|
run_id: str | None = None,
|
1046
999
|
parent_run_id: str | None = None,
|
1047
1000
|
event: Event | None = None,
|
1048
|
-
raise_error: bool | None = None,
|
1049
1001
|
):
|
1050
1002
|
"""Docker job execution.
|
1051
1003
|
|
@@ -1071,5 +1023,4 @@ def docker_execution(
|
|
1071
1023
|
},
|
1072
1024
|
)
|
1073
1025
|
print(params)
|
1074
|
-
print(raise_error)
|
1075
1026
|
return result.catch(status=SUCCESS)
|