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.
Files changed (62) hide show
  1. {ddeutil_workflow-0.0.18/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.19}/PKG-INFO +5 -5
  2. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/README.md +4 -4
  3. ddeutil_workflow-0.0.19/src/ddeutil/workflow/__about__.py +1 -0
  4. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/__cron.py +1 -0
  5. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/conf.py +18 -15
  6. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/job.py +12 -0
  7. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/on.py +13 -0
  8. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/scheduler.py +157 -84
  9. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/utils.py +32 -19
  10. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19/src/ddeutil_workflow.egg-info}/PKG-INFO +5 -5
  11. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/SOURCES.txt +0 -4
  12. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test__cron.py +3 -1
  13. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_conf_log.py +5 -0
  14. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job.py +1 -0
  15. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job_strategy.py +41 -0
  16. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_on.py +7 -4
  17. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_scheduler.py +55 -16
  18. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_scheduler_tasks.py +27 -8
  19. ddeutil_workflow-0.0.19/tests/test_stage.py +74 -0
  20. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_bash.py +6 -6
  21. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_filter.py +11 -2
  22. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_template.py +44 -54
  23. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow.py +66 -43
  24. ddeutil_workflow-0.0.19/tests/test_workflow_poke.py +56 -0
  25. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow_run.py +70 -16
  26. ddeutil_workflow-0.0.18/src/ddeutil/workflow/__about__.py +0 -1
  27. ddeutil_workflow-0.0.18/tests/test__conf_exist.py +0 -12
  28. ddeutil_workflow-0.0.18/tests/test_stage.py +0 -58
  29. ddeutil_workflow-0.0.18/tests/test_workflow_matrix.py +0 -156
  30. ddeutil_workflow-0.0.18/tests/test_workflow_on.py +0 -12
  31. ddeutil_workflow-0.0.18/tests/test_workflow_params.py +0 -23
  32. ddeutil_workflow-0.0.18/tests/test_workflow_poke.py +0 -22
  33. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/LICENSE +0 -0
  34. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/pyproject.toml +0 -0
  35. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/setup.cfg +0 -0
  36. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/__init__.py +0 -0
  37. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/__types.py +0 -0
  38. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/api.py +0 -0
  39. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/cli.py +0 -0
  40. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/exceptions.py +0 -0
  41. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/repeat.py +0 -0
  42. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/route.py +0 -0
  43. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil/workflow/stage.py +0 -0
  44. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  45. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
  46. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
  47. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  48. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test__regex.py +0 -0
  49. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_conf.py +0 -0
  50. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job_py.py +0 -0
  51. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_job_strategy_run.py +0 -0
  52. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_params.py +0 -0
  53. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_hook.py +0 -0
  54. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_py.py +0 -0
  55. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_stage_trigger.py +0 -0
  56. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils.py +0 -0
  57. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_params.py +0 -0
  58. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_result.py +0 -0
  59. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_utils_tag.py +0 -0
  60. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow_depends.py +0 -0
  61. {ddeutil_workflow-0.0.18 → ddeutil_workflow-0.0.19}/tests/test_workflow_job_run.py +0 -0
  62. {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.18
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 task. That mean you should not
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
- (Because Python 3.13 unlock GIL :unlock:)
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 task. That mean you should not
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
- (Because Python 3.13 unlock GIL :unlock:)
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"
@@ -646,6 +646,7 @@ class CronJob:
646
646
 
647
647
  :param date: An initial date that want to mark as the start point.
648
648
  :param tz: A string timezone that want to change on runner.
649
+
649
650
  :rtype: CronRunner
650
651
  """
651
652
  return CronRunner(self, date, tz=tz)
@@ -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
- cls,
416
- name: str,
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
- :param queue: A list of queue of datetime that already run in the
426
- future.
429
+
430
+ :rtype: bool
431
+ :return: Return False if the release log was not pointed or created.
427
432
  """
428
- # NOTE: Check environ variable was set for real writing.
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
- if not queue:
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