ddeutil-workflow 0.0.26.post0__tar.gz → 0.0.27__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.
Files changed (64) hide show
  1. {ddeutil_workflow-0.0.26.post0/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.27}/PKG-INFO +24 -24
  2. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/README.md +23 -23
  3. ddeutil_workflow-0.0.27/src/ddeutil/workflow/__about__.py +1 -0
  4. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/__init__.py +24 -16
  5. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/conf.py +169 -105
  6. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/exceptions.py +0 -3
  7. ddeutil_workflow-0.0.27/src/ddeutil/workflow/hook.py +153 -0
  8. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/job.py +1 -1
  9. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/scheduler.py +2 -2
  10. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/stage.py +3 -55
  11. ddeutil_workflow-0.0.26.post0/src/ddeutil/workflow/utils.py → ddeutil_workflow-0.0.27/src/ddeutil/workflow/templates.py +20 -283
  12. ddeutil_workflow-0.0.27/src/ddeutil/workflow/utils.py +209 -0
  13. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/workflow.py +6 -7
  14. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27/src/ddeutil_workflow.egg-info}/PKG-INFO +24 -24
  15. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil_workflow.egg-info/SOURCES.txt +5 -3
  16. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_conf.py +19 -8
  17. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_conf_log.py +6 -1
  18. ddeutil_workflow-0.0.26.post0/tests/test_utils_tag.py → ddeutil_workflow-0.0.27/tests/test_hook_tag.py +10 -3
  19. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_job.py +1 -1
  20. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_schedule_control.py +5 -5
  21. ddeutil_workflow-0.0.26.post0/tests/test_utils_template.py → ddeutil_workflow-0.0.27/tests/test_templates.py +1 -1
  22. ddeutil_workflow-0.0.26.post0/tests/test_utils_filter.py → ddeutil_workflow-0.0.27/tests/test_templates_filter.py +18 -5
  23. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_utils.py +14 -3
  24. ddeutil_workflow-0.0.26.post0/src/ddeutil/workflow/__about__.py +0 -1
  25. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/LICENSE +0 -0
  26. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/pyproject.toml +0 -0
  27. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/setup.cfg +0 -0
  28. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/__cron.py +0 -0
  29. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/__types.py +0 -0
  30. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/api/__init__.py +0 -0
  31. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/api/api.py +0 -0
  32. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/api/repeat.py +0 -0
  33. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/api/route.py +0 -0
  34. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/cron.py +0 -0
  35. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/params.py +0 -0
  36. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil/workflow/result.py +0 -0
  37. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  38. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
  39. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  40. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test__cron.py +0 -0
  41. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test__regex.py +0 -0
  42. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_cron_on.py +0 -0
  43. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_job_exec_py.py +0 -0
  44. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_job_exec_strategy.py +0 -0
  45. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_job_strategy.py +0 -0
  46. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_params.py +0 -0
  47. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_result.py +0 -0
  48. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_schedule.py +0 -0
  49. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_schedule_tasks.py +0 -0
  50. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_stage.py +0 -0
  51. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_stage_exec_bash.py +0 -0
  52. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_stage_exec_hook.py +0 -0
  53. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_stage_exec_py.py +0 -0
  54. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_stage_exec_trigger.py +0 -0
  55. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow.py +0 -0
  56. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_exec.py +0 -0
  57. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_exec_hook.py +0 -0
  58. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_exec_needs.py +0 -0
  59. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_job_exec.py +0 -0
  60. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_poke.py +0 -0
  61. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_release.py +0 -0
  62. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_release_and_queue.py +0 -0
  63. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_schedule.py +0 -0
  64. {ddeutil_workflow-0.0.26.post0 → ddeutil_workflow-0.0.27}/tests/test_workflow_task.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.26.post0
3
+ Version: 0.0.27
4
4
  Summary: Lightweight workflow orchestration with less dependencies
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -168,29 +168,29 @@ The main configuration that use to dynamic changing with your propose of this
168
168
  application. If any configuration values do not set yet, it will use default value
169
169
  and do not raise any error to you.
170
170
 
171
- | Environment | Component | Default | Description | Remark |
172
- |:-------------------------------------------|:---------:|:-----------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|--------|
173
- | **WORKFLOW_ROOT_PATH** | Core | `.` | The root path of the workflow application. | |
174
- | **WORKFLOW_CORE_REGISTRY** | Core | `src,src.ddeutil.workflow,tests,tests.utils` | List of importable string for the hook stage. | |
175
- | **WORKFLOW_CORE_REGISTRY_FILTER** | Core | `src.ddeutil.workflow.utils,ddeutil.workflow.utils` | List of importable string for the filter template. | |
176
- | **WORKFLOW_CORE_PATH_CONF** | Core | `conf` | The config path that keep all template `.yaml` files. | |
177
- | **WORKFLOW_CORE_TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. | |
178
- | **WORKFLOW_CORE_STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. | |
179
- | **WORKFLOW_CORE_STAGE_RAISE_ERROR** | Core | `false` | A flag that all stage raise StageException from stage execution. | |
180
- | **WORKFLOW_CORE_JOB_DEFAULT_ID** | Core | `false` | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. | |
181
- | **WORKFLOW_CORE_JOB_RAISE_ERROR** | Core | `true` | A flag that all job raise JobException from job strategy execution. | |
182
- | **WORKFLOW_CORE_MAX_NUM_POKING** | Core | `4` | . | |
183
- | **WORKFLOW_CORE_MAX_JOB_PARALLEL** | Core | `2` | The maximum job number that able to run parallel in workflow executor. | |
184
- | **WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT** | Core | `600` | | |
185
- | **WORKFLOW_CORE_MAX_CRON_PER_WORKFLOW** | Core | `5` | | |
186
- | **WORKFLOW_CORE_MAX_QUEUE_COMPLETE_HIST** | Core | `16` | | |
187
- | **WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE** | Core | `true` | A flog that enable generating ID with `md5` algorithm. | |
188
- | **WORKFLOW_LOG_PATH** | Log | `./logs` | The log path of the workflow saving log. | |
189
- | **WORKFLOW_LOG_DEBUG_MODE** | Log | `true` | A flag that enable logging with debug level mode. | |
190
- | **WORKFLOW_LOG_ENABLE_WRITE** | Log | `true` | A flag that enable logging object saving log to its destination. | |
191
- | **WORKFLOW_APP_MAX_PROCESS** | Schedule | `2` | The maximum process worker number that run in scheduler app module. | |
192
- | **WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS** | Schedule | `100` | A schedule per process that run parallel. | |
193
- | **WORKFLOW_APP_STOP_BOUNDARY_DELTA** | Schedule | `'{"minutes": 5, "seconds": 20}'` | A time delta value that use to stop scheduler app in json string format. | |
171
+ | Environment | Component | Default | Description | Remark |
172
+ |:-------------------------------------------|:---------:|:----------------------------------|:-------------------------------------------------------------------------------------------------------------------|--------|
173
+ | **WORKFLOW_ROOT_PATH** | Core | `.` | The root path of the workflow application. | |
174
+ | **WORKFLOW_CORE_REGISTRY** | Core | `src` | List of importable string for the hook stage. | |
175
+ | **WORKFLOW_CORE_REGISTRY_FILTER** | Core | `ddeutil.workflow.utils` | List of importable string for the filter template. | |
176
+ | **WORKFLOW_CORE_PATH_CONF** | Core | `conf` | The config path that keep all template `.yaml` files. | |
177
+ | **WORKFLOW_CORE_TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. | |
178
+ | **WORKFLOW_CORE_STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. | |
179
+ | **WORKFLOW_CORE_STAGE_RAISE_ERROR** | Core | `false` | A flag that all stage raise StageException from stage execution. | |
180
+ | **WORKFLOW_CORE_JOB_DEFAULT_ID** | Core | `false` | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. | |
181
+ | **WORKFLOW_CORE_JOB_RAISE_ERROR** | Core | `true` | A flag that all job raise JobException from job strategy execution. | |
182
+ | **WORKFLOW_CORE_MAX_NUM_POKING** | Core | `4` | . | |
183
+ | **WORKFLOW_CORE_MAX_JOB_PARALLEL** | Core | `2` | The maximum job number that able to run parallel in workflow executor. | |
184
+ | **WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT** | Core | `600` | | |
185
+ | **WORKFLOW_CORE_MAX_CRON_PER_WORKFLOW** | Core | `5` | | |
186
+ | **WORKFLOW_CORE_MAX_QUEUE_COMPLETE_HIST** | Core | `16` | | |
187
+ | **WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE** | Core | `true` | A flog that enable generating ID with `md5` algorithm. | |
188
+ | **WORKFLOW_LOG_PATH** | Log | `./logs` | The log path of the workflow saving log. | |
189
+ | **WORKFLOW_LOG_DEBUG_MODE** | Log | `true` | A flag that enable logging with debug level mode. | |
190
+ | **WORKFLOW_LOG_ENABLE_WRITE** | Log | `true` | A flag that enable logging object saving log to its destination. | |
191
+ | **WORKFLOW_APP_MAX_PROCESS** | Schedule | `2` | The maximum process worker number that run in scheduler app module. | |
192
+ | **WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS** | Schedule | `100` | A schedule per process that run parallel. | |
193
+ | **WORKFLOW_APP_STOP_BOUNDARY_DELTA** | Schedule | `'{"minutes": 5, "seconds": 20}'` | A time delta value that use to stop scheduler app in json string format. | |
194
194
 
195
195
  **API Application**:
196
196
 
@@ -136,29 +136,29 @@ The main configuration that use to dynamic changing with your propose of this
136
136
  application. If any configuration values do not set yet, it will use default value
137
137
  and do not raise any error to you.
138
138
 
139
- | Environment | Component | Default | Description | Remark |
140
- |:-------------------------------------------|:---------:|:-----------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|--------|
141
- | **WORKFLOW_ROOT_PATH** | Core | `.` | The root path of the workflow application. | |
142
- | **WORKFLOW_CORE_REGISTRY** | Core | `src,src.ddeutil.workflow,tests,tests.utils` | List of importable string for the hook stage. | |
143
- | **WORKFLOW_CORE_REGISTRY_FILTER** | Core | `src.ddeutil.workflow.utils,ddeutil.workflow.utils` | List of importable string for the filter template. | |
144
- | **WORKFLOW_CORE_PATH_CONF** | Core | `conf` | The config path that keep all template `.yaml` files. | |
145
- | **WORKFLOW_CORE_TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. | |
146
- | **WORKFLOW_CORE_STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. | |
147
- | **WORKFLOW_CORE_STAGE_RAISE_ERROR** | Core | `false` | A flag that all stage raise StageException from stage execution. | |
148
- | **WORKFLOW_CORE_JOB_DEFAULT_ID** | Core | `false` | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. | |
149
- | **WORKFLOW_CORE_JOB_RAISE_ERROR** | Core | `true` | A flag that all job raise JobException from job strategy execution. | |
150
- | **WORKFLOW_CORE_MAX_NUM_POKING** | Core | `4` | . | |
151
- | **WORKFLOW_CORE_MAX_JOB_PARALLEL** | Core | `2` | The maximum job number that able to run parallel in workflow executor. | |
152
- | **WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT** | Core | `600` | | |
153
- | **WORKFLOW_CORE_MAX_CRON_PER_WORKFLOW** | Core | `5` | | |
154
- | **WORKFLOW_CORE_MAX_QUEUE_COMPLETE_HIST** | Core | `16` | | |
155
- | **WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE** | Core | `true` | A flog that enable generating ID with `md5` algorithm. | |
156
- | **WORKFLOW_LOG_PATH** | Log | `./logs` | The log path of the workflow saving log. | |
157
- | **WORKFLOW_LOG_DEBUG_MODE** | Log | `true` | A flag that enable logging with debug level mode. | |
158
- | **WORKFLOW_LOG_ENABLE_WRITE** | Log | `true` | A flag that enable logging object saving log to its destination. | |
159
- | **WORKFLOW_APP_MAX_PROCESS** | Schedule | `2` | The maximum process worker number that run in scheduler app module. | |
160
- | **WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS** | Schedule | `100` | A schedule per process that run parallel. | |
161
- | **WORKFLOW_APP_STOP_BOUNDARY_DELTA** | Schedule | `'{"minutes": 5, "seconds": 20}'` | A time delta value that use to stop scheduler app in json string format. | |
139
+ | Environment | Component | Default | Description | Remark |
140
+ |:-------------------------------------------|:---------:|:----------------------------------|:-------------------------------------------------------------------------------------------------------------------|--------|
141
+ | **WORKFLOW_ROOT_PATH** | Core | `.` | The root path of the workflow application. | |
142
+ | **WORKFLOW_CORE_REGISTRY** | Core | `src` | List of importable string for the hook stage. | |
143
+ | **WORKFLOW_CORE_REGISTRY_FILTER** | Core | `ddeutil.workflow.utils` | List of importable string for the filter template. | |
144
+ | **WORKFLOW_CORE_PATH_CONF** | Core | `conf` | The config path that keep all template `.yaml` files. | |
145
+ | **WORKFLOW_CORE_TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. | |
146
+ | **WORKFLOW_CORE_STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. | |
147
+ | **WORKFLOW_CORE_STAGE_RAISE_ERROR** | Core | `false` | A flag that all stage raise StageException from stage execution. | |
148
+ | **WORKFLOW_CORE_JOB_DEFAULT_ID** | Core | `false` | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. | |
149
+ | **WORKFLOW_CORE_JOB_RAISE_ERROR** | Core | `true` | A flag that all job raise JobException from job strategy execution. | |
150
+ | **WORKFLOW_CORE_MAX_NUM_POKING** | Core | `4` | . | |
151
+ | **WORKFLOW_CORE_MAX_JOB_PARALLEL** | Core | `2` | The maximum job number that able to run parallel in workflow executor. | |
152
+ | **WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT** | Core | `600` | | |
153
+ | **WORKFLOW_CORE_MAX_CRON_PER_WORKFLOW** | Core | `5` | | |
154
+ | **WORKFLOW_CORE_MAX_QUEUE_COMPLETE_HIST** | Core | `16` | | |
155
+ | **WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE** | Core | `true` | A flog that enable generating ID with `md5` algorithm. | |
156
+ | **WORKFLOW_LOG_PATH** | Log | `./logs` | The log path of the workflow saving log. | |
157
+ | **WORKFLOW_LOG_DEBUG_MODE** | Log | `true` | A flag that enable logging with debug level mode. | |
158
+ | **WORKFLOW_LOG_ENABLE_WRITE** | Log | `true` | A flag that enable logging object saving log to its destination. | |
159
+ | **WORKFLOW_APP_MAX_PROCESS** | Schedule | `2` | The maximum process worker number that run in scheduler app module. | |
160
+ | **WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS** | Schedule | `100` | A schedule per process that run parallel. | |
161
+ | **WORKFLOW_APP_STOP_BOUNDARY_DELTA** | Schedule | `'{"minutes": 5, "seconds": 20}'` | A time delta value that use to stop scheduler app in json string format. | |
162
162
 
163
163
  **API Application**:
164
164
 
@@ -0,0 +1 @@
1
+ __version__: str = "0.0.27"
@@ -3,11 +3,15 @@
3
3
  # Licensed under the MIT License. See LICENSE in the project root for
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
- from .__cron import CronRunner
6
+ from .__cron import CronJob, CronRunner
7
7
  from .conf import (
8
8
  Config,
9
- FileLog,
10
9
  Loader,
10
+ Log,
11
+ config,
12
+ env,
13
+ get_log,
14
+ get_logger,
11
15
  )
12
16
  from .cron import (
13
17
  On,
@@ -21,6 +25,13 @@ from .exceptions import (
21
25
  UtilException,
22
26
  WorkflowException,
23
27
  )
28
+ from .hook import (
29
+ ReturnTagFunc,
30
+ TagFunc,
31
+ extract_hook,
32
+ make_registry,
33
+ tag,
34
+ )
24
35
  from .job import (
25
36
  Job,
26
37
  Strategy,
@@ -45,33 +56,30 @@ from .stage import (
45
56
  PyStage,
46
57
  Stage,
47
58
  TriggerStage,
48
- extract_hook,
49
59
  )
50
- from .utils import (
60
+ from .templates import (
51
61
  FILTERS,
52
62
  FilterFunc,
53
63
  FilterRegistry,
54
- ReturnTagFunc,
55
- TagFunc,
64
+ custom_filter,
65
+ get_args_const,
66
+ has_template,
67
+ make_filter_registry,
68
+ map_post_filter,
69
+ not_in_template,
70
+ param2template,
71
+ str2template,
72
+ )
73
+ from .utils import (
56
74
  batch,
57
75
  cross_product,
58
- custom_filter,
59
76
  dash2underscore,
60
77
  delay,
61
78
  filter_func,
62
79
  gen_id,
63
- get_args_const,
64
80
  get_diff_sec,
65
81
  get_dt_now,
66
- has_template,
67
82
  make_exec,
68
- make_filter_registry,
69
- make_registry,
70
- map_post_filter,
71
- not_in_template,
72
- param2template,
73
- str2template,
74
- tag,
75
83
  )
76
84
  from .workflow import (
77
85
  Workflow,
@@ -13,27 +13,30 @@ from collections.abc import Iterator
13
13
  from datetime import datetime, timedelta
14
14
  from functools import cached_property, lru_cache
15
15
  from pathlib import Path
16
- from typing import ClassVar, Optional, TypeVar, Union
16
+ from typing import ClassVar, Optional, Union
17
17
  from zoneinfo import ZoneInfo
18
18
 
19
19
  from ddeutil.core import str2bool
20
20
  from ddeutil.io import YamlFlResolve
21
- from dotenv import load_dotenv
22
21
  from pydantic import BaseModel, Field
23
22
  from pydantic.functional_validators import model_validator
24
23
  from typing_extensions import Self
25
24
 
26
25
  from .__types import DictData, TupleStr
27
26
 
28
- AnyModel = TypeVar("AnyModel", bound=BaseModel)
29
- AnyModelType = type[AnyModel]
30
27
 
31
- load_dotenv()
28
+ def env(var: str, default: str | None = None) -> str | None: # pragma: no cov
29
+ return os.getenv(f"WORKFLOW_{var}", default)
30
+
31
+
32
+ def glob_files(path: Path) -> Iterator[Path]: # pragma: no cov
33
+ yield from (file for file in path.rglob("*") if file.is_file())
32
34
 
33
- env = os.getenv
34
35
 
35
36
  __all__: TupleStr = (
37
+ "env",
36
38
  "get_logger",
39
+ "get_log",
37
40
  "Config",
38
41
  "SimLoad",
39
42
  "Loader",
@@ -52,6 +55,14 @@ def get_logger(name: str):
52
55
  :param name: A module name that want to log.
53
56
  """
54
57
  lg = logging.getLogger(name)
58
+
59
+ # NOTE: Developers using this package can then disable all logging just for
60
+ # this package by;
61
+ #
62
+ # `logging.getLogger('ddeutil.workflow').propagate = False`
63
+ #
64
+ lg.addHandler(logging.NullHandler())
65
+
55
66
  formatter = logging.Formatter(
56
67
  fmt=(
57
68
  "%(asctime)s.%(msecs)03d (%(name)-10s, %(process)-5d, "
@@ -68,115 +79,139 @@ def get_logger(name: str):
68
79
  return lg
69
80
 
70
81
 
71
- class Config:
82
+ class Config: # pragma: no cov
72
83
  """Config object for keeping application configuration on current session
73
84
  without changing when if the application still running.
74
85
  """
75
86
 
76
87
  # NOTE: Core
77
- root_path: Path = Path(os.getenv("WORKFLOW_ROOT_PATH", "."))
78
- tz: ZoneInfo = ZoneInfo(env("WORKFLOW_CORE_TIMEZONE", "UTC"))
79
- gen_id_simple_mode: bool = str2bool(
80
- os.getenv("WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE", "true")
81
- )
88
+ @property
89
+ def root_path(self) -> Path:
90
+ return Path(env("ROOT_PATH", "."))
91
+
92
+ @property
93
+ def conf_path(self) -> Path:
94
+ """Config path that use root_path class argument for this construction.
95
+
96
+ :rtype: Path
97
+ """
98
+ return self.root_path / env("CORE_PATH_CONF", "conf")
99
+
100
+ @property
101
+ def tz(self) -> ZoneInfo:
102
+ return ZoneInfo(env("CORE_TIMEZONE", "UTC"))
103
+
104
+ @property
105
+ def gen_id_simple_mode(self) -> bool:
106
+ return str2bool(env("CORE_GENERATE_ID_SIMPLE_MODE", "true"))
82
107
 
83
108
  # NOTE: Register
84
- regis_hook_str: str = os.getenv(
85
- "WORKFLOW_CORE_REGISTRY", "src,src.ddeutil.workflow,tests,tests.utils"
86
- )
87
- regis_filter_str: str = os.getenv(
88
- "WORKFLOW_CORE_REGISTRY_FILTER", "ddeutil.workflow.utils"
89
- )
109
+ @property
110
+ def regis_hook(self) -> list[str]:
111
+ regis_hook_str: str = env("CORE_REGISTRY", "src")
112
+ return [r.strip() for r in regis_hook_str.split(",")]
113
+
114
+ @property
115
+ def regis_filter(self) -> list[str]:
116
+ regis_filter_str: str = env(
117
+ "CORE_REGISTRY_FILTER", "ddeutil.workflow.templates"
118
+ )
119
+ return [r.strip() for r in regis_filter_str.split(",")]
90
120
 
91
121
  # NOTE: Logging
92
- debug: bool = str2bool(os.getenv("WORKFLOW_LOG_DEBUG_MODE", "true"))
93
- enable_write_log: bool = str2bool(
94
- os.getenv("WORKFLOW_LOG_ENABLE_WRITE", "false")
95
- )
96
- log_path: Path = Path(os.getenv("WORKFLOW_LOG_PATH", "./logs"))
122
+ @property
123
+ def log_path(self) -> Path:
124
+ return Path(env("LOG_PATH", "./logs"))
125
+
126
+ @property
127
+ def debug(self) -> bool:
128
+ return str2bool(env("LOG_DEBUG_MODE", "true"))
129
+
130
+ @property
131
+ def enable_write_log(self) -> bool:
132
+ return str2bool(env("LOG_ENABLE_WRITE", "false"))
97
133
 
98
134
  # NOTE: Stage
99
- stage_raise_error: bool = str2bool(
100
- env("WORKFLOW_CORE_STAGE_RAISE_ERROR", "false")
101
- )
102
- stage_default_id: bool = str2bool(
103
- env("WORKFLOW_CORE_STAGE_DEFAULT_ID", "false")
104
- )
135
+ @property
136
+ def stage_raise_error(self) -> bool:
137
+ return str2bool(env("CORE_STAGE_RAISE_ERROR", "false"))
105
138
 
106
- # NOTE: Job
107
- job_raise_error: bool = str2bool(
108
- env("WORKFLOW_CORE_JOB_RAISE_ERROR", "true")
109
- )
110
- job_default_id: bool = str2bool(
111
- env("WORKFLOW_CORE_JOB_DEFAULT_ID", "false")
112
- )
139
+ @property
140
+ def stage_default_id(self) -> bool:
141
+ return str2bool(env("CORE_STAGE_DEFAULT_ID", "false"))
113
142
 
114
- # NOTE: Workflow
115
- max_job_parallel: int = int(env("WORKFLOW_CORE_MAX_JOB_PARALLEL", "2"))
116
- max_job_exec_timeout: int = int(
117
- env("WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT", "600")
118
- )
119
- max_poking_pool_worker: int = int(
120
- os.getenv("WORKFLOW_CORE_MAX_NUM_POKING", "4")
121
- )
122
- max_on_per_workflow: int = int(
123
- env("WORKFLOW_CORE_MAX_CRON_PER_WORKFLOW", "5")
124
- )
125
- max_queue_complete_hist: int = int(
126
- os.getenv("WORKFLOW_CORE_MAX_QUEUE_COMPLETE_HIST", "16")
127
- )
143
+ # NOTE: Job
144
+ @property
145
+ def job_raise_error(self) -> bool:
146
+ return str2bool(env("CORE_JOB_RAISE_ERROR", "true"))
128
147
 
129
- # NOTE: Schedule App
130
- max_schedule_process: int = int(env("WORKFLOW_APP_MAX_PROCESS", "2"))
131
- max_schedule_per_process: int = int(
132
- env("WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS", "100")
133
- )
134
- stop_boundary_delta_str: str = env(
135
- "WORKFLOW_APP_STOP_BOUNDARY_DELTA", '{"minutes": 5, "seconds": 20}'
136
- )
148
+ @property
149
+ def job_default_id(self) -> bool:
150
+ return str2bool(env("CORE_JOB_DEFAULT_ID", "false"))
137
151
 
138
- # NOTE: API
139
- prefix_path: str = env("WORKFLOW_API_PREFIX_PATH", "/api/v1")
140
- enable_route_workflow: bool = str2bool(
141
- env("WORKFLOW_API_ENABLE_ROUTE_WORKFLOW", "true")
142
- )
143
- enable_route_schedule: bool = str2bool(
144
- env("WORKFLOW_API_ENABLE_ROUTE_SCHEDULE", "true")
145
- )
152
+ # NOTE: Workflow
153
+ @property
154
+ def max_job_parallel(self) -> int:
155
+ max_job_parallel = int(env("CORE_MAX_JOB_PARALLEL", "2"))
146
156
 
147
- def __init__(self) -> None:
148
157
  # VALIDATE: the MAX_JOB_PARALLEL value should not less than 0.
149
- if self.max_job_parallel < 0:
158
+ if max_job_parallel < 0:
150
159
  raise ValueError(
151
- f"``MAX_JOB_PARALLEL`` should more than 0 but got "
152
- f"{self.max_job_parallel}."
160
+ f"``WORKFLOW_MAX_JOB_PARALLEL`` should more than 0 but got "
161
+ f"{max_job_parallel}."
153
162
  )
163
+ return max_job_parallel
164
+
165
+ @property
166
+ def max_job_exec_timeout(self) -> int:
167
+ return int(env("CORE_MAX_JOB_EXEC_TIMEOUT", "600"))
168
+
169
+ @property
170
+ def max_poking_pool_worker(self) -> int:
171
+ return int(env("CORE_MAX_NUM_POKING", "4"))
172
+
173
+ @property
174
+ def max_on_per_workflow(self) -> int:
175
+ return int(env("CORE_MAX_CRON_PER_WORKFLOW", "5"))
176
+
177
+ @property
178
+ def max_queue_complete_hist(self) -> int:
179
+ return int(env("CORE_MAX_QUEUE_COMPLETE_HIST", "16"))
180
+
181
+ # NOTE: Schedule App
182
+ @property
183
+ def max_schedule_process(self) -> int:
184
+ return int(env("APP_MAX_PROCESS", "2"))
154
185
 
186
+ @property
187
+ def max_schedule_per_process(self) -> int:
188
+ return int(env("APP_MAX_SCHEDULE_PER_PROCESS", "100"))
189
+
190
+ @property
191
+ def stop_boundary_delta(self) -> timedelta:
192
+ stop_boundary_delta_str: str = env(
193
+ "APP_STOP_BOUNDARY_DELTA", '{"minutes": 5, "seconds": 20}'
194
+ )
155
195
  try:
156
- self.stop_boundary_delta: timedelta = timedelta(
157
- **json.loads(self.stop_boundary_delta_str)
158
- )
196
+ return timedelta(**json.loads(stop_boundary_delta_str))
159
197
  except Exception as err:
160
198
  raise ValueError(
161
199
  "Config ``WORKFLOW_APP_STOP_BOUNDARY_DELTA`` can not parsing to"
162
- f"timedelta with {self.stop_boundary_delta_str}."
200
+ f"timedelta with {stop_boundary_delta_str}."
163
201
  ) from err
164
202
 
203
+ # NOTE: API
165
204
  @property
166
- def conf_path(self) -> Path:
167
- """Config path that use root_path class argument for this construction.
168
-
169
- :rtype: Path
170
- """
171
- return self.root_path / os.getenv("WORKFLOW_CORE_PATH_CONF", "conf")
205
+ def prefix_path(self) -> str:
206
+ return env("API_PREFIX_PATH", "/api/v1")
172
207
 
173
208
  @property
174
- def regis_hook(self) -> list[str]:
175
- return [r.strip() for r in self.regis_hook_str.split(",")]
209
+ def enable_route_workflow(self) -> bool:
210
+ return str2bool(env("API_ENABLE_ROUTE_WORKFLOW", "true"))
176
211
 
177
212
  @property
178
- def regis_filter(self) -> list[str]:
179
- return [r.strip() for r in self.regis_filter_str.split(",")]
213
+ def enable_route_schedule(self) -> bool:
214
+ return str2bool(env("API_ENABLE_ROUTE_SCHEDULE", "true"))
180
215
 
181
216
 
182
217
  class SimLoad:
@@ -206,14 +241,9 @@ class SimLoad:
206
241
  externals: DictData | None = None,
207
242
  ) -> None:
208
243
  self.data: DictData = {}
209
- for file in conf.conf_path.rglob("*"):
210
- if not file.is_file():
211
- continue
212
-
213
- if data := self.filter_suffix(
214
- file,
215
- name,
216
- ):
244
+ for file in glob_files(conf.conf_path):
245
+
246
+ if data := self.filter_suffix(file, name):
217
247
  self.data = data
218
248
 
219
249
  # VALIDATE: check the data that reading should not empty.
@@ -245,10 +275,7 @@ class SimLoad:
245
275
  :rtype: Iterator[tuple[str, DictData]]
246
276
  """
247
277
  exclude: list[str] = excluded or []
248
- for file in conf.conf_path.rglob("*"):
249
-
250
- if not file.is_file():
251
- continue
278
+ for file in glob_files(conf.conf_path):
252
279
 
253
280
  for key, data in cls.filter_suffix(file).items():
254
281
 
@@ -274,7 +301,7 @@ class SimLoad:
274
301
  """Return object of string type which implement on any registry. The
275
302
  object type.
276
303
 
277
- :rtype: AnyModelType
304
+ :rtype: str
278
305
  """
279
306
  if _typ := self.data.get("type"):
280
307
  return _typ
@@ -283,6 +310,10 @@ class SimLoad:
283
310
  )
284
311
 
285
312
 
313
+ config = Config()
314
+ logger = get_logger("ddeutil.workflow")
315
+
316
+
286
317
  class Loader(SimLoad):
287
318
  """Loader Object that get the config `yaml` file from current path.
288
319
 
@@ -308,15 +339,11 @@ class Loader(SimLoad):
308
339
  :rtype: Iterator[tuple[str, DictData]]
309
340
  """
310
341
  return super().finds(
311
- obj=obj, conf=Config(), included=included, excluded=excluded
342
+ obj=obj, conf=config, included=included, excluded=excluded
312
343
  )
313
344
 
314
345
  def __init__(self, name: str, externals: DictData) -> None:
315
- super().__init__(name, conf=Config(), externals=externals)
316
-
317
-
318
- config = Config()
319
- logger = get_logger("ddeutil.workflow")
346
+ super().__init__(name, conf=config, externals=externals)
320
347
 
321
348
 
322
349
  class BaseLog(BaseModel, ABC):
@@ -398,8 +425,8 @@ class FileLog(BaseLog):
398
425
  workflow name and release values. If a release does not pass to an input
399
426
  argument, it will return the latest release from the current log path.
400
427
 
401
- :param name:
402
- :param release:
428
+ :param name: A workflow name that want to search log.
429
+ :param release: A release datetime that want to search log.
403
430
 
404
431
  :raise FileNotFoundError:
405
432
  :raise NotImplementedError:
@@ -463,8 +490,14 @@ class FileLog(BaseLog):
463
490
 
464
491
  :rtype: Self
465
492
  """
493
+ from .utils import cut_id
494
+
466
495
  # NOTE: Check environ variable was set for real writing.
467
496
  if not config.enable_write_log:
497
+ logger.debug(
498
+ f"({cut_id(self.run_id)}) [LOG]: Skip writing log cause "
499
+ f"config was set"
500
+ )
468
501
  return self
469
502
 
470
503
  log_file: Path = self.pointer() / f"{self.run_id}.log"
@@ -481,7 +514,32 @@ class FileLog(BaseLog):
481
514
 
482
515
  class SQLiteLog(BaseLog): # pragma: no cov
483
516
 
517
+ table: str = "workflow_log"
518
+ ddl: str = """
519
+ workflow str,
520
+ release int,
521
+ type str,
522
+ context json,
523
+ parent_run_id int,
524
+ run_id int,
525
+ update datetime
526
+ primary key ( run_id )
527
+ """
528
+
484
529
  def save(self, excluded: list[str] | None) -> None:
530
+ """Save logging data that receive a context data from a workflow
531
+ execution result.
532
+ """
533
+ from .utils import cut_id
534
+
535
+ # NOTE: Check environ variable was set for real writing.
536
+ if not config.enable_write_log:
537
+ logger.debug(
538
+ f"({cut_id(self.run_id)}) [LOG]: Skip writing log cause "
539
+ f"config was set"
540
+ )
541
+ return self
542
+
485
543
  raise NotImplementedError("SQLiteLog does not implement yet.")
486
544
 
487
545
 
@@ -489,3 +547,9 @@ Log = Union[
489
547
  FileLog,
490
548
  SQLiteLog,
491
549
  ]
550
+
551
+
552
+ def get_log() -> Log: # pragma: no cov
553
+ if config.log_path.is_file():
554
+ return SQLiteLog
555
+ return FileLog
@@ -29,6 +29,3 @@ class WorkflowFailException(WorkflowException): ...
29
29
 
30
30
 
31
31
  class ParamValueException(WorkflowException): ...
32
-
33
-
34
- class CliException(BaseWorkflowException): ...