ddeutil-workflow 0.0.50__tar.gz → 0.0.52__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.50/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.52}/PKG-INFO +69 -13
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/README.md +66 -10
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/pyproject.toml +2 -2
- ddeutil_workflow-0.0.52/src/ddeutil/workflow/__about__.py +1 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__init__.py +7 -56
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/conf.py +2 -2
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/cron.py +46 -20
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/job.py +181 -100
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/logs.py +22 -18
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/params.py +56 -16
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/reusables.py +4 -2
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/scheduler.py +5 -1
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/stages.py +320 -154
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/workflow.py +83 -74
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52/src/ddeutil_workflow.egg-info}/PKG-INFO +69 -13
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/requires.txt +2 -2
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_conf.py +4 -1
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_job.py +14 -6
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_job_exec_strategy.py +2 -2
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_params.py +17 -0
- ddeutil_workflow-0.0.52/tests/test_result.py +105 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_stage.py +29 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_stage_handler_exec.py +258 -47
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec.py +70 -5
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec_release.py +4 -4
- ddeutil_workflow-0.0.50/src/ddeutil/workflow/__about__.py +0 -1
- ddeutil_workflow-0.0.50/tests/test_result.py +0 -59
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/LICENSE +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/setup.cfg +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__cron.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__main__.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/__types.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/__init__.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/api.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/log.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/repeat.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/job.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/logs.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/schedules.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/exceptions.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/result.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil/workflow/utils.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/SOURCES.txt +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test__cron.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test__regex.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_cron_on.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_job_exec.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_job_strategy.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_logs_audit.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_logs_trace.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_release.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_release_queue.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_reusables_call_tag.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_reusables_template.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_reusables_template_filter.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_schedule.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_schedule_pending.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_schedule_tasks.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_schedule_workflow.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_scheduler_control.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_utils.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_workflow.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec_job.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/tests/test_workflow_exec_poke.py +0 -0
- {ddeutil_workflow-0.0.50 → ddeutil_workflow-0.0.52}/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.
|
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
|
@@ -94,7 +94,8 @@ flowchart LR
|
|
94
94
|
|
95
95
|
## 📦 Installation
|
96
96
|
|
97
|
-
This project need `ddeutil` and `ddeutil-io` extension namespace packages
|
97
|
+
This project need `ddeutil` and `ddeutil-io` extension namespace packages to be
|
98
|
+
the base deps.
|
98
99
|
If you want to install this package with application add-ons, you should add
|
99
100
|
`app` in installation;
|
100
101
|
|
@@ -103,7 +104,7 @@ If you want to install this package with application add-ons, you should add
|
|
103
104
|
| Python | `ddeutil-workflow` | :heavy_check_mark: |
|
104
105
|
| FastAPI Server | `ddeutil-workflow[api]` | :heavy_check_mark: |
|
105
106
|
|
106
|
-
##
|
107
|
+
## 🎯 Usage
|
107
108
|
|
108
109
|
This is examples that use workflow file for running common Data Engineering
|
109
110
|
use-case.
|
@@ -153,12 +154,54 @@ run-py-local:
|
|
153
154
|
|
154
155
|
# Arguments of target data that want to land.
|
155
156
|
writing_mode: flatten
|
156
|
-
|
157
|
+
aws:
|
158
|
+
path: my-data/open-data/${{ params.source-extract }}
|
157
159
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
160
|
+
# This Authentication code should implement with your custom call
|
161
|
+
# function. The template allow you to use environment variable.
|
162
|
+
access_client_id: ${AWS_ACCESS_CLIENT_ID}
|
163
|
+
access_client_secret: ${AWS_ACCESS_CLIENT_SECRET}
|
164
|
+
```
|
165
|
+
|
166
|
+
Before execute this workflow, you should implement caller function first.
|
167
|
+
|
168
|
+
```text
|
169
|
+
registry-caller/
|
170
|
+
╰─ tasks.py
|
171
|
+
```
|
172
|
+
|
173
|
+
This function will store as module that will import from `WORKFLOW_CORE_REGISTRY_CALLER`
|
174
|
+
value (This config can override by extra parameters with `registry_caller` key).
|
175
|
+
|
176
|
+
```python
|
177
|
+
from ddeutil.workflow import Result, tag
|
178
|
+
from ddeutil.workflow.exceptions import StageException
|
179
|
+
from pydantic import BaseModel, SecretStr
|
180
|
+
|
181
|
+
class AwsCredential(BaseModel):
|
182
|
+
path: str
|
183
|
+
access_client_id: str
|
184
|
+
access_client_secret: SecretStr
|
185
|
+
|
186
|
+
class RestAuth(BaseModel):
|
187
|
+
type: str
|
188
|
+
keys: SecretStr
|
189
|
+
|
190
|
+
@tag("requests", alias="get-api-with-oauth-to-s3")
|
191
|
+
def get_api_with_oauth_to_s3(
|
192
|
+
method: str,
|
193
|
+
url: str,
|
194
|
+
body: dict[str, str],
|
195
|
+
auth: RestAuth,
|
196
|
+
writing_node: str,
|
197
|
+
aws: AwsCredential,
|
198
|
+
result: Result,
|
199
|
+
) -> dict[str, int]:
|
200
|
+
result.trace.info("[CALLER]: Start get data via RestAPI to S3.")
|
201
|
+
result.trace.info(f"... {method}: {url}")
|
202
|
+
if method != "post":
|
203
|
+
raise StageException(f"RestAPI does not support for {method} action.")
|
204
|
+
return {"records": 1000}
|
162
205
|
```
|
163
206
|
|
164
207
|
The above workflow template is main executor pipeline that you want to do. If you
|
@@ -253,13 +296,15 @@ only.
|
|
253
296
|
## :rocket: Deployment
|
254
297
|
|
255
298
|
This package able to run as an application service for receive manual trigger
|
256
|
-
from
|
257
|
-
like crontab job but via Python API.
|
299
|
+
from any node via RestAPI or use to be Scheduler background application
|
300
|
+
like crontab job but via Python API or FastAPI app.
|
258
301
|
|
259
302
|
### API Server
|
260
303
|
|
304
|
+
This server use FastAPI package to be the base application.
|
305
|
+
|
261
306
|
```shell
|
262
|
-
(venv) $ uvicorn ddeutil.workflow.api:app \
|
307
|
+
(.venv) $ uvicorn ddeutil.workflow.api:app \
|
263
308
|
--host 127.0.0.1 \
|
264
309
|
--port 80 \
|
265
310
|
--no-access-log
|
@@ -269,8 +314,19 @@ like crontab job but via Python API.
|
|
269
314
|
> If this package already deploy, it is able to use multiprocess;
|
270
315
|
> `uvicorn ddeutil.workflow.api:app --host 127.0.0.1 --port 80 --workers 4`
|
271
316
|
|
317
|
+
### Local Schedule
|
318
|
+
|
319
|
+
> [!WARNING]
|
320
|
+
> This CLI does not implement yet.
|
321
|
+
|
322
|
+
```shell
|
323
|
+
(.venv) $ ddeutil-workflow schedule
|
324
|
+
```
|
325
|
+
|
272
326
|
### Docker Container
|
273
327
|
|
328
|
+
Build a Docker container from this package.
|
329
|
+
|
274
330
|
```shell
|
275
331
|
$ docker build -t ddeutil-workflow:latest -f .container/Dockerfile .
|
276
332
|
$ docker run -i ddeutil-workflow:latest ddeutil-workflow
|
@@ -25,8 +25,8 @@ classifiers = [
|
|
25
25
|
]
|
26
26
|
requires-python = ">=3.9.13"
|
27
27
|
dependencies = [
|
28
|
-
"ddeutil[checksum]>=0.4.
|
29
|
-
"ddeutil-io[yaml,toml]>=0.2.
|
28
|
+
"ddeutil[checksum]>=0.4.7",
|
29
|
+
"ddeutil-io[yaml,toml]>=0.2.11",
|
30
30
|
"pydantic==2.11.1",
|
31
31
|
"python-dotenv==1.1.0",
|
32
32
|
"schedule==1.2.2,<2.0.0",
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__: str = "0.0.52"
|
@@ -11,25 +11,9 @@ from .conf import (
|
|
11
11
|
config,
|
12
12
|
env,
|
13
13
|
)
|
14
|
-
from .cron import
|
15
|
-
|
16
|
-
|
17
|
-
interval2crontab,
|
18
|
-
)
|
19
|
-
from .exceptions import (
|
20
|
-
JobException,
|
21
|
-
ParamValueException,
|
22
|
-
StageException,
|
23
|
-
UtilException,
|
24
|
-
WorkflowException,
|
25
|
-
)
|
26
|
-
from .job import (
|
27
|
-
Job,
|
28
|
-
RunsOn,
|
29
|
-
Strategy,
|
30
|
-
local_execute,
|
31
|
-
local_execute_strategy,
|
32
|
-
)
|
14
|
+
from .cron import *
|
15
|
+
from .exceptions import *
|
16
|
+
from .job import *
|
33
17
|
from .logs import (
|
34
18
|
Audit,
|
35
19
|
AuditModel,
|
@@ -41,13 +25,7 @@ from .logs import (
|
|
41
25
|
get_dt_tznow,
|
42
26
|
get_trace,
|
43
27
|
)
|
44
|
-
from .params import
|
45
|
-
ChoiceParam,
|
46
|
-
DatetimeParam,
|
47
|
-
IntParam,
|
48
|
-
Param,
|
49
|
-
StrParam,
|
50
|
-
)
|
28
|
+
from .params import *
|
51
29
|
from .result import (
|
52
30
|
CANCEL,
|
53
31
|
FAILED,
|
@@ -82,33 +60,6 @@ from .scheduler import (
|
|
82
60
|
schedule_runner,
|
83
61
|
schedule_task,
|
84
62
|
)
|
85
|
-
from .stages import
|
86
|
-
|
87
|
-
|
88
|
-
EmptyStage,
|
89
|
-
ForEachStage,
|
90
|
-
ParallelStage,
|
91
|
-
PyStage,
|
92
|
-
Stage,
|
93
|
-
TriggerStage,
|
94
|
-
)
|
95
|
-
from .utils import (
|
96
|
-
batch,
|
97
|
-
cross_product,
|
98
|
-
default_gen_id,
|
99
|
-
delay,
|
100
|
-
filter_func,
|
101
|
-
gen_id,
|
102
|
-
get_diff_sec,
|
103
|
-
get_dt_now,
|
104
|
-
make_exec,
|
105
|
-
reach_next_minute,
|
106
|
-
replace_sec,
|
107
|
-
wait_to_next_minute,
|
108
|
-
)
|
109
|
-
from .workflow import (
|
110
|
-
Release,
|
111
|
-
ReleaseQueue,
|
112
|
-
Workflow,
|
113
|
-
WorkflowTask,
|
114
|
-
)
|
63
|
+
from .stages import *
|
64
|
+
from .utils import *
|
65
|
+
from .workflow import *
|
@@ -360,13 +360,13 @@ def dynamic(
|
|
360
360
|
:param extras: An extra values that pass at run-time.
|
361
361
|
"""
|
362
362
|
rsx: Optional[T] = extras[key] if extras and key in extras else None
|
363
|
-
rs: Optional[T] =
|
363
|
+
rs: Optional[T] = getattr(config, key, None) if f is None else f
|
364
364
|
if rsx is not None and not isinstance(rsx, type(rs)):
|
365
365
|
raise TypeError(
|
366
366
|
f"Type of config {key!r} from extras: {rsx!r} does not valid "
|
367
367
|
f"as config {type(rs)}."
|
368
368
|
)
|
369
|
-
return rsx
|
369
|
+
return rsx if rsx is not None else rs
|
370
370
|
|
371
371
|
|
372
372
|
class Loader(SimLoad):
|
@@ -16,18 +16,13 @@ from pydantic.functional_validators import field_validator, model_validator
|
|
16
16
|
from typing_extensions import Self
|
17
17
|
|
18
18
|
from .__cron import WEEKDAYS, CronJob, CronJobYear, CronRunner, Options
|
19
|
-
from .__types import DictData, DictStr
|
19
|
+
from .__types import DictData, DictStr
|
20
20
|
from .conf import Loader
|
21
21
|
|
22
|
-
__all__: TupleStr = (
|
23
|
-
"On",
|
24
|
-
"YearOn",
|
25
|
-
"interval2crontab",
|
26
|
-
)
|
27
|
-
|
28
22
|
|
29
23
|
def interval2crontab(
|
30
24
|
interval: Literal["daily", "weekly", "monthly"],
|
25
|
+
*,
|
31
26
|
day: str | None = None,
|
32
27
|
time: str = "00:00",
|
33
28
|
) -> str:
|
@@ -67,7 +62,7 @@ class On(BaseModel):
|
|
67
62
|
"""On Pydantic model (Warped crontab object by model).
|
68
63
|
|
69
64
|
See Also:
|
70
|
-
*
|
65
|
+
* `generate()` is the main use-case of this schedule object.
|
71
66
|
"""
|
72
67
|
|
73
68
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
@@ -76,12 +71,18 @@ class On(BaseModel):
|
|
76
71
|
DictData,
|
77
72
|
Field(
|
78
73
|
default_factory=dict,
|
79
|
-
description=
|
74
|
+
description=(
|
75
|
+
"An extras parameters that want to pass to the CronJob field."
|
76
|
+
),
|
80
77
|
),
|
81
78
|
]
|
82
79
|
cronjob: Annotated[
|
83
80
|
CronJob,
|
84
|
-
Field(
|
81
|
+
Field(
|
82
|
+
description=(
|
83
|
+
"A Cronjob object that use for validate and generate datetime.",
|
84
|
+
),
|
85
|
+
),
|
85
86
|
]
|
86
87
|
tz: Annotated[
|
87
88
|
str,
|
@@ -118,6 +119,8 @@ class On(BaseModel):
|
|
118
119
|
|
119
120
|
:param name: A name of config that will get from loader.
|
120
121
|
:param extras: An extra parameter that will keep in extras.
|
122
|
+
|
123
|
+
:rtype: Self
|
121
124
|
"""
|
122
125
|
extras: DictData = extras or {}
|
123
126
|
loader: Loader = Loader(name, externals=extras)
|
@@ -142,9 +145,7 @@ class On(BaseModel):
|
|
142
145
|
)
|
143
146
|
)
|
144
147
|
if "cronjob" not in loader_data:
|
145
|
-
raise ValueError(
|
146
|
-
"Config does not set ``cronjob`` or ``interval`` keys"
|
147
|
-
)
|
148
|
+
raise ValueError("Config does not set `cronjob` or `interval` keys")
|
148
149
|
return cls.model_validate(
|
149
150
|
obj=dict(
|
150
151
|
cronjob=loader_data.pop("cronjob"),
|
@@ -155,7 +156,13 @@ class On(BaseModel):
|
|
155
156
|
|
156
157
|
@model_validator(mode="before")
|
157
158
|
def __prepare_values(cls, values: DictData) -> DictData:
|
158
|
-
"""Extract tz key from value and change name to timezone key.
|
159
|
+
"""Extract tz key from value and change name to timezone key.
|
160
|
+
|
161
|
+
:param values: (DictData) A data that want to pass for create an On
|
162
|
+
model.
|
163
|
+
|
164
|
+
:rtype: DictData
|
165
|
+
"""
|
159
166
|
if tz := values.pop("tz", None):
|
160
167
|
values["timezone"] = tz
|
161
168
|
return values
|
@@ -170,8 +177,8 @@ class On(BaseModel):
|
|
170
177
|
try:
|
171
178
|
_ = ZoneInfo(value)
|
172
179
|
return value
|
173
|
-
except ZoneInfoNotFoundError as
|
174
|
-
raise ValueError(f"Invalid timezone: {value}") from
|
180
|
+
except ZoneInfoNotFoundError as e:
|
181
|
+
raise ValueError(f"Invalid timezone: {value}") from e
|
175
182
|
|
176
183
|
@field_validator(
|
177
184
|
"cronjob", mode="before", json_schema_input_type=Union[CronJob, str]
|
@@ -180,9 +187,13 @@ class On(BaseModel):
|
|
180
187
|
cls, value: str | CronJob, info: ValidationInfo
|
181
188
|
) -> CronJob:
|
182
189
|
"""Prepare crontab value that able to receive with string type.
|
183
|
-
This step will get options kwargs from extras and pass to the
|
190
|
+
This step will get options kwargs from extras field and pass to the
|
184
191
|
CronJob object.
|
185
192
|
|
193
|
+
:param value: (str | CronJobYear) A cronjob value that want to create.
|
194
|
+
:param info: (ValidationInfo) A validation info object that use to get
|
195
|
+
the extra parameters for create cronjob.
|
196
|
+
|
186
197
|
:rtype: CronJob
|
187
198
|
"""
|
188
199
|
extras: DictData = info.data.get("extras", {})
|
@@ -203,12 +214,17 @@ class On(BaseModel):
|
|
203
214
|
def __serialize_cronjob(self, value: CronJob) -> str:
|
204
215
|
"""Serialize the cronjob field that store with CronJob object.
|
205
216
|
|
217
|
+
:param value: (CronJob) The CronJob field.
|
218
|
+
|
206
219
|
:rtype: str
|
207
220
|
"""
|
208
221
|
return str(value)
|
209
222
|
|
210
223
|
def generate(self, start: str | datetime) -> CronRunner:
|
211
|
-
"""Return
|
224
|
+
"""Return CronRunner object from an initial datetime.
|
225
|
+
|
226
|
+
:param start: (str | datetime) A string or datetime for generate the
|
227
|
+
CronRunner object.
|
212
228
|
|
213
229
|
:rtype: CronRunner
|
214
230
|
"""
|
@@ -242,16 +258,26 @@ class YearOn(On):
|
|
242
258
|
# NOTE: This is fields of the base schedule.
|
243
259
|
cronjob: Annotated[
|
244
260
|
CronJobYear,
|
245
|
-
Field(
|
261
|
+
Field(
|
262
|
+
description=(
|
263
|
+
"A Cronjob object that use for validate and generate datetime.",
|
264
|
+
),
|
265
|
+
),
|
246
266
|
]
|
247
267
|
|
248
268
|
@field_validator(
|
249
|
-
"cronjob", mode="before", json_schema_input_type=Union[
|
269
|
+
"cronjob", mode="before", json_schema_input_type=Union[CronJobYear, str]
|
250
270
|
)
|
251
271
|
def __prepare_cronjob(
|
252
272
|
cls, value: str | CronJobYear, info: ValidationInfo
|
253
273
|
) -> CronJobYear:
|
254
274
|
"""Prepare crontab value that able to receive with string type.
|
275
|
+
This step will get options kwargs from extras field and pass to the
|
276
|
+
CronJobYear object.
|
277
|
+
|
278
|
+
:param value: (str | CronJobYear) A cronjob value that want to create.
|
279
|
+
:param info: (ValidationInfo) A validation info object that use to get
|
280
|
+
the extra parameters for create cronjob.
|
255
281
|
|
256
282
|
:rtype: CronJobYear
|
257
283
|
"""
|