ddeutil-workflow 0.0.65__py3-none-any.whl → 0.0.66__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/__main__.py +1 -27
- ddeutil/workflow/cli.py +66 -0
- ddeutil/workflow/conf.py +1 -5
- ddeutil/workflow/errors.py +0 -3
- ddeutil/workflow/job.py +6 -10
- ddeutil/workflow/logs.py +1 -1
- ddeutil/workflow/result.py +1 -3
- ddeutil/workflow/stages.py +9 -0
- ddeutil/workflow/workflow.py +25 -11
- {ddeutil_workflow-0.0.65.dist-info → ddeutil_workflow-0.0.66.dist-info}/METADATA +2 -2
- {ddeutil_workflow-0.0.65.dist-info → ddeutil_workflow-0.0.66.dist-info}/RECORD +16 -15
- {ddeutil_workflow-0.0.65.dist-info → ddeutil_workflow-0.0.66.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.65.dist-info → ddeutil_workflow-0.0.66.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.65.dist-info → ddeutil_workflow-0.0.66.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.65.dist-info → ddeutil_workflow-0.0.66.dist-info}/top_level.txt +0 -0
ddeutil/workflow/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__: str = "0.0.
|
1
|
+
__version__: str = "0.0.66"
|
ddeutil/workflow/__main__.py
CHANGED
@@ -1,30 +1,4 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
app = typer.Typer()
|
4
|
-
|
5
|
-
|
6
|
-
@app.callback()
|
7
|
-
def callback():
|
8
|
-
"""
|
9
|
-
Awesome Portal Gun
|
10
|
-
"""
|
11
|
-
|
12
|
-
|
13
|
-
@app.command()
|
14
|
-
def provision():
|
15
|
-
"""
|
16
|
-
Shoot the portal gun
|
17
|
-
"""
|
18
|
-
typer.echo("Shooting portal gun")
|
19
|
-
|
20
|
-
|
21
|
-
@app.command()
|
22
|
-
def job():
|
23
|
-
"""
|
24
|
-
Load the portal gun
|
25
|
-
"""
|
26
|
-
typer.echo("Loading portal gun")
|
27
|
-
|
1
|
+
from .cli import app
|
28
2
|
|
29
3
|
if __name__ == "__main__":
|
30
4
|
app()
|
ddeutil/workflow/cli.py
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Annotated, Any
|
3
|
+
|
4
|
+
import typer
|
5
|
+
import uvicorn
|
6
|
+
|
7
|
+
from .__about__ import __version__
|
8
|
+
from .api import app as fastapp
|
9
|
+
from .api.logs import LOGGING_CONFIG
|
10
|
+
|
11
|
+
app = typer.Typer(
|
12
|
+
pretty_exceptions_enable=True,
|
13
|
+
)
|
14
|
+
|
15
|
+
|
16
|
+
@app.callback()
|
17
|
+
def callback():
|
18
|
+
"""
|
19
|
+
Awesome Portal Gun
|
20
|
+
"""
|
21
|
+
typer.echo("Start call from callback function")
|
22
|
+
|
23
|
+
|
24
|
+
@app.command()
|
25
|
+
def version():
|
26
|
+
"""Get the ddeutil-workflow package version."""
|
27
|
+
typer.echo(__version__)
|
28
|
+
|
29
|
+
|
30
|
+
@app.command()
|
31
|
+
def job(
|
32
|
+
params: Annotated[str, typer.Option(help="A job execute parameters")],
|
33
|
+
):
|
34
|
+
"""Job execution on the local.
|
35
|
+
|
36
|
+
Example:
|
37
|
+
... workflow-cli job --params "{\"test\": 1}"
|
38
|
+
"""
|
39
|
+
try:
|
40
|
+
params_dict: dict[str, Any] = json.loads(params)
|
41
|
+
except json.JSONDecodeError as e:
|
42
|
+
raise ValueError(f"params does not support format: {params!r}.") from e
|
43
|
+
typer.echo(f"Job params: {params_dict}")
|
44
|
+
|
45
|
+
|
46
|
+
@app.command()
|
47
|
+
def api(
|
48
|
+
host: Annotated[str, typer.Option(help="A host url.")] = "0.0.0.0",
|
49
|
+
port: Annotated[int, typer.Option(help="A port url.")] = 80,
|
50
|
+
debug: Annotated[bool, typer.Option(help="A debug mode flag")] = True,
|
51
|
+
):
|
52
|
+
"""
|
53
|
+
Provision API application from the FastAPI.
|
54
|
+
"""
|
55
|
+
|
56
|
+
uvicorn.run(
|
57
|
+
fastapp,
|
58
|
+
host=host,
|
59
|
+
port=port,
|
60
|
+
log_config=uvicorn.config.LOGGING_CONFIG | LOGGING_CONFIG,
|
61
|
+
log_level=("DEBUG" if debug else "INFO"),
|
62
|
+
)
|
63
|
+
|
64
|
+
|
65
|
+
if __name__ == "__main__":
|
66
|
+
app()
|
ddeutil/workflow/conf.py
CHANGED
@@ -18,7 +18,7 @@ from zoneinfo import ZoneInfo
|
|
18
18
|
from ddeutil.core import str2bool
|
19
19
|
from ddeutil.io import YamlFlResolve, search_env_replace
|
20
20
|
from ddeutil.io.paths import glob_files, is_ignored, read_ignore
|
21
|
-
from pydantic import SecretStr
|
21
|
+
from pydantic import SecretStr
|
22
22
|
|
23
23
|
from .__types import DictData
|
24
24
|
|
@@ -470,7 +470,3 @@ class CallerSecret(SecretStr): # pragma: no cov
|
|
470
470
|
:rtype: str
|
471
471
|
"""
|
472
472
|
return pass_env(super().get_secret_value())
|
473
|
-
|
474
|
-
|
475
|
-
# NOTE: Define the caller secret type for use it directly in the caller func.
|
476
|
-
CallerSecretType = TypeAdapter(CallerSecret)
|
ddeutil/workflow/errors.py
CHANGED
ddeutil/workflow/job.py
CHANGED
@@ -39,7 +39,6 @@ from pydantic import BaseModel, Discriminator, Field, SecretStr, Tag
|
|
39
39
|
from pydantic.functional_validators import field_validator, model_validator
|
40
40
|
from typing_extensions import Self
|
41
41
|
|
42
|
-
from . import JobSkipError
|
43
42
|
from .__types import DictData, DictStr, Matrix, StrOrNone
|
44
43
|
from .errors import JobCancelError, JobError, to_dict
|
45
44
|
from .result import (
|
@@ -774,7 +773,7 @@ def local_execute_strategy(
|
|
774
773
|
*,
|
775
774
|
result: Optional[Result] = None,
|
776
775
|
event: Optional[Event] = None,
|
777
|
-
) -> Result:
|
776
|
+
) -> tuple[Status, Result]:
|
778
777
|
"""Local strategy execution with passing dynamic parameters from the
|
779
778
|
job execution and strategy matrix.
|
780
779
|
|
@@ -799,7 +798,7 @@ def local_execute_strategy(
|
|
799
798
|
:raise JobError: If stage execution raise any error as `StageError`.
|
800
799
|
:raise JobError: If the result from execution has `FAILED` status.
|
801
800
|
|
802
|
-
:rtype: Result
|
801
|
+
:rtype: tuple[Status, Result]
|
803
802
|
"""
|
804
803
|
result: Result = result or Result(
|
805
804
|
run_id=gen_id(job.id or "EMPTY", unique=True),
|
@@ -899,9 +898,7 @@ def local_execute_strategy(
|
|
899
898
|
},
|
900
899
|
},
|
901
900
|
)
|
902
|
-
|
903
|
-
raise JobSkipError("All stage was skipped.")
|
904
|
-
return result
|
901
|
+
return status, result
|
905
902
|
|
906
903
|
|
907
904
|
def local_execute(
|
@@ -1017,14 +1014,13 @@ def local_execute(
|
|
1017
1014
|
|
1018
1015
|
for i, future in enumerate(done, start=0):
|
1019
1016
|
try:
|
1020
|
-
statuses[i] = future.result()
|
1017
|
+
statuses[i], _ = future.result()
|
1021
1018
|
except JobError as e:
|
1022
1019
|
statuses[i] = get_status_from_error(e)
|
1023
1020
|
result.trace.error(
|
1024
|
-
f"[JOB]: {ls}
|
1021
|
+
f"[JOB]: {ls} Handler:||{e.__class__.__name__}: {e}"
|
1025
1022
|
)
|
1026
|
-
|
1027
|
-
mark_errors(context, e)
|
1023
|
+
mark_errors(context, e)
|
1028
1024
|
except CancelledError:
|
1029
1025
|
pass
|
1030
1026
|
|
ddeutil/workflow/logs.py
CHANGED
@@ -848,7 +848,7 @@ class FileAudit(BaseAudit):
|
|
848
848
|
"audit_path", extras=self.extras
|
849
849
|
) / self.filename_fmt.format(name=self.name, release=self.release)
|
850
850
|
|
851
|
-
def save(self, excluded: Optional[list[str]]) -> Self:
|
851
|
+
def save(self, excluded: Optional[list[str]] = None) -> Self:
|
852
852
|
"""Save logging data that receive a context data from a workflow
|
853
853
|
execution result.
|
854
854
|
|
ddeutil/workflow/result.py
CHANGED
@@ -28,7 +28,6 @@ from . import (
|
|
28
28
|
StageSkipError,
|
29
29
|
WorkflowCancelError,
|
30
30
|
WorkflowError,
|
31
|
-
WorkflowSkipError,
|
32
31
|
)
|
33
32
|
from .__types import DictData
|
34
33
|
from .conf import dynamic
|
@@ -106,13 +105,12 @@ def get_status_from_error(
|
|
106
105
|
JobSkipError,
|
107
106
|
WorkflowError,
|
108
107
|
WorkflowCancelError,
|
109
|
-
WorkflowSkipError,
|
110
108
|
Exception,
|
111
109
|
BaseException,
|
112
110
|
]
|
113
111
|
) -> Status:
|
114
112
|
"""Get the Status from the error object."""
|
115
|
-
if isinstance(error, (StageSkipError, JobSkipError
|
113
|
+
if isinstance(error, (StageSkipError, JobSkipError)):
|
116
114
|
return SKIP
|
117
115
|
elif isinstance(
|
118
116
|
error, (StageCancelError, JobCancelError, WorkflowCancelError)
|
ddeutil/workflow/stages.py
CHANGED
@@ -1610,6 +1610,9 @@ class ParallelStage(BaseNestedStage):
|
|
1610
1610
|
(Default is None)
|
1611
1611
|
|
1612
1612
|
:raise StageCancelError: If event was set.
|
1613
|
+
:raise StageCancelError: If result from a nested-stage return canceled
|
1614
|
+
status.
|
1615
|
+
:raise StageError: If result from a nested-stage return failed status.
|
1613
1616
|
|
1614
1617
|
:rtype: tuple[Status, Result]
|
1615
1618
|
"""
|
@@ -1854,9 +1857,11 @@ class ForEachStage(BaseNestedStage):
|
|
1854
1857
|
result.trace.debug(f"[STAGE]: Execute Item: {item!r}")
|
1855
1858
|
key: StrOrInt = index if self.use_index_as_key else item
|
1856
1859
|
|
1860
|
+
# NOTE: Create nested-context data from the passing context.
|
1857
1861
|
context: DictData = copy.deepcopy(params)
|
1858
1862
|
context.update({"item": item, "loop": index})
|
1859
1863
|
nestet_context: DictData = {"item": item, "stages": {}}
|
1864
|
+
|
1860
1865
|
total_stage: int = len(self.stages)
|
1861
1866
|
skips: list[bool] = [False] * total_stage
|
1862
1867
|
for i, stage in enumerate(self.stages, start=0):
|
@@ -1959,6 +1964,10 @@ class ForEachStage(BaseNestedStage):
|
|
1959
1964
|
) -> Result:
|
1960
1965
|
"""Execute the stages that pass each item form the foreach field.
|
1961
1966
|
|
1967
|
+
This stage will use fail-fast strategy if it was set concurrency
|
1968
|
+
value more than 1. It will cancel all nested-stage execution when it has
|
1969
|
+
any item loop raise failed or canceled error.
|
1970
|
+
|
1962
1971
|
:param params: (DictData) A parameter data.
|
1963
1972
|
:param result: (Result) A Result instance for return context and status.
|
1964
1973
|
:param event: (Event) An Event manager instance that use to cancel this
|
ddeutil/workflow/workflow.py
CHANGED
@@ -379,6 +379,7 @@ class Workflow(BaseModel):
|
|
379
379
|
override_log_name: Optional[str] = None,
|
380
380
|
result: Optional[Result] = None,
|
381
381
|
timeout: int = 600,
|
382
|
+
excluded: Optional[list[str]] = None,
|
382
383
|
) -> Result:
|
383
384
|
"""Release the workflow which is executes workflow with writing audit
|
384
385
|
log tracking. The method is overriding parameter with the release
|
@@ -405,6 +406,8 @@ class Workflow(BaseModel):
|
|
405
406
|
:param result: (Result) A result object for keeping context and status
|
406
407
|
data.
|
407
408
|
:param timeout: (int) A workflow execution time out in second unit.
|
409
|
+
:param excluded: (list[str]) A list of key that want to exclude from
|
410
|
+
audit data.
|
408
411
|
|
409
412
|
:rtype: Result
|
410
413
|
"""
|
@@ -453,7 +456,7 @@ class Workflow(BaseModel):
|
|
453
456
|
run_id=result.run_id,
|
454
457
|
execution_time=result.alive_time(),
|
455
458
|
extras=self.extras,
|
456
|
-
).save(excluded=
|
459
|
+
).save(excluded=excluded)
|
457
460
|
)
|
458
461
|
return result.catch(
|
459
462
|
status=rs.status,
|
@@ -586,6 +589,19 @@ class Workflow(BaseModel):
|
|
586
589
|
|-name: ...
|
587
590
|
╰-message: ...
|
588
591
|
|
592
|
+
--> Ok --> Result
|
593
|
+
|-status: FAILED
|
594
|
+
╰-context:
|
595
|
+
╰-errors:
|
596
|
+
|-name: ...
|
597
|
+
╰-message: ...
|
598
|
+
|
599
|
+
--> Ok --> Result
|
600
|
+
╰-status: SKIP
|
601
|
+
|
602
|
+
--> Ok --> Result
|
603
|
+
╰-status: SUCCESS
|
604
|
+
|
589
605
|
:param params: A parameter data that will parameterize before execution.
|
590
606
|
:param run_id: (Optional[str]) A workflow running ID.
|
591
607
|
:param parent_run_id: (Optional[str]) A parent workflow running ID.
|
@@ -725,25 +741,23 @@ class Workflow(BaseModel):
|
|
725
741
|
|
726
742
|
if not_timeout_flag:
|
727
743
|
job_queue.join()
|
728
|
-
|
729
|
-
for i, future in enumerate(as_completed(futures), start=0):
|
744
|
+
for total, future in enumerate(as_completed(futures), start=0):
|
730
745
|
try:
|
731
|
-
statuses[
|
746
|
+
statuses[total], _ = future.result()
|
732
747
|
except WorkflowError as e:
|
733
|
-
statuses[
|
734
|
-
total_future += 1
|
748
|
+
statuses[total] = get_status_from_error(e)
|
735
749
|
|
736
750
|
# NOTE: Update skipped status from the job trigger.
|
737
751
|
for i in range(skip_count):
|
738
|
-
statuses[
|
752
|
+
statuses[total + 1 + i] = SKIP
|
739
753
|
|
740
754
|
# NOTE: Update status from none-parallel job execution.
|
741
755
|
for i, s in enumerate(sequence_statuses, start=0):
|
742
|
-
statuses[
|
756
|
+
statuses[total + 1 + skip_count + i] = s
|
743
757
|
|
744
|
-
|
745
|
-
|
746
|
-
|
758
|
+
return result.catch(
|
759
|
+
status=validate_statuses(statuses), context=context
|
760
|
+
)
|
747
761
|
|
748
762
|
event.set()
|
749
763
|
for future in futures:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ddeutil-workflow
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.66
|
4
4
|
Summary: Lightweight workflow orchestration
|
5
5
|
Author-email: ddeutils <korawich.anu@gmail.com>
|
6
6
|
License: MIT
|
@@ -27,7 +27,7 @@ Requires-Dist: ddeutil-io[toml,yaml]>=0.2.14
|
|
27
27
|
Requires-Dist: pydantic==2.11.4
|
28
28
|
Requires-Dist: pydantic-extra-types==2.10.4
|
29
29
|
Requires-Dist: python-dotenv==1.1.0
|
30
|
-
Requires-Dist:
|
30
|
+
Requires-Dist: typer==0.15.4
|
31
31
|
Provides-Extra: all
|
32
32
|
Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "all"
|
33
33
|
Requires-Dist: uvicorn; extra == "all"
|
@@ -1,28 +1,29 @@
|
|
1
|
-
ddeutil/workflow/__about__.py,sha256=
|
1
|
+
ddeutil/workflow/__about__.py,sha256=nkpvuhIACvuey4B8paloyuDA6U0uRDZqZ83SUGdlIt4,28
|
2
2
|
ddeutil/workflow/__cron.py,sha256=BOKQcreiex0SAigrK1gnLxpvOeF3aca_rQwyz9Kfve4,28751
|
3
3
|
ddeutil/workflow/__init__.py,sha256=JfFZlPRDgR2J0rb0SRejt1OSrOrD3GGv9Um14z8MMfs,901
|
4
|
-
ddeutil/workflow/__main__.py,sha256=
|
4
|
+
ddeutil/workflow/__main__.py,sha256=Qd-f8z2Q2vpiEP2x6PBFsJrpACWDVxFKQk820MhFmHo,59
|
5
5
|
ddeutil/workflow/__types.py,sha256=uNfoRbVmNK5O37UUMVnqcmoghD9oMS1q9fXC0APnjSI,4584
|
6
|
-
ddeutil/workflow/
|
7
|
-
ddeutil/workflow/
|
6
|
+
ddeutil/workflow/cli.py,sha256=__ydzTs_e9C1tHqhnyp__0iKelZJRUwZn8VsyVYw5yo,1458
|
7
|
+
ddeutil/workflow/conf.py,sha256=w1WDWZDCvRVDSz2HnJxeqySzpYWSubJZjTVjXO9imK0,14669
|
8
|
+
ddeutil/workflow/errors.py,sha256=vh_AbLegPbb61TvfwHmjSz0t2SH09RQuGHCKmoyNjn0,2934
|
8
9
|
ddeutil/workflow/event.py,sha256=S2eJAZZx_V5TuQ0l417hFVCtjWXnfNPZBgSCICzxQ48,11041
|
9
|
-
ddeutil/workflow/job.py,sha256=
|
10
|
-
ddeutil/workflow/logs.py,sha256=
|
10
|
+
ddeutil/workflow/job.py,sha256=qcbKSOa39256nfJHL0vKJsHrelcRujX5KET2IEGS8dw,38995
|
11
|
+
ddeutil/workflow/logs.py,sha256=4rL8TsRJsYVqyPfLjFW5bSoWtRwUgwmaRONu7nnVxQ8,31374
|
11
12
|
ddeutil/workflow/params.py,sha256=Pco3DyjptC5Jkx53dhLL9xlIQdJvNAZs4FLzMUfXpbQ,12402
|
12
|
-
ddeutil/workflow/result.py,sha256=
|
13
|
+
ddeutil/workflow/result.py,sha256=CzSjK9EQtSS0a1z8b6oBW4gg8t0uVbZ_Ppx4Ckvcork,7786
|
13
14
|
ddeutil/workflow/reusables.py,sha256=jPrOCbxagqRvRFGXJzIyDa1wKV5AZ4crZyJ10cldQP0,21620
|
14
|
-
ddeutil/workflow/stages.py,sha256=
|
15
|
+
ddeutil/workflow/stages.py,sha256=s6Jc4A6ApXT40JMrnVf7n7yHG_mmjfHJIOU_niMW5hI,102555
|
15
16
|
ddeutil/workflow/utils.py,sha256=slhBbsBNl0yaSk9EOiCK6UL-o7smgHVsLT7svRqAWXU,10436
|
16
|
-
ddeutil/workflow/workflow.py,sha256=
|
17
|
+
ddeutil/workflow/workflow.py,sha256=AcSGqsH1N4LqWhYIcCPy9CoV_AGlXUrBgjpl-gniv6g,28267
|
17
18
|
ddeutil/workflow/api/__init__.py,sha256=0UIilYwW29RL6HrCRHACSWvnATJVLSJzXiCMny0bHQk,2627
|
18
19
|
ddeutil/workflow/api/logs.py,sha256=NMTnOnsBrDB5129329xF2myLdrb-z9k1MQrmrP7qXJw,1818
|
19
20
|
ddeutil/workflow/api/routes/__init__.py,sha256=jC1pM7q4_eo45IyO3hQbbe6RnL9B8ibRq_K6aCMP6Ag,434
|
20
21
|
ddeutil/workflow/api/routes/job.py,sha256=32TkNm7QY9gt6fxIqEPjDqPgc8XqDiMPjUb7disSrCw,2143
|
21
22
|
ddeutil/workflow/api/routes/logs.py,sha256=QJH8IF102897WLfCJ29-1g15wl29M9Yq6omroZfbahs,5305
|
22
23
|
ddeutil/workflow/api/routes/workflows.py,sha256=Gmg3e-K5rfi95pbRtWI_aIr5C089sIde_vefZVvh3U0,4420
|
23
|
-
ddeutil_workflow-0.0.
|
24
|
-
ddeutil_workflow-0.0.
|
25
|
-
ddeutil_workflow-0.0.
|
26
|
-
ddeutil_workflow-0.0.
|
27
|
-
ddeutil_workflow-0.0.
|
28
|
-
ddeutil_workflow-0.0.
|
24
|
+
ddeutil_workflow-0.0.66.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
|
25
|
+
ddeutil_workflow-0.0.66.dist-info/METADATA,sha256=LhCFhlwk3N5wbnylmi7zi3sAJqwxkRVlrRThlNB4Cj0,16676
|
26
|
+
ddeutil_workflow-0.0.66.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
27
|
+
ddeutil_workflow-0.0.66.dist-info/entry_points.txt,sha256=qDTpPSauL0ciO6T4iSVt8bJeYrVEkkoEEw_RlGx6Kgk,63
|
28
|
+
ddeutil_workflow-0.0.66.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
|
29
|
+
ddeutil_workflow-0.0.66.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|