ddeutil-workflow 0.0.50__py3-none-any.whl → 0.0.51__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/job.py CHANGED
@@ -27,18 +27,18 @@ from threading import Event
27
27
  from typing import Annotated, Any, Literal, Optional, Union
28
28
 
29
29
  from ddeutil.core import freeze_args
30
- from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag
30
+ from pydantic import BaseModel, ConfigDict, Discriminator, Field, SecretStr, Tag
31
31
  from pydantic.functional_validators import field_validator, model_validator
32
32
  from typing_extensions import Self
33
33
 
34
- from .__types import DictData, DictStr, Matrix, TupleStr
34
+ from .__types import DictData, DictStr, Matrix
35
35
  from .exceptions import (
36
36
  JobException,
37
37
  StageException,
38
38
  UtilException,
39
39
  to_dict,
40
40
  )
41
- from .result import FAILED, SKIP, SUCCESS, WAIT, Result, Status
41
+ from .result import CANCEL, FAILED, SKIP, SUCCESS, WAIT, Result, Status
42
42
  from .reusables import has_template, param2template
43
43
  from .stages import Stage
44
44
  from .utils import cross_product, filter_func, gen_id
@@ -46,21 +46,6 @@ from .utils import cross_product, filter_func, gen_id
46
46
  MatrixFilter = list[dict[str, Union[str, int]]]
47
47
 
48
48
 
49
- __all__: TupleStr = (
50
- "Strategy",
51
- "Job",
52
- "Rule",
53
- "RunsOn",
54
- "RunsOnModel",
55
- "OnLocal",
56
- "OnSelfHosted",
57
- "OnK8s",
58
- "make",
59
- "local_execute_strategy",
60
- "local_execute",
61
- )
62
-
63
-
64
49
  @freeze_args
65
50
  @lru_cache
66
51
  def make(
@@ -120,7 +105,6 @@ def make(
120
105
 
121
106
  add.append(inc)
122
107
 
123
- # NOTE: Merge all matrix together.
124
108
  final.extend(add)
125
109
  return final
126
110
 
@@ -210,8 +194,8 @@ class RunsOn(str, Enum):
210
194
 
211
195
  LOCAL: str = "local"
212
196
  SELF_HOSTED: str = "self_hosted"
213
- K8S: str = "k8s"
214
197
  AZ_BATCH: str = "azure_batch"
198
+ DOCKER: str = "docker"
215
199
 
216
200
 
217
201
  class BaseRunsOn(BaseModel): # pragma: no cov
@@ -221,10 +205,14 @@ class BaseRunsOn(BaseModel): # pragma: no cov
221
205
 
222
206
  model_config = ConfigDict(use_enum_values=True)
223
207
 
224
- type: Literal[RunsOn.LOCAL]
208
+ type: RunsOn = Field(description="A runs-on type.")
225
209
  args: DictData = Field(
226
210
  default_factory=dict,
227
211
  alias="with",
212
+ description=(
213
+ "An argument that pass to the runs-on execution function. This "
214
+ "args will override by this child-model with specific args model."
215
+ ),
228
216
  )
229
217
 
230
218
 
@@ -235,7 +223,9 @@ class OnLocal(BaseRunsOn): # pragma: no cov
235
223
 
236
224
 
237
225
  class SelfHostedArgs(BaseModel):
238
- host: str
226
+ """Self-Hosted arguments."""
227
+
228
+ host: str = Field(description="A host URL of the target self-hosted.")
239
229
 
240
230
 
241
231
  class OnSelfHosted(BaseRunsOn): # pragma: no cov
@@ -245,20 +235,48 @@ class OnSelfHosted(BaseRunsOn): # pragma: no cov
245
235
  args: SelfHostedArgs = Field(alias="with")
246
236
 
247
237
 
248
- class OnK8s(BaseRunsOn): # pragma: no cov
249
- """Runs-on Kubernetes."""
238
+ class AzBatchArgs(BaseModel):
239
+ batch_account_name: str
240
+ batch_account_key: SecretStr
241
+ batch_account_url: str
242
+ storage_account_name: str
243
+ storage_account_key: SecretStr
244
+
245
+
246
+ class OnAzBatch(BaseRunsOn): # pragma: no cov
247
+
248
+ type: Literal[RunsOn.AZ_BATCH] = Field(default=RunsOn.AZ_BATCH)
249
+ args: AzBatchArgs = Field(alias="with")
250
+
251
+
252
+ class DockerArgs(BaseModel):
253
+ image: str = Field(
254
+ default="ubuntu-latest",
255
+ description=(
256
+ "An image that want to run like `ubuntu-22.04`, `windows-latest`, "
257
+ ", `ubuntu-24.04-arm`, or `macos-14`"
258
+ ),
259
+ )
260
+ env: DictData = Field(default_factory=dict)
261
+ volume: DictData = Field(default_factory=dict)
262
+
250
263
 
251
- type: Literal[RunsOn.K8S] = Field(default=RunsOn.K8S)
264
+ class OnDocker(BaseRunsOn): # pragma: no cov
265
+ """Runs-on Docker container."""
266
+
267
+ type: Literal[RunsOn.DOCKER] = Field(default=RunsOn.DOCKER)
268
+ args: DockerArgs = Field(alias="with", default_factory=DockerArgs)
252
269
 
253
270
 
254
271
  def get_discriminator_runs_on(model: dict[str, Any]) -> str:
272
+ """Get discriminator of the RunsOn models."""
255
273
  return model.get("type", "local")
256
274
 
257
275
 
258
276
  RunsOnModel = Annotated[
259
277
  Union[
260
- Annotated[OnK8s, Tag(RunsOn.K8S)],
261
278
  Annotated[OnSelfHosted, Tag(RunsOn.SELF_HOSTED)],
279
+ Annotated[OnDocker, Tag(RunsOn.DOCKER)],
262
280
  Annotated[OnLocal, Tag(RunsOn.LOCAL)],
263
281
  ],
264
282
  Discriminator(get_discriminator_runs_on),
@@ -288,7 +306,6 @@ class Job(BaseModel):
288
306
  ... "name": "Some stage",
289
307
  ... "run": "print('Hello World')",
290
308
  ... },
291
- ... ...
292
309
  ... ],
293
310
  ... }
294
311
  """
@@ -327,7 +344,7 @@ class Job(BaseModel):
327
344
  )
328
345
  needs: list[str] = Field(
329
346
  default_factory=list,
330
- description="A list of the job ID that want to run before this job.",
347
+ description="A list of the job that want to run before this job model.",
331
348
  )
332
349
  strategy: Strategy = Field(
333
350
  default_factory=Strategy,
@@ -359,7 +376,7 @@ class Job(BaseModel):
359
376
  name: str = stage.iden
360
377
  if name in rs:
361
378
  raise ValueError(
362
- "Stage name in jobs object should not be duplicate."
379
+ f"Stage name, {name!r}, should not be duplicate."
363
380
  )
364
381
  rs.append(name)
365
382
  return value
@@ -372,7 +389,9 @@ class Job(BaseModel):
372
389
  """
373
390
  # VALIDATE: Validate job id should not dynamic with params template.
374
391
  if has_template(self.id):
375
- raise ValueError("Job ID should not has any template.")
392
+ raise ValueError(
393
+ f"Job ID, {self.id!r}, should not has any template."
394
+ )
376
395
 
377
396
  return self
378
397
 
@@ -390,7 +409,7 @@ class Job(BaseModel):
390
409
  if self.extras:
391
410
  stage.extras = self.extras
392
411
  return stage
393
- raise ValueError(f"Stage ID {stage_id} does not exists")
412
+ raise ValueError(f"Stage {stage_id!r} does not exists in this job.")
394
413
 
395
414
  def check_needs(
396
415
  self,
@@ -497,14 +516,14 @@ class Job(BaseModel):
497
516
  ... (i) output: {'strategy-01': bar, 'strategy-02': bar}
498
517
  ... (ii) to: {'jobs': {}}
499
518
 
500
- The result of the `to` variable will be;
519
+ The result of the `to` argument will be;
501
520
 
502
521
  ... (iii) to: {
503
522
  'jobs': {
504
523
  '<job-id>': {
505
524
  'strategies': {
506
525
  'strategy-01': bar,
507
- 'strategy-02': bar
526
+ 'strategy-02': bar,
508
527
  }
509
528
  }
510
529
  }
@@ -527,22 +546,27 @@ class Job(BaseModel):
527
546
  "This job do not set the ID before setting execution output."
528
547
  )
529
548
 
530
- # NOTE: If the job ID did not set, it will use index of jobs key
531
- # instead.
532
549
  _id: str = self.id or job_id
533
-
534
550
  errors: DictData = (
535
551
  {"errors": output.pop("errors", {})} if "errors" in output else {}
536
552
  )
553
+ skipping: dict[str, bool] = (
554
+ {"skipped": output.pop("skipped", False)}
555
+ if "skipped" in output
556
+ else {}
557
+ )
537
558
 
538
- if "SKIP" in output: # pragma: no cov
539
- to["jobs"][_id] = output["SKIP"]
540
- elif self.strategy.is_set():
541
- to["jobs"][_id] = {"strategies": output, **errors}
559
+ if self.strategy.is_set():
560
+ to["jobs"][_id] = {"strategies": output, **skipping, **errors}
561
+ elif len(k := output.keys()) > 1: # pragma: no cov
562
+ raise JobException(
563
+ "Strategy output from execution return more than one ID while "
564
+ "this job does not set strategy."
565
+ )
542
566
  else:
543
- _output = output.get(next(iter(output), "FIRST"), {})
567
+ _output: DictData = {} if len(k) == 0 else output[list(k)[0]]
544
568
  _output.pop("matrix", {})
545
- to["jobs"][_id] = {**_output, **errors}
569
+ to["jobs"][_id] = {**_output, **skipping, **errors}
546
570
  return to
547
571
 
548
572
  def execute(
@@ -559,6 +583,9 @@ class Job(BaseModel):
559
583
  execution. It will generate matrix values at the first step and run
560
584
  multithread on this metrics to the `stages` field of this job.
561
585
 
586
+ This method be execution routing for call dynamic execution function
587
+ with specific target `runs-on` value.
588
+
562
589
  :param params: An input parameters that use on job execution.
563
590
  :param run_id: (str) A job running ID.
564
591
  :param parent_run_id: (str) A parent workflow running ID.
@@ -566,8 +593,10 @@ class Job(BaseModel):
566
593
  data.
567
594
  :param event: (Event) An event manager that pass to the
568
595
  PoolThreadExecutor.
569
- :param raise_error: (bool) A flag that all this method raise error to the
570
- strategy execution. Default is `True`.
596
+ :param raise_error: (bool) A flag that all this method raise error to
597
+ the strategy execution. Default is `True`.
598
+
599
+ :raise NotImplementedError: If the `runs-on` value does not implement.
571
600
 
572
601
  :rtype: Result
573
602
  """
@@ -579,28 +608,36 @@ class Job(BaseModel):
579
608
  extras=self.extras,
580
609
  )
581
610
 
582
- result.trace.info(f"[JOB]: Start execute job: {self.id!r}")
611
+ result.trace.info(
612
+ f"[JOB]: Execute: {self.id!r} on {self.runs_on.type!r}"
613
+ )
583
614
  if self.runs_on.type == RunsOn.LOCAL:
584
615
  return local_execute(
585
- job=self,
586
- params=params,
587
- result=result,
616
+ self,
617
+ params,
618
+ run_id=run_id,
619
+ parent_run_id=parent_run_id,
588
620
  event=event,
589
621
  raise_error=raise_error,
590
622
  )
591
623
  elif self.runs_on.type == RunsOn.SELF_HOSTED: # pragma: no cov
592
624
  pass
593
- elif self.runs_on.type == RunsOn.K8S: # pragma: no cov
594
- pass
625
+ elif self.runs_on.type == RunsOn.DOCKER: # pragma: no cov
626
+ docker_execution(
627
+ self,
628
+ params,
629
+ run_id=run_id,
630
+ parent_run_id=parent_run_id,
631
+ event=event,
632
+ raise_error=raise_error,
633
+ )
595
634
 
596
635
  # pragma: no cov
597
636
  result.trace.error(
598
- f"[JOB]: Job executor does not support for runs-on type: "
599
- f"{self.runs_on.type} yet"
637
+ f"[JOB]: Execution not support runs-on: {self.runs_on.type!r} yet."
600
638
  )
601
639
  raise NotImplementedError(
602
- f"The job runs-on other type: {self.runs_on.type} does not "
603
- f"support yet."
640
+ f"Execution runs-on type: {self.runs_on.type} does not support yet."
604
641
  )
605
642
 
606
643
 
@@ -622,6 +659,8 @@ def local_execute_strategy(
622
659
 
623
660
  The result of this execution will return result with strategy ID
624
661
  that generated from the `gen_id` function with an input strategy value.
662
+ For each stage that execution with this strategy metrix, it will use the
663
+ `set_outputs` method for reconstruct result context data.
625
664
 
626
665
  :raise JobException: If it has any error from `StageException` or
627
666
  `UtilException`.
@@ -645,8 +684,10 @@ def local_execute_strategy(
645
684
  context.update({"matrix": strategy, "stages": {}})
646
685
 
647
686
  if strategy:
648
- result.trace.info(f"[JOB]: Execute Strategy ID: {strategy_id}")
649
- result.trace.info(f"[JOB]: ... Matrix: {strategy_id}")
687
+ result.trace.info(f"[JOB]: Execute Strategy: {strategy_id!r}")
688
+ result.trace.info(f"[JOB]: ... matrix: {strategy!r}")
689
+ else:
690
+ result.trace.info("[JOB]: Execute Empty-Strategy")
650
691
 
651
692
  for stage in job.stages:
652
693
 
@@ -654,7 +695,7 @@ def local_execute_strategy(
654
695
  stage.extras = job.extras
655
696
 
656
697
  if stage.is_skipped(params=context):
657
- result.trace.info(f"[STAGE]: Skip stage: {stage.iden!r}")
698
+ result.trace.info(f"[JOB]: Skip Stage: {stage.iden!r}")
658
699
  stage.set_outputs(output={"skipped": True}, to=context)
659
700
  continue
660
701
 
@@ -664,7 +705,7 @@ def local_execute_strategy(
664
705
  "strategy execution."
665
706
  )
666
707
  return result.catch(
667
- status=FAILED,
708
+ status=CANCEL,
668
709
  context={
669
710
  strategy_id: {
670
711
  "matrix": strategy,
@@ -675,6 +716,7 @@ def local_execute_strategy(
675
716
  )
676
717
 
677
718
  try:
719
+ result.trace.info(f"[JOB]: Execute Stage: {stage.iden!r}")
678
720
  rs: Result = stage.handler_execute(
679
721
  params=context,
680
722
  run_id=result.run_id,
@@ -682,22 +724,6 @@ def local_execute_strategy(
682
724
  event=event,
683
725
  )
684
726
  stage.set_outputs(rs.context, to=context)
685
- if rs.status == FAILED:
686
- error_msg: str = (
687
- f"Job strategy was break because it has a stage, "
688
- f"{stage.iden}, failed without raise error."
689
- )
690
- return result.catch(
691
- status=FAILED,
692
- context={
693
- strategy_id: {
694
- "matrix": strategy,
695
- "stages": filter_func(context.pop("stages", {})),
696
- "errors": JobException(error_msg).to_dict(),
697
- },
698
- },
699
- )
700
-
701
727
  except (StageException, UtilException) as e:
702
728
  result.trace.error(f"[JOB]: {e.__class__.__name__}: {e}")
703
729
  if raise_error:
@@ -716,6 +742,22 @@ def local_execute_strategy(
716
742
  },
717
743
  )
718
744
 
745
+ if rs.status == FAILED:
746
+ error_msg: str = (
747
+ f"Job strategy was break because stage, {stage.iden}, "
748
+ f"failed without raise error."
749
+ )
750
+ return result.catch(
751
+ status=FAILED,
752
+ context={
753
+ strategy_id: {
754
+ "matrix": strategy,
755
+ "stages": filter_func(context.pop("stages", {})),
756
+ "errors": JobException(error_msg).to_dict(),
757
+ },
758
+ },
759
+ )
760
+
719
761
  return result.catch(
720
762
  status=SUCCESS,
721
763
  context={
@@ -733,7 +775,6 @@ def local_execute(
733
775
  *,
734
776
  run_id: str | None = None,
735
777
  parent_run_id: str | None = None,
736
- result: Result | None = None,
737
778
  event: Event | None = None,
738
779
  raise_error: bool = True,
739
780
  ) -> Result:
@@ -748,8 +789,6 @@ def local_execute(
748
789
  :param params: (DictData) An input parameters that use on job execution.
749
790
  :param run_id: (str) A job running ID for this execution.
750
791
  :param parent_run_id: (str) A parent workflow running ID for this release.
751
- :param result: (Result) A result object for keeping context and status
752
- data.
753
792
  :param event: (Event) An event manager that pass to the PoolThreadExecutor.
754
793
  :param raise_error: (bool) A flag that all this method raise error to the
755
794
  strategy execution. Default is `True`.
@@ -757,12 +796,12 @@ def local_execute(
757
796
  :rtype: Result
758
797
  """
759
798
  result: Result = Result.construct_with_rs_or_id(
760
- result,
761
799
  run_id=run_id,
762
800
  parent_run_id=parent_run_id,
763
801
  id_logic=(job.id or "not-set"),
764
802
  extras=job.extras,
765
803
  )
804
+
766
805
  event: Event = Event() if event is None else event
767
806
 
768
807
  # NOTE: Normal Job execution without parallel strategy matrix. It uses
@@ -773,7 +812,7 @@ def local_execute(
773
812
 
774
813
  if event and event.is_set(): # pragma: no cov
775
814
  return result.catch(
776
- status=FAILED,
815
+ status=CANCEL,
777
816
  context={
778
817
  "errors": JobException(
779
818
  "Job strategy was canceled from event that had set "
@@ -791,7 +830,7 @@ def local_execute(
791
830
  raise_error=raise_error,
792
831
  )
793
832
 
794
- return result.catch(status=result.status)
833
+ return result
795
834
 
796
835
  fail_fast_flag: bool = job.strategy.fail_fast
797
836
  ls: str = "Fail-Fast" if fail_fast_flag else "All-Completed"
@@ -802,7 +841,7 @@ def local_execute(
802
841
 
803
842
  if event and event.is_set(): # pragma: no cov
804
843
  return result.catch(
805
- status=FAILED,
844
+ status=CANCEL,
806
845
  context={
807
846
  "errors": JobException(
808
847
  "Job strategy was canceled from event that had set "
@@ -841,7 +880,7 @@ def local_execute(
841
880
 
842
881
  if len(done) != len(futures):
843
882
  result.trace.warning(
844
- "[JOB]: Set the event for stop running stage."
883
+ "[JOB]: Set event for stop pending stage future."
845
884
  )
846
885
  event.set()
847
886
  for future in not_done:
@@ -850,7 +889,7 @@ def local_execute(
850
889
  nd: str = (
851
890
  f", the strategies do not run is {not_done}" if not_done else ""
852
891
  )
853
- result.trace.debug(f"[JOB]: Strategy is set Fail Fast{nd}")
892
+ result.trace.debug(f"[JOB]: Strategy set Fail-Fast{nd}")
854
893
 
855
894
  for future in done:
856
895
  try:
@@ -871,7 +910,6 @@ def self_hosted_execute(
871
910
  *,
872
911
  run_id: str | None = None,
873
912
  parent_run_id: str | None = None,
874
- result: Result | None = None,
875
913
  event: Event | None = None,
876
914
  raise_error: bool = True,
877
915
  ) -> Result: # pragma: no cov
@@ -883,8 +921,6 @@ def self_hosted_execute(
883
921
  :param params: (DictData) An input parameters that use on job execution.
884
922
  :param run_id: (str) A job running ID for this execution.
885
923
  :param parent_run_id: (str) A parent workflow running ID for this release.
886
- :param result: (Result) A result object for keeping context and status
887
- data.
888
924
  :param event: (Event) An event manager that pass to the PoolThreadExecutor.
889
925
  :param raise_error: (bool) A flag that all this method raise error to the
890
926
  strategy execution.
@@ -892,7 +928,6 @@ def self_hosted_execute(
892
928
  :rtype: Result
893
929
  """
894
930
  result: Result = Result.construct_with_rs_or_id(
895
- result,
896
931
  run_id=run_id,
897
932
  parent_run_id=parent_run_id,
898
933
  id_logic=(job.id or "not-set"),
@@ -901,7 +936,7 @@ def self_hosted_execute(
901
936
 
902
937
  if event and event.is_set():
903
938
  return result.catch(
904
- status=FAILED,
939
+ status=CANCEL,
905
940
  context={
906
941
  "errors": JobException(
907
942
  "Job self-hosted execution was canceled from event that "
@@ -943,7 +978,6 @@ def azure_batch_execute(
943
978
  *,
944
979
  run_id: str | None = None,
945
980
  parent_run_id: str | None = None,
946
- result: Result | None = None,
947
981
  event: Event | None = None,
948
982
  raise_error: bool | None = None,
949
983
  ) -> Result: # pragma no cov
@@ -962,17 +996,19 @@ def azure_batch_execute(
962
996
  the compute node before running the script.
963
997
  - Monitor the job and retrieve the output files from Azure Storage.
964
998
 
999
+ References:
1000
+ - https://docs.azure.cn/en-us/batch/tutorial-parallel-python
1001
+
965
1002
  :param job:
966
1003
  :param params:
967
1004
  :param run_id:
968
1005
  :param parent_run_id:
969
- :param result:
970
1006
  :param event:
971
1007
  :param raise_error:
972
- :return:
1008
+
1009
+ :rtype: Result
973
1010
  """
974
1011
  result: Result = Result.construct_with_rs_or_id(
975
- result,
976
1012
  run_id=run_id,
977
1013
  parent_run_id=parent_run_id,
978
1014
  id_logic=(job.id or "not-set"),
@@ -980,7 +1016,7 @@ def azure_batch_execute(
980
1016
  )
981
1017
  if event and event.is_set():
982
1018
  return result.catch(
983
- status=FAILED,
1019
+ status=CANCEL,
984
1020
  context={
985
1021
  "errors": JobException(
986
1022
  "Job azure-batch execution was canceled from event that "
@@ -991,3 +1027,40 @@ def azure_batch_execute(
991
1027
  print(params)
992
1028
  print(raise_error)
993
1029
  return result.catch(status=SUCCESS)
1030
+
1031
+
1032
+ def docker_execution(
1033
+ job: Job,
1034
+ params: DictData,
1035
+ *,
1036
+ run_id: str | None = None,
1037
+ parent_run_id: str | None = None,
1038
+ event: Event | None = None,
1039
+ raise_error: bool | None = None,
1040
+ ):
1041
+ """Docker job execution.
1042
+
1043
+ Steps:
1044
+ - Pull the image
1045
+ - Install this workflow package
1046
+ - Start push job to run to target Docker container.
1047
+ """
1048
+ result: Result = Result.construct_with_rs_or_id(
1049
+ run_id=run_id,
1050
+ parent_run_id=parent_run_id,
1051
+ id_logic=(job.id or "not-set"),
1052
+ extras=job.extras,
1053
+ )
1054
+ if event and event.is_set():
1055
+ return result.catch(
1056
+ status=CANCEL,
1057
+ context={
1058
+ "errors": JobException(
1059
+ "Job Docker execution was canceled from event that "
1060
+ "had set before start execution."
1061
+ ).to_dict()
1062
+ },
1063
+ )
1064
+ print(params)
1065
+ print(raise_error)
1066
+ return result.catch(status=SUCCESS)
ddeutil/workflow/logs.py CHANGED
@@ -224,18 +224,30 @@ class BaseTrace(ABC): # pragma: no cov
224
224
  "Adjust make message method for this trace object before using."
225
225
  )
226
226
 
227
- def debug(self, message: str):
228
- """Write trace log with append mode and logging this message with the
229
- DEBUG level.
227
+ def __logging(
228
+ self, message: str, mode: str, *, is_err: bool = False
229
+ ) -> None:
230
+ """Write trace log with append mode and logging this message with any
231
+ logging level.
230
232
 
231
233
  :param message: (str) A message that want to log.
232
234
  """
233
235
  msg: str = self.make_message(message)
234
236
 
235
- if dynamic("debug", extras=self.extras):
236
- self.writer(msg)
237
+ if mode != "debug" or (
238
+ mode == "debug" and dynamic("debug", extras=self.extras)
239
+ ):
240
+ self.writer(msg, is_err=is_err)
241
+
242
+ getattr(logger, mode)(msg, stacklevel=3)
243
+
244
+ def debug(self, message: str):
245
+ """Write trace log with append mode and logging this message with the
246
+ DEBUG level.
237
247
 
238
- logger.debug(msg, stacklevel=2)
248
+ :param message: (str) A message that want to log.
249
+ """
250
+ self.__logging(message, mode="debug")
239
251
 
240
252
  def info(self, message: str) -> None:
241
253
  """Write trace log with append mode and logging this message with the
@@ -243,9 +255,7 @@ class BaseTrace(ABC): # pragma: no cov
243
255
 
244
256
  :param message: (str) A message that want to log.
245
257
  """
246
- msg: str = self.make_message(message)
247
- self.writer(msg)
248
- logger.info(msg, stacklevel=2)
258
+ self.__logging(message, mode="info")
249
259
 
250
260
  def warning(self, message: str) -> None:
251
261
  """Write trace log with append mode and logging this message with the
@@ -253,9 +263,7 @@ class BaseTrace(ABC): # pragma: no cov
253
263
 
254
264
  :param message: (str) A message that want to log.
255
265
  """
256
- msg: str = self.make_message(message)
257
- self.writer(msg)
258
- logger.warning(msg, stacklevel=2)
266
+ self.__logging(message, mode="warning")
259
267
 
260
268
  def error(self, message: str) -> None:
261
269
  """Write trace log with append mode and logging this message with the
@@ -263,9 +271,7 @@ class BaseTrace(ABC): # pragma: no cov
263
271
 
264
272
  :param message: (str) A message that want to log.
265
273
  """
266
- msg: str = self.make_message(message)
267
- self.writer(msg, is_err=True)
268
- logger.error(msg, stacklevel=2)
274
+ self.__logging(message, mode="error", is_err=True)
269
275
 
270
276
  def exception(self, message: str) -> None:
271
277
  """Write trace log with append mode and logging this message with the
@@ -273,9 +279,7 @@ class BaseTrace(ABC): # pragma: no cov
273
279
 
274
280
  :param message: (str) A message that want to log.
275
281
  """
276
- msg: str = self.make_message(message)
277
- self.writer(msg, is_err=True)
278
- logger.exception(msg, stacklevel=2)
282
+ self.__logging(message, mode="exception", is_err=True)
279
283
 
280
284
  async def adebug(self, message: str) -> None: # pragma: no cov
281
285
  """Async write trace log with append mode and logging this message with