ddeutil-workflow 0.0.13__tar.gz → 0.0.14__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.13 → ddeutil_workflow-0.0.14}/PKG-INFO +6 -4
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/README.md +2 -1
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/pyproject.toml +3 -3
- ddeutil_workflow-0.0.14/src/ddeutil/workflow/__about__.py +1 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/__init__.py +4 -1
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/__types.py +24 -8
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/api.py +2 -2
- ddeutil_workflow-0.0.14/src/ddeutil/workflow/conf.py +41 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/cron.py +19 -12
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/job.py +189 -153
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/log.py +28 -14
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/scheduler.py +233 -112
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/stage.py +66 -33
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/utils.py +106 -40
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil_workflow.egg-info/PKG-INFO +6 -4
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil_workflow.egg-info/SOURCES.txt +5 -3
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil_workflow.egg-info/requires.txt +3 -2
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test__regex.py +24 -5
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_job_py.py +12 -15
- ddeutil_workflow-0.0.14/tests/test_job_strategy.py +68 -0
- ddeutil_workflow-0.0.14/tests/test_pipeline_run.py +127 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_scheduler.py +14 -12
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_stage_py.py +15 -17
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_utils.py +1 -1
- ddeutil_workflow-0.0.14/tests/test_utils_result.py +82 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_utils_template.py +15 -7
- ddeutil_workflow-0.0.13/src/ddeutil/workflow/__about__.py +0 -1
- ddeutil_workflow-0.0.13/tests/test_pipeline_run.py +0 -22
- ddeutil_workflow-0.0.13/tests/test_utils_result.py +0 -32
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/LICENSE +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/setup.cfg +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/cli.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/exceptions.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/on.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/repeat.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil/workflow/route.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test__conf_exist.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_conf.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_cron.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_job.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_log.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_on.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_params.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_pipeline_if.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_pipeline_matrix.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_pipeline_on.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_pipeline_params.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_pipeline_run_raise.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_pipeline_task.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_poke.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_stage.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_stage_bash.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_stage_condition.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_stage_hook.py +0 -0
- {ddeutil_workflow-0.0.13 → ddeutil_workflow-0.0.14}/tests/test_stage_trigger.py +0 -0
- /ddeutil_workflow-0.0.13/tests/test_pipeline.py → /ddeutil_workflow-0.0.14/tests/test_workflow.py +0 -0
- /ddeutil_workflow-0.0.13/tests/test_pipeline_desc.py → /ddeutil_workflow-0.0.14/tests/test_workflow_desc.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ddeutil-workflow
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.14
|
4
4
|
Summary: Lightweight workflow orchestration with less dependencies
|
5
5
|
Author-email: ddeutils <korawich.anu@gmail.com>
|
6
6
|
License: MIT
|
@@ -22,12 +22,13 @@ Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Requires-Python: >=3.9.13
|
23
23
|
Description-Content-Type: text/markdown
|
24
24
|
License-File: LICENSE
|
25
|
-
Requires-Dist: ddeutil
|
25
|
+
Requires-Dist: ddeutil>=0.4.0
|
26
|
+
Requires-Dist: ddeutil-io>=0.1.13
|
26
27
|
Requires-Dist: python-dotenv==1.0.1
|
27
28
|
Requires-Dist: typer<1.0.0,==0.12.5
|
28
29
|
Requires-Dist: schedule<2.0.0,==1.2.2
|
29
30
|
Provides-Extra: api
|
30
|
-
Requires-Dist: fastapi<1.0.0,>=0.
|
31
|
+
Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "api"
|
31
32
|
|
32
33
|
# Workflow
|
33
34
|
|
@@ -62,7 +63,7 @@ configuration. It called **Metadata Driven Data Workflow**.
|
|
62
63
|
|
63
64
|
> [!NOTE]
|
64
65
|
> _Disclaimer_: I inspire the dynamic statement from the [**GitHub Action**](https://github.com/features/actions)
|
65
|
-
> `.yml` files and all of config file from several data orchestration framework
|
66
|
+
> with `.yml` files and all of config file from several data orchestration framework
|
66
67
|
> tools from my experience on Data Engineer. :grimacing:
|
67
68
|
>
|
68
69
|
> Other workflow that I interest on them and pick some interested feature to this
|
@@ -92,6 +93,7 @@ this package with application add-ons, you should add `app` in installation;
|
|
92
93
|
> | ddeutil-workflow:python3.10 | `3.10` | :x: |
|
93
94
|
> | ddeutil-workflow:python3.11 | `3.11` | :x: |
|
94
95
|
> | ddeutil-workflow:python3.12 | `3.12` | :x: |
|
96
|
+
> | ddeutil-workflow:python3.12 | `3.13` | :x: |
|
95
97
|
|
96
98
|
## :beers: Usage
|
97
99
|
|
@@ -31,7 +31,7 @@ configuration. It called **Metadata Driven Data Workflow**.
|
|
31
31
|
|
32
32
|
> [!NOTE]
|
33
33
|
> _Disclaimer_: I inspire the dynamic statement from the [**GitHub Action**](https://github.com/features/actions)
|
34
|
-
> `.yml` files and all of config file from several data orchestration framework
|
34
|
+
> with `.yml` files and all of config file from several data orchestration framework
|
35
35
|
> tools from my experience on Data Engineer. :grimacing:
|
36
36
|
>
|
37
37
|
> Other workflow that I interest on them and pick some interested feature to this
|
@@ -61,6 +61,7 @@ this package with application add-ons, you should add `app` in installation;
|
|
61
61
|
> | ddeutil-workflow:python3.10 | `3.10` | :x: |
|
62
62
|
> | ddeutil-workflow:python3.11 | `3.11` | :x: |
|
63
63
|
> | ddeutil-workflow:python3.12 | `3.12` | :x: |
|
64
|
+
> | ddeutil-workflow:python3.12 | `3.13` | :x: |
|
64
65
|
|
65
66
|
## :beers: Usage
|
66
67
|
|
@@ -26,7 +26,8 @@ classifiers = [
|
|
26
26
|
]
|
27
27
|
requires-python = ">=3.9.13"
|
28
28
|
dependencies = [
|
29
|
-
"ddeutil
|
29
|
+
"ddeutil>=0.4.0",
|
30
|
+
"ddeutil-io>=0.1.13",
|
30
31
|
"python-dotenv==1.0.1",
|
31
32
|
"typer==0.12.5,<1.0.0",
|
32
33
|
"schedule==1.2.2,<2.0.0",
|
@@ -35,7 +36,7 @@ dynamic = ["version"]
|
|
35
36
|
|
36
37
|
[project.optional-dependencies]
|
37
38
|
api = [
|
38
|
-
"fastapi>=0.
|
39
|
+
"fastapi>=0.115.0,<1.0.0",
|
39
40
|
]
|
40
41
|
|
41
42
|
[project.urls]
|
@@ -62,7 +63,6 @@ concurrency = ["thread", "multiprocessing"]
|
|
62
63
|
source = ["ddeutil.workflow", "tests"]
|
63
64
|
omit = [
|
64
65
|
"src/ddeutil/workflow/__about__.py",
|
65
|
-
"scripts/",
|
66
66
|
# Omit this files because it does not ready to production.
|
67
67
|
"src/ddeutil/workflow/api.py",
|
68
68
|
"src/ddeutil/workflow/cli.py",
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__: str = "0.0.14"
|
@@ -12,7 +12,10 @@ from .exceptions import (
|
|
12
12
|
)
|
13
13
|
from .job import Job, Strategy
|
14
14
|
from .on import On, interval2crontab
|
15
|
-
from .scheduler import
|
15
|
+
from .scheduler import (
|
16
|
+
Schedule,
|
17
|
+
Workflow,
|
18
|
+
)
|
16
19
|
from .stage import Stage, handler_result
|
17
20
|
from .utils import (
|
18
21
|
Param,
|
@@ -24,20 +24,32 @@ MatrixExclude = list[dict[str, Union[str, int]]]
|
|
24
24
|
|
25
25
|
|
26
26
|
class Re:
|
27
|
-
"""Regular expression config."""
|
27
|
+
"""Regular expression config for this package."""
|
28
28
|
|
29
|
-
# NOTE:
|
30
|
-
#
|
29
|
+
# NOTE:
|
30
|
+
# Regular expression:
|
31
|
+
# - Version 1:
|
32
|
+
# \${{\s*(?P<caller>[a-zA-Z0-9_.\s'\"\[\]\(\)\-\{}]+?)\s*(?P<post_filters>(?:\|\s*(?:[a-zA-Z0-9_]{3,}[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]+)\s*)*)}}
|
33
|
+
# - Version 2 (2024-09-30):
|
34
|
+
# \${{\s*(?P<caller>(?P<caller_prefix>[a-zA-Z_-]+\.)*(?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+))\s*(?P<post_filters>(?:\|\s*(?:[a-zA-Z0-9_]{3,}[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]+)\s*)*)}}
|
35
|
+
#
|
36
|
+
# Examples:
|
37
|
+
# - ${{ params.asat_dt }}
|
38
|
+
# - ${{ params.source.table }}
|
39
|
+
#
|
31
40
|
__re_caller: str = r"""
|
32
41
|
\$
|
33
42
|
{{
|
34
43
|
\s*
|
35
44
|
(?P<caller>
|
36
|
-
[a-zA-
|
37
|
-
|
45
|
+
(?P<caller_prefix>[a-zA-Z_-]+\.)*
|
46
|
+
(?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+)
|
47
|
+
)
|
48
|
+
\s*
|
38
49
|
(?P<post_filters>
|
39
50
|
(?:
|
40
|
-
|
51
|
+
\|
|
52
|
+
\s*
|
41
53
|
(?:[a-zA-Z0-9_]{3,}[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]*)
|
42
54
|
\s*
|
43
55
|
)*
|
@@ -48,8 +60,12 @@ class Re:
|
|
48
60
|
__re_caller, MULTILINE | IGNORECASE | UNICODE | VERBOSE
|
49
61
|
)
|
50
62
|
|
51
|
-
# NOTE:
|
52
|
-
#
|
63
|
+
# NOTE:
|
64
|
+
# Regular expression:
|
65
|
+
# ^(?P<path>[^/@]+)/(?P<func>[^@]+)@(?P<tag>.+)$
|
66
|
+
#
|
67
|
+
# Examples:
|
68
|
+
# - tasks/function@dummy
|
53
69
|
__re_task_fmt: str = r"""
|
54
70
|
^
|
55
71
|
(?P<path>[^/@]+)
|
@@ -25,7 +25,7 @@ from pydantic import BaseModel
|
|
25
25
|
from .__about__ import __version__
|
26
26
|
from .log import get_logger
|
27
27
|
from .repeat import repeat_at, repeat_every
|
28
|
-
from .scheduler import
|
28
|
+
from .scheduler import WorkflowTaskData
|
29
29
|
|
30
30
|
load_dotenv()
|
31
31
|
logger = get_logger("ddeutil.workflow")
|
@@ -36,7 +36,7 @@ class State(TypedDict):
|
|
36
36
|
upper_result: dict[str, str]
|
37
37
|
scheduler: list[str]
|
38
38
|
workflow_threads: dict[str, Thread]
|
39
|
-
workflow_tasks: list[
|
39
|
+
workflow_tasks: list[WorkflowTaskData]
|
40
40
|
workflow_queue: dict[str, list[datetime]]
|
41
41
|
workflow_running: dict[str, list[datetime]]
|
42
42
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# ------------------------------------------------------------------------------
|
2
|
+
# Copyright (c) 2022 Korawich Anuttra. All rights reserved.
|
3
|
+
# Licensed under the MIT License. See LICENSE in the project root for
|
4
|
+
# license information.
|
5
|
+
# ------------------------------------------------------------------------------
|
6
|
+
from __future__ import annotations
|
7
|
+
|
8
|
+
import os
|
9
|
+
from zoneinfo import ZoneInfo
|
10
|
+
|
11
|
+
from ddeutil.core import str2bool
|
12
|
+
from dotenv import load_dotenv
|
13
|
+
|
14
|
+
load_dotenv()
|
15
|
+
env = os.getenv
|
16
|
+
|
17
|
+
|
18
|
+
class Config:
|
19
|
+
# NOTE: Core
|
20
|
+
tz: ZoneInfo = ZoneInfo(env("WORKFLOW_CORE_TIMEZONE", "UTC"))
|
21
|
+
|
22
|
+
# NOTE: Stage
|
23
|
+
stage_raise_error: bool = str2bool(
|
24
|
+
env("WORKFLOW_CORE_STAGE_RAISE_ERROR", "true")
|
25
|
+
)
|
26
|
+
stage_default_id: bool = str2bool(
|
27
|
+
env("WORKFLOW_CORE_STAGE_DEFAULT_ID", "false")
|
28
|
+
)
|
29
|
+
|
30
|
+
# NOTE: Workflow
|
31
|
+
max_job_parallel: int = int(env("WORKFLOW_CORE_MAX_JOB_PARALLEL", "2"))
|
32
|
+
|
33
|
+
def __init__(self):
|
34
|
+
if self.max_job_parallel < 0:
|
35
|
+
raise ValueError(
|
36
|
+
f"MAX_JOB_PARALLEL should more than 0 but got "
|
37
|
+
f"{self.max_job_parallel}."
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
config = Config()
|
@@ -14,7 +14,7 @@ from typing import ClassVar, Optional, Union
|
|
14
14
|
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
15
15
|
|
16
16
|
from ddeutil.core import (
|
17
|
-
|
17
|
+
checker,
|
18
18
|
isinstance_check,
|
19
19
|
must_split,
|
20
20
|
)
|
@@ -38,16 +38,21 @@ class CronYearLimit(Exception): ...
|
|
38
38
|
|
39
39
|
|
40
40
|
def str2cron(value: str) -> str:
|
41
|
-
"""Convert Special String to Crontab.
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
41
|
+
"""Convert Special String with the @ prefix to Crontab value.
|
42
|
+
|
43
|
+
:param value: A string value that want to convert to cron value.
|
44
|
+
:rtype: str
|
45
|
+
|
46
|
+
Table:
|
47
|
+
|
48
|
+
@reboot Run once, at system startup
|
49
|
+
@yearly Run once every year, "0 0 1 1 *"
|
50
|
+
@annually (same as @yearly)
|
51
|
+
@monthly Run once every month, "0 0 1 * *"
|
52
|
+
@weekly Run once every week, "0 0 * * 0"
|
53
|
+
@daily Run once each day, "0 0 * * *"
|
54
|
+
@midnight (same as @daily)
|
55
|
+
@hourly Run once an hour, "0 * * * *"
|
51
56
|
"""
|
52
57
|
mapping_spacial_str = {
|
53
58
|
"@reboot": "",
|
@@ -321,7 +326,9 @@ class CronPart:
|
|
321
326
|
self._parse_range(value_range)
|
322
327
|
)
|
323
328
|
|
324
|
-
if (
|
329
|
+
if (
|
330
|
+
value_step and not checker.is_int(value_step)
|
331
|
+
) or value_step == "":
|
325
332
|
raise ValueError(
|
326
333
|
f"Invalid interval step value {value_step!r} for "
|
327
334
|
f"{self.unit.name!r}"
|