ddeutil-workflow 0.0.19__py3-none-any.whl → 0.0.21__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 +28 -2
- ddeutil/workflow/__init__.py +9 -4
- ddeutil/workflow/__types.py +1 -0
- ddeutil/workflow/conf.py +34 -25
- ddeutil/workflow/exceptions.py +4 -0
- ddeutil/workflow/job.py +96 -101
- ddeutil/workflow/on.py +4 -15
- ddeutil/workflow/scheduler.py +60 -963
- ddeutil/workflow/stage.py +94 -68
- ddeutil/workflow/utils.py +29 -24
- ddeutil/workflow/workflow.py +1132 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.21.dist-info}/METADATA +9 -8
- ddeutil_workflow-0.0.21.dist-info/RECORD +22 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.21.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.19.dist-info/RECORD +0 -21
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.21.dist-info}/LICENSE +0 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.21.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.21.dist-info}/top_level.txt +0 -0
ddeutil/workflow/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__: str = "0.0.
|
1
|
+
__version__: str = "0.0.21"
|
ddeutil/workflow/__cron.py
CHANGED
@@ -653,6 +653,20 @@ class CronJob:
|
|
653
653
|
|
654
654
|
|
655
655
|
class CronJobYear(CronJob):
|
656
|
+
"""The Cron Job Converter with Year extension object that generate datetime
|
657
|
+
dimension of cron job schedule format,
|
658
|
+
|
659
|
+
* * * * * * <command to execute>
|
660
|
+
|
661
|
+
(i) minute (0 - 59)
|
662
|
+
(ii) hour (0 - 23)
|
663
|
+
(iii) day of the month (1 - 31)
|
664
|
+
(iv) month (1 - 12)
|
665
|
+
(v) day of the week (0 - 6) (Sunday to Saturday; 7 is also Sunday
|
666
|
+
on some systems)
|
667
|
+
(vi) year (1990 - 2100)
|
668
|
+
"""
|
669
|
+
|
656
670
|
cron_length = 6
|
657
671
|
cron_units = CRON_UNITS_YEAR
|
658
672
|
|
@@ -705,9 +719,17 @@ class CronRunner:
|
|
705
719
|
else:
|
706
720
|
self.date: datetime = datetime.now(tz=self.tz)
|
707
721
|
|
722
|
+
# NOTE: Add one second if the microsecond value more than 0.
|
723
|
+
if self.date.microsecond > 0:
|
724
|
+
self.date: datetime = self.date.replace(microsecond=0) + timedelta(
|
725
|
+
seconds=1
|
726
|
+
)
|
727
|
+
|
708
728
|
# NOTE: Add one minute if the second value more than 0.
|
709
729
|
if self.date.second > 0:
|
710
|
-
self.date: datetime = self.date + timedelta(
|
730
|
+
self.date: datetime = self.date.replace(second=0) + timedelta(
|
731
|
+
minutes=1
|
732
|
+
)
|
711
733
|
|
712
734
|
self.__start_date: datetime = self.date
|
713
735
|
self.cron: CronJob | CronJobYear = cron
|
@@ -753,7 +775,7 @@ class CronRunner:
|
|
753
775
|
not self.__shift_date(mode, reverse)
|
754
776
|
for mode in ("year", "month", "day", "hour", "minute")
|
755
777
|
):
|
756
|
-
return copy.deepcopy(self.date
|
778
|
+
return copy.deepcopy(self.date)
|
757
779
|
|
758
780
|
raise RecursionError("Unable to find execution time for schedule")
|
759
781
|
|
@@ -802,6 +824,10 @@ class CronRunner:
|
|
802
824
|
# NOTE: Replace date that less than it mode to zero.
|
803
825
|
self.date: datetime = replace_date(self.date, mode, reverse=reverse)
|
804
826
|
|
827
|
+
# NOTE: Replace second and microsecond values that change from
|
828
|
+
# the replace_date func with reverse flag.
|
829
|
+
self.date: datetime = self.date.replace(second=0, microsecond=0)
|
830
|
+
|
805
831
|
if current_value != getattr(self.date, switch[mode]):
|
806
832
|
return mode != "month"
|
807
833
|
|
ddeutil/workflow/__init__.py
CHANGED
@@ -15,7 +15,10 @@ from .exceptions import (
|
|
15
15
|
UtilException,
|
16
16
|
WorkflowException,
|
17
17
|
)
|
18
|
-
from .job import
|
18
|
+
from .job import (
|
19
|
+
Job,
|
20
|
+
Strategy,
|
21
|
+
)
|
19
22
|
from .on import (
|
20
23
|
On,
|
21
24
|
YearOn,
|
@@ -24,8 +27,6 @@ from .on import (
|
|
24
27
|
from .scheduler import (
|
25
28
|
Schedule,
|
26
29
|
ScheduleWorkflow,
|
27
|
-
Workflow,
|
28
|
-
WorkflowTaskData,
|
29
30
|
)
|
30
31
|
from .stage import (
|
31
32
|
BashStage,
|
@@ -34,7 +35,7 @@ from .stage import (
|
|
34
35
|
PyStage,
|
35
36
|
Stage,
|
36
37
|
TriggerStage,
|
37
|
-
|
38
|
+
extract_hook,
|
38
39
|
)
|
39
40
|
from .utils import (
|
40
41
|
FILTERS,
|
@@ -70,3 +71,7 @@ from .utils import (
|
|
70
71
|
str2template,
|
71
72
|
tag,
|
72
73
|
)
|
74
|
+
from .workflow import (
|
75
|
+
Workflow,
|
76
|
+
WorkflowTaskData,
|
77
|
+
)
|
ddeutil/workflow/__types.py
CHANGED
ddeutil/workflow/conf.py
CHANGED
@@ -33,6 +33,29 @@ load_dotenv()
|
|
33
33
|
env = os.getenv
|
34
34
|
|
35
35
|
|
36
|
+
@lru_cache
|
37
|
+
def get_logger(name: str):
|
38
|
+
"""Return logger object with an input module name.
|
39
|
+
|
40
|
+
:param name: A module name that want to log.
|
41
|
+
"""
|
42
|
+
lg = logging.getLogger(name)
|
43
|
+
formatter = logging.Formatter(
|
44
|
+
fmt=(
|
45
|
+
"%(asctime)s.%(msecs)03d (%(name)-10s, %(process)-5d, "
|
46
|
+
"%(thread)-5d) [%(levelname)-7s] %(message)-120s "
|
47
|
+
"(%(filename)s:%(lineno)s)"
|
48
|
+
),
|
49
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
50
|
+
)
|
51
|
+
stream = logging.StreamHandler()
|
52
|
+
stream.setFormatter(formatter)
|
53
|
+
lg.addHandler(stream)
|
54
|
+
|
55
|
+
lg.setLevel(logging.DEBUG if config.debug else logging.INFO)
|
56
|
+
return lg
|
57
|
+
|
58
|
+
|
36
59
|
class Config:
|
37
60
|
"""Config object for keeping application configuration on current session
|
38
61
|
without changing when if the application still running.
|
@@ -77,6 +100,9 @@ class Config:
|
|
77
100
|
|
78
101
|
# NOTE: Workflow
|
79
102
|
max_job_parallel: int = int(env("WORKFLOW_CORE_MAX_JOB_PARALLEL", "2"))
|
103
|
+
max_job_exec_timeout: int = int(
|
104
|
+
env("WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT", "600")
|
105
|
+
)
|
80
106
|
max_poking_pool_worker: int = int(
|
81
107
|
os.getenv("WORKFLOW_CORE_MAX_NUM_POKING", "4")
|
82
108
|
)
|
@@ -98,12 +124,14 @@ class Config:
|
|
98
124
|
os.getenv("WORKFLOW_API_ENABLE_ROUTE_SCHEDULE", "true")
|
99
125
|
)
|
100
126
|
|
101
|
-
def __init__(self):
|
127
|
+
def __init__(self) -> None:
|
128
|
+
# VALIDATE: the MAX_JOB_PARALLEL value should not less than 0.
|
102
129
|
if self.max_job_parallel < 0:
|
103
130
|
raise ValueError(
|
104
131
|
f"``MAX_JOB_PARALLEL`` should more than 0 but got "
|
105
132
|
f"{self.max_job_parallel}."
|
106
133
|
)
|
134
|
+
|
107
135
|
try:
|
108
136
|
self.stop_boundary_delta: timedelta = timedelta(
|
109
137
|
**json.loads(self.stop_boundary_delta_str)
|
@@ -287,29 +315,7 @@ def get_type(t: str, params: Config) -> AnyModelType:
|
|
287
315
|
|
288
316
|
|
289
317
|
config = Config()
|
290
|
-
|
291
|
-
|
292
|
-
@lru_cache
|
293
|
-
def get_logger(name: str):
|
294
|
-
"""Return logger object with an input module name.
|
295
|
-
|
296
|
-
:param name: A module name that want to log.
|
297
|
-
"""
|
298
|
-
logger = logging.getLogger(name)
|
299
|
-
formatter = logging.Formatter(
|
300
|
-
fmt=(
|
301
|
-
"%(asctime)s.%(msecs)03d (%(name)-10s, %(process)-5d, "
|
302
|
-
"%(thread)-5d) [%(levelname)-7s] %(message)-120s "
|
303
|
-
"(%(filename)s:%(lineno)s)"
|
304
|
-
),
|
305
|
-
datefmt="%Y-%m-%d %H:%M:%S",
|
306
|
-
)
|
307
|
-
stream = logging.StreamHandler()
|
308
|
-
stream.setFormatter(formatter)
|
309
|
-
logger.addHandler(stream)
|
310
|
-
|
311
|
-
logger.setLevel(logging.DEBUG if config.debug else logging.INFO)
|
312
|
-
return logger
|
318
|
+
logger = get_logger("ddeutil.workflow")
|
313
319
|
|
314
320
|
|
315
321
|
class BaseLog(BaseModel, ABC):
|
@@ -319,8 +325,8 @@ class BaseLog(BaseModel, ABC):
|
|
319
325
|
"""
|
320
326
|
|
321
327
|
name: str = Field(description="A workflow name.")
|
322
|
-
on: str = Field(description="A cronjob string of this piepline schedule.")
|
323
328
|
release: datetime = Field(description="A release datetime.")
|
329
|
+
type: str = Field(description="A running type before logging.")
|
324
330
|
context: DictData = Field(
|
325
331
|
default_factory=dict,
|
326
332
|
description=(
|
@@ -462,6 +468,9 @@ class FileLog(BaseLog):
|
|
462
468
|
if not config.enable_write_log:
|
463
469
|
return self
|
464
470
|
|
471
|
+
logger.debug(
|
472
|
+
f"({self.run_id}) [LOG]: Start writing log: {self.name!r}."
|
473
|
+
)
|
465
474
|
log_file: Path = self.pointer() / f"{self.run_id}.log"
|
466
475
|
log_file.write_text(
|
467
476
|
json.dumps(
|
ddeutil/workflow/exceptions.py
CHANGED
@@ -3,6 +3,10 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
+
"""Exception objects for this package do not do anything because I want to
|
7
|
+
create the lightweight workflow package. So, this module do just a exception
|
8
|
+
annotate for handle error only.
|
9
|
+
"""
|
6
10
|
from __future__ import annotations
|
7
11
|
|
8
12
|
|