ddeutil-workflow 0.0.57__py3-none-any.whl → 0.0.59__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/__cron.py +3 -3
- ddeutil/workflow/conf.py +7 -5
- ddeutil/workflow/event.py +12 -12
- ddeutil/workflow/exceptions.py +3 -3
- ddeutil/workflow/job.py +44 -36
- ddeutil/workflow/logs.py +123 -82
- ddeutil/workflow/params.py +13 -5
- ddeutil/workflow/result.py +18 -18
- ddeutil/workflow/reusables.py +9 -9
- ddeutil/workflow/scheduler.py +17 -15
- ddeutil/workflow/stages.py +149 -102
- ddeutil/workflow/utils.py +6 -6
- ddeutil/workflow/workflow.py +48 -48
- {ddeutil_workflow-0.0.57.dist-info → ddeutil_workflow-0.0.59.dist-info}/METADATA +6 -3
- ddeutil_workflow-0.0.59.dist-info/RECORD +31 -0
- {ddeutil_workflow-0.0.57.dist-info → ddeutil_workflow-0.0.59.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.57.dist-info/RECORD +0 -31
- {ddeutil_workflow-0.0.57.dist-info → ddeutil_workflow-0.0.59.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.57.dist-info → ddeutil_workflow-0.0.59.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.57.dist-info → ddeutil_workflow-0.0.59.dist-info}/top_level.txt +0 -0
ddeutil/workflow/logs.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
# [x] Use dynamic config
|
7
6
|
# [x] Use fix config for `get_logger`, and Model initialize step.
|
8
7
|
"""A Logs module contain Trace dataclass and Audit Pydantic model.
|
9
8
|
"""
|
@@ -14,7 +13,6 @@ import logging
|
|
14
13
|
import os
|
15
14
|
from abc import ABC, abstractmethod
|
16
15
|
from collections.abc import Iterator
|
17
|
-
from dataclasses import field
|
18
16
|
from datetime import datetime
|
19
17
|
from functools import lru_cache
|
20
18
|
from inspect import Traceback, currentframe, getframeinfo
|
@@ -22,12 +20,11 @@ from pathlib import Path
|
|
22
20
|
from threading import get_ident
|
23
21
|
from typing import ClassVar, Literal, Optional, TypeVar, Union
|
24
22
|
|
25
|
-
from pydantic import BaseModel, Field
|
26
|
-
from pydantic.dataclasses import dataclass
|
23
|
+
from pydantic import BaseModel, ConfigDict, Field
|
27
24
|
from pydantic.functional_validators import model_validator
|
28
25
|
from typing_extensions import Self
|
29
26
|
|
30
|
-
from .__types import DictData
|
27
|
+
from .__types import DictData
|
31
28
|
from .conf import config, dynamic
|
32
29
|
from .utils import cut_id, get_dt_now, prepare_newline
|
33
30
|
|
@@ -70,31 +67,44 @@ def get_dt_tznow() -> datetime: # pragma: no cov
|
|
70
67
|
return get_dt_now(tz=config.tz)
|
71
68
|
|
72
69
|
|
70
|
+
PREFIX_LOGS: dict[str, dict] = {
|
71
|
+
"CALLER": {"emoji": ""},
|
72
|
+
"STAGE": {"emoji": ""},
|
73
|
+
"JOB": {"emoji": ""},
|
74
|
+
"WORKFLOW": {"emoji": "🏃"},
|
75
|
+
"RELEASE": {"emoji": ""},
|
76
|
+
"POKE": {"emoji": ""},
|
77
|
+
} # pragma: no cov
|
78
|
+
|
79
|
+
|
73
80
|
class TraceMeta(BaseModel): # pragma: no cov
|
74
81
|
"""Trace Metadata model for making the current metadata of this CPU, Memory
|
75
82
|
process, and thread data.
|
76
83
|
"""
|
77
84
|
|
78
|
-
mode: Literal["stdout", "stderr"]
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
+
mode: Literal["stdout", "stderr"] = Field(description="A meta mode.")
|
86
|
+
level: str = Field(description="A log level.")
|
87
|
+
datetime: str = Field(description="A datetime in string format.")
|
88
|
+
process: int = Field(description="A process ID.")
|
89
|
+
thread: int = Field(description="A thread ID.")
|
90
|
+
message: str = Field(description="A message log.")
|
91
|
+
filename: str = Field(description="A filename of this log.")
|
92
|
+
lineno: int = Field(description="A line number of this log.")
|
85
93
|
|
86
94
|
@classmethod
|
87
95
|
def make(
|
88
96
|
cls,
|
89
97
|
mode: Literal["stdout", "stderr"],
|
90
98
|
message: str,
|
99
|
+
level: str,
|
91
100
|
*,
|
92
101
|
extras: Optional[DictData] = None,
|
93
102
|
) -> Self:
|
94
103
|
"""Make the current TraceMeta instance that catching local state.
|
95
104
|
|
96
|
-
:param mode: A metadata mode.
|
97
|
-
:param message: A message.
|
105
|
+
:param mode: (Literal["stdout", "stderr"]) A metadata mode.
|
106
|
+
:param message: (str) A message.
|
107
|
+
:param level: (str) A log level.
|
98
108
|
:param extras: (DictData) An extra parameter that want to override core
|
99
109
|
config values.
|
100
110
|
|
@@ -106,6 +116,7 @@ class TraceMeta(BaseModel): # pragma: no cov
|
|
106
116
|
extras: DictData = extras or {}
|
107
117
|
return cls(
|
108
118
|
mode=mode,
|
119
|
+
level=level,
|
109
120
|
datetime=(
|
110
121
|
get_dt_now(tz=dynamic("tz", extras=extras)).strftime(
|
111
122
|
dynamic("log_datetime_format", extras=extras)
|
@@ -140,13 +151,11 @@ class TraceData(BaseModel): # pragma: no cov
|
|
140
151
|
|
141
152
|
:rtype: Self
|
142
153
|
"""
|
143
|
-
data:
|
144
|
-
|
145
|
-
if (file / "stdout.txt").exists():
|
146
|
-
data["stdout"] = (file / "stdout.txt").read_text(encoding="utf-8")
|
154
|
+
data: DictData = {"stdout": "", "stderr": "", "meta": []}
|
147
155
|
|
148
|
-
|
149
|
-
|
156
|
+
for mode in ("stdout", "stderr"):
|
157
|
+
if (file / f"{mode}.txt").exists():
|
158
|
+
data[mode] = (file / f"{mode}.txt").read_text(encoding="utf-8")
|
150
159
|
|
151
160
|
if (file / "metadata.json").exists():
|
152
161
|
data["meta"] = [
|
@@ -161,19 +170,28 @@ class TraceData(BaseModel): # pragma: no cov
|
|
161
170
|
return cls.model_validate(data)
|
162
171
|
|
163
172
|
|
164
|
-
|
165
|
-
|
166
|
-
"""Base Trace dataclass with abstraction class property."""
|
173
|
+
class BaseTrace(BaseModel, ABC): # pragma: no cov
|
174
|
+
"""Base Trace model with abstraction class property."""
|
167
175
|
|
168
|
-
|
169
|
-
|
170
|
-
|
176
|
+
model_config = ConfigDict(frozen=True)
|
177
|
+
|
178
|
+
run_id: str = Field(default="A running ID")
|
179
|
+
parent_run_id: Optional[str] = Field(
|
180
|
+
default=None, description="A parent running ID"
|
181
|
+
)
|
182
|
+
extras: DictData = Field(
|
183
|
+
default_factory=dict,
|
184
|
+
description=(
|
185
|
+
"An extra parameter that want to override on the core config "
|
186
|
+
"values."
|
187
|
+
),
|
188
|
+
)
|
171
189
|
|
172
190
|
@classmethod
|
173
191
|
@abstractmethod
|
174
192
|
def find_traces(
|
175
193
|
cls,
|
176
|
-
path: Path
|
194
|
+
path: Optional[Path] = None,
|
177
195
|
extras: Optional[DictData] = None,
|
178
196
|
) -> Iterator[TraceData]: # pragma: no cov
|
179
197
|
raise NotImplementedError(
|
@@ -187,7 +205,7 @@ class BaseTrace(ABC): # pragma: no cov
|
|
187
205
|
run_id: str,
|
188
206
|
force_raise: bool = True,
|
189
207
|
*,
|
190
|
-
path: Path
|
208
|
+
path: Optional[Path] = None,
|
191
209
|
extras: Optional[DictData] = None,
|
192
210
|
) -> TraceData:
|
193
211
|
raise NotImplementedError(
|
@@ -196,24 +214,30 @@ class BaseTrace(ABC): # pragma: no cov
|
|
196
214
|
)
|
197
215
|
|
198
216
|
@abstractmethod
|
199
|
-
def writer(self, message: str, is_err: bool = False) -> None:
|
217
|
+
def writer(self, message: str, level: str, is_err: bool = False) -> None:
|
200
218
|
"""Write a trace message after making to target pointer object. The
|
201
219
|
target can be anything be inherited this class and overwrite this method
|
202
220
|
such as file, console, or database.
|
203
221
|
|
204
|
-
:param message: A message after making.
|
205
|
-
:param
|
222
|
+
:param message: (str) A message after making.
|
223
|
+
:param level: (str) A log level.
|
224
|
+
:param is_err: (bool) A flag for writing with an error trace or not.
|
225
|
+
(Default be False)
|
206
226
|
"""
|
207
227
|
raise NotImplementedError(
|
208
228
|
"Create writer logic for this trace object before using."
|
209
229
|
)
|
210
230
|
|
211
231
|
@abstractmethod
|
212
|
-
async def awriter(
|
232
|
+
async def awriter(
|
233
|
+
self, message: str, level: str, is_err: bool = False
|
234
|
+
) -> None:
|
213
235
|
"""Async Write a trace message after making to target pointer object.
|
214
236
|
|
215
|
-
:param message:
|
216
|
-
:param
|
237
|
+
:param message: (str) A message after making.
|
238
|
+
:param level: (str) A log level.
|
239
|
+
:param is_err: (bool) A flag for writing with an error trace or not.
|
240
|
+
(Default be False)
|
217
241
|
"""
|
218
242
|
raise NotImplementedError(
|
219
243
|
"Create async writer logic for this trace object before using."
|
@@ -244,7 +268,7 @@ class BaseTrace(ABC): # pragma: no cov
|
|
244
268
|
if mode != "debug" or (
|
245
269
|
mode == "debug" and dynamic("debug", extras=self.extras)
|
246
270
|
):
|
247
|
-
self.writer(msg, is_err=is_err)
|
271
|
+
self.writer(msg, level=mode, is_err=is_err)
|
248
272
|
|
249
273
|
getattr(logger, mode)(msg, stacklevel=3)
|
250
274
|
|
@@ -288,16 +312,30 @@ class BaseTrace(ABC): # pragma: no cov
|
|
288
312
|
"""
|
289
313
|
self.__logging(message, mode="exception", is_err=True)
|
290
314
|
|
315
|
+
async def __alogging(
|
316
|
+
self, message: str, mode: str, *, is_err: bool = False
|
317
|
+
) -> None:
|
318
|
+
"""Write trace log with append mode and logging this message with any
|
319
|
+
logging level.
|
320
|
+
|
321
|
+
:param message: (str) A message that want to log.
|
322
|
+
"""
|
323
|
+
msg: str = prepare_newline(self.make_message(message))
|
324
|
+
|
325
|
+
if mode != "debug" or (
|
326
|
+
mode == "debug" and dynamic("debug", extras=self.extras)
|
327
|
+
):
|
328
|
+
await self.awriter(msg, level=mode, is_err=is_err)
|
329
|
+
|
330
|
+
getattr(logger, mode)(msg, stacklevel=3)
|
331
|
+
|
291
332
|
async def adebug(self, message: str) -> None: # pragma: no cov
|
292
333
|
"""Async write trace log with append mode and logging this message with
|
293
334
|
the DEBUG level.
|
294
335
|
|
295
336
|
:param message: (str) A message that want to log.
|
296
337
|
"""
|
297
|
-
|
298
|
-
if dynamic("debug", extras=self.extras):
|
299
|
-
await self.awriter(msg)
|
300
|
-
logger.info(msg, stacklevel=2)
|
338
|
+
await self.__alogging(message, mode="debug")
|
301
339
|
|
302
340
|
async def ainfo(self, message: str) -> None: # pragma: no cov
|
303
341
|
"""Async write trace log with append mode and logging this message with
|
@@ -305,9 +343,7 @@ class BaseTrace(ABC): # pragma: no cov
|
|
305
343
|
|
306
344
|
:param message: (str) A message that want to log.
|
307
345
|
"""
|
308
|
-
|
309
|
-
await self.awriter(msg)
|
310
|
-
logger.info(msg, stacklevel=2)
|
346
|
+
await self.__alogging(message, mode="info")
|
311
347
|
|
312
348
|
async def awarning(self, message: str) -> None: # pragma: no cov
|
313
349
|
"""Async write trace log with append mode and logging this message with
|
@@ -315,9 +351,7 @@ class BaseTrace(ABC): # pragma: no cov
|
|
315
351
|
|
316
352
|
:param message: (str) A message that want to log.
|
317
353
|
"""
|
318
|
-
|
319
|
-
await self.awriter(msg)
|
320
|
-
logger.warning(msg, stacklevel=2)
|
354
|
+
await self.__alogging(message, mode="warning")
|
321
355
|
|
322
356
|
async def aerror(self, message: str) -> None: # pragma: no cov
|
323
357
|
"""Async write trace log with append mode and logging this message with
|
@@ -325,9 +359,7 @@ class BaseTrace(ABC): # pragma: no cov
|
|
325
359
|
|
326
360
|
:param message: (str) A message that want to log.
|
327
361
|
"""
|
328
|
-
|
329
|
-
await self.awriter(msg, is_err=True)
|
330
|
-
logger.error(msg, stacklevel=2)
|
362
|
+
await self.__alogging(message, mode="error", is_err=True)
|
331
363
|
|
332
364
|
async def aexception(self, message: str) -> None: # pragma: no cov
|
333
365
|
"""Async write trace log with append mode and logging this message with
|
@@ -335,9 +367,7 @@ class BaseTrace(ABC): # pragma: no cov
|
|
335
367
|
|
336
368
|
:param message: (str) A message that want to log.
|
337
369
|
"""
|
338
|
-
|
339
|
-
await self.awriter(msg, is_err=True)
|
340
|
-
logger.exception(msg, stacklevel=2)
|
370
|
+
await self.__alogging(message, mode="exception", is_err=True)
|
341
371
|
|
342
372
|
|
343
373
|
class FileTrace(BaseTrace): # pragma: no cov
|
@@ -346,12 +376,12 @@ class FileTrace(BaseTrace): # pragma: no cov
|
|
346
376
|
@classmethod
|
347
377
|
def find_traces(
|
348
378
|
cls,
|
349
|
-
path: Path
|
379
|
+
path: Optional[Path] = None,
|
350
380
|
extras: Optional[DictData] = None,
|
351
381
|
) -> Iterator[TraceData]: # pragma: no cov
|
352
382
|
"""Find trace logs.
|
353
383
|
|
354
|
-
:param path: (Path)
|
384
|
+
:param path: (Path) A trace path that want to find.
|
355
385
|
:param extras: An extra parameter that want to override core config.
|
356
386
|
"""
|
357
387
|
for file in sorted(
|
@@ -364,16 +394,16 @@ class FileTrace(BaseTrace): # pragma: no cov
|
|
364
394
|
def find_trace_with_id(
|
365
395
|
cls,
|
366
396
|
run_id: str,
|
367
|
-
force_raise: bool = True,
|
368
397
|
*,
|
369
|
-
|
398
|
+
force_raise: bool = True,
|
399
|
+
path: Optional[Path] = None,
|
370
400
|
extras: Optional[DictData] = None,
|
371
401
|
) -> TraceData:
|
372
402
|
"""Find trace log with an input specific run ID.
|
373
403
|
|
374
404
|
:param run_id: A running ID of trace log.
|
375
|
-
:param force_raise:
|
376
|
-
:param path:
|
405
|
+
:param force_raise: (bool)
|
406
|
+
:param path: (Path)
|
377
407
|
:param extras: An extra parameter that want to override core config.
|
378
408
|
"""
|
379
409
|
base_path: Path = path or dynamic("trace_path", extras=extras)
|
@@ -385,7 +415,7 @@ class FileTrace(BaseTrace): # pragma: no cov
|
|
385
415
|
f"Trace log on path {base_path}, does not found trace "
|
386
416
|
f"'run_id={run_id}'."
|
387
417
|
)
|
388
|
-
return
|
418
|
+
return TraceData(stdout="", stderr="")
|
389
419
|
|
390
420
|
@property
|
391
421
|
def pointer(self) -> Path:
|
@@ -419,7 +449,7 @@ class FileTrace(BaseTrace): # pragma: no cov
|
|
419
449
|
"""
|
420
450
|
return f"({self.cut_id}) {message}"
|
421
451
|
|
422
|
-
def writer(self, message: str, is_err: bool = False) -> None:
|
452
|
+
def writer(self, message: str, level: str, is_err: bool = False) -> None:
|
423
453
|
"""Write a trace message after making to target file and write metadata
|
424
454
|
in the same path of standard files.
|
425
455
|
|
@@ -429,16 +459,19 @@ class FileTrace(BaseTrace): # pragma: no cov
|
|
429
459
|
... ./logs/run_id=<run-id>/stdout.txt
|
430
460
|
... ./logs/run_id=<run-id>/stderr.txt
|
431
461
|
|
432
|
-
:param message: A message after making.
|
462
|
+
:param message: (str) A message after making.
|
463
|
+
:param level: (str) A log level.
|
433
464
|
:param is_err: A flag for writing with an error trace or not.
|
434
465
|
"""
|
435
466
|
if not dynamic("enable_write_log", extras=self.extras):
|
436
467
|
return
|
437
468
|
|
438
|
-
|
439
|
-
trace_meta: TraceMeta = TraceMeta.make(
|
469
|
+
mode: Literal["stdout", "stderr"] = "stderr" if is_err else "stdout"
|
470
|
+
trace_meta: TraceMeta = TraceMeta.make(
|
471
|
+
mode=mode, level=level, message=message
|
472
|
+
)
|
440
473
|
|
441
|
-
with (self.pointer / f"{
|
474
|
+
with (self.pointer / f"{mode}.txt").open(
|
442
475
|
mode="at", encoding="utf-8"
|
443
476
|
) as f:
|
444
477
|
fmt: str = dynamic("log_format_file", extras=self.extras)
|
@@ -450,7 +483,7 @@ class FileTrace(BaseTrace): # pragma: no cov
|
|
450
483
|
f.write(trace_meta.model_dump_json() + "\n")
|
451
484
|
|
452
485
|
async def awriter(
|
453
|
-
self, message: str, is_err: bool = False
|
486
|
+
self, message: str, level: str, is_err: bool = False
|
454
487
|
) -> None: # pragma: no cov
|
455
488
|
"""Write with async mode."""
|
456
489
|
if not dynamic("enable_write_log", extras=self.extras):
|
@@ -461,11 +494,13 @@ class FileTrace(BaseTrace): # pragma: no cov
|
|
461
494
|
except ImportError as e:
|
462
495
|
raise ImportError("Async mode need aiofiles package") from e
|
463
496
|
|
464
|
-
|
465
|
-
trace_meta: TraceMeta = TraceMeta.make(
|
497
|
+
mode: Literal["stdout", "stderr"] = "stderr" if is_err else "stdout"
|
498
|
+
trace_meta: TraceMeta = TraceMeta.make(
|
499
|
+
mode=mode, level=level, message=message
|
500
|
+
)
|
466
501
|
|
467
502
|
async with aiofiles.open(
|
468
|
-
self.pointer / f"{
|
503
|
+
self.pointer / f"{mode}.txt", mode="at", encoding="utf-8"
|
469
504
|
) as f:
|
470
505
|
fmt: str = dynamic("log_format_file", extras=self.extras)
|
471
506
|
await f.write(f"{fmt}\n".format(**trace_meta.model_dump()))
|
@@ -493,7 +528,7 @@ class SQLiteTrace(BaseTrace): # pragma: no cov
|
|
493
528
|
@classmethod
|
494
529
|
def find_traces(
|
495
530
|
cls,
|
496
|
-
path: Path
|
531
|
+
path: Optional[Path] = None,
|
497
532
|
extras: Optional[DictData] = None,
|
498
533
|
) -> Iterator[TraceData]: ...
|
499
534
|
|
@@ -503,15 +538,19 @@ class SQLiteTrace(BaseTrace): # pragma: no cov
|
|
503
538
|
run_id: str,
|
504
539
|
force_raise: bool = True,
|
505
540
|
*,
|
506
|
-
path: Path
|
541
|
+
path: Optional[Path] = None,
|
507
542
|
extras: Optional[DictData] = None,
|
508
543
|
) -> TraceData: ...
|
509
544
|
|
510
545
|
def make_message(self, message: str) -> str: ...
|
511
546
|
|
512
|
-
def writer(
|
547
|
+
def writer(
|
548
|
+
self, message: str, level: str, is_err: bool = False
|
549
|
+
) -> None: ...
|
513
550
|
|
514
|
-
def awriter(
|
551
|
+
def awriter(
|
552
|
+
self, message: str, level: str, is_err: bool = False
|
553
|
+
) -> None: ...
|
515
554
|
|
516
555
|
|
517
556
|
Trace = TypeVar("Trace", bound=BaseTrace)
|
@@ -524,7 +563,7 @@ TraceModel = Union[
|
|
524
563
|
def get_trace(
|
525
564
|
run_id: str,
|
526
565
|
*,
|
527
|
-
parent_run_id: str
|
566
|
+
parent_run_id: Optional[str] = None,
|
528
567
|
extras: Optional[DictData] = None,
|
529
568
|
) -> TraceModel: # pragma: no cov
|
530
569
|
"""Get dynamic Trace instance from the core config (it can override by an
|
@@ -539,9 +578,11 @@ def get_trace(
|
|
539
578
|
"""
|
540
579
|
if dynamic("trace_path", extras=extras).is_file():
|
541
580
|
return SQLiteTrace(
|
542
|
-
run_id, parent_run_id=parent_run_id, extras=(extras or {})
|
581
|
+
run_id=run_id, parent_run_id=parent_run_id, extras=(extras or {})
|
543
582
|
)
|
544
|
-
return FileTrace(
|
583
|
+
return FileTrace(
|
584
|
+
run_id=run_id, parent_run_id=parent_run_id, extras=(extras or {})
|
585
|
+
)
|
545
586
|
|
546
587
|
|
547
588
|
class BaseAudit(BaseModel, ABC):
|
@@ -605,7 +646,7 @@ class BaseAudit(BaseModel, ABC):
|
|
605
646
|
def find_audit_with_release(
|
606
647
|
cls,
|
607
648
|
name: str,
|
608
|
-
release: datetime
|
649
|
+
release: Optional[datetime] = None,
|
609
650
|
*,
|
610
651
|
extras: Optional[DictData] = None,
|
611
652
|
) -> Self:
|
@@ -617,7 +658,7 @@ class BaseAudit(BaseModel, ABC):
|
|
617
658
|
"""To something before end up of initial log model."""
|
618
659
|
|
619
660
|
@abstractmethod
|
620
|
-
def save(self, excluded: list[str]
|
661
|
+
def save(self, excluded: Optional[list[str]]) -> None: # pragma: no cov
|
621
662
|
"""Save this model logging to target logging store."""
|
622
663
|
raise NotImplementedError("Audit should implement ``save`` method.")
|
623
664
|
|
@@ -662,7 +703,7 @@ class FileAudit(BaseAudit):
|
|
662
703
|
def find_audit_with_release(
|
663
704
|
cls,
|
664
705
|
name: str,
|
665
|
-
release: datetime
|
706
|
+
release: Optional[datetime] = None,
|
666
707
|
*,
|
667
708
|
extras: Optional[DictData] = None,
|
668
709
|
) -> Self:
|
@@ -735,7 +776,7 @@ class FileAudit(BaseAudit):
|
|
735
776
|
"audit_path", extras=self.extras
|
736
777
|
) / self.filename_fmt.format(name=self.name, release=self.release)
|
737
778
|
|
738
|
-
def save(self, excluded: list[str]
|
779
|
+
def save(self, excluded: Optional[list[str]]) -> Self:
|
739
780
|
"""Save logging data that receive a context data from a workflow
|
740
781
|
execution result.
|
741
782
|
|
@@ -744,7 +785,7 @@ class FileAudit(BaseAudit):
|
|
744
785
|
|
745
786
|
:rtype: Self
|
746
787
|
"""
|
747
|
-
trace:
|
788
|
+
trace: TraceModel = get_trace(
|
748
789
|
self.run_id,
|
749
790
|
parent_run_id=self.parent_run_id,
|
750
791
|
extras=self.extras,
|
@@ -804,16 +845,16 @@ class SQLiteAudit(BaseAudit): # pragma: no cov
|
|
804
845
|
def find_audit_with_release(
|
805
846
|
cls,
|
806
847
|
name: str,
|
807
|
-
release: datetime
|
848
|
+
release: Optional[datetime] = None,
|
808
849
|
*,
|
809
850
|
extras: Optional[DictData] = None,
|
810
851
|
) -> Self: ...
|
811
852
|
|
812
|
-
def save(self, excluded: list[str]
|
853
|
+
def save(self, excluded: Optional[list[str]]) -> SQLiteAudit:
|
813
854
|
"""Save logging data that receive a context data from a workflow
|
814
855
|
execution result.
|
815
856
|
"""
|
816
|
-
trace:
|
857
|
+
trace: TraceModel = get_trace(
|
817
858
|
self.run_id,
|
818
859
|
parent_run_id=self.parent_run_id,
|
819
860
|
extras=self.extras,
|
ddeutil/workflow/params.py
CHANGED
@@ -82,7 +82,9 @@ class DateParam(DefaultParam): # pragma: no cov
|
|
82
82
|
description="A default date that make from the current date func.",
|
83
83
|
)
|
84
84
|
|
85
|
-
def receive(
|
85
|
+
def receive(
|
86
|
+
self, value: Optional[Union[str, datetime, date]] = None
|
87
|
+
) -> date:
|
86
88
|
"""Receive value that match with date. If an input value pass with
|
87
89
|
None, it will use default value instead.
|
88
90
|
|
@@ -121,7 +123,9 @@ class DatetimeParam(DefaultParam):
|
|
121
123
|
),
|
122
124
|
)
|
123
125
|
|
124
|
-
def receive(
|
126
|
+
def receive(
|
127
|
+
self, value: Optional[Union[str, datetime, date]] = None
|
128
|
+
) -> datetime:
|
125
129
|
"""Receive value that match with datetime. If an input value pass with
|
126
130
|
None, it will use default value instead.
|
127
131
|
|
@@ -155,11 +159,11 @@ class StrParam(DefaultParam):
|
|
155
159
|
|
156
160
|
type: Literal["str"] = "str"
|
157
161
|
|
158
|
-
def receive(self, value: str
|
162
|
+
def receive(self, value: Optional[str] = None) -> Optional[str]:
|
159
163
|
"""Receive value that match with str.
|
160
164
|
|
161
165
|
:param value: A value that want to validate with string parameter type.
|
162
|
-
:rtype: str
|
166
|
+
:rtype: Optional[str]
|
163
167
|
"""
|
164
168
|
if value is None:
|
165
169
|
return self.default
|
@@ -171,7 +175,7 @@ class IntParam(DefaultParam):
|
|
171
175
|
|
172
176
|
type: Literal["int"] = "int"
|
173
177
|
|
174
|
-
def receive(self, value: int
|
178
|
+
def receive(self, value: Optional[int] = None) -> Optional[int]:
|
175
179
|
"""Receive value that match with int.
|
176
180
|
|
177
181
|
:param value: A value that want to validate with integer parameter type.
|
@@ -190,10 +194,13 @@ class IntParam(DefaultParam):
|
|
190
194
|
|
191
195
|
|
192
196
|
class FloatParam(DefaultParam): # pragma: no cov
|
197
|
+
"""Float parameter."""
|
198
|
+
|
193
199
|
type: Literal["float"] = "float"
|
194
200
|
precision: int = 6
|
195
201
|
|
196
202
|
def rounding(self, value: float) -> float:
|
203
|
+
"""Rounding float value with the specific precision field."""
|
197
204
|
round_str: str = f"{{0:.{self.precision}f}}"
|
198
205
|
return float(round_str.format(round(value, self.precision)))
|
199
206
|
|
@@ -224,6 +231,7 @@ class DecimalParam(DefaultParam): # pragma: no cov
|
|
224
231
|
precision: int = 6
|
225
232
|
|
226
233
|
def rounding(self, value: Decimal) -> Decimal:
|
234
|
+
"""Rounding float value with the specific precision field."""
|
227
235
|
return value.quantize(Decimal(10) ** -self.precision)
|
228
236
|
|
229
237
|
def receive(self, value: float | Decimal | None = None) -> Decimal:
|
ddeutil/workflow/result.py
CHANGED
@@ -3,16 +3,16 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
"""A Result module. It is the data context transfer objects that use by all
|
7
|
+
object in this package. This module provide Status enum object and Result
|
8
|
+
dataclass.
|
9
9
|
"""
|
10
10
|
from __future__ import annotations
|
11
11
|
|
12
12
|
from dataclasses import field
|
13
13
|
from datetime import datetime
|
14
14
|
from enum import IntEnum
|
15
|
-
from typing import Optional
|
15
|
+
from typing import Optional, Union
|
16
16
|
|
17
17
|
from pydantic import ConfigDict
|
18
18
|
from pydantic.dataclasses import dataclass
|
@@ -22,7 +22,7 @@ from typing_extensions import Self
|
|
22
22
|
from .__types import DictData
|
23
23
|
from .conf import dynamic
|
24
24
|
from .exceptions import ResultException
|
25
|
-
from .logs import
|
25
|
+
from .logs import TraceModel, get_dt_tznow, get_trace
|
26
26
|
from .utils import default_gen_id, gen_id, get_dt_now
|
27
27
|
|
28
28
|
|
@@ -31,11 +31,11 @@ class Status(IntEnum):
|
|
31
31
|
Result dataclass object.
|
32
32
|
"""
|
33
33
|
|
34
|
-
SUCCESS
|
35
|
-
FAILED
|
36
|
-
WAIT
|
37
|
-
SKIP
|
38
|
-
CANCEL
|
34
|
+
SUCCESS = 0
|
35
|
+
FAILED = 1
|
36
|
+
WAIT = 2
|
37
|
+
SKIP = 3
|
38
|
+
CANCEL = 4
|
39
39
|
|
40
40
|
@property
|
41
41
|
def emoji(self) -> str:
|
@@ -75,16 +75,16 @@ class Result:
|
|
75
75
|
parent_run_id: Optional[str] = field(default=None, compare=False)
|
76
76
|
ts: datetime = field(default_factory=get_dt_tznow, compare=False)
|
77
77
|
|
78
|
-
trace: Optional[
|
78
|
+
trace: Optional[TraceModel] = field(default=None, compare=False, repr=False)
|
79
79
|
extras: DictData = field(default_factory=dict, compare=False, repr=False)
|
80
80
|
|
81
81
|
@classmethod
|
82
82
|
def construct_with_rs_or_id(
|
83
83
|
cls,
|
84
|
-
result: Result
|
85
|
-
run_id: str
|
86
|
-
parent_run_id: str
|
87
|
-
id_logic: str
|
84
|
+
result: Optional[Result] = None,
|
85
|
+
run_id: Optional[str] = None,
|
86
|
+
parent_run_id: Optional[str] = None,
|
87
|
+
id_logic: Optional[str] = None,
|
88
88
|
*,
|
89
89
|
extras: DictData | None = None,
|
90
90
|
) -> Self:
|
@@ -121,7 +121,7 @@ class Result:
|
|
121
121
|
:rtype: Self
|
122
122
|
"""
|
123
123
|
if self.trace is None: # pragma: no cov
|
124
|
-
self.trace:
|
124
|
+
self.trace: TraceModel = get_trace(
|
125
125
|
self.run_id,
|
126
126
|
parent_run_id=self.parent_run_id,
|
127
127
|
extras=self.extras,
|
@@ -136,14 +136,14 @@ class Result:
|
|
136
136
|
:rtype: Self
|
137
137
|
"""
|
138
138
|
self.parent_run_id: str = running_id
|
139
|
-
self.trace:
|
139
|
+
self.trace: TraceModel = get_trace(
|
140
140
|
self.run_id, parent_run_id=running_id, extras=self.extras
|
141
141
|
)
|
142
142
|
return self
|
143
143
|
|
144
144
|
def catch(
|
145
145
|
self,
|
146
|
-
status: int
|
146
|
+
status: Union[int, Status],
|
147
147
|
context: DictData | None = None,
|
148
148
|
**kwargs,
|
149
149
|
) -> Self:
|