ddeutil-workflow 0.0.18__tar.gz → 0.0.19__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.
- {ddeutil_workflow-0.0.18/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.19}/PKG-INFO +5 -5
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/README.md +4 -4
- ddeutil_workflow-0.0.19/src/ddeutil/workflow/__about__.py +1 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/__cron.py +1 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/conf.py +18 -15
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/job.py +12 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/on.py +13 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/scheduler.py +157 -84
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/utils.py +32 -19
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19/src/ddeutil_workflow.egg-info}/PKG-INFO +5 -5
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/SOURCES.txt +0 -4
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test__cron.py +3 -1
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_conf_log.py +5 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job.py +1 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job_strategy.py +41 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_on.py +7 -4
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_scheduler.py +55 -16
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_scheduler_tasks.py +27 -8
- ddeutil_workflow-0.0.19/tests/test_stage.py +74 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_bash.py +6 -6
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_filter.py +11 -2
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_template.py +44 -54
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow.py +66 -43
- ddeutil_workflow-0.0.19/tests/test_workflow_poke.py +56 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow_run.py +70 -16
- ddeutil_workflow-0.0.18/src/ddeutil/workflow/__about__.py +0 -1
- ddeutil_workflow-0.0.18/tests/test__conf_exist.py +0 -12
- ddeutil_workflow-0.0.18/tests/test_stage.py +0 -58
- ddeutil_workflow-0.0.18/tests/test_workflow_matrix.py +0 -156
- ddeutil_workflow-0.0.18/tests/test_workflow_on.py +0 -12
- ddeutil_workflow-0.0.18/tests/test_workflow_params.py +0 -23
- ddeutil_workflow-0.0.18/tests/test_workflow_poke.py +0 -22
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/LICENSE +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/pyproject.toml +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/setup.cfg +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/__init__.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/__types.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/api.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/cli.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/exceptions.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/repeat.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/route.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/stage.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test__regex.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_conf.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job_py.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job_strategy_run.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_params.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_hook.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_py.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_trigger.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_params.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_result.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_tag.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow_depends.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow_job_run.py +0 -0
- {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/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.
|
3
|
+
Version: 0.0.19
|
4
4
|
Summary: Lightweight workflow orchestration with less dependencies
|
5
5
|
Author-email: ddeutils <korawich.anu@gmail.com>
|
6
6
|
License: MIT
|
@@ -46,7 +46,7 @@ for easy to make a simple metadata driven for data workflow orchestration.
|
|
46
46
|
It can to use for data operator by a `.yaml` template.
|
47
47
|
|
48
48
|
> [!WARNING]
|
49
|
-
> This package provide only orchestration workload
|
49
|
+
> This package provide only orchestration workload. That mean you should not
|
50
50
|
> use the workflow stage to process any large volume data which use lot of compute
|
51
51
|
> resource. :cold_sweat:
|
52
52
|
|
@@ -58,10 +58,10 @@ configuration. It called **Metadata Driven Data Workflow**.
|
|
58
58
|
|
59
59
|
**:pushpin: <u>Rules of This Workflow engine</u>**:
|
60
60
|
|
61
|
-
1. Minimum frequency unit of scheduling is **1 minute** :warning:
|
61
|
+
1. The Minimum frequency unit of scheduling is **1 minute** :warning:
|
62
62
|
2. Can not re-run only failed stage and its pending downstream :rotating_light:
|
63
63
|
3. All parallel tasks inside workflow engine use Multi-Threading
|
64
|
-
(
|
64
|
+
(Python 3.13 unlock GIL :unlock:)
|
65
65
|
|
66
66
|
> [!NOTE]
|
67
67
|
> _Disclaimer_: I inspire the dynamic statement from the [**GitHub Action**](https://github.com/features/actions)
|
@@ -183,7 +183,7 @@ application. If any configuration values do not set yet, it will use default val
|
|
183
183
|
and do not raise any error to you.
|
184
184
|
|
185
185
|
| Environment | Component | Default | Description | Remark |
|
186
|
-
|
186
|
+
|:----------------------------------------|:----------|:---------------------------------|--------------------------------------------------------------------------------------------------------------------|--------|
|
187
187
|
| `WORKFLOW_ROOT_PATH` | Core | . | The root path of the workflow application. | |
|
188
188
|
| `WORKFLOW_CORE_REGISTRY` | Core | src.ddeutil.workflow,tests.utils | List of importable string for the hook stage. | |
|
189
189
|
| `WORKFLOW_CORE_REGISTRY_FILTER` | Core | ddeutil.workflow.utils | List of importable string for the filter template. | |
|
@@ -13,7 +13,7 @@ for easy to make a simple metadata driven for data workflow orchestration.
|
|
13
13
|
It can to use for data operator by a `.yaml` template.
|
14
14
|
|
15
15
|
> [!WARNING]
|
16
|
-
> This package provide only orchestration workload
|
16
|
+
> This package provide only orchestration workload. That mean you should not
|
17
17
|
> use the workflow stage to process any large volume data which use lot of compute
|
18
18
|
> resource. :cold_sweat:
|
19
19
|
|
@@ -25,10 +25,10 @@ configuration. It called **Metadata Driven Data Workflow**.
|
|
25
25
|
|
26
26
|
**:pushpin: <u>Rules of This Workflow engine</u>**:
|
27
27
|
|
28
|
-
1. Minimum frequency unit of scheduling is **1 minute** :warning:
|
28
|
+
1. The Minimum frequency unit of scheduling is **1 minute** :warning:
|
29
29
|
2. Can not re-run only failed stage and its pending downstream :rotating_light:
|
30
30
|
3. All parallel tasks inside workflow engine use Multi-Threading
|
31
|
-
(
|
31
|
+
(Python 3.13 unlock GIL :unlock:)
|
32
32
|
|
33
33
|
> [!NOTE]
|
34
34
|
> _Disclaimer_: I inspire the dynamic statement from the [**GitHub Action**](https://github.com/features/actions)
|
@@ -150,7 +150,7 @@ application. If any configuration values do not set yet, it will use default val
|
|
150
150
|
and do not raise any error to you.
|
151
151
|
|
152
152
|
| Environment | Component | Default | Description | Remark |
|
153
|
-
|
153
|
+
|:----------------------------------------|:----------|:---------------------------------|--------------------------------------------------------------------------------------------------------------------|--------|
|
154
154
|
| `WORKFLOW_ROOT_PATH` | Core | . | The root path of the workflow application. | |
|
155
155
|
| `WORKFLOW_CORE_REGISTRY` | Core | src.ddeutil.workflow,tests.utils | List of importable string for the hook stage. | |
|
156
156
|
| `WORKFLOW_CORE_REGISTRY_FILTER` | Core | ddeutil.workflow.utils | List of importable string for the filter template. | |
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__: str = "0.0.19"
|
@@ -195,6 +195,7 @@ class SimLoad:
|
|
195
195
|
:param conf: A config object.
|
196
196
|
:param include:
|
197
197
|
:param exclude:
|
198
|
+
|
198
199
|
:rtype: Iterator[tuple[str, DictData]]
|
199
200
|
"""
|
200
201
|
exclude: list[str] = exclude or []
|
@@ -247,12 +248,14 @@ class Loader(SimLoad):
|
|
247
248
|
include: list[str] | None = None,
|
248
249
|
exclude: list[str] | None = None,
|
249
250
|
**kwargs,
|
250
|
-
) -> DictData:
|
251
|
+
) -> Iterator[tuple[str, DictData]]:
|
251
252
|
"""Override the find class method from the Simple Loader object.
|
252
253
|
|
253
254
|
:param obj: A object that want to validate matching before return.
|
254
255
|
:param include:
|
255
256
|
:param exclude:
|
257
|
+
|
258
|
+
:rtype: Iterator[tuple[str, DictData]]
|
256
259
|
"""
|
257
260
|
return super().finds(
|
258
261
|
obj=obj, conf=Config(), include=include, exclude=exclude
|
@@ -268,6 +271,7 @@ def get_type(t: str, params: Config) -> AnyModelType:
|
|
268
271
|
:param t: A importable type string.
|
269
272
|
:param params: A config parameters that use registry to search this
|
270
273
|
type.
|
274
|
+
|
271
275
|
:rtype: AnyModelType
|
272
276
|
"""
|
273
277
|
try:
|
@@ -366,6 +370,8 @@ class FileLog(BaseLog):
|
|
366
370
|
workflow name.
|
367
371
|
|
368
372
|
:param name: A workflow name that want to search release logging data.
|
373
|
+
|
374
|
+
:rtype: Iterator[Self]
|
369
375
|
"""
|
370
376
|
pointer: Path = config.root_path / f"./logs/workflow={name}"
|
371
377
|
if not pointer.exists():
|
@@ -387,6 +393,9 @@ class FileLog(BaseLog):
|
|
387
393
|
workflow name and release values. If a release does not pass to an input
|
388
394
|
argument, it will return the latest release from the current log path.
|
389
395
|
|
396
|
+
:param name:
|
397
|
+
:param release:
|
398
|
+
|
390
399
|
:raise FileNotFoundError:
|
391
400
|
:raise NotImplementedError:
|
392
401
|
|
@@ -411,21 +420,17 @@ class FileLog(BaseLog):
|
|
411
420
|
return cls.model_validate(obj=json.load(f))
|
412
421
|
|
413
422
|
@classmethod
|
414
|
-
def is_pointed(
|
415
|
-
|
416
|
-
|
417
|
-
release: datetime,
|
418
|
-
*,
|
419
|
-
queue: list[datetime] | None = None,
|
420
|
-
) -> bool:
|
421
|
-
"""Check this log already point in the destination.
|
423
|
+
def is_pointed(cls, name: str, release: datetime) -> bool:
|
424
|
+
"""Check the release log already pointed or created at the destination
|
425
|
+
log path.
|
422
426
|
|
423
427
|
:param name: A workflow name.
|
424
428
|
:param release: A release datetime.
|
425
|
-
|
426
|
-
|
429
|
+
|
430
|
+
:rtype: bool
|
431
|
+
:return: Return False if the release log was not pointed or created.
|
427
432
|
"""
|
428
|
-
# NOTE:
|
433
|
+
# NOTE: Return False if enable writing log flag does not set.
|
429
434
|
if not config.enable_write_log:
|
430
435
|
return False
|
431
436
|
|
@@ -434,9 +439,7 @@ class FileLog(BaseLog):
|
|
434
439
|
name=name, release=release
|
435
440
|
)
|
436
441
|
|
437
|
-
|
438
|
-
return pointer.exists()
|
439
|
-
return pointer.exists() or (release in queue)
|
442
|
+
return pointer.exists()
|
440
443
|
|
441
444
|
def pointer(self) -> Path:
|
442
445
|
"""Return release directory path that was generated from model data.
|
@@ -19,6 +19,7 @@ from concurrent.futures import (
|
|
19
19
|
as_completed,
|
20
20
|
wait,
|
21
21
|
)
|
22
|
+
from enum import Enum
|
22
23
|
from functools import lru_cache
|
23
24
|
from textwrap import dedent
|
24
25
|
from threading import Event
|
@@ -198,6 +199,11 @@ class Strategy(BaseModel):
|
|
198
199
|
return make(self.matrix, self.include, self.exclude)
|
199
200
|
|
200
201
|
|
202
|
+
class TriggerRules(str, Enum):
|
203
|
+
all_success: str = "all_success"
|
204
|
+
all_failed: str = "all_failed"
|
205
|
+
|
206
|
+
|
201
207
|
class Job(BaseModel):
|
202
208
|
"""Job Pydantic model object (group of stages).
|
203
209
|
|
@@ -245,6 +251,11 @@ class Job(BaseModel):
|
|
245
251
|
default_factory=list,
|
246
252
|
description="A list of Stage of this job.",
|
247
253
|
)
|
254
|
+
trigger_rule: TriggerRules = Field(
|
255
|
+
default=TriggerRules.all_success,
|
256
|
+
description="A trigger rule of tracking needed jobs.",
|
257
|
+
serialization_alias="trigger-rule",
|
258
|
+
)
|
248
259
|
needs: list[str] = Field(
|
249
260
|
default_factory=list,
|
250
261
|
description="A list of the job ID that want to run before this job.",
|
@@ -269,6 +280,7 @@ class Job(BaseModel):
|
|
269
280
|
:rtype: DictData
|
270
281
|
"""
|
271
282
|
dash2underscore("runs-on", values)
|
283
|
+
dash2underscore("trigger-rule", values)
|
272
284
|
return values
|
273
285
|
|
274
286
|
@field_validator("desc", mode="after")
|
@@ -190,6 +190,19 @@ class On(BaseModel):
|
|
190
190
|
"""
|
191
191
|
return self.generate(start=start).next
|
192
192
|
|
193
|
+
# def pop(self, queue: list[datetime]) -> datetime:
|
194
|
+
# """Pop the matching datetime value from list of datetime alias queue."""
|
195
|
+
# for dt in queue:
|
196
|
+
# if self.next(dt) == dt:
|
197
|
+
# return dt
|
198
|
+
#
|
199
|
+
# # NOTE: Add 1 second value to the current datetime for forcing crontab
|
200
|
+
# # runner generate the next datetime instead if current datetime be
|
201
|
+
# # valid because I already replaced second to zero before passing.
|
202
|
+
# return datetime.now(tz=config.tz).replace(
|
203
|
+
# second=0, microsecond=0
|
204
|
+
# ) + timedelta(seconds=1)
|
205
|
+
|
193
206
|
|
194
207
|
class YearOn(On):
|
195
208
|
"""Implement On Year Schedule Model for limit year matrix that use by some
|