ddeutil-workflow 0.0.28__tar.gz → 0.0.30__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 (65) hide show
  1. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/PKG-INFO +19 -15
  2. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/README.md +17 -13
  3. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/pyproject.toml +7 -1
  4. ddeutil_workflow-0.0.30/src/ddeutil/workflow/__about__.py +1 -0
  5. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/__init__.py +1 -0
  6. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/__types.py +11 -9
  7. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/api/api.py +2 -2
  8. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/conf.py +38 -10
  9. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/hook.py +6 -2
  10. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/scheduler.py +11 -11
  11. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/templates.py +1 -4
  12. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/workflow.py +76 -66
  13. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil_workflow.egg-info/PKG-INFO +19 -15
  14. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil_workflow.egg-info/SOURCES.txt +4 -9
  15. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil_workflow.egg-info/requires.txt +1 -1
  16. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_conf.py +4 -2
  17. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_hook_tag.py +3 -2
  18. ddeutil_workflow-0.0.28/tests/test_workflow_release_and_queue.py → ddeutil_workflow-0.0.30/tests/test_release_and_queue.py +17 -19
  19. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_schedule_tasks.py +4 -4
  20. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_stage.py +1 -0
  21. ddeutil_workflow-0.0.30/tests/test_stage_handler_exec.py +209 -0
  22. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_workflow_exec.py +208 -2
  23. ddeutil_workflow-0.0.28/tests/test_workflow_poke.py → ddeutil_workflow-0.0.30/tests/test_workflow_exec_poke.py +5 -0
  24. ddeutil_workflow-0.0.28/tests/test_workflow_release.py → ddeutil_workflow-0.0.30/tests/test_workflow_exec_release.py +6 -6
  25. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_workflow_schedule.py +7 -9
  26. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_workflow_task.py +6 -6
  27. ddeutil_workflow-0.0.28/src/ddeutil/workflow/__about__.py +0 -1
  28. ddeutil_workflow-0.0.28/tests/test_stage_exec_bash.py +0 -34
  29. ddeutil_workflow-0.0.28/tests/test_stage_exec_hook.py +0 -46
  30. ddeutil_workflow-0.0.28/tests/test_stage_exec_py.py +0 -87
  31. ddeutil_workflow-0.0.28/tests/test_stage_exec_trigger.py +0 -30
  32. ddeutil_workflow-0.0.28/tests/test_workflow_exec_hook.py +0 -91
  33. ddeutil_workflow-0.0.28/tests/test_workflow_exec_needs.py +0 -74
  34. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/LICENSE +0 -0
  35. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/setup.cfg +0 -0
  36. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/__cron.py +0 -0
  37. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/api/__init__.py +0 -0
  38. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/api/repeat.py +0 -0
  39. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/api/route.py +0 -0
  40. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/cron.py +0 -0
  41. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/exceptions.py +0 -0
  42. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/job.py +0 -0
  43. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/params.py +0 -0
  44. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/result.py +0 -0
  45. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/stage.py +0 -0
  46. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil/workflow/utils.py +0 -0
  47. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  48. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  49. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test__cron.py +0 -0
  50. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test__regex.py +0 -0
  51. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_conf_log.py +0 -0
  52. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_cron_on.py +0 -0
  53. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_job.py +0 -0
  54. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_job_exec_py.py +0 -0
  55. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_job_exec_strategy.py +0 -0
  56. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_job_strategy.py +0 -0
  57. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_params.py +0 -0
  58. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_result.py +0 -0
  59. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_schedule.py +0 -0
  60. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_schedule_control.py +0 -0
  61. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_templates.py +0 -0
  62. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_templates_filter.py +0 -0
  63. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_utils.py +0 -0
  64. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_workflow.py +0 -0
  65. {ddeutil_workflow-0.0.28 → ddeutil_workflow-0.0.30}/tests/test_workflow_job_exec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.28
3
+ Version: 0.0.30
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -22,7 +22,7 @@ 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==0.4.6
25
+ Requires-Dist: ddeutil>=0.4.6
26
26
  Requires-Dist: ddeutil-io[toml,yaml]>=0.2.3
27
27
  Requires-Dist: pydantic==2.10.6
28
28
  Requires-Dist: python-dotenv==1.0.1
@@ -55,6 +55,8 @@ the input parameters per use-case instead.
55
55
  This way I can handle a lot of logical workflows in our orgs with only metadata
56
56
  configuration. It called **Metadata Driven Data Workflow**.
57
57
 
58
+ ---
59
+
58
60
  **:pushpin: <u>Rules of This Workflow engine</u>**:
59
61
 
60
62
  1. The Minimum frequency unit of scheduling is **1 minute** :warning:
@@ -62,8 +64,14 @@ configuration. It called **Metadata Driven Data Workflow**.
62
64
  3. All parallel tasks inside workflow engine use Multi-Threading
63
65
  (Python 3.13 unlock GIL :unlock:)
64
66
 
67
+ ---
68
+
65
69
  **:memo: <u>Workflow Diagrams</u>**:
66
70
 
71
+ This diagram show where is this application run on the production infrastructure.
72
+ You will see that this application do only running code with stress-less which mean
73
+ you should to set the data layer separate this core program before run this application.
74
+
67
75
  ```mermaid
68
76
  flowchart LR
69
77
  subgraph Interface
@@ -204,9 +212,9 @@ schedule-run-local-wf:
204
212
 
205
213
  ## :cookie: Configuration
206
214
 
207
- The main configuration that use to dynamic changing with your objective of this
208
- application. If any configuration values do not set yet, it will use default value
209
- and do not raise any error to you.
215
+ The main configuration that use to dynamic changing this workflow engine for your
216
+ objective use environment variable only. If any configuration values do not set yet,
217
+ it will use default value and do not raise any error to you.
210
218
 
211
219
  > [!IMPORTANT]
212
220
  > The config value that you will set on the environment should combine with
@@ -215,8 +223,8 @@ and do not raise any error to you.
215
223
  | Name | Component | Default | Description |
216
224
  |:-----------------------------|:---------:|:----------------------------------|:-------------------------------------------------------------------------------------------------------------------|
217
225
  | **ROOT_PATH** | Core | `.` | The root path of the workflow application. |
218
- | **REGISTRY** | Core | `src` | List of importable string for the hook stage. |
219
- | **REGISTRY_FILTER** | Core | `ddeutil.workflow.utils` | List of importable string for the filter template. |
226
+ | **REGISTRY** | Core | `.` | List of importable string for the hook stage. |
227
+ | **REGISTRY_FILTER** | Core | `ddeutil.workflow.templates` | List of importable string for the filter template. |
220
228
  | **CONF_PATH** | Core | `conf` | The config path that keep all template `.yaml` files. |
221
229
  | **TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. |
222
230
  | **STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. |
@@ -238,6 +246,9 @@ and do not raise any error to you.
238
246
 
239
247
  **API Application**:
240
248
 
249
+ This config part use for the workflow application that build from the FastAPI
250
+ only.
251
+
241
252
  | Environment | Component | Default | Description |
242
253
  |:---------------------------|:-----------:|---------|------------------------------------------------------------------------------------|
243
254
  | **ENABLE_ROUTE_WORKFLOW** | API | `true` | A flag that enable workflow route to manage execute manually and workflow logging. |
@@ -264,16 +275,9 @@ like crontab job but via Python API.
264
275
 
265
276
  ### Docker Container
266
277
 
267
- Create Docker image;
268
-
269
278
  ```shell
270
279
  $ docker build -t ddeutil-workflow:latest -f .container/Dockerfile .
271
- ```
272
-
273
- Run the above Docker image;
274
-
275
- ```shell
276
- $ docker run -i ddeutil-workflow:latest
280
+ $ docker run -i ddeutil-workflow:latest ddeutil-workflow
277
281
  ```
278
282
 
279
283
  ## :speech_balloon: Contribute
@@ -23,6 +23,8 @@ the input parameters per use-case instead.
23
23
  This way I can handle a lot of logical workflows in our orgs with only metadata
24
24
  configuration. It called **Metadata Driven Data Workflow**.
25
25
 
26
+ ---
27
+
26
28
  **:pushpin: <u>Rules of This Workflow engine</u>**:
27
29
 
28
30
  1. The Minimum frequency unit of scheduling is **1 minute** :warning:
@@ -30,8 +32,14 @@ configuration. It called **Metadata Driven Data Workflow**.
30
32
  3. All parallel tasks inside workflow engine use Multi-Threading
31
33
  (Python 3.13 unlock GIL :unlock:)
32
34
 
35
+ ---
36
+
33
37
  **:memo: <u>Workflow Diagrams</u>**:
34
38
 
39
+ This diagram show where is this application run on the production infrastructure.
40
+ You will see that this application do only running code with stress-less which mean
41
+ you should to set the data layer separate this core program before run this application.
42
+
35
43
  ```mermaid
36
44
  flowchart LR
37
45
  subgraph Interface
@@ -172,9 +180,9 @@ schedule-run-local-wf:
172
180
 
173
181
  ## :cookie: Configuration
174
182
 
175
- The main configuration that use to dynamic changing with your objective of this
176
- application. If any configuration values do not set yet, it will use default value
177
- and do not raise any error to you.
183
+ The main configuration that use to dynamic changing this workflow engine for your
184
+ objective use environment variable only. If any configuration values do not set yet,
185
+ it will use default value and do not raise any error to you.
178
186
 
179
187
  > [!IMPORTANT]
180
188
  > The config value that you will set on the environment should combine with
@@ -183,8 +191,8 @@ and do not raise any error to you.
183
191
  | Name | Component | Default | Description |
184
192
  |:-----------------------------|:---------:|:----------------------------------|:-------------------------------------------------------------------------------------------------------------------|
185
193
  | **ROOT_PATH** | Core | `.` | The root path of the workflow application. |
186
- | **REGISTRY** | Core | `src` | List of importable string for the hook stage. |
187
- | **REGISTRY_FILTER** | Core | `ddeutil.workflow.utils` | List of importable string for the filter template. |
194
+ | **REGISTRY** | Core | `.` | List of importable string for the hook stage. |
195
+ | **REGISTRY_FILTER** | Core | `ddeutil.workflow.templates` | List of importable string for the filter template. |
188
196
  | **CONF_PATH** | Core | `conf` | The config path that keep all template `.yaml` files. |
189
197
  | **TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. |
190
198
  | **STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. |
@@ -206,6 +214,9 @@ and do not raise any error to you.
206
214
 
207
215
  **API Application**:
208
216
 
217
+ This config part use for the workflow application that build from the FastAPI
218
+ only.
219
+
209
220
  | Environment | Component | Default | Description |
210
221
  |:---------------------------|:-----------:|---------|------------------------------------------------------------------------------------|
211
222
  | **ENABLE_ROUTE_WORKFLOW** | API | `true` | A flag that enable workflow route to manage execute manually and workflow logging. |
@@ -232,16 +243,9 @@ like crontab job but via Python API.
232
243
 
233
244
  ### Docker Container
234
245
 
235
- Create Docker image;
236
-
237
246
  ```shell
238
247
  $ docker build -t ddeutil-workflow:latest -f .container/Dockerfile .
239
- ```
240
-
241
- Run the above Docker image;
242
-
243
- ```shell
244
- $ docker run -i ddeutil-workflow:latest
248
+ $ docker run -i ddeutil-workflow:latest ddeutil-workflow
245
249
  ```
246
250
 
247
251
  ## :speech_balloon: Contribute
@@ -26,7 +26,7 @@ classifiers = [
26
26
  ]
27
27
  requires-python = ">=3.9.13"
28
28
  dependencies = [
29
- "ddeutil==0.4.6",
29
+ "ddeutil>=0.4.6",
30
30
  "ddeutil-io[yaml,toml]>=0.2.3",
31
31
  "pydantic==2.10.6",
32
32
  "python-dotenv==1.0.1",
@@ -77,6 +77,12 @@ exclude_lines = [
77
77
 
78
78
  [tool.pytest.ini_options]
79
79
  pythonpath = ["src"]
80
+ # NOTE: You can deslect multiple markers by '-m "not (poke or api)"'
81
+ markers = [
82
+ "poke: marks tests as slow by poking (deselect with '-m \"not poke\"')",
83
+ "schedule: marks tests as schedule (deselect with '-m \"not schedule\"')",
84
+ "api: marks tests as api (deselect with '-m \"not api\"')",
85
+ ]
80
86
  console_output_style = "count"
81
87
  addopts = [
82
88
  "--strict-config",
@@ -0,0 +1 @@
1
+ __version__: str = "0.0.30"
@@ -4,6 +4,7 @@
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
6
  from .__cron import CronJob, CronRunner
7
+ from .__types import Re
7
8
  from .conf import (
8
9
  Config,
9
10
  Loader,
@@ -27,6 +27,8 @@ Matrix = dict[str, Union[list[str], list[int]]]
27
27
 
28
28
 
29
29
  class Context(TypedDict):
30
+ """TypeDict support the Context."""
31
+
30
32
  params: dict[str, Any]
31
33
  jobs: dict[str, Any]
32
34
 
@@ -71,14 +73,14 @@ class Re:
71
73
  # - ${{ params.source?.schema }}
72
74
  #
73
75
  __re_caller: str = r"""
74
- \$
75
- {{
76
- \s*
76
+ \$ # start with $
77
+ {{ # value open with {{
78
+ \s* # whitespace or not
77
79
  (?P<caller>
78
80
  (?P<caller_prefix>(?:[a-zA-Z_-]+\??\.)*)
79
81
  (?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+\??)
80
82
  )
81
- \s*
83
+ \s* # whitespace or not
82
84
  (?P<post_filters>
83
85
  (?:
84
86
  \|\s*
@@ -88,7 +90,7 @@ class Re:
88
90
  )\s*
89
91
  )*
90
92
  )
91
- }}
93
+ }} # value close with }}
92
94
  """
93
95
  RE_CALLER: Pattern = re.compile(
94
96
  __re_caller, MULTILINE | IGNORECASE | UNICODE | VERBOSE
@@ -103,13 +105,13 @@ class Re:
103
105
  # - tasks/function@dummy
104
106
  #
105
107
  __re_task_fmt: str = r"""
106
- ^
108
+ ^ # start task format
107
109
  (?P<path>[^/@]+)
108
- /
110
+ / # start get function with /
109
111
  (?P<func>[^@]+)
110
- @
112
+ @ # start tag with @
111
113
  (?P<tag>.+)
112
- $
114
+ $ # end task format
113
115
  """
114
116
  RE_TASK_FMT: Pattern = re.compile(
115
117
  __re_task_fmt, MULTILINE | IGNORECASE | UNICODE | VERBOSE
@@ -18,7 +18,7 @@ from fastapi.responses import UJSONResponse
18
18
  from ..__about__ import __version__
19
19
  from ..conf import config, get_logger
20
20
  from ..scheduler import ReleaseThread, ReleaseThreads
21
- from ..workflow import WorkflowQueue, WorkflowTask
21
+ from ..workflow import ReleaseQueue, WorkflowTask
22
22
  from .repeat import repeat_at
23
23
 
24
24
  load_dotenv()
@@ -31,7 +31,7 @@ class State(TypedDict):
31
31
  scheduler: list[str]
32
32
  workflow_threads: ReleaseThreads
33
33
  workflow_tasks: list[WorkflowTask]
34
- workflow_queue: dict[str, WorkflowQueue]
34
+ workflow_queue: dict[str, ReleaseQueue]
35
35
 
36
36
 
37
37
  @contextlib.asynccontextmanager
@@ -13,7 +13,7 @@ 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, Union
16
+ from typing import ClassVar, Optional, TypeVar, Union
17
17
  from zoneinfo import ZoneInfo
18
18
 
19
19
  from ddeutil.core import str2bool
@@ -39,6 +39,7 @@ __all__: TupleStr = (
39
39
  "env",
40
40
  "get_logger",
41
41
  "get_log",
42
+ "C",
42
43
  "Config",
43
44
  "SimLoad",
44
45
  "Loader",
@@ -81,18 +82,42 @@ def get_logger(name: str):
81
82
  return lg
82
83
 
83
84
 
84
- class Config: # pragma: no cov
85
+ class BaseConfig: # pragma: no cov
86
+ """BaseConfig object inheritable."""
87
+
88
+ __slots__ = ()
89
+
90
+ @property
91
+ def root_path(self) -> Path:
92
+ """Root path or the project path.
93
+
94
+ :rtype: Path
95
+ """
96
+ return Path(os.getenv("ROOT_PATH", "."))
97
+
98
+ @property
99
+ def conf_path(self) -> Path:
100
+ """Config path that use root_path class argument for this construction.
101
+
102
+ :rtype: Path
103
+ """
104
+ return self.root_path / os.getenv("CONF_PATH", "conf")
105
+
106
+
107
+ class Config(BaseConfig): # pragma: no cov
85
108
  """Config object for keeping core configurations on the current session
86
109
  without changing when if the application still running.
87
110
 
88
111
  The config value can change when you call that config property again.
89
112
  """
90
113
 
91
- __slots__ = ()
92
-
93
114
  # NOTE: Core
94
115
  @property
95
116
  def root_path(self) -> Path:
117
+ """Root path or the project path.
118
+
119
+ :rtype: Path
120
+ """
96
121
  return Path(env("CORE_ROOT_PATH", "."))
97
122
 
98
123
  @property
@@ -114,7 +139,7 @@ class Config: # pragma: no cov
114
139
  # NOTE: Register
115
140
  @property
116
141
  def regis_hook(self) -> list[str]:
117
- regis_hook_str: str = env("CORE_REGISTRY", "src")
142
+ regis_hook_str: str = env("CORE_REGISTRY", ".")
118
143
  return [r.strip() for r in regis_hook_str.split(",")]
119
144
 
120
145
  @property
@@ -220,6 +245,9 @@ class Config: # pragma: no cov
220
245
  return str2bool(env("API_ENABLE_ROUTE_SCHEDULE", "true"))
221
246
 
222
247
 
248
+ C = TypeVar("C", bound=BaseConfig)
249
+
250
+
223
251
  class SimLoad:
224
252
  """Simple Load Object that will search config data by given some identity
225
253
  value like name of workflow or on.
@@ -243,7 +271,7 @@ class SimLoad:
243
271
  def __init__(
244
272
  self,
245
273
  name: str,
246
- conf: Config,
274
+ conf: C,
247
275
  externals: DictData | None = None,
248
276
  ) -> None:
249
277
  self.data: DictData = {}
@@ -256,7 +284,7 @@ class SimLoad:
256
284
  if not self.data:
257
285
  raise ValueError(f"Config {name!r} does not found on conf path")
258
286
 
259
- self.conf: Config = conf
287
+ self.conf: C = conf
260
288
  self.externals: DictData = externals or {}
261
289
  self.data.update(self.externals)
262
290
 
@@ -264,7 +292,7 @@ class SimLoad:
264
292
  def finds(
265
293
  cls,
266
294
  obj: object,
267
- conf: Config,
295
+ conf: C,
268
296
  *,
269
297
  included: list[str] | None = None,
270
298
  excluded: list[str] | None = None,
@@ -273,7 +301,7 @@ class SimLoad:
273
301
  method can use include and exclude list of identity name for filter and
274
302
  adds-on.
275
303
 
276
- :param obj: A object that want to validate matching before return.
304
+ :param obj: An object that want to validate matching before return.
277
305
  :param conf: A config object.
278
306
  :param included:
279
307
  :param excluded:
@@ -338,7 +366,7 @@ class Loader(SimLoad):
338
366
  ) -> Iterator[tuple[str, DictData]]:
339
367
  """Override the find class method from the Simple Loader object.
340
368
 
341
- :param obj: A object that want to validate matching before return.
369
+ :param obj: An object that want to validate matching before return.
342
370
  :param included:
343
371
  :param excluded:
344
372
 
@@ -50,6 +50,7 @@ def tag(
50
50
  :param: name: A tag name for make different use-case of a function.
51
51
  :param: alias: A alias function name that keeping in registries. If this
52
52
  value does not supply, it will use original function name from __name__.
53
+
53
54
  :rtype: Callable[P, TagFunc]
54
55
  """
55
56
 
@@ -58,7 +59,7 @@ def tag(
58
59
  func.name = alias or func.__name__.replace("_", "-")
59
60
 
60
61
  @wraps(func)
61
- def wrapped(*args, **kwargs):
62
+ def wrapped(*args: P.args, **kwargs: P.kwargs) -> TagFunc:
62
63
  # NOTE: Able to do anything before calling hook function.
63
64
  return func(*args, **kwargs)
64
65
 
@@ -74,10 +75,13 @@ def make_registry(submodule: str) -> dict[str, Registry]:
74
75
  """Return registries of all functions that able to called with task.
75
76
 
76
77
  :param submodule: A module prefix that want to import registry.
78
+
77
79
  :rtype: dict[str, Registry]
78
80
  """
79
81
  rs: dict[str, Registry] = {}
80
- for module in config.regis_hook:
82
+ regis_hooks: list[str] = config.regis_hook
83
+ regis_hooks.extend(["ddeutil.vendors"])
84
+ for module in regis_hooks:
81
85
  # NOTE: try to sequential import task functions
82
86
  try:
83
87
  importer = import_module(f"{module}.{submodule}")
@@ -58,7 +58,7 @@ from .utils import (
58
58
  batch,
59
59
  delay,
60
60
  )
61
- from .workflow import Workflow, WorkflowQueue, WorkflowRelease, WorkflowTask
61
+ from .workflow import Release, ReleaseQueue, Workflow, WorkflowTask
62
62
 
63
63
  P = ParamSpec("P")
64
64
  logger = get_logger("ddeutil.workflow")
@@ -170,7 +170,7 @@ class WorkflowSchedule(BaseModel):
170
170
  def tasks(
171
171
  self,
172
172
  start_date: datetime,
173
- queue: dict[str, WorkflowQueue],
173
+ queue: dict[str, ReleaseQueue],
174
174
  *,
175
175
  externals: DictData | None = None,
176
176
  ) -> list[WorkflowTask]:
@@ -193,7 +193,7 @@ class WorkflowSchedule(BaseModel):
193
193
 
194
194
  # NOTE: Loading workflow model from the name of workflow.
195
195
  wf: Workflow = Workflow.from_loader(self.name, externals=extras)
196
- wf_queue: WorkflowQueue = queue[self.alias]
196
+ wf_queue: ReleaseQueue = queue[self.alias]
197
197
 
198
198
  # IMPORTANT: Create the default 'on' value if it does not pass the `on`
199
199
  # field to the Schedule object.
@@ -280,7 +280,7 @@ class Schedule(BaseModel):
280
280
  def tasks(
281
281
  self,
282
282
  start_date: datetime,
283
- queue: dict[str, WorkflowQueue],
283
+ queue: dict[str, ReleaseQueue],
284
284
  *,
285
285
  externals: DictData | None = None,
286
286
  ) -> list[WorkflowTask]:
@@ -289,7 +289,7 @@ class Schedule(BaseModel):
289
289
 
290
290
  :param start_date: A start date that get from the workflow schedule.
291
291
  :param queue: A mapping of name and list of datetime for queue.
292
- :type queue: dict[str, WorkflowQueue]
292
+ :type queue: dict[str, ReleaseQueue]
293
293
  :param externals: An external parameters that pass to the Loader object.
294
294
  :type externals: DictData | None
295
295
 
@@ -302,7 +302,7 @@ class Schedule(BaseModel):
302
302
  for workflow in self.workflows:
303
303
 
304
304
  if workflow.alias not in queue:
305
- queue[workflow.alias] = WorkflowQueue()
305
+ queue[workflow.alias] = ReleaseQueue()
306
306
 
307
307
  workflow_tasks.extend(
308
308
  workflow.tasks(start_date, queue=queue, externals=externals)
@@ -355,7 +355,7 @@ ReleaseThreads = dict[str, ReleaseThread]
355
355
  def schedule_task(
356
356
  tasks: list[WorkflowTask],
357
357
  stop: datetime,
358
- queue: dict[str, WorkflowQueue],
358
+ queue: dict[str, ReleaseQueue],
359
359
  threads: ReleaseThreads,
360
360
  log: type[Log],
361
361
  ) -> CancelJob | None:
@@ -366,7 +366,7 @@ def schedule_task(
366
366
 
367
367
  :param tasks: A list of WorkflowTask object.
368
368
  :param stop: A stop datetime object that force stop running scheduler.
369
- :param queue: A mapping of alias name and WorkflowQueue object.
369
+ :param queue: A mapping of alias name and ReleaseQueue object.
370
370
  :param threads: A mapping of alias name and Thread object.
371
371
  :param log: A log class that want to make log object.
372
372
 
@@ -390,7 +390,7 @@ def schedule_task(
390
390
  #
391
391
  for task in tasks:
392
392
 
393
- q: WorkflowQueue = queue[task.alias]
393
+ q: ReleaseQueue = queue[task.alias]
394
394
 
395
395
  # NOTE: Start adding queue and move the runner date in the WorkflowTask.
396
396
  task.queue(stop, q, log=log)
@@ -418,7 +418,7 @@ def schedule_task(
418
418
  continue
419
419
 
420
420
  # NOTE: Pop the latest release and push it to running.
421
- release: WorkflowRelease = heappop(q.queue)
421
+ release: Release = heappop(q.queue)
422
422
  heappush(q.running, release)
423
423
 
424
424
  logger.info(
@@ -499,7 +499,7 @@ def schedule_control(
499
499
  stop_date: datetime = stop or (start_date + config.stop_boundary_delta)
500
500
 
501
501
  # IMPORTANT: Create main mapping of queue and thread object.
502
- queue: dict[str, WorkflowQueue] = {}
502
+ queue: dict[str, ReleaseQueue] = {}
503
503
  threads: ReleaseThreads = {}
504
504
 
505
505
  start_date_waiting: datetime = start_date.replace(
@@ -295,10 +295,7 @@ def str2template(
295
295
  return search_env_replace(value)
296
296
 
297
297
 
298
- def param2template(
299
- value: Any,
300
- params: DictData,
301
- ) -> Any:
298
+ def param2template(value: Any, params: DictData) -> Any:
302
299
  """Pass param to template string that can search by ``RE_CALLER`` regular
303
300
  expression.
304
301