ddeutil-workflow 0.0.52__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 -160
- ddeutil/workflow/stages.py +664 -388
- ddeutil/workflow/utils.py +5 -4
- ddeutil/workflow/workflow.py +105 -230
- {ddeutil_workflow-0.0.52.dist-info → ddeutil_workflow-0.0.54.dist-info}/METADATA +1 -7
- {ddeutil_workflow-0.0.52.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.52.dist-info → ddeutil_workflow-0.0.54.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.52.dist-info → ddeutil_workflow-0.0.54.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.52.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,6 +559,7 @@ class Job(BaseModel):
|
|
555
559
|
)
|
556
560
|
|
557
561
|
_id: str = self.id or job_id
|
562
|
+
output: DictData = output.copy()
|
558
563
|
errors: DictData = (
|
559
564
|
{"errors": output.pop("errors", {})} if "errors" in output else {}
|
560
565
|
)
|
@@ -583,9 +588,7 @@ class Job(BaseModel):
|
|
583
588
|
*,
|
584
589
|
run_id: str | None = None,
|
585
590
|
parent_run_id: str | None = None,
|
586
|
-
result: Result | None = None,
|
587
591
|
event: Event | None = None,
|
588
|
-
raise_error: bool = True,
|
589
592
|
) -> Result:
|
590
593
|
"""Job execution with passing dynamic parameters from the workflow
|
591
594
|
execution. It will generate matrix values at the first step and run
|
@@ -594,22 +597,18 @@ class Job(BaseModel):
|
|
594
597
|
This method be execution routing for call dynamic execution function
|
595
598
|
with specific target `runs-on` value.
|
596
599
|
|
597
|
-
:param params:
|
600
|
+
:param params: (DictData) A parameter data.
|
598
601
|
:param run_id: (str) A job running ID.
|
599
602
|
:param parent_run_id: (str) A parent workflow running ID.
|
600
|
-
:param
|
601
|
-
|
602
|
-
:param event: (Event) An event manager that pass to the
|
603
|
-
PoolThreadExecutor.
|
604
|
-
:param raise_error: (bool) A flag that all this method raise error to
|
605
|
-
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.
|
606
605
|
|
607
|
-
: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.
|
608
608
|
|
609
609
|
:rtype: Result
|
610
610
|
"""
|
611
611
|
result: Result = Result.construct_with_rs_or_id(
|
612
|
-
result,
|
613
612
|
run_id=run_id,
|
614
613
|
parent_run_id=parent_run_id,
|
615
614
|
id_logic=(self.id or "not-set"),
|
@@ -626,7 +625,6 @@ class Job(BaseModel):
|
|
626
625
|
run_id=run_id,
|
627
626
|
parent_run_id=parent_run_id,
|
628
627
|
event=event,
|
629
|
-
raise_error=raise_error,
|
630
628
|
)
|
631
629
|
elif self.runs_on.type == RunsOn.SELF_HOSTED: # pragma: no cov
|
632
630
|
pass
|
@@ -637,15 +635,14 @@ class Job(BaseModel):
|
|
637
635
|
run_id=run_id,
|
638
636
|
parent_run_id=parent_run_id,
|
639
637
|
event=event,
|
640
|
-
raise_error=raise_error,
|
641
638
|
)
|
642
639
|
|
643
640
|
# pragma: no cov
|
644
641
|
result.trace.error(
|
645
|
-
f"[JOB]:
|
642
|
+
f"[JOB]: Execute not support runs-on: {self.runs_on.type!r} yet."
|
646
643
|
)
|
647
644
|
raise NotImplementedError(
|
648
|
-
f"
|
645
|
+
f"Execute runs-on type: {self.runs_on.type} does not support yet."
|
649
646
|
)
|
650
647
|
|
651
648
|
|
@@ -656,7 +653,6 @@ def local_execute_strategy(
|
|
656
653
|
*,
|
657
654
|
result: Result | None = None,
|
658
655
|
event: Event | None = None,
|
659
|
-
raise_error: bool = True,
|
660
656
|
) -> Result:
|
661
657
|
"""Local job strategy execution with passing dynamic parameters from the
|
662
658
|
workflow execution to strategy matrix.
|
@@ -670,32 +666,33 @@ def local_execute_strategy(
|
|
670
666
|
For each stage that execution with this strategy metrix, it will use the
|
671
667
|
`set_outputs` method for reconstruct result context data.
|
672
668
|
|
673
|
-
:raise JobException: If it has any error from `StageException` or
|
674
|
-
`UtilException`.
|
675
|
-
|
676
669
|
:param job: (Job) A job model that want to execute.
|
677
670
|
:param strategy: A strategy metrix value that use on this execution.
|
678
671
|
This value will pass to the `matrix` key for templating.
|
679
|
-
:param params: A
|
680
|
-
:param result: (Result) A
|
681
|
-
|
682
|
-
|
683
|
-
|
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`.
|
684
679
|
|
685
680
|
:rtype: Result
|
686
681
|
"""
|
687
|
-
|
688
|
-
|
682
|
+
result: Result = result or Result(
|
683
|
+
run_id=gen_id(job.id or "not-set", unique=True),
|
684
|
+
extras=job.extras,
|
685
|
+
)
|
689
686
|
|
690
687
|
strategy_id: str = gen_id(strategy)
|
691
688
|
context: DictData = copy.deepcopy(params)
|
692
689
|
context.update({"matrix": strategy, "stages": {}})
|
693
690
|
|
694
691
|
if strategy:
|
695
|
-
result.trace.info(f"[JOB]:
|
692
|
+
result.trace.info(f"[JOB]: Start Strategy: {strategy_id!r}")
|
696
693
|
result.trace.info(f"[JOB]: ... matrix: {strategy!r}")
|
697
694
|
else:
|
698
|
-
result.trace.info("[JOB]:
|
695
|
+
result.trace.info("[JOB]: Start Strategy: EMPTY")
|
699
696
|
|
700
697
|
for stage in job.stages:
|
701
698
|
|
@@ -734,12 +731,7 @@ def local_execute_strategy(
|
|
734
731
|
stage.set_outputs(rs.context, to=context)
|
735
732
|
except (StageException, UtilException) as e:
|
736
733
|
result.trace.error(f"[JOB]: {e.__class__.__name__}: {e}")
|
737
|
-
|
738
|
-
raise JobException(
|
739
|
-
f"Stage execution error: {e.__class__.__name__}: {e}"
|
740
|
-
) from None
|
741
|
-
|
742
|
-
return result.catch(
|
734
|
+
result.catch(
|
743
735
|
status=FAILED,
|
744
736
|
context={
|
745
737
|
strategy_id: {
|
@@ -749,13 +741,17 @@ def local_execute_strategy(
|
|
749
741
|
},
|
750
742
|
},
|
751
743
|
)
|
744
|
+
raise JobException(
|
745
|
+
f"Stage raise: {e.__class__.__name__}: {e}"
|
746
|
+
) from e
|
752
747
|
|
753
748
|
if rs.status == FAILED:
|
754
749
|
error_msg: str = (
|
755
|
-
f"
|
756
|
-
f"
|
750
|
+
f"Strategy break because stage, {stage.iden!r}, return FAILED "
|
751
|
+
f"status."
|
757
752
|
)
|
758
|
-
|
753
|
+
result.trace.warning(f"[JOB]: {error_msg}")
|
754
|
+
result.catch(
|
759
755
|
status=FAILED,
|
760
756
|
context={
|
761
757
|
strategy_id: {
|
@@ -765,6 +761,7 @@ def local_execute_strategy(
|
|
765
761
|
},
|
766
762
|
},
|
767
763
|
)
|
764
|
+
raise JobException(error_msg)
|
768
765
|
|
769
766
|
return result.catch(
|
770
767
|
status=SUCCESS,
|
@@ -784,22 +781,20 @@ def local_execute(
|
|
784
781
|
run_id: str | None = None,
|
785
782
|
parent_run_id: str | None = None,
|
786
783
|
event: Event | None = None,
|
787
|
-
raise_error: bool = True,
|
788
784
|
) -> Result:
|
789
785
|
"""Local job execution with passing dynamic parameters from the workflow
|
790
786
|
execution or itself execution. It will generate matrix values at the first
|
791
787
|
step and run multithread on this metrics to the `stages` field of this job.
|
792
788
|
|
793
|
-
This method does not raise any JobException if it runs with
|
789
|
+
This method does not raise any `JobException` if it runs with
|
794
790
|
multi-threading strategy.
|
795
791
|
|
796
|
-
:param job: (Job) A job model
|
797
|
-
:param params: (DictData)
|
798
|
-
:param run_id: (str) A job running ID
|
799
|
-
:param parent_run_id: (str) A parent workflow running ID
|
800
|
-
:param event: (Event) An
|
801
|
-
|
802
|
-
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.
|
803
798
|
|
804
799
|
:rtype: Result
|
805
800
|
"""
|
@@ -811,40 +806,12 @@ def local_execute(
|
|
811
806
|
)
|
812
807
|
|
813
808
|
event: Event = Event() if event is None else event
|
814
|
-
|
815
|
-
# NOTE: Normal Job execution without parallel strategy matrix. It uses
|
816
|
-
# for-loop to control strategy execution sequentially.
|
817
|
-
if (not job.strategy.is_set()) or job.strategy.max_parallel == 1:
|
818
|
-
|
819
|
-
for strategy in job.strategy.make():
|
820
|
-
|
821
|
-
if event and event.is_set(): # pragma: no cov
|
822
|
-
return result.catch(
|
823
|
-
status=CANCEL,
|
824
|
-
context={
|
825
|
-
"errors": JobException(
|
826
|
-
"Job strategy was canceled from event that had set "
|
827
|
-
"before strategy execution."
|
828
|
-
).to_dict()
|
829
|
-
},
|
830
|
-
)
|
831
|
-
|
832
|
-
local_execute_strategy(
|
833
|
-
job,
|
834
|
-
strategy,
|
835
|
-
params,
|
836
|
-
result=result,
|
837
|
-
event=event,
|
838
|
-
raise_error=raise_error,
|
839
|
-
)
|
840
|
-
|
841
|
-
return result
|
842
|
-
|
843
809
|
fail_fast_flag: bool = job.strategy.fail_fast
|
844
810
|
ls: str = "Fail-Fast" if fail_fast_flag else "All-Completed"
|
811
|
+
workers: int = job.strategy.max_parallel
|
845
812
|
result.trace.info(
|
846
|
-
f"[JOB]:
|
847
|
-
f"
|
813
|
+
f"[JOB]: {ls}-Execute: {job.id} with {workers} "
|
814
|
+
f"worker{'s' if workers > 1 else ''}."
|
848
815
|
)
|
849
816
|
|
850
817
|
if event and event.is_set(): # pragma: no cov
|
@@ -852,15 +819,14 @@ def local_execute(
|
|
852
819
|
status=CANCEL,
|
853
820
|
context={
|
854
821
|
"errors": JobException(
|
855
|
-
"Job
|
856
|
-
"
|
822
|
+
"Job was canceled from event that had set before "
|
823
|
+
"local execution."
|
857
824
|
).to_dict()
|
858
825
|
},
|
859
826
|
)
|
860
827
|
|
861
828
|
with ThreadPoolExecutor(
|
862
|
-
max_workers=
|
863
|
-
thread_name_prefix="job_strategy_exec_",
|
829
|
+
max_workers=workers, thread_name_prefix="job_strategy_exec_"
|
864
830
|
) as executor:
|
865
831
|
|
866
832
|
futures: list[Future] = [
|
@@ -871,7 +837,6 @@ def local_execute(
|
|
871
837
|
params=params,
|
872
838
|
result=result,
|
873
839
|
event=event,
|
874
|
-
raise_error=raise_error,
|
875
840
|
)
|
876
841
|
for strategy in job.strategy.make()
|
877
842
|
]
|
@@ -880,12 +845,9 @@ def local_execute(
|
|
880
845
|
status: Status = SUCCESS
|
881
846
|
|
882
847
|
if not fail_fast_flag:
|
883
|
-
done = as_completed(futures
|
848
|
+
done: list[Future] = as_completed(futures)
|
884
849
|
else:
|
885
|
-
done, not_done = wait(
|
886
|
-
futures, timeout=1800, return_when=FIRST_EXCEPTION
|
887
|
-
)
|
888
|
-
|
850
|
+
done, not_done = wait(futures, return_when=FIRST_EXCEPTION)
|
889
851
|
if len(done) != len(futures):
|
890
852
|
result.trace.warning(
|
891
853
|
"[JOB]: Set event for stop pending stage future."
|
@@ -894,10 +856,8 @@ def local_execute(
|
|
894
856
|
for future in not_done:
|
895
857
|
future.cancel()
|
896
858
|
|
897
|
-
nd: str =
|
898
|
-
|
899
|
-
)
|
900
|
-
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}")
|
901
861
|
|
902
862
|
for future in done:
|
903
863
|
try:
|
@@ -905,10 +865,12 @@ def local_execute(
|
|
905
865
|
except JobException as e:
|
906
866
|
status = FAILED
|
907
867
|
result.trace.error(
|
908
|
-
f"[JOB]: {ls}
|
868
|
+
f"[JOB]: {ls}: {e.__class__.__name__}:{NEWLINE}{e}"
|
909
869
|
)
|
910
|
-
|
911
|
-
|
870
|
+
if "errors" in context:
|
871
|
+
context["errors"].append(e.to_dict())
|
872
|
+
else:
|
873
|
+
context["errors"] = [e.to_dict()]
|
912
874
|
return result.catch(status=status, context=context)
|
913
875
|
|
914
876
|
|
@@ -919,19 +881,17 @@ def self_hosted_execute(
|
|
919
881
|
run_id: str | None = None,
|
920
882
|
parent_run_id: str | None = None,
|
921
883
|
event: Event | None = None,
|
922
|
-
raise_error: bool = True,
|
923
884
|
) -> Result: # pragma: no cov
|
924
885
|
"""Self-Hosted job execution with passing dynamic parameters from the
|
925
886
|
workflow execution or itself execution. It will make request to the
|
926
887
|
self-hosted host url.
|
927
888
|
|
928
889
|
:param job: (Job) A job model that want to execute.
|
929
|
-
:param params: (DictData)
|
930
|
-
:param run_id: (str) A job running ID
|
931
|
-
:param parent_run_id: (str) A parent workflow running ID
|
932
|
-
:param event: (Event) An
|
933
|
-
|
934
|
-
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.
|
935
895
|
|
936
896
|
:rtype: Result
|
937
897
|
"""
|
@@ -947,8 +907,8 @@ def self_hosted_execute(
|
|
947
907
|
status=CANCEL,
|
948
908
|
context={
|
949
909
|
"errors": JobException(
|
950
|
-
"Job
|
951
|
-
"
|
910
|
+
"Job was canceled from event that had set before start "
|
911
|
+
"self-hosted execution."
|
952
912
|
).to_dict()
|
953
913
|
},
|
954
914
|
)
|
@@ -963,20 +923,17 @@ def self_hosted_execute(
|
|
963
923
|
"job": job.model_dump(),
|
964
924
|
"params": params,
|
965
925
|
"result": result.__dict__,
|
966
|
-
"raise_error": raise_error,
|
967
926
|
},
|
968
927
|
)
|
969
928
|
except requests.exceptions.RequestException as e:
|
970
929
|
return result.catch(status=FAILED, context={"errors": to_dict(e)})
|
971
930
|
|
972
931
|
if resp.status_code != 200:
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
)
|
932
|
+
raise JobException(
|
933
|
+
f"Job execution error from request to self-hosted: "
|
934
|
+
f"{job.runs_on.args.host!r}"
|
935
|
+
)
|
978
936
|
|
979
|
-
return result.catch(status=FAILED)
|
980
937
|
return result.catch(status=SUCCESS)
|
981
938
|
|
982
939
|
|
@@ -987,7 +944,6 @@ def azure_batch_execute(
|
|
987
944
|
run_id: str | None = None,
|
988
945
|
parent_run_id: str | None = None,
|
989
946
|
event: Event | None = None,
|
990
|
-
raise_error: bool | None = None,
|
991
947
|
) -> Result: # pragma no cov
|
992
948
|
"""Azure Batch job execution that will run all job's stages on the Azure
|
993
949
|
Batch Node and extract the result file to be returning context result.
|
@@ -1012,7 +968,6 @@ def azure_batch_execute(
|
|
1012
968
|
:param run_id:
|
1013
969
|
:param parent_run_id:
|
1014
970
|
:param event:
|
1015
|
-
:param raise_error:
|
1016
971
|
|
1017
972
|
:rtype: Result
|
1018
973
|
"""
|
@@ -1027,13 +982,12 @@ def azure_batch_execute(
|
|
1027
982
|
status=CANCEL,
|
1028
983
|
context={
|
1029
984
|
"errors": JobException(
|
1030
|
-
"Job
|
1031
|
-
"
|
985
|
+
"Job was canceled from event that had set before start "
|
986
|
+
"azure-batch execution."
|
1032
987
|
).to_dict()
|
1033
988
|
},
|
1034
989
|
)
|
1035
990
|
print(params)
|
1036
|
-
print(raise_error)
|
1037
991
|
return result.catch(status=SUCCESS)
|
1038
992
|
|
1039
993
|
|
@@ -1044,7 +998,6 @@ def docker_execution(
|
|
1044
998
|
run_id: str | None = None,
|
1045
999
|
parent_run_id: str | None = None,
|
1046
1000
|
event: Event | None = None,
|
1047
|
-
raise_error: bool | None = None,
|
1048
1001
|
):
|
1049
1002
|
"""Docker job execution.
|
1050
1003
|
|
@@ -1070,5 +1023,4 @@ def docker_execution(
|
|
1070
1023
|
},
|
1071
1024
|
)
|
1072
1025
|
print(params)
|
1073
|
-
print(raise_error)
|
1074
1026
|
return result.catch(status=SUCCESS)
|