ddeutil-workflow 0.0.50__py3-none-any.whl → 0.0.52__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/__init__.py +7 -56
- ddeutil/workflow/conf.py +2 -2
- ddeutil/workflow/cron.py +46 -20
- ddeutil/workflow/job.py +181 -100
- ddeutil/workflow/logs.py +22 -18
- ddeutil/workflow/params.py +56 -16
- ddeutil/workflow/reusables.py +4 -2
- ddeutil/workflow/scheduler.py +5 -1
- ddeutil/workflow/stages.py +320 -154
- ddeutil/workflow/workflow.py +83 -74
- {ddeutil_workflow-0.0.50.dist-info → ddeutil_workflow-0.0.52.dist-info}/METADATA +69 -13
- {ddeutil_workflow-0.0.50.dist-info → ddeutil_workflow-0.0.52.dist-info}/RECORD +16 -16
- {ddeutil_workflow-0.0.50.dist-info → ddeutil_workflow-0.0.52.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.50.dist-info → ddeutil_workflow-0.0.52.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.50.dist-info → ddeutil_workflow-0.0.52.dist-info}/top_level.txt +0 -0
ddeutil/workflow/workflow.py
CHANGED
@@ -6,6 +6,9 @@
|
|
6
6
|
# [x] Use dynamic config
|
7
7
|
"""A Workflow module that is the core module of this package. It keeps Release
|
8
8
|
and Workflow Pydantic models.
|
9
|
+
|
10
|
+
I will implement timeout on the workflow execution layer only because the
|
11
|
+
main propose of this package in Workflow model.
|
9
12
|
"""
|
10
13
|
from __future__ import annotations
|
11
14
|
|
@@ -36,7 +39,7 @@ from .__cron import CronJob, CronRunner
|
|
36
39
|
from .__types import DictData, TupleStr
|
37
40
|
from .conf import Loader, SimLoad, dynamic
|
38
41
|
from .cron import On
|
39
|
-
from .exceptions import JobException, WorkflowException
|
42
|
+
from .exceptions import JobException, UtilException, WorkflowException
|
40
43
|
from .job import Job
|
41
44
|
from .logs import Audit, get_audit
|
42
45
|
from .params import Param
|
@@ -368,7 +371,9 @@ class Workflow(BaseModel):
|
|
368
371
|
def from_conf(
|
369
372
|
cls,
|
370
373
|
name: str,
|
374
|
+
*,
|
371
375
|
extras: DictData | None = None,
|
376
|
+
loader: type[Loader] = None,
|
372
377
|
) -> Self:
|
373
378
|
"""Create Workflow instance from the Loader object that only receive
|
374
379
|
an input workflow name. The loader object will use this workflow name to
|
@@ -377,12 +382,13 @@ class Workflow(BaseModel):
|
|
377
382
|
:param name: A workflow name that want to pass to Loader object.
|
378
383
|
:param extras: An extra parameters that want to pass to Loader
|
379
384
|
object.
|
385
|
+
:param loader: A loader class for override default loader object.
|
380
386
|
|
381
387
|
:raise ValueError: If the type does not match with current object.
|
382
388
|
|
383
389
|
:rtype: Self
|
384
390
|
"""
|
385
|
-
loader: Loader = Loader(name, externals=(extras or {}))
|
391
|
+
loader: Loader = (loader or Loader)(name, externals=(extras or {}))
|
386
392
|
|
387
393
|
# NOTE: Validate the config type match with current connection model
|
388
394
|
if loader.type != cls.__name__:
|
@@ -404,6 +410,7 @@ class Workflow(BaseModel):
|
|
404
410
|
path: Path,
|
405
411
|
*,
|
406
412
|
extras: DictData | None = None,
|
413
|
+
loader: type[Loader] = None,
|
407
414
|
) -> Self:
|
408
415
|
"""Create Workflow instance from the specific path. The loader object
|
409
416
|
will use this workflow name and path to searching configuration data of
|
@@ -413,12 +420,13 @@ class Workflow(BaseModel):
|
|
413
420
|
:param path: (Path) A config path that want to search.
|
414
421
|
:param extras: (DictData) An extra parameters that want to override core
|
415
422
|
config values.
|
423
|
+
:param loader: A loader class for override default loader object.
|
416
424
|
|
417
425
|
:raise ValueError: If the type does not match with current object.
|
418
426
|
|
419
427
|
:rtype: Self
|
420
428
|
"""
|
421
|
-
loader: SimLoad = SimLoad(
|
429
|
+
loader: SimLoad = (loader or SimLoad)(
|
422
430
|
name, conf_path=path, externals=(extras or {})
|
423
431
|
)
|
424
432
|
# NOTE: Validate the config type match with current connection model
|
@@ -636,20 +644,20 @@ class Workflow(BaseModel):
|
|
636
644
|
run_id: str | None = None,
|
637
645
|
parent_run_id: str | None = None,
|
638
646
|
audit: type[Audit] = None,
|
639
|
-
queue: ReleaseQueue
|
647
|
+
queue: Optional[ReleaseQueue] = None,
|
640
648
|
override_log_name: str | None = None,
|
641
|
-
result: Result
|
649
|
+
result: Optional[Result] = None,
|
650
|
+
timeout: int = 600,
|
642
651
|
) -> Result:
|
643
652
|
"""Release the workflow execution with overriding parameter with the
|
644
653
|
release templating that include logical date (release date), execution
|
645
654
|
date, or running id to the params.
|
646
655
|
|
647
656
|
This method allow workflow use audit object to save the execution
|
648
|
-
result to audit destination like file audit to the local
|
649
|
-
directory.
|
657
|
+
result to audit destination like file audit to the local `./logs` path.
|
650
658
|
|
651
659
|
Steps:
|
652
|
-
- Initialize
|
660
|
+
- Initialize Release and validate ReleaseQueue.
|
653
661
|
- Create release data for pass to parameter templating function.
|
654
662
|
- Execute this workflow with mapping release data to its parameters.
|
655
663
|
- Writing result audit
|
@@ -658,15 +666,15 @@ class Workflow(BaseModel):
|
|
658
666
|
|
659
667
|
:param release: A release datetime or Release object.
|
660
668
|
:param params: A workflow parameter that pass to execute method.
|
661
|
-
:param
|
662
|
-
:param
|
663
|
-
:param parent_run_id: A parent workflow running ID for this release.
|
669
|
+
:param run_id: (str) A workflow running ID.
|
670
|
+
:param parent_run_id: (str) A parent workflow running ID.
|
664
671
|
:param audit: An audit class that want to save the execution result.
|
665
|
-
:param queue: A ReleaseQueue object.
|
666
|
-
:param override_log_name: An override logging name that use
|
667
|
-
the workflow name.
|
672
|
+
:param queue: (ReleaseQueue) A ReleaseQueue object.
|
673
|
+
:param override_log_name: (str) An override logging name that use
|
674
|
+
instead the workflow name.
|
668
675
|
:param result: (Result) A result object for keeping context and status
|
669
676
|
data.
|
677
|
+
:param timeout: (int) A workflow execution time out in second unit.
|
670
678
|
|
671
679
|
:raise TypeError: If a queue parameter does not match with ReleaseQueue
|
672
680
|
type.
|
@@ -683,7 +691,8 @@ class Workflow(BaseModel):
|
|
683
691
|
extras=self.extras,
|
684
692
|
)
|
685
693
|
|
686
|
-
|
694
|
+
# VALIDATE: check type of queue that valid with ReleaseQueue.
|
695
|
+
if queue and not isinstance(queue, ReleaseQueue):
|
687
696
|
raise TypeError(
|
688
697
|
"The queue argument should be ReleaseQueue object only."
|
689
698
|
)
|
@@ -693,36 +702,29 @@ class Workflow(BaseModel):
|
|
693
702
|
release: Release = Release.from_dt(release, extras=self.extras)
|
694
703
|
|
695
704
|
result.trace.debug(
|
696
|
-
f"[RELEASE]: Start
|
697
|
-
f"{release.date:%Y-%m-%d %H:%M:%S}"
|
705
|
+
f"[RELEASE]: Start {name!r} : {release.date:%Y-%m-%d %H:%M:%S}"
|
698
706
|
)
|
699
|
-
|
700
|
-
# NOTE: Release parameters that use to templating on the schedule
|
701
|
-
# config data.
|
702
|
-
release_params: DictData = {
|
703
|
-
"release": {
|
704
|
-
"logical_date": release.date,
|
705
|
-
"execute_date": datetime.now(
|
706
|
-
tz=dynamic("tz", extras=self.extras)
|
707
|
-
),
|
708
|
-
"run_id": result.run_id,
|
709
|
-
"timezone": dynamic("tz", extras=self.extras),
|
710
|
-
}
|
711
|
-
}
|
712
|
-
|
713
|
-
# NOTE: Execute workflow with templating params from release mapping.
|
714
|
-
# The result context that return from execution method is:
|
715
|
-
#
|
716
|
-
# ... {"params": ..., "jobs": ...}
|
717
|
-
#
|
718
707
|
self.execute(
|
719
|
-
params=param2template(
|
708
|
+
params=param2template(
|
709
|
+
params,
|
710
|
+
params={
|
711
|
+
"release": {
|
712
|
+
"logical_date": release.date,
|
713
|
+
"execute_date": datetime.now(
|
714
|
+
tz=dynamic("tz", extras=self.extras)
|
715
|
+
),
|
716
|
+
"run_id": result.run_id,
|
717
|
+
"timezone": dynamic("tz", extras=self.extras),
|
718
|
+
}
|
719
|
+
},
|
720
|
+
extras=self.extras,
|
721
|
+
),
|
720
722
|
result=result,
|
721
723
|
parent_run_id=result.parent_run_id,
|
724
|
+
timeout=timeout,
|
722
725
|
)
|
723
726
|
result.trace.debug(
|
724
|
-
f"[RELEASE]: End
|
725
|
-
f"{release.date:%Y-%m-%d %H:%M:%S}"
|
727
|
+
f"[RELEASE]: End {name!r} : {release.date:%Y-%m-%d %H:%M:%S}"
|
726
728
|
)
|
727
729
|
|
728
730
|
# NOTE: Saving execution result to destination of the input audit
|
@@ -741,19 +743,10 @@ class Workflow(BaseModel):
|
|
741
743
|
).save(excluded=None)
|
742
744
|
)
|
743
745
|
|
744
|
-
|
745
|
-
if queue is not None:
|
746
|
+
if queue:
|
746
747
|
queue.remove_running(release)
|
747
748
|
queue.mark_complete(release)
|
748
749
|
|
749
|
-
# NOTE: Remove the params key from the result context for deduplicate.
|
750
|
-
# This step is prepare result context for this release method.
|
751
|
-
context: DictData = result.context
|
752
|
-
jobs: DictData = context.pop("jobs", {})
|
753
|
-
errors: DictData = (
|
754
|
-
{"errors": context.pop("errors", {})} if "errors" in context else {}
|
755
|
-
)
|
756
|
-
|
757
750
|
return result.catch(
|
758
751
|
status=SUCCESS,
|
759
752
|
context={
|
@@ -763,8 +756,7 @@ class Workflow(BaseModel):
|
|
763
756
|
"logical_date": release.date,
|
764
757
|
"release": release,
|
765
758
|
},
|
766
|
-
"outputs": {"jobs": jobs},
|
767
|
-
**errors,
|
759
|
+
"outputs": {"jobs": result.context.pop("jobs", {})},
|
768
760
|
},
|
769
761
|
)
|
770
762
|
|
@@ -1004,6 +996,12 @@ class Workflow(BaseModel):
|
|
1004
996
|
f"workflow."
|
1005
997
|
)
|
1006
998
|
|
999
|
+
job: Job = self.job(name=job_id)
|
1000
|
+
if job.is_skipped(params=params):
|
1001
|
+
result.trace.info(f"[WORKFLOW]: Skip job: {job_id!r}")
|
1002
|
+
job.set_outputs(output={"skipped": True}, to=params)
|
1003
|
+
return result.catch(status=SKIP, context=params)
|
1004
|
+
|
1007
1005
|
if event and event.is_set(): # pragma: no cov
|
1008
1006
|
raise WorkflowException(
|
1009
1007
|
"Workflow job was canceled from event that had set before "
|
@@ -1011,27 +1009,31 @@ class Workflow(BaseModel):
|
|
1011
1009
|
)
|
1012
1010
|
|
1013
1011
|
try:
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
run_id=result.run_id,
|
1024
|
-
parent_run_id=result.parent_run_id,
|
1025
|
-
event=event,
|
1026
|
-
).context,
|
1027
|
-
to=params,
|
1028
|
-
)
|
1029
|
-
except JobException as e:
|
1012
|
+
result.trace.info(f"[WORKFLOW]: Execute Job: {job_id!r}")
|
1013
|
+
rs: Result = job.execute(
|
1014
|
+
params=params,
|
1015
|
+
run_id=result.run_id,
|
1016
|
+
parent_run_id=result.parent_run_id,
|
1017
|
+
event=event,
|
1018
|
+
)
|
1019
|
+
job.set_outputs(rs.context, to=params)
|
1020
|
+
except (JobException, UtilException) as e:
|
1030
1021
|
result.trace.error(f"[WORKFLOW]: {e.__class__.__name__}: {e}")
|
1031
1022
|
raise WorkflowException(
|
1032
1023
|
f"Get job execution error {job_id}: JobException: {e}"
|
1033
1024
|
) from None
|
1034
1025
|
|
1026
|
+
if rs.status == FAILED:
|
1027
|
+
error_msg: str = (
|
1028
|
+
f"Workflow job, {job.id}, failed without raise error."
|
1029
|
+
)
|
1030
|
+
return result.catch(
|
1031
|
+
status=FAILED,
|
1032
|
+
context={
|
1033
|
+
"errors": WorkflowException(error_msg).to_dict(),
|
1034
|
+
**params,
|
1035
|
+
},
|
1036
|
+
)
|
1035
1037
|
return result.catch(status=SUCCESS, context=params)
|
1036
1038
|
|
1037
1039
|
def execute(
|
@@ -1083,7 +1085,7 @@ class Workflow(BaseModel):
|
|
1083
1085
|
extras=self.extras,
|
1084
1086
|
)
|
1085
1087
|
|
1086
|
-
result.trace.info(f"[WORKFLOW]:
|
1088
|
+
result.trace.info(f"[WORKFLOW]: Execute: {self.name!r} ...")
|
1087
1089
|
if not self.jobs:
|
1088
1090
|
result.trace.warning(
|
1089
1091
|
f"[WORKFLOW]: {self.name!r} does not have any jobs"
|
@@ -1128,7 +1130,7 @@ class Workflow(BaseModel):
|
|
1128
1130
|
timeout=timeout,
|
1129
1131
|
event=event,
|
1130
1132
|
)
|
1131
|
-
except WorkflowException as e:
|
1133
|
+
except (WorkflowException, JobException) as e:
|
1132
1134
|
status: Status = FAILED
|
1133
1135
|
context.update({"errors": e.to_dict()})
|
1134
1136
|
|
@@ -1167,7 +1169,7 @@ class Workflow(BaseModel):
|
|
1167
1169
|
"max_job_exec_timeout", f=timeout, extras=self.extras
|
1168
1170
|
)
|
1169
1171
|
event: Event = event or Event()
|
1170
|
-
result.trace.debug(f"
|
1172
|
+
result.trace.debug(f"... Run {self.name!r} with threading.")
|
1171
1173
|
with ThreadPoolExecutor(
|
1172
1174
|
max_workers=dynamic("max_job_parallel", extras=self.extras),
|
1173
1175
|
thread_name_prefix="wf_exec_threading_",
|
@@ -1192,7 +1194,7 @@ class Workflow(BaseModel):
|
|
1192
1194
|
)
|
1193
1195
|
elif check == SKIP: # pragma: no cov
|
1194
1196
|
result.trace.info(f"[JOB]: Skip job: {job_id!r}")
|
1195
|
-
job.set_outputs({"
|
1197
|
+
job.set_outputs(output={"skipped": True}, to=context)
|
1196
1198
|
job_queue.task_done()
|
1197
1199
|
continue
|
1198
1200
|
|
@@ -1259,7 +1261,7 @@ class Workflow(BaseModel):
|
|
1259
1261
|
"max_job_exec_timeout", f=timeout, extras=self.extras
|
1260
1262
|
)
|
1261
1263
|
event: Event = event or Event()
|
1262
|
-
result.trace.debug(f"
|
1264
|
+
result.trace.debug(f"... Run {self.name!r} with non-threading.")
|
1263
1265
|
with ThreadPoolExecutor(
|
1264
1266
|
max_workers=1,
|
1265
1267
|
thread_name_prefix="wf_exec_non_threading_",
|
@@ -1284,7 +1286,7 @@ class Workflow(BaseModel):
|
|
1284
1286
|
)
|
1285
1287
|
elif check == SKIP: # pragma: no cov
|
1286
1288
|
result.trace.info(f"[JOB]: Skip job: {job_id!r}")
|
1287
|
-
job.set_outputs({"
|
1289
|
+
job.set_outputs(output={"skipped": True}, to=context)
|
1288
1290
|
job_queue.task_done()
|
1289
1291
|
continue
|
1290
1292
|
|
@@ -1297,7 +1299,7 @@ class Workflow(BaseModel):
|
|
1297
1299
|
event=event,
|
1298
1300
|
)
|
1299
1301
|
time.sleep(0.025)
|
1300
|
-
elif future.done():
|
1302
|
+
elif future.done() or future.cancelled():
|
1301
1303
|
if e := future.exception():
|
1302
1304
|
result.trace.error(f"[WORKFLOW]: {e}")
|
1303
1305
|
raise WorkflowException(str(e))
|
@@ -1317,6 +1319,13 @@ class Workflow(BaseModel):
|
|
1317
1319
|
|
1318
1320
|
if not_timeout_flag:
|
1319
1321
|
job_queue.join()
|
1322
|
+
if future: # pragma: no cov
|
1323
|
+
if e := future.exception():
|
1324
|
+
result.trace.error(f"[WORKFLOW]: {e}")
|
1325
|
+
raise WorkflowException(str(e))
|
1326
|
+
|
1327
|
+
future.result()
|
1328
|
+
|
1320
1329
|
return context
|
1321
1330
|
|
1322
1331
|
result.trace.error(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ddeutil-workflow
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.52
|
4
4
|
Summary: Lightweight workflow orchestration
|
5
5
|
Author-email: ddeutils <korawich.anu@gmail.com>
|
6
6
|
License: MIT
|
@@ -22,8 +22,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Requires-Python: >=3.9.13
|
23
23
|
Description-Content-Type: text/markdown
|
24
24
|
License-File: LICENSE
|
25
|
-
Requires-Dist: ddeutil[checksum]>=0.4.
|
26
|
-
Requires-Dist: ddeutil-io[toml,yaml]>=0.2.
|
25
|
+
Requires-Dist: ddeutil[checksum]>=0.4.7
|
26
|
+
Requires-Dist: ddeutil-io[toml,yaml]>=0.2.11
|
27
27
|
Requires-Dist: pydantic==2.11.1
|
28
28
|
Requires-Dist: python-dotenv==1.1.0
|
29
29
|
Requires-Dist: schedule<2.0.0,==1.2.2
|
@@ -139,7 +139,8 @@ flowchart LR
|
|
139
139
|
|
140
140
|
## 📦 Installation
|
141
141
|
|
142
|
-
This project need `ddeutil` and `ddeutil-io` extension namespace packages
|
142
|
+
This project need `ddeutil` and `ddeutil-io` extension namespace packages to be
|
143
|
+
the base deps.
|
143
144
|
If you want to install this package with application add-ons, you should add
|
144
145
|
`app` in installation;
|
145
146
|
|
@@ -148,7 +149,7 @@ If you want to install this package with application add-ons, you should add
|
|
148
149
|
| Python | `ddeutil-workflow` | :heavy_check_mark: |
|
149
150
|
| FastAPI Server | `ddeutil-workflow[api]` | :heavy_check_mark: |
|
150
151
|
|
151
|
-
##
|
152
|
+
## 🎯 Usage
|
152
153
|
|
153
154
|
This is examples that use workflow file for running common Data Engineering
|
154
155
|
use-case.
|
@@ -198,12 +199,54 @@ run-py-local:
|
|
198
199
|
|
199
200
|
# Arguments of target data that want to land.
|
200
201
|
writing_mode: flatten
|
201
|
-
|
202
|
+
aws:
|
203
|
+
path: my-data/open-data/${{ params.source-extract }}
|
202
204
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
205
|
+
# This Authentication code should implement with your custom call
|
206
|
+
# function. The template allow you to use environment variable.
|
207
|
+
access_client_id: ${AWS_ACCESS_CLIENT_ID}
|
208
|
+
access_client_secret: ${AWS_ACCESS_CLIENT_SECRET}
|
209
|
+
```
|
210
|
+
|
211
|
+
Before execute this workflow, you should implement caller function first.
|
212
|
+
|
213
|
+
```text
|
214
|
+
registry-caller/
|
215
|
+
╰─ tasks.py
|
216
|
+
```
|
217
|
+
|
218
|
+
This function will store as module that will import from `WORKFLOW_CORE_REGISTRY_CALLER`
|
219
|
+
value (This config can override by extra parameters with `registry_caller` key).
|
220
|
+
|
221
|
+
```python
|
222
|
+
from ddeutil.workflow import Result, tag
|
223
|
+
from ddeutil.workflow.exceptions import StageException
|
224
|
+
from pydantic import BaseModel, SecretStr
|
225
|
+
|
226
|
+
class AwsCredential(BaseModel):
|
227
|
+
path: str
|
228
|
+
access_client_id: str
|
229
|
+
access_client_secret: SecretStr
|
230
|
+
|
231
|
+
class RestAuth(BaseModel):
|
232
|
+
type: str
|
233
|
+
keys: SecretStr
|
234
|
+
|
235
|
+
@tag("requests", alias="get-api-with-oauth-to-s3")
|
236
|
+
def get_api_with_oauth_to_s3(
|
237
|
+
method: str,
|
238
|
+
url: str,
|
239
|
+
body: dict[str, str],
|
240
|
+
auth: RestAuth,
|
241
|
+
writing_node: str,
|
242
|
+
aws: AwsCredential,
|
243
|
+
result: Result,
|
244
|
+
) -> dict[str, int]:
|
245
|
+
result.trace.info("[CALLER]: Start get data via RestAPI to S3.")
|
246
|
+
result.trace.info(f"... {method}: {url}")
|
247
|
+
if method != "post":
|
248
|
+
raise StageException(f"RestAPI does not support for {method} action.")
|
249
|
+
return {"records": 1000}
|
207
250
|
```
|
208
251
|
|
209
252
|
The above workflow template is main executor pipeline that you want to do. If you
|
@@ -298,13 +341,15 @@ only.
|
|
298
341
|
## :rocket: Deployment
|
299
342
|
|
300
343
|
This package able to run as an application service for receive manual trigger
|
301
|
-
from
|
302
|
-
like crontab job but via Python API.
|
344
|
+
from any node via RestAPI or use to be Scheduler background application
|
345
|
+
like crontab job but via Python API or FastAPI app.
|
303
346
|
|
304
347
|
### API Server
|
305
348
|
|
349
|
+
This server use FastAPI package to be the base application.
|
350
|
+
|
306
351
|
```shell
|
307
|
-
(venv) $ uvicorn ddeutil.workflow.api:app \
|
352
|
+
(.venv) $ uvicorn ddeutil.workflow.api:app \
|
308
353
|
--host 127.0.0.1 \
|
309
354
|
--port 80 \
|
310
355
|
--no-access-log
|
@@ -314,8 +359,19 @@ like crontab job but via Python API.
|
|
314
359
|
> If this package already deploy, it is able to use multiprocess;
|
315
360
|
> `uvicorn ddeutil.workflow.api:app --host 127.0.0.1 --port 80 --workers 4`
|
316
361
|
|
362
|
+
### Local Schedule
|
363
|
+
|
364
|
+
> [!WARNING]
|
365
|
+
> This CLI does not implement yet.
|
366
|
+
|
367
|
+
```shell
|
368
|
+
(.venv) $ ddeutil-workflow schedule
|
369
|
+
```
|
370
|
+
|
317
371
|
### Docker Container
|
318
372
|
|
373
|
+
Build a Docker container from this package.
|
374
|
+
|
319
375
|
```shell
|
320
376
|
$ docker build -t ddeutil-workflow:latest -f .container/Dockerfile .
|
321
377
|
$ docker run -i ddeutil-workflow:latest ddeutil-workflow
|
@@ -1,20 +1,20 @@
|
|
1
|
-
ddeutil/workflow/__about__.py,sha256=
|
1
|
+
ddeutil/workflow/__about__.py,sha256=MtoJJkgninIGWDAMMmKrIvqN_o2rzywSeEdJGILbStY,28
|
2
2
|
ddeutil/workflow/__cron.py,sha256=h8rLeIUAAEB2SdZ4Jhch7LU1Yl3bbJ-iNNJ3tQ0eYVM,28095
|
3
|
-
ddeutil/workflow/__init__.py,sha256=
|
3
|
+
ddeutil/workflow/__init__.py,sha256=noE8LNRcgq32m9OnIFcQqh0P7PXWdp-SGmvBCYIXgf4,1338
|
4
4
|
ddeutil/workflow/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
ddeutil/workflow/__types.py,sha256=8jBdbfb3aZSetjz0mvNrpGHwwxJff7mK8_4v41cLqlc,4316
|
6
|
-
ddeutil/workflow/conf.py,sha256=
|
7
|
-
ddeutil/workflow/cron.py,sha256=
|
6
|
+
ddeutil/workflow/conf.py,sha256=80rgmJKFU7BlH5xTLnghGzGhE8C6LFAQykd9mjHSjo8,12528
|
7
|
+
ddeutil/workflow/cron.py,sha256=WS2MInn0Sp5DKlZDZH5VFZ5AA0Q3_AnBnYEU4lZSv4I,9779
|
8
8
|
ddeutil/workflow/exceptions.py,sha256=r4Jrf9qtVPALU4wh4bnb_OYqC-StqSQJEmFC-_QK934,1408
|
9
|
-
ddeutil/workflow/job.py,sha256
|
10
|
-
ddeutil/workflow/logs.py,sha256=
|
11
|
-
ddeutil/workflow/params.py,sha256=
|
9
|
+
ddeutil/workflow/job.py,sha256=l2YpbUQoXYQ46S7Ne3Ttefc_jKBy14q3_PKf3k_cmNU,35213
|
10
|
+
ddeutil/workflow/logs.py,sha256=rsoBrUGQrooou18fg2yvPsB8NOaXnUA5ThQpBr_WVMg,26598
|
11
|
+
ddeutil/workflow/params.py,sha256=FKY4Oo1Ze4QZKRfAk7rqKsi44YaJQAbqAtXM6vlO2hI,11392
|
12
12
|
ddeutil/workflow/result.py,sha256=27nPQq9CETLCVczv4vvFEF9w2TllHZ_ROfyDoLFxRWM,5647
|
13
|
-
ddeutil/workflow/reusables.py,sha256=
|
14
|
-
ddeutil/workflow/scheduler.py,sha256=
|
15
|
-
ddeutil/workflow/stages.py,sha256=
|
13
|
+
ddeutil/workflow/reusables.py,sha256=iXcS7Gg-71qVX4ln0ILTDx03cTtUnj_rNoXHTVdVrxc,17636
|
14
|
+
ddeutil/workflow/scheduler.py,sha256=4G5AogkmnsTKe7jKYSfU35qjubR82WQ8CLtEe9kqPTE,28304
|
15
|
+
ddeutil/workflow/stages.py,sha256=R6yVp5-9xw6fslY7oE2pNzpMmgHiBN-2AVG2hCc5-QI,69241
|
16
16
|
ddeutil/workflow/utils.py,sha256=zbVttaMFMRLuuBJdSJf7D9qtz8bOnQIBq-rHI3Eqy4M,7821
|
17
|
-
ddeutil/workflow/workflow.py,sha256=
|
17
|
+
ddeutil/workflow/workflow.py,sha256=2ZBNW3-vcP8bpKrK184wSCukq3wpT6G0z25Su5bapR0,50832
|
18
18
|
ddeutil/workflow/api/__init__.py,sha256=F53NMBWtb9IKaDWkPU5KvybGGfKAcbehgn6TLBwHuuM,21
|
19
19
|
ddeutil/workflow/api/api.py,sha256=CWtPLgOv2Jus9E7nzG5mG2Z32ZEkUK3JWQ2htZyMRpA,5244
|
20
20
|
ddeutil/workflow/api/log.py,sha256=NMTnOnsBrDB5129329xF2myLdrb-z9k1MQrmrP7qXJw,1818
|
@@ -24,8 +24,8 @@ ddeutil/workflow/api/routes/job.py,sha256=oPwBVP0Mxwxv-bGPlfmxQQ9PcVl0ev9HoPzndp
|
|
24
24
|
ddeutil/workflow/api/routes/logs.py,sha256=U6vOni3wd-ZTOwd3yVdSOpgyRmNdcgfngU5KlLM3Cww,5383
|
25
25
|
ddeutil/workflow/api/routes/schedules.py,sha256=EgUjyRGhsm6UNaMj5luh6TcY6l571sCHcla-BL1iOfY,4829
|
26
26
|
ddeutil/workflow/api/routes/workflows.py,sha256=JcDOrn1deK8ztFRcMTNATQejG6KMA7JxZLVc4QeBsP4,4527
|
27
|
-
ddeutil_workflow-0.0.
|
28
|
-
ddeutil_workflow-0.0.
|
29
|
-
ddeutil_workflow-0.0.
|
30
|
-
ddeutil_workflow-0.0.
|
31
|
-
ddeutil_workflow-0.0.
|
27
|
+
ddeutil_workflow-0.0.52.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
|
28
|
+
ddeutil_workflow-0.0.52.dist-info/METADATA,sha256=DGXGAp4BlgOfkP-CWnMFkl8_ZwvoAtMZqB5uUpcoEBs,19425
|
29
|
+
ddeutil_workflow-0.0.52.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
30
|
+
ddeutil_workflow-0.0.52.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
|
31
|
+
ddeutil_workflow-0.0.52.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|