ddeutil-workflow 0.0.42__py3-none-any.whl → 0.0.44__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.
@@ -56,14 +56,13 @@ from .conf import Loader, SimLoad, config, get_logger
56
56
  from .cron import On
57
57
  from .exceptions import ScheduleException, WorkflowException
58
58
  from .logs import Audit, get_audit
59
- from .result import Result, Status
59
+ from .result import SUCCESS, Result
60
60
  from .utils import batch, delay
61
61
  from .workflow import Release, ReleaseQueue, Workflow, WorkflowTask
62
62
 
63
63
  P = ParamSpec("P")
64
- logger = get_logger("ddeutil.workflow")
65
64
 
66
- # NOTE: Adjust logging level on the `schedule` package.
65
+ logger = get_logger("ddeutil.workflow")
67
66
  logging.getLogger("schedule").setLevel(logging.INFO)
68
67
 
69
68
 
@@ -393,7 +392,7 @@ class Schedule(BaseModel):
393
392
  audit=audit,
394
393
  )
395
394
 
396
- return result.catch(status=Status.SUCCESS)
395
+ return result.catch(status=SUCCESS)
397
396
 
398
397
 
399
398
  ResultOrCancel = Union[type[CancelJob], Result]
@@ -572,9 +571,7 @@ def schedule_task(
572
571
  f"[SCHEDULE]: End schedule task that run since "
573
572
  f"{current_date:%Y-%m-%d %H:%M:%S} {'=' * 30}"
574
573
  )
575
- return result.catch(
576
- status=Status.SUCCESS, context={"task_date": current_date}
577
- )
574
+ return result.catch(status=SUCCESS, context={"task_date": current_date})
578
575
 
579
576
 
580
577
  def monitor(
@@ -690,7 +687,7 @@ def scheduler_pending(
690
687
  f"[SCHEDULE]: Queue: {[list(queue[wf].queue) for wf in queue]}"
691
688
  )
692
689
  return result.catch(
693
- status=Status.SUCCESS,
690
+ status=SUCCESS,
694
691
  context={
695
692
  "threads": [
696
693
  {
@@ -759,7 +756,7 @@ def schedule_control(
759
756
  audit=audit,
760
757
  )
761
758
 
762
- return result.catch(status=Status.SUCCESS, context={"schedules": schedules})
759
+ return result.catch(status=SUCCESS, context={"schedules": schedules})
763
760
 
764
761
 
765
762
  def schedule_runner(
@@ -4,18 +4,18 @@
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
6
  # [x] Use dynamic config
7
- """Stage Model that use for getting stage data template from the Job Model.
8
- The stage handle the minimize task that run in some thread (same thread at
9
- its job owner) that mean it is the lowest executor of a workflow that can
10
- tracking logs.
7
+ """Stage model. It stores all stage model that use for getting stage data template
8
+ from the Job Model. The stage handle the minimize task that run in some thread
9
+ (same thread at its job owner) that mean it is the lowest executor of a workflow
10
+ that can tracking logs.
11
11
 
12
12
  The output of stage execution only return 0 status because I do not want to
13
13
  handle stage error on this stage model. I think stage model should have a lot of
14
14
  use-case, and it does not worry when I want to create a new one.
15
15
 
16
- Execution --> Ok --> Result with 0
16
+ Execution --> Ok --> Result with SUCCESS
17
17
 
18
- --> Error ┬-> Result with 1 (if env var was set)
18
+ --> Error ┬-> Result with FAILED (if env var was set)
19
19
  ╰-> Raise StageException(...)
20
20
 
21
21
  On the context I/O that pass to a stage object at execute process. The
@@ -52,7 +52,7 @@ from typing_extensions import Self
52
52
  from .__types import DictData, DictStr, TupleStr
53
53
  from .conf import dynamic
54
54
  from .exceptions import StageException, to_dict
55
- from .result import Result, Status
55
+ from .result import FAILED, SUCCESS, Result, Status
56
56
  from .reusables import TagFunc, extract_call, not_in_template, param2template
57
57
  from .utils import (
58
58
  gen_id,
@@ -80,6 +80,10 @@ class BaseStage(BaseModel, ABC):
80
80
  This class is the abstraction class for any stage class.
81
81
  """
82
82
 
83
+ extras: DictData = Field(
84
+ default_factory=dict,
85
+ description="An extra override config values.",
86
+ )
83
87
  id: Optional[str] = Field(
84
88
  default=None,
85
89
  description=(
@@ -95,10 +99,6 @@ class BaseStage(BaseModel, ABC):
95
99
  description="A stage condition statement to allow stage executable.",
96
100
  alias="if",
97
101
  )
98
- extras: DictData = Field(
99
- default_factory=dict,
100
- description="An extra override config values.",
101
- )
102
102
 
103
103
  @property
104
104
  def iden(self) -> str:
@@ -170,12 +170,12 @@ class BaseStage(BaseModel, ABC):
170
170
  specific environment variable,`WORKFLOW_CORE_STAGE_RAISE_ERROR`.
171
171
 
172
172
  Execution --> Ok --> Result
173
- |-status: Status.SUCCESS
173
+ |-status: SUCCESS
174
174
  ╰-context:
175
175
  ╰-outputs: ...
176
176
 
177
177
  --> Error --> Result (if env var was set)
178
- |-status: Status.FAILED
178
+ |-status: FAILED
179
179
  ╰-errors:
180
180
  |-class: ...
181
181
  |-name: ...
@@ -209,26 +209,25 @@ class BaseStage(BaseModel, ABC):
209
209
 
210
210
  try:
211
211
  rs: Result = self.execute(params, result=result, event=event)
212
- if to is not None:
213
- return self.set_outputs(rs.context, to=to)
214
- return rs
215
- except Exception as err:
216
- result.trace.error(f"[STAGE]: {err.__class__.__name__}: {err}")
212
+ return self.set_outputs(rs.context, to=to) if to is not None else rs
213
+ except Exception as e:
214
+ result.trace.error(f"[STAGE]: {e.__class__.__name__}: {e}")
217
215
 
218
216
  if dynamic("stage_raise_error", f=raise_error, extras=self.extras):
219
- if isinstance(err, StageException):
217
+ if isinstance(e, StageException):
220
218
  raise
221
219
 
222
220
  raise StageException(
223
221
  f"{self.__class__.__name__}: \n\t"
224
- f"{err.__class__.__name__}: {err}"
225
- ) from None
226
-
227
- errors: DictData = {"errors": to_dict(err)}
228
- if to is not None:
229
- return self.set_outputs(errors, to=to)
230
-
231
- return result.catch(status=Status.FAILED, context=errors)
222
+ f"{e.__class__.__name__}: {e}"
223
+ ) from e
224
+
225
+ errors: DictData = {"errors": to_dict(e)}
226
+ return (
227
+ self.set_outputs(errors, to=to)
228
+ if to is not None
229
+ else result.catch(status=FAILED, context=errors)
230
+ )
232
231
 
233
232
  def set_outputs(self, output: DictData, to: DictData) -> DictData:
234
233
  """Set an outputs from execution process to the received context. The
@@ -315,8 +314,8 @@ class BaseStage(BaseModel, ABC):
315
314
  if not isinstance(rs, bool):
316
315
  raise TypeError("Return type of condition does not be boolean")
317
316
  return not rs
318
- except Exception as err:
319
- raise StageException(f"{err.__class__.__name__}: {err}") from err
317
+ except Exception as e:
318
+ raise StageException(f"{e.__class__.__name__}: {e}") from e
320
319
 
321
320
 
322
321
  class BaseAsyncStage(BaseStage):
@@ -328,7 +327,10 @@ class BaseAsyncStage(BaseStage):
328
327
  *,
329
328
  result: Result | None = None,
330
329
  event: Event | None = None,
331
- ) -> Result: ...
330
+ ) -> Result:
331
+ raise NotImplementedError(
332
+ "Async Stage should implement `execute` method."
333
+ )
332
334
 
333
335
  @abstractmethod
334
336
  async def axecute(
@@ -393,25 +395,23 @@ class BaseAsyncStage(BaseStage):
393
395
  if to is not None:
394
396
  return self.set_outputs(rs.context, to=to)
395
397
  return rs
396
- except Exception as err:
397
- await result.trace.aerror(
398
- f"[STAGE]: {err.__class__.__name__}: {err}"
399
- )
398
+ except Exception as e:
399
+ await result.trace.aerror(f"[STAGE]: {e.__class__.__name__}: {e}")
400
400
 
401
401
  if dynamic("stage_raise_error", f=raise_error, extras=self.extras):
402
- if isinstance(err, StageException):
402
+ if isinstance(e, StageException):
403
403
  raise
404
404
 
405
405
  raise StageException(
406
406
  f"{self.__class__.__name__}: \n\t"
407
- f"{err.__class__.__name__}: {err}"
407
+ f"{e.__class__.__name__}: {e}"
408
408
  ) from None
409
409
 
410
- errors: DictData = {"errors": to_dict(err)}
410
+ errors: DictData = {"errors": to_dict(e)}
411
411
  if to is not None:
412
412
  return self.set_outputs(errors, to=to)
413
413
 
414
- return result.catch(status=Status.FAILED, context=errors)
414
+ return result.catch(status=FAILED, context=errors)
415
415
 
416
416
 
417
417
  class EmptyStage(BaseAsyncStage):
@@ -472,7 +472,7 @@ class EmptyStage(BaseAsyncStage):
472
472
  result.trace.info(f"[STAGE]: ... sleep ({self.sleep} seconds)")
473
473
  time.sleep(self.sleep)
474
474
 
475
- return result.catch(status=Status.SUCCESS)
475
+ return result.catch(status=SUCCESS)
476
476
 
477
477
  async def axecute(
478
478
  self,
@@ -510,7 +510,7 @@ class EmptyStage(BaseAsyncStage):
510
510
  )
511
511
  await asyncio.sleep(self.sleep)
512
512
 
513
- return result.catch(status=Status.SUCCESS)
513
+ return result.catch(status=SUCCESS)
514
514
 
515
515
 
516
516
  class BashStage(BaseStage):
@@ -619,17 +619,17 @@ class BashStage(BaseStage):
619
619
 
620
620
  if rs.returncode > 0:
621
621
  # NOTE: Prepare stderr message that returning from subprocess.
622
- err: str = (
622
+ e: str = (
623
623
  rs.stderr.encode("utf-8").decode("utf-16")
624
624
  if "\\x00" in rs.stderr
625
625
  else rs.stderr
626
626
  ).removesuffix("\n")
627
627
  raise StageException(
628
- f"Subprocess: {err}\nRunning Statement:\n---\n"
628
+ f"Subprocess: {e}\nRunning Statement:\n---\n"
629
629
  f"```bash\n{bash}\n```"
630
630
  )
631
631
  return result.catch(
632
- status=Status.SUCCESS,
632
+ status=SUCCESS,
633
633
  context={
634
634
  "return_code": rs.returncode,
635
635
  "stdout": None if (out := rs.stdout.strip("\n")) == "" else out,
@@ -749,7 +749,7 @@ class PyStage(BaseStage):
749
749
  )
750
750
 
751
751
  return result.catch(
752
- status=Status.SUCCESS, context={"locals": lc, "globals": gb}
752
+ status=SUCCESS, context={"locals": lc, "globals": gb}
753
753
  )
754
754
 
755
755
 
@@ -871,7 +871,7 @@ class CallStage(BaseStage):
871
871
  f"Return type: '{t_func.name}@{t_func.tag}' does not serialize "
872
872
  f"to result model, you change return type to `dict`."
873
873
  )
874
- return result.catch(status=Status.SUCCESS, context=rs)
874
+ return result.catch(status=SUCCESS, context=rs)
875
875
 
876
876
 
877
877
  class TriggerStage(BaseStage):
@@ -999,19 +999,11 @@ class ParallelStage(BaseStage): # pragma: no cov
999
999
  ).context,
1000
1000
  to=context,
1001
1001
  )
1002
- except StageException as err: # pragma: no cov
1002
+ except StageException as e: # pragma: no cov
1003
1003
  result.trace.error(
1004
- f"[STAGE]: Catch:\n\t{err.__class__.__name__}:" f"\n\t{err}"
1005
- )
1006
- context.update(
1007
- {
1008
- "errors": {
1009
- "class": err,
1010
- "name": err.__class__.__name__,
1011
- "message": f"{err.__class__.__name__}: {err}",
1012
- },
1013
- },
1004
+ f"[STAGE]: Catch:\n\t{e.__class__.__name__}:" f"\n\t{e}"
1014
1005
  )
1006
+ context.update({"errors": e.to_dict()})
1015
1007
  return context
1016
1008
 
1017
1009
  def execute(
@@ -1041,7 +1033,7 @@ class ParallelStage(BaseStage): # pragma: no cov
1041
1033
  f"[STAGE]: Parallel-Execute with {self.max_parallel_core} cores."
1042
1034
  )
1043
1035
  rs: DictData = {"parallel": {}}
1044
- status = Status.SUCCESS
1036
+ status = SUCCESS
1045
1037
  with ThreadPoolExecutor(
1046
1038
  max_workers=self.max_parallel_core,
1047
1039
  thread_name_prefix="parallel_stage_exec_",
@@ -1065,7 +1057,7 @@ class ParallelStage(BaseStage): # pragma: no cov
1065
1057
  rs["parallel"][context.pop("branch")] = context
1066
1058
 
1067
1059
  if "errors" in context:
1068
- status = Status.FAILED
1060
+ status = FAILED
1069
1061
 
1070
1062
  return result.catch(status=status, context=rs)
1071
1063
 
@@ -1144,7 +1136,7 @@ class ForEachStage(BaseStage):
1144
1136
 
1145
1137
  result.trace.info(f"[STAGE]: Foreach-Execute: {foreach!r}.")
1146
1138
  rs: DictData = {"items": foreach, "foreach": {}}
1147
- status = Status.SUCCESS
1139
+ status: Status = SUCCESS
1148
1140
  # TODO: Implement concurrent more than 1.
1149
1141
  for item in foreach:
1150
1142
  result.trace.debug(f"[STAGE]: Execute foreach item: {item!r}")
@@ -1161,21 +1153,12 @@ class ForEachStage(BaseStage):
1161
1153
  ).context,
1162
1154
  to=context,
1163
1155
  )
1164
- except StageException as err: # pragma: no cov
1165
- status = Status.FAILED
1156
+ except StageException as e: # pragma: no cov
1157
+ status = FAILED
1166
1158
  result.trace.error(
1167
- f"[STAGE]: Catch:\n\t{err.__class__.__name__}:"
1168
- f"\n\t{err}"
1169
- )
1170
- context.update(
1171
- {
1172
- "errors": {
1173
- "class": err,
1174
- "name": err.__class__.__name__,
1175
- "message": f"{err.__class__.__name__}: {err}",
1176
- },
1177
- },
1159
+ f"[STAGE]: Catch:\n\t{e.__class__.__name__}:" f"\n\t{e}"
1178
1160
  )
1161
+ context.update({"errors": e.to_dict()})
1179
1162
 
1180
1163
  rs["foreach"][item] = context
1181
1164
 
@@ -1288,7 +1271,7 @@ class CaseStage(BaseStage): # pragma: no cov
1288
1271
  result: Result = Result(
1289
1272
  run_id=gen_id(self.name + (self.id or ""), unique=True)
1290
1273
  )
1291
- status = Status.SUCCESS
1274
+ status = SUCCESS
1292
1275
  _case = param2template(self.case, params, extras=self.extras)
1293
1276
  _else = None
1294
1277
  context = {}
@@ -1310,21 +1293,12 @@ class CaseStage(BaseStage): # pragma: no cov
1310
1293
  ).context,
1311
1294
  to=context,
1312
1295
  )
1313
- except StageException as err: # pragma: no cov
1314
- status = Status.FAILED
1296
+ except StageException as e: # pragma: no cov
1297
+ status = FAILED
1315
1298
  result.trace.error(
1316
- f"[STAGE]: Catch:\n\t{err.__class__.__name__}:"
1317
- f"\n\t{err}"
1318
- )
1319
- context.update(
1320
- {
1321
- "errors": {
1322
- "class": err,
1323
- "name": err.__class__.__name__,
1324
- "message": f"{err.__class__.__name__}: {err}",
1325
- },
1326
- },
1299
+ f"[STAGE]: Catch:\n\t{e.__class__.__name__}:" f"\n\t{e}"
1327
1300
  )
1301
+ context.update({"errors": e.to_dict()})
1328
1302
 
1329
1303
  return result.catch(status=status, context=context)
1330
1304