ddeutil-workflow 0.0.23__py3-none-any.whl → 0.0.25__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 +6 -0
- ddeutil/workflow/__init__.py +8 -7
- ddeutil/workflow/api/__init__.py +1 -0
- ddeutil/workflow/{api.py → api/api.py} +43 -21
- ddeutil/workflow/{repeat.py → api/repeat.py} +2 -2
- ddeutil/workflow/{route.py → api/route.py} +81 -62
- ddeutil/workflow/cli.py +33 -55
- ddeutil/workflow/conf.py +38 -45
- ddeutil/workflow/{on.py → cron.py} +1 -1
- ddeutil/workflow/exceptions.py +3 -0
- ddeutil/workflow/scheduler.py +212 -165
- ddeutil/workflow/stage.py +5 -0
- ddeutil/workflow/utils.py +7 -5
- ddeutil/workflow/workflow.py +149 -149
- {ddeutil_workflow-0.0.23.dist-info → ddeutil_workflow-0.0.25.dist-info}/METADATA +33 -35
- ddeutil_workflow-0.0.25.dist-info/RECORD +25 -0
- ddeutil_workflow-0.0.23.dist-info/RECORD +0 -24
- {ddeutil_workflow-0.0.23.dist-info → ddeutil_workflow-0.0.25.dist-info}/LICENSE +0 -0
- {ddeutil_workflow-0.0.23.dist-info → ddeutil_workflow-0.0.25.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.23.dist-info → ddeutil_workflow-0.0.25.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.23.dist-info → ddeutil_workflow-0.0.25.dist-info}/top_level.txt +0 -0
ddeutil/workflow/conf.py
CHANGED
@@ -16,14 +16,14 @@ from pathlib import Path
|
|
16
16
|
from typing import ClassVar, Optional, TypeVar, Union
|
17
17
|
from zoneinfo import ZoneInfo
|
18
18
|
|
19
|
-
from ddeutil.core import
|
19
|
+
from ddeutil.core import str2bool
|
20
20
|
from ddeutil.io import PathSearch, YamlFlResolve
|
21
21
|
from dotenv import load_dotenv
|
22
22
|
from pydantic import BaseModel, Field
|
23
23
|
from pydantic.functional_validators import model_validator
|
24
24
|
from typing_extensions import Self
|
25
25
|
|
26
|
-
from .__types import DictData
|
26
|
+
from .__types import DictData, TupleStr
|
27
27
|
|
28
28
|
AnyModel = TypeVar("AnyModel", bound=BaseModel)
|
29
29
|
AnyModelType = type[AnyModel]
|
@@ -32,6 +32,18 @@ load_dotenv()
|
|
32
32
|
|
33
33
|
env = os.getenv
|
34
34
|
|
35
|
+
__all__: TupleStr = (
|
36
|
+
"get_logger",
|
37
|
+
"Config",
|
38
|
+
"SimLoad",
|
39
|
+
"Loader",
|
40
|
+
"config",
|
41
|
+
"logger",
|
42
|
+
"FileLog",
|
43
|
+
"SQLiteLog",
|
44
|
+
"Log",
|
45
|
+
)
|
46
|
+
|
35
47
|
|
36
48
|
@lru_cache
|
37
49
|
def get_logger(name: str):
|
@@ -70,7 +82,7 @@ class Config:
|
|
70
82
|
|
71
83
|
# NOTE: Register
|
72
84
|
regis_hook_str: str = os.getenv(
|
73
|
-
"WORKFLOW_CORE_REGISTRY", "ddeutil.workflow"
|
85
|
+
"WORKFLOW_CORE_REGISTRY", "src,src.ddeutil.workflow,tests,tests.utils"
|
74
86
|
)
|
75
87
|
regis_filter_str: str = os.getenv(
|
76
88
|
"WORKFLOW_CORE_REGISTRY_FILTER", "ddeutil.workflow.utils"
|
@@ -107,7 +119,10 @@ class Config:
|
|
107
119
|
os.getenv("WORKFLOW_CORE_MAX_NUM_POKING", "4")
|
108
120
|
)
|
109
121
|
max_on_per_workflow: int = int(
|
110
|
-
env("
|
122
|
+
env("WORKFLOW_CORE_MAX_CRON_PER_WORKFLOW", "5")
|
123
|
+
)
|
124
|
+
max_queue_complete_hist: int = int(
|
125
|
+
os.getenv("WORKFLOW_CORE_MAX_QUEUE_COMPLETE_HIST", "16")
|
111
126
|
)
|
112
127
|
|
113
128
|
# NOTE: Schedule App
|
@@ -120,11 +135,12 @@ class Config:
|
|
120
135
|
)
|
121
136
|
|
122
137
|
# NOTE: API
|
138
|
+
prefix_path: str = env("WORKFLOW_API_PREFIX_PATH", "/api/v1")
|
123
139
|
enable_route_workflow: bool = str2bool(
|
124
|
-
|
140
|
+
env("WORKFLOW_API_ENABLE_ROUTE_WORKFLOW", "true")
|
125
141
|
)
|
126
142
|
enable_route_schedule: bool = str2bool(
|
127
|
-
|
143
|
+
env("WORKFLOW_API_ENABLE_ROUTE_SCHEDULE", "true")
|
128
144
|
)
|
129
145
|
|
130
146
|
def __init__(self) -> None:
|
@@ -215,8 +231,8 @@ class SimLoad:
|
|
215
231
|
obj: object,
|
216
232
|
conf: Config,
|
217
233
|
*,
|
218
|
-
|
219
|
-
|
234
|
+
included: list[str] | None = None,
|
235
|
+
excluded: list[str] | None = None,
|
220
236
|
) -> Iterator[tuple[str, DictData]]:
|
221
237
|
"""Find all data that match with object type in config path. This class
|
222
238
|
method can use include and exclude list of identity name for filter and
|
@@ -224,22 +240,23 @@ class SimLoad:
|
|
224
240
|
|
225
241
|
:param obj: A object that want to validate matching before return.
|
226
242
|
:param conf: A config object.
|
227
|
-
:param
|
228
|
-
:param
|
243
|
+
:param included:
|
244
|
+
:param excluded:
|
229
245
|
|
230
246
|
:rtype: Iterator[tuple[str, DictData]]
|
231
247
|
"""
|
232
|
-
exclude: list[str] =
|
248
|
+
exclude: list[str] = excluded or []
|
233
249
|
for file in PathSearch(conf.conf_path).files:
|
250
|
+
|
234
251
|
for key, data in cls.filter_suffix(file).items():
|
235
252
|
|
236
253
|
if key in exclude:
|
237
254
|
continue
|
238
255
|
|
239
|
-
if
|
256
|
+
if data["type"] == obj.__name__:
|
240
257
|
yield key, (
|
241
|
-
{k: data[k] for k in data if k in
|
242
|
-
if
|
258
|
+
{k: data[k] for k in data if k in included}
|
259
|
+
if included
|
243
260
|
else data
|
244
261
|
)
|
245
262
|
|
@@ -251,14 +268,14 @@ class SimLoad:
|
|
251
268
|
return {}
|
252
269
|
|
253
270
|
@cached_property
|
254
|
-
def type(self) ->
|
271
|
+
def type(self) -> str:
|
255
272
|
"""Return object of string type which implement on any registry. The
|
256
273
|
object type.
|
257
274
|
|
258
275
|
:rtype: AnyModelType
|
259
276
|
"""
|
260
277
|
if _typ := self.data.get("type"):
|
261
|
-
return
|
278
|
+
return _typ
|
262
279
|
raise ValueError(
|
263
280
|
f"the 'type' value: {_typ} does not exists in config data."
|
264
281
|
)
|
@@ -276,47 +293,26 @@ class Loader(SimLoad):
|
|
276
293
|
cls,
|
277
294
|
obj: object,
|
278
295
|
*,
|
279
|
-
|
280
|
-
|
296
|
+
included: list[str] | None = None,
|
297
|
+
excluded: list[str] | None = None,
|
281
298
|
**kwargs,
|
282
299
|
) -> Iterator[tuple[str, DictData]]:
|
283
300
|
"""Override the find class method from the Simple Loader object.
|
284
301
|
|
285
302
|
:param obj: A object that want to validate matching before return.
|
286
|
-
:param
|
287
|
-
:param
|
303
|
+
:param included:
|
304
|
+
:param excluded:
|
288
305
|
|
289
306
|
:rtype: Iterator[tuple[str, DictData]]
|
290
307
|
"""
|
291
308
|
return super().finds(
|
292
|
-
obj=obj, conf=Config(),
|
309
|
+
obj=obj, conf=Config(), included=included, excluded=excluded
|
293
310
|
)
|
294
311
|
|
295
312
|
def __init__(self, name: str, externals: DictData) -> None:
|
296
313
|
super().__init__(name, conf=Config(), externals=externals)
|
297
314
|
|
298
315
|
|
299
|
-
def get_type(t: str, params: Config) -> AnyModelType:
|
300
|
-
"""Return import type from string importable value in the type key.
|
301
|
-
|
302
|
-
:param t: A importable type string.
|
303
|
-
:param params: A config parameters that use registry to search this
|
304
|
-
type.
|
305
|
-
|
306
|
-
:rtype: AnyModelType
|
307
|
-
"""
|
308
|
-
try:
|
309
|
-
# NOTE: Auto adding module prefix if it does not set
|
310
|
-
return import_string(f"ddeutil.workflow.{t}")
|
311
|
-
except ModuleNotFoundError:
|
312
|
-
for registry in params.regis_hook:
|
313
|
-
try:
|
314
|
-
return import_string(f"{registry}.{t}")
|
315
|
-
except ModuleNotFoundError:
|
316
|
-
continue
|
317
|
-
return import_string(f"{t}")
|
318
|
-
|
319
|
-
|
320
316
|
config = Config()
|
321
317
|
logger = get_logger("ddeutil.workflow")
|
322
318
|
|
@@ -472,9 +468,6 @@ class FileLog(BaseLog):
|
|
472
468
|
if not config.enable_write_log:
|
473
469
|
return self
|
474
470
|
|
475
|
-
logger.debug(
|
476
|
-
f"({self.run_id}) [LOG]: Start writing log: {self.name!r}."
|
477
|
-
)
|
478
471
|
log_file: Path = self.pointer() / f"{self.run_id}.log"
|
479
472
|
log_file.write_text(
|
480
473
|
json.dumps(
|
@@ -121,7 +121,7 @@ class On(BaseModel):
|
|
121
121
|
loader: Loader = Loader(name, externals=externals)
|
122
122
|
|
123
123
|
# NOTE: Validate the config type match with current connection model
|
124
|
-
if loader.type != cls:
|
124
|
+
if loader.type != cls.__name__:
|
125
125
|
raise ValueError(f"Type {loader.type} does not match with {cls}")
|
126
126
|
|
127
127
|
loader_data: DictData = loader.data
|