ddeutil-workflow 0.0.19__py3-none-any.whl → 0.0.20__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 +28 -2
- ddeutil/workflow/__init__.py +9 -4
- ddeutil/workflow/conf.py +31 -25
- ddeutil/workflow/exceptions.py +4 -0
- ddeutil/workflow/job.py +46 -45
- ddeutil/workflow/on.py +4 -15
- ddeutil/workflow/scheduler.py +60 -963
- ddeutil/workflow/stage.py +92 -66
- ddeutil/workflow/utils.py +29 -24
- ddeutil/workflow/workflow.py +1084 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.20.dist-info}/METADATA +8 -8
- ddeutil_workflow-0.0.20.dist-info/RECORD +22 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.20.dist-info}/WHEEL +1 -1
- ddeutil_workflow-0.0.19.dist-info/RECORD +0 -21
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.20.dist-info}/LICENSE +0 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.20.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.19.dist-info → ddeutil_workflow-0.0.20.dist-info}/top_level.txt +0 -0
ddeutil/workflow/stage.py
CHANGED
@@ -13,8 +13,9 @@ handle stage error on this stage model. I think stage model should have a lot of
|
|
13
13
|
usecase and it does not worry when I want to create a new one.
|
14
14
|
|
15
15
|
Execution --> Ok --> Result with 0
|
16
|
+
|
16
17
|
--> Error --> Result with 1 (if env var was set)
|
17
|
-
--> Raise StageException
|
18
|
+
--> Raise StageException(...)
|
18
19
|
|
19
20
|
On the context I/O that pass to a stage object at execute process. The
|
20
21
|
execute method receives a `params={"params": {...}}` value for mapping to
|
@@ -68,16 +69,13 @@ logger = get_logger("ddeutil.workflow")
|
|
68
69
|
|
69
70
|
|
70
71
|
__all__: TupleStr = (
|
71
|
-
"BaseStage",
|
72
72
|
"EmptyStage",
|
73
73
|
"BashStage",
|
74
74
|
"PyStage",
|
75
75
|
"HookStage",
|
76
76
|
"TriggerStage",
|
77
77
|
"Stage",
|
78
|
-
"HookSearchData",
|
79
78
|
"extract_hook",
|
80
|
-
"handler_result",
|
81
79
|
)
|
82
80
|
|
83
81
|
|
@@ -90,15 +88,17 @@ def handler_result(message: str | None = None) -> DecoratorResult:
|
|
90
88
|
environment variable,`WORKFLOW_CORE_STAGE_RAISE_ERROR`.
|
91
89
|
|
92
90
|
Execution --> Ok --> Result
|
93
|
-
|
94
|
-
|
95
|
-
|
91
|
+
|-status: 0
|
92
|
+
|-context:
|
93
|
+
|-outputs: ...
|
94
|
+
|
96
95
|
--> Error --> Result (if env var was set)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
|-status: 1
|
97
|
+
|-context:
|
98
|
+
|-error: ...
|
99
|
+
|-error_message: ...
|
100
|
+
|
101
|
+
--> Error --> Raise StageException(...)
|
102
102
|
|
103
103
|
On the last step, it will set the running ID on a return result object
|
104
104
|
from current stage ID before release the final result.
|
@@ -119,13 +119,18 @@ def handler_result(message: str | None = None) -> DecoratorResult:
|
|
119
119
|
|
120
120
|
@wraps(func)
|
121
121
|
def wrapped(self: Stage, *args, **kwargs):
|
122
|
+
|
123
|
+
if not (run_id := kwargs.get("run_id")):
|
124
|
+
run_id: str = gen_id(self.name + (self.id or ""), unique=True)
|
125
|
+
kwargs["run_id"] = run_id
|
126
|
+
|
122
127
|
try:
|
123
128
|
# NOTE: Start calling origin function with a passing args.
|
124
|
-
return func(self, *args, **kwargs)
|
129
|
+
return func(self, *args, **kwargs)
|
125
130
|
except Exception as err:
|
126
131
|
# NOTE: Start catching error from the stage execution.
|
127
132
|
logger.error(
|
128
|
-
f"({
|
133
|
+
f"({run_id}) [STAGE]: {err.__class__.__name__}: {err}"
|
129
134
|
)
|
130
135
|
if config.stage_raise_error:
|
131
136
|
# NOTE: If error that raise from stage execution course by
|
@@ -148,7 +153,8 @@ def handler_result(message: str | None = None) -> DecoratorResult:
|
|
148
153
|
"error": err,
|
149
154
|
"error_message": f"{err.__class__.__name__}: {err}",
|
150
155
|
},
|
151
|
-
|
156
|
+
run_id=run_id,
|
157
|
+
)
|
152
158
|
|
153
159
|
return wrapped
|
154
160
|
|
@@ -159,6 +165,8 @@ class BaseStage(BaseModel, ABC):
|
|
159
165
|
"""Base Stage Model that keep only id and name fields for the stage
|
160
166
|
metadata. If you want to implement any custom stage, you can use this class
|
161
167
|
to parent and implement ``self.execute()`` method only.
|
168
|
+
|
169
|
+
This class is the abstraction class for any stage class.
|
162
170
|
"""
|
163
171
|
|
164
172
|
id: Optional[str] = Field(
|
@@ -176,12 +184,15 @@ class BaseStage(BaseModel, ABC):
|
|
176
184
|
description="A stage condition statement to allow stage executable.",
|
177
185
|
alias="if",
|
178
186
|
)
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
187
|
+
|
188
|
+
@property
|
189
|
+
def iden(self) -> str:
|
190
|
+
"""Return identity of this stage object that return the id field first.
|
191
|
+
If the id does not set, it will use name field instead.
|
192
|
+
|
193
|
+
:rtype: str
|
194
|
+
"""
|
195
|
+
return self.id or self.name
|
185
196
|
|
186
197
|
@model_validator(mode="after")
|
187
198
|
def __prepare_running_id__(self) -> Self:
|
@@ -194,8 +205,6 @@ class BaseStage(BaseModel, ABC):
|
|
194
205
|
|
195
206
|
:rtype: Self
|
196
207
|
"""
|
197
|
-
if self.run_id is None:
|
198
|
-
self.run_id = gen_id(self.name + (self.id or ""), unique=True)
|
199
208
|
|
200
209
|
# VALIDATE: Validate stage id and name should not dynamic with params
|
201
210
|
# template. (allow only matrix)
|
@@ -206,21 +215,14 @@ class BaseStage(BaseModel, ABC):
|
|
206
215
|
|
207
216
|
return self
|
208
217
|
|
209
|
-
def get_running_id(self, run_id: str) -> Self:
|
210
|
-
"""Return Stage model object that changing stage running ID with an
|
211
|
-
input running ID.
|
212
|
-
|
213
|
-
:param run_id: A replace stage running ID.
|
214
|
-
:rtype: Self
|
215
|
-
"""
|
216
|
-
return self.model_copy(update={"run_id": run_id})
|
217
|
-
|
218
218
|
@abstractmethod
|
219
|
-
def execute(self, params: DictData) -> Result:
|
219
|
+
def execute(self, params: DictData, *, run_id: str | None = None) -> Result:
|
220
220
|
"""Execute abstraction method that action something by sub-model class.
|
221
221
|
This is important method that make this class is able to be the stage.
|
222
222
|
|
223
223
|
:param params: A parameter data that want to use in this execution.
|
224
|
+
:param run_id: A running stage ID for this execution.
|
225
|
+
|
224
226
|
:rtype: Result
|
225
227
|
"""
|
226
228
|
raise NotImplementedError("Stage should implement ``execute`` method.")
|
@@ -248,10 +250,9 @@ class BaseStage(BaseModel, ABC):
|
|
248
250
|
:rtype: DictData
|
249
251
|
"""
|
250
252
|
if self.id is None and not config.stage_default_id:
|
251
|
-
logger.
|
252
|
-
|
253
|
-
|
254
|
-
f"True."
|
253
|
+
logger.warning(
|
254
|
+
"Output does not set because this stage does not set ID or "
|
255
|
+
"default stage ID config flag not be True."
|
255
256
|
)
|
256
257
|
return to
|
257
258
|
|
@@ -267,7 +268,6 @@ class BaseStage(BaseModel, ABC):
|
|
267
268
|
)
|
268
269
|
|
269
270
|
# NOTE: Set the output to that stage generated ID with ``outputs`` key.
|
270
|
-
logger.debug(f"({self.run_id}) [STAGE]: Set outputs to {_id!r}")
|
271
271
|
to["stages"][_id] = {"outputs": output}
|
272
272
|
return to
|
273
273
|
|
@@ -283,18 +283,22 @@ class BaseStage(BaseModel, ABC):
|
|
283
283
|
:param params: A parameters that want to pass to condition template.
|
284
284
|
:rtype: bool
|
285
285
|
"""
|
286
|
+
# NOTE: Return false result if condition does not set.
|
286
287
|
if self.condition is None:
|
287
288
|
return False
|
288
289
|
|
289
290
|
params: DictData = {} if params is None else params
|
290
|
-
|
291
|
+
|
291
292
|
try:
|
292
|
-
|
293
|
+
# WARNING: The eval build-in function is vary dangerous. So, it
|
294
|
+
# should us the re module to validate eval-string before running.
|
295
|
+
rs: bool = eval(
|
296
|
+
param2template(self.condition, params), globals() | params, {}
|
297
|
+
)
|
293
298
|
if not isinstance(rs, bool):
|
294
299
|
raise TypeError("Return type of condition does not be boolean")
|
295
300
|
return not rs
|
296
301
|
except Exception as err:
|
297
|
-
logger.error(f"({self.run_id}) [STAGE]: {err}")
|
298
302
|
raise StageException(f"{err.__class__.__name__}: {err}") from err
|
299
303
|
|
300
304
|
|
@@ -319,7 +323,8 @@ class EmptyStage(BaseStage):
|
|
319
323
|
ge=0,
|
320
324
|
)
|
321
325
|
|
322
|
-
|
326
|
+
@handler_result()
|
327
|
+
def execute(self, params: DictData, *, run_id: str | None = None) -> Result:
|
323
328
|
"""Execution method for the Empty stage that do only logging out to
|
324
329
|
stdout. This method does not use the `handler_result` decorator because
|
325
330
|
it does not get any error from logging function.
|
@@ -329,15 +334,17 @@ class EmptyStage(BaseStage):
|
|
329
334
|
|
330
335
|
:param params: A context data that want to add output result. But this
|
331
336
|
stage does not pass any output.
|
337
|
+
:param run_id: A running stage ID for this execution.
|
338
|
+
|
332
339
|
:rtype: Result
|
333
340
|
"""
|
334
341
|
logger.info(
|
335
|
-
f"({
|
342
|
+
f"({run_id}) [STAGE]: Empty-Execute: {self.name!r}: "
|
336
343
|
f"( {param2template(self.echo, params=params) or '...'} )"
|
337
344
|
)
|
338
345
|
if self.sleep > 0:
|
339
346
|
time.sleep(self.sleep)
|
340
|
-
return Result(status=0, context={})
|
347
|
+
return Result(status=0, context={}, run_id=run_id)
|
341
348
|
|
342
349
|
|
343
350
|
class BashStage(BaseStage):
|
@@ -369,17 +376,25 @@ class BashStage(BaseStage):
|
|
369
376
|
)
|
370
377
|
|
371
378
|
@contextlib.contextmanager
|
372
|
-
def
|
379
|
+
def create_sh_file(
|
380
|
+
self, bash: str, env: DictStr, run_id: str | None = None
|
381
|
+
) -> Iterator[TupleStr]:
|
373
382
|
"""Return context of prepared bash statement that want to execute. This
|
374
383
|
step will write the `.sh` file before giving this file name to context.
|
375
384
|
After that, it will auto delete this file automatic.
|
376
385
|
|
377
386
|
:param bash: A bash statement that want to execute.
|
378
387
|
:param env: An environment variable that use on this bash statement.
|
388
|
+
:param run_id: A running stage ID that use for writing sh file instead
|
389
|
+
generate by UUID4.
|
379
390
|
:rtype: Iterator[TupleStr]
|
380
391
|
"""
|
381
|
-
|
392
|
+
run_id: str = run_id or uuid.uuid4()
|
393
|
+
f_name: str = f"{run_id}.sh"
|
382
394
|
f_shebang: str = "bash" if sys.platform.startswith("win") else "sh"
|
395
|
+
|
396
|
+
logger.debug(f"({run_id}) [STAGE]: Start create `{f_name}` file.")
|
397
|
+
|
383
398
|
with open(f"./{f_name}", mode="w", newline="\n") as f:
|
384
399
|
# NOTE: write header of `.sh` file
|
385
400
|
f.write(f"#!/bin/{f_shebang}\n\n")
|
@@ -393,29 +408,27 @@ class BashStage(BaseStage):
|
|
393
408
|
# NOTE: Make this .sh file able to executable.
|
394
409
|
make_exec(f"./{f_name}")
|
395
410
|
|
396
|
-
logger.debug(
|
397
|
-
f"({self.run_id}) [STAGE]: Start create `.sh` file and running a "
|
398
|
-
f"bash statement."
|
399
|
-
)
|
400
|
-
|
401
411
|
yield [f_shebang, f_name]
|
402
412
|
|
403
413
|
# Note: Remove .sh file that use to run bash.
|
404
414
|
Path(f"./{f_name}").unlink()
|
405
415
|
|
406
416
|
@handler_result()
|
407
|
-
def execute(self, params: DictData) -> Result:
|
417
|
+
def execute(self, params: DictData, *, run_id: str | None = None) -> Result:
|
408
418
|
"""Execute the Bash statement with the Python build-in ``subprocess``
|
409
419
|
package.
|
410
420
|
|
411
421
|
:param params: A parameter data that want to use in this execution.
|
422
|
+
:param run_id: A running stage ID for this execution.
|
423
|
+
|
412
424
|
:rtype: Result
|
413
425
|
"""
|
414
426
|
bash: str = param2template(dedent(self.bash), params)
|
415
|
-
|
416
|
-
|
427
|
+
|
428
|
+
logger.info(f"({run_id}) [STAGE]: Shell-Execute: {self.name}")
|
429
|
+
with self.create_sh_file(
|
430
|
+
bash=bash, env=param2template(self.env, params), run_id=run_id
|
417
431
|
) as sh:
|
418
|
-
logger.info(f"({self.run_id}) [STAGE]: Shell-Execute: {sh}")
|
419
432
|
rs: CompletedProcess = subprocess.run(
|
420
433
|
sh, shell=False, capture_output=True, text=True
|
421
434
|
)
|
@@ -437,6 +450,7 @@ class BashStage(BaseStage):
|
|
437
450
|
"stdout": rs.stdout.rstrip("\n") or None,
|
438
451
|
"stderr": rs.stderr.rstrip("\n") or None,
|
439
452
|
},
|
453
|
+
run_id=run_id,
|
440
454
|
)
|
441
455
|
|
442
456
|
|
@@ -501,11 +515,13 @@ class PyStage(BaseStage):
|
|
501
515
|
return to
|
502
516
|
|
503
517
|
@handler_result()
|
504
|
-
def execute(self, params: DictData) -> Result:
|
518
|
+
def execute(self, params: DictData, *, run_id: str | None = None) -> Result:
|
505
519
|
"""Execute the Python statement that pass all globals and input params
|
506
520
|
to globals argument on ``exec`` build-in function.
|
507
521
|
|
508
522
|
:param params: A parameter that want to pass before run any statement.
|
523
|
+
:param run_id: A running stage ID for this execution.
|
524
|
+
|
509
525
|
:rtype: Result
|
510
526
|
"""
|
511
527
|
# NOTE: Replace the run statement that has templating value.
|
@@ -518,12 +534,16 @@ class PyStage(BaseStage):
|
|
518
534
|
lc: DictData = {}
|
519
535
|
|
520
536
|
# NOTE: Start exec the run statement.
|
521
|
-
logger.info(f"({
|
537
|
+
logger.info(f"({run_id}) [STAGE]: Py-Execute: {self.name}")
|
538
|
+
|
539
|
+
# WARNING: The exec build-in function is vary dangerous. So, it
|
540
|
+
# should us the re module to validate exec-string before running.
|
522
541
|
exec(run, _globals, lc)
|
523
542
|
|
524
543
|
return Result(
|
525
544
|
status=0,
|
526
545
|
context={"locals": lc, "globals": _globals},
|
546
|
+
run_id=run_id,
|
527
547
|
)
|
528
548
|
|
529
549
|
|
@@ -603,7 +623,7 @@ class HookStage(BaseStage):
|
|
603
623
|
)
|
604
624
|
|
605
625
|
@handler_result()
|
606
|
-
def execute(self, params: DictData) -> Result:
|
626
|
+
def execute(self, params: DictData, *, run_id: str | None = None) -> Result:
|
607
627
|
"""Execute the Hook function that already in the hook registry.
|
608
628
|
|
609
629
|
:raise ValueError: When the necessary arguments of hook function do not
|
@@ -613,6 +633,9 @@ class HookStage(BaseStage):
|
|
613
633
|
|
614
634
|
:param params: A parameter that want to pass before run any statement.
|
615
635
|
:type params: DictData
|
636
|
+
:param run_id: A running stage ID for this execution.
|
637
|
+
:type: str | None
|
638
|
+
|
616
639
|
:rtype: Result
|
617
640
|
"""
|
618
641
|
t_func_hook: str = param2template(self.uses, params)
|
@@ -637,7 +660,7 @@ class HookStage(BaseStage):
|
|
637
660
|
args[k] = args.pop(k.removeprefix("_"))
|
638
661
|
|
639
662
|
logger.info(
|
640
|
-
f"({
|
663
|
+
f"({run_id}) [STAGE]: Hook-Execute: {t_func.name}@{t_func.tag}"
|
641
664
|
)
|
642
665
|
rs: DictData = t_func(**param2template(args, params))
|
643
666
|
|
@@ -648,7 +671,7 @@ class HookStage(BaseStage):
|
|
648
671
|
f"Return type: '{t_func.name}@{t_func.tag}' does not serialize "
|
649
672
|
f"to result model, you change return type to `dict`."
|
650
673
|
)
|
651
|
-
return Result(status=0, context=rs)
|
674
|
+
return Result(status=0, context=rs, run_id=run_id)
|
652
675
|
|
653
676
|
|
654
677
|
class TriggerStage(BaseStage):
|
@@ -675,11 +698,13 @@ class TriggerStage(BaseStage):
|
|
675
698
|
)
|
676
699
|
|
677
700
|
@handler_result("Raise from TriggerStage")
|
678
|
-
def execute(self, params: DictData) -> Result:
|
701
|
+
def execute(self, params: DictData, *, run_id: str | None = None) -> Result:
|
679
702
|
"""Trigger another workflow execution. It will waiting the trigger
|
680
703
|
workflow running complete before catching its result.
|
681
704
|
|
682
705
|
:param params: A parameter data that want to use in this execution.
|
706
|
+
:param run_id: A running stage ID for this execution.
|
707
|
+
|
683
708
|
:rtype: Result
|
684
709
|
"""
|
685
710
|
# NOTE: Lazy import this workflow object.
|
@@ -690,11 +715,12 @@ class TriggerStage(BaseStage):
|
|
690
715
|
|
691
716
|
# NOTE: Set running workflow ID from running stage ID to external
|
692
717
|
# params on Loader object.
|
693
|
-
wf: Workflow = Workflow.from_loader(
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
718
|
+
wf: Workflow = Workflow.from_loader(name=_trigger)
|
719
|
+
logger.info(f"({run_id}) [STAGE]: Trigger-Execute: {_trigger!r}")
|
720
|
+
return wf.execute(
|
721
|
+
params=param2template(self.params, params),
|
722
|
+
run_id=run_id,
|
723
|
+
).set_run_id(run_id)
|
698
724
|
|
699
725
|
|
700
726
|
# NOTE:
|
ddeutil/workflow/utils.py
CHANGED
@@ -13,7 +13,7 @@ from abc import ABC, abstractmethod
|
|
13
13
|
from ast import Call, Constant, Expr, Module, Name, parse
|
14
14
|
from collections.abc import Iterator
|
15
15
|
from dataclasses import field
|
16
|
-
from datetime import date, datetime
|
16
|
+
from datetime import date, datetime, timedelta
|
17
17
|
from functools import wraps
|
18
18
|
from hashlib import md5
|
19
19
|
from importlib import import_module
|
@@ -48,26 +48,34 @@ AnyModelType = type[AnyModel]
|
|
48
48
|
logger = logging.getLogger("ddeutil.workflow")
|
49
49
|
|
50
50
|
|
51
|
-
def get_dt_now(
|
51
|
+
def get_dt_now(
|
52
|
+
tz: ZoneInfo | None = None, offset: float = 0.0
|
53
|
+
) -> datetime: # pragma: no cov
|
52
54
|
"""Return the current datetime object.
|
53
55
|
|
54
56
|
:param tz:
|
57
|
+
:param offset:
|
55
58
|
:return: The current datetime object that use an input timezone or UTC.
|
56
59
|
"""
|
57
|
-
return datetime.now(tz=(tz or ZoneInfo("UTC")))
|
60
|
+
return datetime.now(tz=(tz or ZoneInfo("UTC"))) - timedelta(seconds=offset)
|
58
61
|
|
59
62
|
|
60
63
|
def get_diff_sec(
|
61
|
-
dt: datetime, tz: ZoneInfo | None = None
|
64
|
+
dt: datetime, tz: ZoneInfo | None = None, offset: float = 0.0
|
62
65
|
) -> int: # pragma: no cov
|
63
66
|
"""Return second value that come from diff of an input datetime and the
|
64
67
|
current datetime with specific timezone.
|
65
68
|
|
66
69
|
:param dt:
|
67
70
|
:param tz:
|
71
|
+
:param offset:
|
68
72
|
"""
|
69
73
|
return round(
|
70
|
-
(
|
74
|
+
(
|
75
|
+
dt
|
76
|
+
- datetime.now(tz=(tz or ZoneInfo("UTC")))
|
77
|
+
- timedelta(seconds=offset)
|
78
|
+
).total_seconds()
|
71
79
|
)
|
72
80
|
|
73
81
|
|
@@ -350,12 +358,10 @@ class Result:
|
|
350
358
|
|
351
359
|
status: int = field(default=2)
|
352
360
|
context: DictData = field(default_factory=dict)
|
353
|
-
start_at: datetime = field(default_factory=get_dt_now, compare=False)
|
354
|
-
end_at: Optional[datetime] = field(default=None, compare=False)
|
355
361
|
|
356
362
|
# NOTE: Ignore this field to compare another result model with __eq__.
|
357
|
-
|
358
|
-
|
363
|
+
run_id: Optional[str] = field(default=None)
|
364
|
+
parent_run_id: Optional[str] = field(default=None, compare=False)
|
359
365
|
|
360
366
|
@model_validator(mode="after")
|
361
367
|
def __prepare_run_id(self) -> Self:
|
@@ -373,7 +379,7 @@ class Result:
|
|
373
379
|
:param running_id: A running ID that want to update on this model.
|
374
380
|
:rtype: Self
|
375
381
|
"""
|
376
|
-
self.
|
382
|
+
self.run_id = running_id
|
377
383
|
return self
|
378
384
|
|
379
385
|
def set_parent_run_id(self, running_id: str) -> Self:
|
@@ -382,17 +388,9 @@ class Result:
|
|
382
388
|
:param running_id: A running ID that want to update on this model.
|
383
389
|
:rtype: Self
|
384
390
|
"""
|
385
|
-
self.
|
391
|
+
self.parent_run_id: str = running_id
|
386
392
|
return self
|
387
393
|
|
388
|
-
@property
|
389
|
-
def parent_run_id(self) -> str:
|
390
|
-
return self._parent_run_id
|
391
|
-
|
392
|
-
@property
|
393
|
-
def run_id(self) -> str:
|
394
|
-
return self._run_id
|
395
|
-
|
396
394
|
def catch(self, status: int, context: DictData) -> Self:
|
397
395
|
"""Catch the status and context to current data."""
|
398
396
|
self.__dict__["status"] = status
|
@@ -408,8 +406,8 @@ class Result:
|
|
408
406
|
self.__dict__["context"].update(result.context)
|
409
407
|
|
410
408
|
# NOTE: Update running ID from an incoming result.
|
411
|
-
self.
|
412
|
-
self.
|
409
|
+
self.parent_run_id = result.parent_run_id
|
410
|
+
self.run_id = result.run_id
|
413
411
|
return self
|
414
412
|
|
415
413
|
def receive_jobs(self, result: Result) -> Self:
|
@@ -427,8 +425,8 @@ class Result:
|
|
427
425
|
self.__dict__["context"]["jobs"].update(result.context)
|
428
426
|
|
429
427
|
# NOTE: Update running ID from an incoming result.
|
430
|
-
self.
|
431
|
-
self.
|
428
|
+
self.parent_run_id: str = result.parent_run_id
|
429
|
+
self.run_id: str = result.run_id
|
432
430
|
return self
|
433
431
|
|
434
432
|
|
@@ -576,7 +574,14 @@ def get_args_from_filter(
|
|
576
574
|
|
577
575
|
@custom_filter("fmt") # pragma: no cov
|
578
576
|
def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
579
|
-
"""Format datetime object to string with the format.
|
577
|
+
"""Format datetime object to string with the format.
|
578
|
+
|
579
|
+
:param value: A datetime value that want to format to string value.
|
580
|
+
:param fmt: A format string pattern that passing to the `dt.strftime`
|
581
|
+
method.
|
582
|
+
|
583
|
+
:rtype: str
|
584
|
+
"""
|
580
585
|
if isinstance(value, datetime):
|
581
586
|
return value.strftime(fmt)
|
582
587
|
raise UtilException(
|