ddeutil-workflow 0.0.55__py3-none-any.whl → 0.0.57__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/__cron.py +26 -12
- ddeutil/workflow/__init__.py +4 -2
- ddeutil/workflow/__main__.py +30 -0
- ddeutil/workflow/__types.py +1 -0
- ddeutil/workflow/conf.py +163 -101
- ddeutil/workflow/{cron.py → event.py} +37 -20
- ddeutil/workflow/exceptions.py +44 -14
- ddeutil/workflow/job.py +87 -58
- ddeutil/workflow/logs.py +13 -5
- ddeutil/workflow/result.py +9 -4
- ddeutil/workflow/scheduler.py +38 -73
- ddeutil/workflow/stages.py +370 -147
- ddeutil/workflow/utils.py +37 -6
- ddeutil/workflow/workflow.py +243 -302
- {ddeutil_workflow-0.0.55.dist-info → ddeutil_workflow-0.0.57.dist-info}/METADATA +41 -35
- ddeutil_workflow-0.0.57.dist-info/RECORD +31 -0
- {ddeutil_workflow-0.0.55.dist-info → ddeutil_workflow-0.0.57.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.57.dist-info/entry_points.txt +2 -0
- ddeutil_workflow-0.0.55.dist-info/RECORD +0 -30
- {ddeutil_workflow-0.0.55.dist-info → ddeutil_workflow-0.0.57.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.55.dist-info → ddeutil_workflow-0.0.57.dist-info}/top_level.txt +0 -0
ddeutil/workflow/scheduler.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
# [x] Use dynamic config
|
7
6
|
"""The main schedule running is `schedule_runner` function that trigger the
|
8
7
|
multiprocess of `schedule_control` function for listing schedules on the
|
9
8
|
config by `Loader.finds(Schedule)`.
|
@@ -17,6 +16,11 @@ functions; `workflow_task`, and `workflow_monitor`.
|
|
17
16
|
The `schedule_task` will run `task.release` method in threading object
|
18
17
|
for multithreading strategy. This `release` method will run only one crontab
|
19
18
|
value with the on field.
|
19
|
+
|
20
|
+
Steps:
|
21
|
+
- Extract all schedule config on the conf path.
|
22
|
+
- Slice schedules to multiprocess
|
23
|
+
- Start running task.
|
20
24
|
"""
|
21
25
|
from __future__ import annotations
|
22
26
|
|
@@ -34,7 +38,7 @@ from heapq import heappop, heappush
|
|
34
38
|
from pathlib import Path
|
35
39
|
from textwrap import dedent
|
36
40
|
from threading import Thread
|
37
|
-
from typing import Callable, Optional, TypedDict, Union
|
41
|
+
from typing import Any, Callable, Optional, TypedDict, Union
|
38
42
|
|
39
43
|
from pydantic import BaseModel, Field, ValidationInfo
|
40
44
|
from pydantic.functional_validators import field_validator, model_validator
|
@@ -52,8 +56,8 @@ except ImportError: # pragma: no cov
|
|
52
56
|
|
53
57
|
from .__cron import CronRunner
|
54
58
|
from .__types import DictData, TupleStr
|
55
|
-
from .conf import
|
56
|
-
from .
|
59
|
+
from .conf import FileLoad, Loader, dynamic
|
60
|
+
from .event import On
|
57
61
|
from .exceptions import ScheduleException, WorkflowException
|
58
62
|
from .logs import Audit, get_audit
|
59
63
|
from .result import SUCCESS, Result
|
@@ -113,21 +117,15 @@ class ScheduleWorkflow(BaseModel):
|
|
113
117
|
)
|
114
118
|
|
115
119
|
@model_validator(mode="before")
|
116
|
-
def __prepare_before__(cls,
|
117
|
-
"""Prepare incoming values before validating with model fields.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
# VALIDATE: Add default the alias field with the name.
|
126
|
-
if not values.get("alias"):
|
127
|
-
values["alias"] = values.get("name")
|
128
|
-
|
129
|
-
cls.__bypass_on(values, extras=values.get("extras"))
|
130
|
-
return values
|
120
|
+
def __prepare_before__(cls, data: Any) -> Any:
|
121
|
+
"""Prepare incoming values before validating with model fields."""
|
122
|
+
if isinstance(data, dict):
|
123
|
+
# VALIDATE: Add default the alias field with the name.
|
124
|
+
if "alias" not in data:
|
125
|
+
data["alias"] = data.get("name")
|
126
|
+
|
127
|
+
cls.__bypass_on(data, extras=data.get("extras"))
|
128
|
+
return data
|
131
129
|
|
132
130
|
@classmethod
|
133
131
|
def __bypass_on(
|
@@ -135,8 +133,10 @@ class ScheduleWorkflow(BaseModel):
|
|
135
133
|
) -> DictData:
|
136
134
|
"""Bypass and prepare the on data to loaded config data.
|
137
135
|
|
138
|
-
:param data: A data that want to validate for model
|
139
|
-
|
136
|
+
:param data: (DictData) A data that want to validate for the model
|
137
|
+
initialization.
|
138
|
+
:param extras: (DictData) An extra parameter that want to override core
|
139
|
+
config values.
|
140
140
|
|
141
141
|
:rtype: DictData
|
142
142
|
"""
|
@@ -151,7 +151,7 @@ class ScheduleWorkflow(BaseModel):
|
|
151
151
|
# NOTE: Pass on value to Loader and keep on model object to on
|
152
152
|
# field.
|
153
153
|
data["on"] = [
|
154
|
-
|
154
|
+
FileLoad(n, externals=extras).data if isinstance(n, str) else n
|
155
155
|
for n in on
|
156
156
|
]
|
157
157
|
|
@@ -162,6 +162,10 @@ class ScheduleWorkflow(BaseModel):
|
|
162
162
|
"""Validate the on fields should not contain duplicate values and if it
|
163
163
|
contains every minute value, it should have only one on value.
|
164
164
|
|
165
|
+
:param value: (list[On]) A list of `On` object.
|
166
|
+
:param info: (ValidationInfo) An validation info object for getting an
|
167
|
+
extra parameter.
|
168
|
+
|
165
169
|
:rtype: list[On]
|
166
170
|
"""
|
167
171
|
set_ons: set[str] = {str(on.cronjob) for on in value}
|
@@ -191,23 +195,22 @@ class ScheduleWorkflow(BaseModel):
|
|
191
195
|
This task creation need queue to tracking release date already
|
192
196
|
mapped or not.
|
193
197
|
|
194
|
-
:param start_date: A start
|
195
|
-
|
198
|
+
:param start_date: (datetime) A start datetime that get from the
|
199
|
+
workflow schedule.
|
200
|
+
:param queue: (dict[str, ReleaseQueue]) A mapping of name and list of
|
201
|
+
datetime for queue.
|
196
202
|
|
197
203
|
:rtype: list[WorkflowTask]
|
198
204
|
:return: Return the list of WorkflowTask object from the specific
|
199
205
|
input datetime that mapping with the on field.
|
200
206
|
"""
|
201
|
-
workflow_tasks: list[WorkflowTask] = []
|
202
|
-
|
203
|
-
# NOTE: Loading workflow model from the name of workflow.
|
204
207
|
wf: Workflow = Workflow.from_conf(self.name, extras=self.extras)
|
205
208
|
wf_queue: ReleaseQueue = queue[self.alias]
|
206
209
|
|
207
210
|
# IMPORTANT: Create the default 'on' value if it does not pass the `on`
|
208
211
|
# field to the Schedule object.
|
209
212
|
ons: list[On] = self.on or wf.on.copy()
|
210
|
-
|
213
|
+
workflow_tasks: list[WorkflowTask] = []
|
211
214
|
for on in ons:
|
212
215
|
|
213
216
|
# NOTE: Create CronRunner instance from the start_date param.
|
@@ -250,7 +253,7 @@ class Schedule(BaseModel):
|
|
250
253
|
)
|
251
254
|
workflows: list[ScheduleWorkflow] = Field(
|
252
255
|
default_factory=list,
|
253
|
-
description="A list of ScheduleWorkflow
|
256
|
+
description="A list of ScheduleWorkflow model.",
|
254
257
|
)
|
255
258
|
|
256
259
|
@field_validator("desc", mode="after")
|
@@ -267,6 +270,8 @@ class Schedule(BaseModel):
|
|
267
270
|
def from_conf(
|
268
271
|
cls,
|
269
272
|
name: str,
|
273
|
+
*,
|
274
|
+
path: Optional[Path] = None,
|
270
275
|
extras: DictData | None = None,
|
271
276
|
) -> Self:
|
272
277
|
"""Create Schedule instance from the Loader object that only receive
|
@@ -274,6 +279,7 @@ class Schedule(BaseModel):
|
|
274
279
|
searching configuration data of this schedule model in conf path.
|
275
280
|
|
276
281
|
:param name: (str) A schedule name that want to pass to Loader object.
|
282
|
+
:param path: (Path) An override config path.
|
277
283
|
:param extras: An extra parameters that want to pass to Loader
|
278
284
|
object.
|
279
285
|
|
@@ -281,55 +287,14 @@ class Schedule(BaseModel):
|
|
281
287
|
|
282
288
|
:rtype: Self
|
283
289
|
"""
|
284
|
-
loader: Loader =
|
290
|
+
loader: Loader = FileLoad(name, path=path, extras=extras)
|
285
291
|
|
286
292
|
# NOTE: Validate the config type match with current connection model
|
287
293
|
if loader.type != cls.__name__:
|
288
294
|
raise ValueError(f"Type {loader.type} does not match with {cls}")
|
289
295
|
|
290
296
|
loader_data: DictData = copy.deepcopy(loader.data)
|
291
|
-
|
292
|
-
# NOTE: Add name to loader data
|
293
|
-
loader_data["name"] = name.replace(" ", "_")
|
294
|
-
|
295
|
-
if extras:
|
296
|
-
loader_data["extras"] = extras
|
297
|
-
|
298
|
-
return cls.model_validate(obj=loader_data)
|
299
|
-
|
300
|
-
@classmethod
|
301
|
-
def from_path(
|
302
|
-
cls,
|
303
|
-
name: str,
|
304
|
-
path: Path,
|
305
|
-
extras: DictData | None = None,
|
306
|
-
) -> Self:
|
307
|
-
"""Create Schedule instance from the SimLoad object that receive an
|
308
|
-
input schedule name and conf path. The loader object will use this
|
309
|
-
schedule name to searching configuration data of this schedule model
|
310
|
-
in conf path.
|
311
|
-
|
312
|
-
:param name: (str) A schedule name that want to pass to Loader object.
|
313
|
-
:param path: (Path) A config path that want to search.
|
314
|
-
:param extras: An external parameters that want to pass to Loader
|
315
|
-
object.
|
316
|
-
|
317
|
-
:raise ValueError: If the type does not match with current object.
|
318
|
-
|
319
|
-
:rtype: Self
|
320
|
-
"""
|
321
|
-
loader: SimLoad = SimLoad(
|
322
|
-
name, conf_path=path, externals=(extras or {})
|
323
|
-
)
|
324
|
-
|
325
|
-
# NOTE: Validate the config type match with current connection model
|
326
|
-
if loader.type != cls.__name__:
|
327
|
-
raise ValueError(f"Type {loader.type} does not match with {cls}")
|
328
|
-
|
329
|
-
loader_data: DictData = copy.deepcopy(loader.data)
|
330
|
-
|
331
|
-
# NOTE: Add name to loader data
|
332
|
-
loader_data["name"] = name.replace(" ", "_")
|
297
|
+
loader_data["name"] = name
|
333
298
|
|
334
299
|
if extras:
|
335
300
|
loader_data["extras"] = extras
|
@@ -738,7 +703,7 @@ def schedule_control(
|
|
738
703
|
:rtype: Result
|
739
704
|
"""
|
740
705
|
audit: type[Audit] = audit or get_audit(extras=extras)
|
741
|
-
result: Result = Result
|
706
|
+
result: Result = Result.construct_with_rs_or_id(parent_run_id=parent_run_id)
|
742
707
|
|
743
708
|
# NOTE: Create the start and stop datetime.
|
744
709
|
start_date: datetime = datetime.now(tz=dynamic("tz", extras=extras))
|