ddeutil-workflow 0.0.53__tar.gz → 0.0.55__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 (68) hide show
  1. {ddeutil_workflow-0.0.53/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.55}/PKG-INFO +5 -13
  2. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/README.md +4 -12
  3. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/pyproject.toml +0 -2
  4. ddeutil_workflow-0.0.55/src/ddeutil/workflow/__about__.py +1 -0
  5. ddeutil_workflow-0.0.53/src/ddeutil/workflow/api/api.py → ddeutil_workflow-0.0.55/src/ddeutil/workflow/api/__init__.py +2 -2
  6. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/api/routes/job.py +23 -22
  7. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/api/routes/schedules.py +0 -2
  8. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/api/routes/workflows.py +3 -4
  9. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/job.py +125 -170
  10. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/result.py +1 -0
  11. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/scheduler.py +1 -3
  12. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/stages.py +641 -399
  13. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/utils.py +5 -4
  14. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/workflow.py +118 -258
  15. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55/src/ddeutil_workflow.egg-info}/PKG-INFO +5 -13
  16. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil_workflow.egg-info/SOURCES.txt +3 -4
  17. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_job_exec.py +119 -45
  18. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_job_exec_strategy.py +65 -37
  19. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_release_queue.py +6 -2
  20. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_reusables_template.py +5 -0
  21. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_stage.py +1 -1
  22. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_stage_handler_exec.py +58 -2
  23. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_utils.py +1 -1
  24. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_workflow.py +3 -4
  25. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_workflow_exec.py +91 -82
  26. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_workflow_exec_job.py +27 -4
  27. ddeutil_workflow-0.0.53/src/ddeutil/workflow/__about__.py +0 -1
  28. ddeutil_workflow-0.0.53/src/ddeutil/workflow/api/__init__.py +0 -1
  29. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/LICENSE +0 -0
  30. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/setup.cfg +0 -0
  31. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/__cron.py +0 -0
  32. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/__init__.py +0 -0
  33. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/__main__.py +0 -0
  34. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/__types.py +0 -0
  35. /ddeutil_workflow-0.0.53/src/ddeutil/workflow/api/log.py → /ddeutil_workflow-0.0.55/src/ddeutil/workflow/api/logs.py +0 -0
  36. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
  37. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/api/routes/logs.py +0 -0
  38. /ddeutil_workflow-0.0.53/src/ddeutil/workflow/api/repeat.py → /ddeutil_workflow-0.0.55/src/ddeutil/workflow/api/utils.py +0 -0
  39. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/conf.py +0 -0
  40. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/cron.py +0 -0
  41. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/exceptions.py +0 -0
  42. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/logs.py +0 -0
  43. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/params.py +0 -0
  44. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil/workflow/reusables.py +0 -0
  45. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  46. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
  47. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  48. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test__cron.py +0 -0
  49. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test__regex.py +0 -0
  50. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_conf.py +0 -0
  51. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_cron_on.py +0 -0
  52. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_job.py +0 -0
  53. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_logs_audit.py +0 -0
  54. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_logs_trace.py +0 -0
  55. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_params.py +0 -0
  56. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_release.py +0 -0
  57. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_result.py +0 -0
  58. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_reusables_call_tag.py +0 -0
  59. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_reusables_template_filter.py +0 -0
  60. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_schedule.py +0 -0
  61. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_schedule_pending.py +0 -0
  62. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_schedule_tasks.py +0 -0
  63. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_schedule_workflow.py +0 -0
  64. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_scheduler_control.py +0 -0
  65. /ddeutil_workflow-0.0.53/tests/test_job_strategy.py → /ddeutil_workflow-0.0.55/tests/test_strategy.py +0 -0
  66. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_workflow_exec_poke.py +0 -0
  67. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_workflow_exec_release.py +0 -0
  68. {ddeutil_workflow-0.0.53 → ddeutil_workflow-0.0.55}/tests/test_workflow_task.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.53
3
+ Version: 0.0.55
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -62,12 +62,6 @@ by a `.yaml` template.
62
62
  > use the workflow stage to process any large volume data which use a lot of compute
63
63
  > resource :cold_sweat:.
64
64
 
65
- In my opinion, I think it should not create duplicate workflow codes if I can
66
- write with dynamic input parameters on the one template workflow that just change
67
- the input parameters per use-case instead.
68
- This way I can handle a lot of logical workflows in our orgs with only metadata
69
- configuration. It called **Metadata Driven Data Workflow**.
70
-
71
65
  ---
72
66
 
73
67
  **:pushpin: <u>Rules of This Workflow engine</u>**:
@@ -127,12 +121,10 @@ flowchart LR
127
121
 
128
122
  > [!WARNING]
129
123
  > _**Disclaimer**_: I inspire the dynamic YAML statement from the [**GitHub Action**](https://github.com/features/actions),
130
- > and all configs pattern from several data orchestration framework tools from
131
- > my data engineering experience. :grimacing:
132
-
133
- > [!NOTE]
134
- > Other workflow orchestration tools that I interest and pick them to be inspiration
135
- > some for this package:
124
+ > and my experience of data framework configs pattern. :grimacing:
125
+ >
126
+ > Other workflow orchestration services that I interest and pick them to be
127
+ > this project inspiration:
136
128
  >
137
129
  > - [Google **Workflows**](https://cloud.google.com/workflows)
138
130
  > - [AWS **Step Functions**](https://aws.amazon.com/step-functions/)
@@ -17,12 +17,6 @@ by a `.yaml` template.
17
17
  > use the workflow stage to process any large volume data which use a lot of compute
18
18
  > resource :cold_sweat:.
19
19
 
20
- In my opinion, I think it should not create duplicate workflow codes if I can
21
- write with dynamic input parameters on the one template workflow that just change
22
- the input parameters per use-case instead.
23
- This way I can handle a lot of logical workflows in our orgs with only metadata
24
- configuration. It called **Metadata Driven Data Workflow**.
25
-
26
20
  ---
27
21
 
28
22
  **:pushpin: <u>Rules of This Workflow engine</u>**:
@@ -82,12 +76,10 @@ flowchart LR
82
76
 
83
77
  > [!WARNING]
84
78
  > _**Disclaimer**_: I inspire the dynamic YAML statement from the [**GitHub Action**](https://github.com/features/actions),
85
- > and all configs pattern from several data orchestration framework tools from
86
- > my data engineering experience. :grimacing:
87
-
88
- > [!NOTE]
89
- > Other workflow orchestration tools that I interest and pick them to be inspiration
90
- > some for this package:
79
+ > and my experience of data framework configs pattern. :grimacing:
80
+ >
81
+ > Other workflow orchestration services that I interest and pick them to be
82
+ > this project inspiration:
91
83
  >
92
84
  > - [Google **Workflows**](https://cloud.google.com/workflows)
93
85
  > - [AWS **Step Functions**](https://aws.amazon.com/step-functions/)
@@ -79,9 +79,7 @@ source = ["src.ddeutil.workflow"]
79
79
  omit = [
80
80
  "src/ddeutil/workflow/__about__.py",
81
81
  "src/ddeutil/workflow/__cron.py",
82
- "src/ddeutil/workflow/context.py",
83
82
  "src/ddeutil/workflow/api/__init__.py",
84
- "src/ddeutil/workflow/api/api.py",
85
83
  "src/ddeutil/workflow/api/log.py",
86
84
  "src/ddeutil/workflow/api/repeat.py",
87
85
  "src/ddeutil/workflow/api/routes/__init__.py",
@@ -0,0 +1 @@
1
+ __version__: str = "0.0.55"
@@ -24,8 +24,8 @@ from ..conf import api_config, config
24
24
  from ..logs import get_logger
25
25
  from ..scheduler import ReleaseThread, ReleaseThreads
26
26
  from ..workflow import ReleaseQueue, WorkflowTask
27
- from .repeat import repeat_at
28
27
  from .routes import job, log
28
+ from .utils import repeat_at
29
29
 
30
30
  load_dotenv()
31
31
  logger = get_logger("uvicorn.error")
@@ -90,7 +90,7 @@ app.add_middleware(
90
90
  )
91
91
 
92
92
 
93
- @app.get("/")
93
+ @app.get(path="/", response_class=UJSONResponse)
94
94
  async def health():
95
95
  """Index view that not return any template without json status."""
96
96
  return {"message": "Workflow already start up with healthy status."}
@@ -9,7 +9,7 @@ from typing import Any, Optional
9
9
 
10
10
  from fastapi import APIRouter
11
11
  from fastapi.responses import UJSONResponse
12
- from pydantic import BaseModel
12
+ from pydantic import BaseModel, Field
13
13
 
14
14
  from ...__types import DictData
15
15
  from ...exceptions import JobException
@@ -18,33 +18,37 @@ from ...logs import get_logger
18
18
  from ...result import Result
19
19
 
20
20
  logger = get_logger("uvicorn.error")
21
+ job_route = APIRouter(prefix="/job", tags=["job"])
21
22
 
22
23
 
23
- job_route = APIRouter(
24
- prefix="/job",
25
- tags=["job"],
26
- default_response_class=UJSONResponse,
27
- )
24
+ class ResultCreate(BaseModel):
25
+ """Create Result model for receive running IDs to create the Result
26
+ dataclass.
27
+ """
28
28
 
29
-
30
- class ResultPost(BaseModel):
31
- context: DictData
32
- run_id: str
33
- parent_run_id: Optional[str] = None
29
+ run_id: str = Field(description="A running ID.")
30
+ parent_run_id: Optional[str] = Field(
31
+ default=None, description="A parent running ID."
32
+ )
34
33
 
35
34
 
36
- @job_route.post(path="/execute/")
35
+ @job_route.post(path="/execute/", response_class=UJSONResponse)
37
36
  async def job_execute(
38
- result: ResultPost,
37
+ result: ResultCreate,
39
38
  job: Job,
40
39
  params: dict[str, Any],
40
+ extras: Optional[dict[str, Any]] = None,
41
41
  ):
42
- """Execute job via API."""
42
+ """Execute job via RestAPI with execute route path."""
43
43
  rs: Result = Result(
44
- context=result.context,
45
44
  run_id=result.run_id,
46
45
  parent_run_id=result.parent_run_id,
46
+ extras=extras or {},
47
47
  )
48
+
49
+ if extras:
50
+ job.extras = extras
51
+
48
52
  context: DictData = {}
49
53
  try:
50
54
  job.set_outputs(
@@ -56,17 +60,14 @@ async def job_execute(
56
60
  to=context,
57
61
  )
58
62
  except JobException as err:
59
- rs.trace.error(f"[WORKFLOW]: {err.__class__.__name__}: {err}")
63
+ rs.trace.error(f"[JOB]: {err.__class__.__name__}: {err}")
60
64
 
61
65
  return {
62
- "message": "Start execute job via API.",
63
- "result": {
64
- "run_id": rs.run_id,
65
- "parent_run_id": rs.parent_run_id,
66
- },
66
+ "message": "Execute job via RestAPI.",
67
+ "result": {"run_id": rs.run_id, "parent_run_id": rs.parent_run_id},
67
68
  "job": job.model_dump(
68
69
  by_alias=True,
69
- exclude_none=True,
70
+ exclude_none=False,
70
71
  exclude_unset=True,
71
72
  exclude_defaults=True,
72
73
  ),
@@ -17,7 +17,6 @@ from ...logs import get_logger
17
17
  from ...scheduler import Schedule
18
18
 
19
19
  logger = get_logger("uvicorn.error")
20
-
21
20
  schedule_route = APIRouter(
22
21
  prefix="/schedules",
23
22
  tags=["schedules"],
@@ -108,7 +107,6 @@ async def add_deploy_scheduler(request: Request, name: str):
108
107
  schedule.tasks(
109
108
  start_date_waiting,
110
109
  queue=request.state.workflow_queue,
111
- extras={},
112
110
  ),
113
111
  )
114
112
  return {
@@ -21,7 +21,6 @@ from ...result import Result
21
21
  from ...workflow import Workflow
22
22
 
23
23
  logger = get_logger("uvicorn.error")
24
-
25
24
  workflow_route = APIRouter(
26
25
  prefix="/workflows",
27
26
  tags=["workflows"],
@@ -55,7 +54,7 @@ async def get_workflow_by_name(name: str) -> DictData:
55
54
  ) from None
56
55
  return workflow.model_dump(
57
56
  by_alias=True,
58
- exclude_none=True,
57
+ exclude_none=False,
59
58
  exclude_unset=True,
60
59
  exclude_defaults=True,
61
60
  )
@@ -98,7 +97,7 @@ async def get_workflow_audits(name: str):
98
97
  "audits": [
99
98
  audit.model_dump(
100
99
  by_alias=True,
101
- exclude_none=True,
100
+ exclude_none=False,
102
101
  exclude_unset=True,
103
102
  exclude_defaults=True,
104
103
  )
@@ -132,7 +131,7 @@ async def get_workflow_release_audit(name: str, release: str):
132
131
  "message": f"Getting workflow {name!r} audit in release {release}",
133
132
  "audit": audit.model_dump(
134
133
  by_alias=True,
135
- exclude_none=True,
134
+ exclude_none=False,
136
135
  exclude_unset=True,
137
136
  exclude_defaults=True,
138
137
  ),