ddeutil-workflow 0.0.67__py3-none-any.whl → 0.0.69__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.
@@ -3,12 +3,7 @@
3
3
  # Licensed under the MIT License. See LICENSE in the project root for
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
- # [x] Use fix config for `get_logger`, and Model initialize step.
7
- """A Logs module contain Trace and Audit Pydantic models for process log from
8
- the core workflow engine. I separate part of log to 2 types:
9
- - Trace: A stdout and stderr log
10
- - Audit: An audit release log for tracking incremental running workflow.
11
- """
6
+ # [x] Use fix config for `set_logging`, and Model initialize step.
12
7
  from __future__ import annotations
13
8
 
14
9
  import json
@@ -17,7 +12,6 @@ import os
17
12
  import re
18
13
  from abc import ABC, abstractmethod
19
14
  from collections.abc import Iterator
20
- from datetime import datetime
21
15
  from functools import lru_cache
22
16
  from inspect import Traceback, currentframe, getframeinfo
23
17
  from pathlib import Path
@@ -26,7 +20,6 @@ from types import FrameType
26
20
  from typing import ClassVar, Final, Literal, Optional, TypeVar, Union
27
21
 
28
22
  from pydantic import BaseModel, ConfigDict, Field
29
- from pydantic.functional_validators import model_validator
30
23
  from typing_extensions import Self
31
24
 
32
25
  from .__types import DictData
@@ -34,44 +27,35 @@ from .conf import config, dynamic
34
27
  from .utils import cut_id, get_dt_now, prepare_newline
35
28
 
36
29
  METADATA: str = "metadata.json"
30
+ logger = logging.getLogger("ddeutil.workflow")
37
31
 
38
32
 
39
33
  @lru_cache
40
- def get_logger(name: str):
41
- """Return logger object with an input module name.
34
+ def set_logging(name: str) -> logging.Logger:
35
+ """Return logger object with an input module name that already implement the
36
+ custom handler and formatter from this package config.
42
37
 
43
38
  :param name: (str) A module name that want to log.
39
+
40
+ :rtype: logging.Logger
44
41
  """
45
- lg = logging.getLogger(name)
42
+ _logger = logging.getLogger(name)
46
43
 
47
44
  # NOTE: Developers using this package can then disable all logging just for
48
45
  # this package by;
49
46
  #
50
47
  # `logging.getLogger('ddeutil.workflow').propagate = False`
51
48
  #
52
- lg.addHandler(logging.NullHandler())
49
+ _logger.addHandler(logging.NullHandler())
53
50
 
54
51
  formatter = logging.Formatter(
55
- fmt=config.log_format,
56
- datefmt=config.log_datetime_format,
52
+ fmt=config.log_format, datefmt=config.log_datetime_format
57
53
  )
58
- stream = logging.StreamHandler()
59
- stream.setFormatter(formatter)
60
- lg.addHandler(stream)
61
-
62
- lg.setLevel(logging.DEBUG if config.debug else logging.INFO)
63
- return lg
64
-
65
-
66
- logger = get_logger("ddeutil.workflow")
67
-
68
-
69
- def get_dt_tznow() -> datetime: # pragma: no cov
70
- """Return the current datetime object that passing the config timezone.
71
-
72
- :rtype: datetime
73
- """
74
- return get_dt_now(tz=config.tz)
54
+ stream_handler = logging.StreamHandler()
55
+ stream_handler.setFormatter(formatter)
56
+ _logger.addHandler(stream_handler)
57
+ _logger.setLevel(logging.DEBUG if config.debug else logging.INFO)
58
+ return _logger
75
59
 
76
60
 
77
61
  PREFIX_LOGS: Final[dict[str, dict]] = {
@@ -97,8 +81,22 @@ class PrefixMsg(BaseModel):
97
81
  from logging message.
98
82
  """
99
83
 
100
- name: Optional[str] = Field(default=None)
101
- message: Optional[str] = Field(default=None)
84
+ name: Optional[str] = Field(default=None, description="A prefix name.")
85
+ message: Optional[str] = Field(default=None, description="A message.")
86
+
87
+ @classmethod
88
+ def from_str(cls, msg: str) -> Self:
89
+ """Extract message prefix from an input message.
90
+
91
+ Args:
92
+ msg (str): A message that want to extract.
93
+
94
+ Returns:
95
+ PrefixMsg: the validated model from a string message.
96
+ """
97
+ return PrefixMsg.model_validate(
98
+ obj=PREFIX_LOGS_REGEX.search(msg).groupdict()
99
+ )
102
100
 
103
101
  def prepare(self, extras: Optional[DictData] = None) -> str:
104
102
  """Prepare message with force add prefix before writing trace log.
@@ -117,18 +115,6 @@ class PrefixMsg(BaseModel):
117
115
  return f"{emoji}[{name}]: {self.message}"
118
116
 
119
117
 
120
- def extract_msg_prefix(msg: str) -> PrefixMsg:
121
- """Extract message prefix from an input message.
122
-
123
- :param msg: A message that want to extract.
124
-
125
- :rtype: PrefixMsg
126
- """
127
- return PrefixMsg.model_validate(
128
- obj=PREFIX_LOGS_REGEX.search(msg).groupdict()
129
- )
130
-
131
-
132
118
  class TraceMeta(BaseModel): # pragma: no cov
133
119
  """Trace Metadata model for making the current metadata of this CPU, Memory
134
120
  process, and thread data.
@@ -247,10 +233,6 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
247
233
 
248
234
  model_config = ConfigDict(frozen=True)
249
235
 
250
- run_id: str = Field(default="A running ID")
251
- parent_run_id: Optional[str] = Field(
252
- default=None, description="A parent running ID"
253
- )
254
236
  extras: DictData = Field(
255
237
  default_factory=dict,
256
238
  description=(
@@ -258,6 +240,11 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
258
240
  "values."
259
241
  ),
260
242
  )
243
+ run_id: str = Field(description="A running ID")
244
+ parent_run_id: Optional[str] = Field(
245
+ default=None,
246
+ description="A parent running ID",
247
+ )
261
248
 
262
249
  @classmethod
263
250
  @abstractmethod
@@ -266,6 +253,17 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
266
253
  path: Optional[Path] = None,
267
254
  extras: Optional[DictData] = None,
268
255
  ) -> Iterator[TraceData]: # pragma: no cov
256
+ """Return iterator of TraceData models from the target pointer.
257
+
258
+ Args:
259
+ path (:obj:`Path`, optional): A pointer path that want to override.
260
+ extras (:obj:`DictData`, optional): An extras parameter that want to
261
+ override default engine config.
262
+
263
+ Returns:
264
+ Iterator[TracData]: An iterator object that generate a TracData
265
+ model.
266
+ """
269
267
  raise NotImplementedError(
270
268
  "Trace dataclass should implement `find_traces` class-method."
271
269
  )
@@ -286,7 +284,12 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
286
284
  )
287
285
 
288
286
  @abstractmethod
289
- def writer(self, message: str, level: str, is_err: bool = False) -> None:
287
+ def writer(
288
+ self,
289
+ message: str,
290
+ level: str,
291
+ is_err: bool = False,
292
+ ) -> None:
290
293
  """Write a trace message after making to target pointer object. The
291
294
  target can be anything be inherited this class and overwrite this method
292
295
  such as file, console, or database.
@@ -302,7 +305,10 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
302
305
 
303
306
  @abstractmethod
304
307
  async def awriter(
305
- self, message: str, level: str, is_err: bool = False
308
+ self,
309
+ message: str,
310
+ level: str,
311
+ is_err: bool = False,
306
312
  ) -> None:
307
313
  """Async Write a trace message after making to target pointer object.
308
314
 
@@ -327,6 +333,89 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
327
333
  "Adjust make message method for this trace object before using."
328
334
  )
329
335
 
336
+
337
+ class ConsoleTrace(BaseTrace): # pragma: no cov
338
+ """Console Trace log model."""
339
+
340
+ @classmethod
341
+ def find_traces(
342
+ cls,
343
+ path: Optional[Path] = None,
344
+ extras: Optional[DictData] = None,
345
+ ) -> Iterator[TraceData]: # pragma: no cov
346
+ raise NotImplementedError(
347
+ "Console Trace does not support to find history traces data."
348
+ )
349
+
350
+ @classmethod
351
+ def find_trace_with_id(
352
+ cls,
353
+ run_id: str,
354
+ force_raise: bool = True,
355
+ *,
356
+ path: Optional[Path] = None,
357
+ extras: Optional[DictData] = None,
358
+ ) -> TraceData:
359
+ raise NotImplementedError(
360
+ "Console Trace does not support to find history traces data with "
361
+ "the specific running ID."
362
+ )
363
+
364
+ def writer(
365
+ self,
366
+ message: str,
367
+ level: str,
368
+ is_err: bool = False,
369
+ ) -> None:
370
+ """Write a trace message after making to target pointer object. The
371
+ target can be anything be inherited this class and overwrite this method
372
+ such as file, console, or database.
373
+
374
+ :param message: (str) A message after making.
375
+ :param level: (str) A log level.
376
+ :param is_err: (bool) A flag for writing with an error trace or not.
377
+ (Default be False)
378
+ """
379
+
380
+ async def awriter(
381
+ self,
382
+ message: str,
383
+ level: str,
384
+ is_err: bool = False,
385
+ ) -> None:
386
+ """Async Write a trace message after making to target pointer object.
387
+
388
+ :param message: (str) A message after making.
389
+ :param level: (str) A log level.
390
+ :param is_err: (bool) A flag for writing with an error trace or not.
391
+ (Default be False)
392
+ """
393
+
394
+ @property
395
+ def cut_id(self) -> str:
396
+ """Combine cutting ID of parent running ID if it set.
397
+
398
+ :rtype: str
399
+ """
400
+ cut_run_id: str = cut_id(self.run_id)
401
+ if not self.parent_run_id:
402
+ return f"{cut_run_id}"
403
+
404
+ cut_parent_run_id: str = cut_id(self.parent_run_id)
405
+ return f"{cut_parent_run_id} -> {cut_run_id}"
406
+
407
+ def make_message(self, message: str) -> str:
408
+ """Prepare and Make a message before write and log steps.
409
+
410
+ :param message: (str) A message that want to prepare and make before.
411
+
412
+ :rtype: str
413
+ """
414
+ return prepare_newline(
415
+ f"({self.cut_id}) "
416
+ f"{PrefixMsg.from_str(message).prepare(self.extras)}"
417
+ )
418
+
330
419
  def __logging(
331
420
  self, message: str, mode: str, *, is_err: bool = False
332
421
  ) -> None:
@@ -342,7 +431,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
342
431
  ):
343
432
  self.writer(msg, level=mode, is_err=is_err)
344
433
 
345
- getattr(logger, mode)(msg, stacklevel=3)
434
+ getattr(logger, mode)(msg, stacklevel=3, extra={"cut_id": self.cut_id})
346
435
 
347
436
  def debug(self, message: str):
348
437
  """Write trace log with append mode and logging this message with the
@@ -399,7 +488,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
399
488
  ):
400
489
  await self.awriter(msg, level=mode, is_err=is_err)
401
490
 
402
- getattr(logger, mode)(msg, stacklevel=3)
491
+ getattr(logger, mode)(msg, stacklevel=3, extra={"cut_id": self.cut_id})
403
492
 
404
493
  async def adebug(self, message: str) -> None: # pragma: no cov
405
494
  """Async write trace log with append mode and logging this message with
@@ -442,7 +531,7 @@ class BaseTrace(BaseModel, ABC): # pragma: no cov
442
531
  await self.__alogging(message, mode="exception", is_err=True)
443
532
 
444
533
 
445
- class FileTrace(BaseTrace): # pragma: no cov
534
+ class FileTrace(ConsoleTrace): # pragma: no cov
446
535
  """File Trace dataclass that write file to the local storage."""
447
536
 
448
537
  @classmethod
@@ -491,6 +580,11 @@ class FileTrace(BaseTrace): # pragma: no cov
491
580
 
492
581
  @property
493
582
  def pointer(self) -> Path:
583
+ """Pointer of the target path that use to writing trace log or searching
584
+ trace log.
585
+
586
+ :rtype: Path
587
+ """
494
588
  log_file: Path = (
495
589
  dynamic("trace_path", extras=self.extras)
496
590
  / f"run_id={self.parent_run_id or self.run_id}"
@@ -499,31 +593,12 @@ class FileTrace(BaseTrace): # pragma: no cov
499
593
  log_file.mkdir(parents=True)
500
594
  return log_file
501
595
 
502
- @property
503
- def cut_id(self) -> str:
504
- """Combine cutting ID of parent running ID if it set.
505
-
506
- :rtype: str
507
- """
508
- cut_run_id: str = cut_id(self.run_id)
509
- if not self.parent_run_id:
510
- return f"{cut_run_id}"
511
-
512
- cut_parent_run_id: str = cut_id(self.parent_run_id)
513
- return f"{cut_parent_run_id} -> {cut_run_id}"
514
-
515
- def make_message(self, message: str) -> str:
516
- """Prepare and Make a message before write and log steps.
517
-
518
- :param message: (str) A message that want to prepare and make before.
519
-
520
- :rtype: str
521
- """
522
- return prepare_newline(
523
- f"({self.cut_id}) {extract_msg_prefix(message).prepare(self.extras)}"
524
- )
525
-
526
- def writer(self, message: str, level: str, is_err: bool = False) -> None:
596
+ def writer(
597
+ self,
598
+ message: str,
599
+ level: str,
600
+ is_err: bool = False,
601
+ ) -> None:
527
602
  """Write a trace message after making to target file and write metadata
528
603
  in the same path of standard files.
529
604
 
@@ -555,7 +630,10 @@ class FileTrace(BaseTrace): # pragma: no cov
555
630
  f.write(trace_meta.model_dump_json() + "\n")
556
631
 
557
632
  async def awriter(
558
- self, message: str, level: str, is_err: bool = False
633
+ self,
634
+ message: str,
635
+ level: str,
636
+ is_err: bool = False,
559
637
  ) -> None: # pragma: no cov
560
638
  """Write with async mode."""
561
639
  if not dynamic("enable_write_log", extras=self.extras):
@@ -583,7 +661,7 @@ class FileTrace(BaseTrace): # pragma: no cov
583
661
  await f.write(trace_meta.model_dump_json() + "\n")
584
662
 
585
663
 
586
- class SQLiteTrace(BaseTrace): # pragma: no cov
664
+ class SQLiteTrace(ConsoleTrace): # pragma: no cov
587
665
  """SQLite Trace dataclass that write trace log to the SQLite database file."""
588
666
 
589
667
  table_name: ClassVar[str] = "audits"
@@ -617,16 +695,23 @@ class SQLiteTrace(BaseTrace): # pragma: no cov
617
695
  def make_message(self, message: str) -> str: ...
618
696
 
619
697
  def writer(
620
- self, message: str, level: str, is_err: bool = False
698
+ self,
699
+ message: str,
700
+ level: str,
701
+ is_err: bool = False,
621
702
  ) -> None: ...
622
703
 
623
704
  def awriter(
624
- self, message: str, level: str, is_err: bool = False
705
+ self,
706
+ message: str,
707
+ level: str,
708
+ is_err: bool = False,
625
709
  ) -> None: ...
626
710
 
627
711
 
628
712
  Trace = TypeVar("Trace", bound=BaseTrace)
629
713
  TraceModel = Union[
714
+ ConsoleTrace,
630
715
  FileTrace,
631
716
  SQLiteTrace,
632
717
  ]
@@ -655,307 +740,3 @@ def get_trace(
655
740
  return FileTrace(
656
741
  run_id=run_id, parent_run_id=parent_run_id, extras=(extras or {})
657
742
  )
658
-
659
-
660
- class BaseAudit(BaseModel, ABC):
661
- """Base Audit Pydantic Model with abstraction class property that implement
662
- only model fields. This model should to use with inherit to logging
663
- subclass like file, sqlite, etc.
664
- """
665
-
666
- extras: DictData = Field(
667
- default_factory=dict,
668
- description="An extras parameter that want to override core config",
669
- )
670
- name: str = Field(description="A workflow name.")
671
- release: datetime = Field(description="A release datetime.")
672
- type: str = Field(description="A running type before logging.")
673
- context: DictData = Field(
674
- default_factory=dict,
675
- description="A context that receive from a workflow execution result.",
676
- )
677
- parent_run_id: Optional[str] = Field(
678
- default=None, description="A parent running ID."
679
- )
680
- run_id: str = Field(description="A running ID")
681
- update: datetime = Field(default_factory=get_dt_tznow)
682
- execution_time: float = Field(default=0, description="An execution time.")
683
-
684
- @model_validator(mode="after")
685
- def __model_action(self) -> Self:
686
- """Do before the Audit action with WORKFLOW_AUDIT_ENABLE_WRITE env variable.
687
-
688
- :rtype: Self
689
- """
690
- if dynamic("enable_write_audit", extras=self.extras):
691
- self.do_before()
692
- return self
693
-
694
- @classmethod
695
- @abstractmethod
696
- def is_pointed(
697
- cls,
698
- name: str,
699
- release: datetime,
700
- *,
701
- extras: Optional[DictData] = None,
702
- ) -> bool:
703
- raise NotImplementedError(
704
- "Audit should implement `is_pointed` class-method"
705
- )
706
-
707
- @classmethod
708
- @abstractmethod
709
- def find_audits(
710
- cls, name: str, *, extras: Optional[DictData] = None
711
- ) -> Iterator[Self]:
712
- raise NotImplementedError(
713
- "Audit should implement `find_audits` class-method"
714
- )
715
-
716
- @classmethod
717
- @abstractmethod
718
- def find_audit_with_release(
719
- cls,
720
- name: str,
721
- release: Optional[datetime] = None,
722
- *,
723
- extras: Optional[DictData] = None,
724
- ) -> Self:
725
- raise NotImplementedError(
726
- "Audit should implement `find_audit_with_release` class-method"
727
- )
728
-
729
- def do_before(self) -> None: # pragma: no cov
730
- """To something before end up of initial log model."""
731
-
732
- @abstractmethod
733
- def save(self, excluded: Optional[list[str]]) -> None: # pragma: no cov
734
- """Save this model logging to target logging store."""
735
- raise NotImplementedError("Audit should implement ``save`` method.")
736
-
737
-
738
- class FileAudit(BaseAudit):
739
- """File Audit Pydantic Model that use to saving log data from result of
740
- workflow execution. It inherits from BaseAudit model that implement the
741
- ``self.save`` method for file.
742
- """
743
-
744
- filename_fmt: ClassVar[str] = (
745
- "workflow={name}/release={release:%Y%m%d%H%M%S}"
746
- )
747
-
748
- def do_before(self) -> None:
749
- """Create directory of release before saving log file."""
750
- self.pointer().mkdir(parents=True, exist_ok=True)
751
-
752
- @classmethod
753
- def find_audits(
754
- cls, name: str, *, extras: Optional[DictData] = None
755
- ) -> Iterator[Self]:
756
- """Generate the audit data that found from logs path with specific a
757
- workflow name.
758
-
759
- :param name: A workflow name that want to search release logging data.
760
- :param extras: An extra parameter that want to override core config.
761
-
762
- :rtype: Iterator[Self]
763
- """
764
- pointer: Path = (
765
- dynamic("audit_path", extras=extras) / f"workflow={name}"
766
- )
767
- if not pointer.exists():
768
- raise FileNotFoundError(f"Pointer: {pointer.absolute()}.")
769
-
770
- for file in pointer.glob("./release=*/*.log"):
771
- with file.open(mode="r", encoding="utf-8") as f:
772
- yield cls.model_validate(obj=json.load(f))
773
-
774
- @classmethod
775
- def find_audit_with_release(
776
- cls,
777
- name: str,
778
- release: Optional[datetime] = None,
779
- *,
780
- extras: Optional[DictData] = None,
781
- ) -> Self:
782
- """Return the audit data that found from logs path with specific
783
- workflow name and release values. If a release does not pass to an input
784
- argument, it will return the latest release from the current log path.
785
-
786
- :param name: (str) A workflow name that want to search log.
787
- :param release: (datetime) A release datetime that want to search log.
788
- :param extras: An extra parameter that want to override core config.
789
-
790
- :raise FileNotFoundError:
791
- :raise NotImplementedError: If an input release does not pass to this
792
- method. Because this method does not implement latest log.
793
-
794
- :rtype: Self
795
- """
796
- if release is None:
797
- raise NotImplementedError("Find latest log does not implement yet.")
798
-
799
- pointer: Path = (
800
- dynamic("audit_path", extras=extras)
801
- / f"workflow={name}/release={release:%Y%m%d%H%M%S}"
802
- )
803
- if not pointer.exists():
804
- raise FileNotFoundError(
805
- f"Pointer: ./logs/workflow={name}/"
806
- f"release={release:%Y%m%d%H%M%S} does not found."
807
- )
808
-
809
- latest_file: Path = max(pointer.glob("./*.log"), key=os.path.getctime)
810
- with latest_file.open(mode="r", encoding="utf-8") as f:
811
- return cls.model_validate(obj=json.load(f))
812
-
813
- @classmethod
814
- def is_pointed(
815
- cls,
816
- name: str,
817
- release: datetime,
818
- *,
819
- extras: Optional[DictData] = None,
820
- ) -> bool:
821
- """Check the release log already pointed or created at the destination
822
- log path.
823
-
824
- :param name: (str) A workflow name.
825
- :param release: (datetime) A release datetime.
826
- :param extras: An extra parameter that want to override core config.
827
-
828
- :rtype: bool
829
- :return: Return False if the release log was not pointed or created.
830
- """
831
- # NOTE: Return False if enable writing log flag does not set.
832
- if not dynamic("enable_write_audit", extras=extras):
833
- return False
834
-
835
- # NOTE: create pointer path that use the same logic of pointer method.
836
- pointer: Path = dynamic(
837
- "audit_path", extras=extras
838
- ) / cls.filename_fmt.format(name=name, release=release)
839
-
840
- return pointer.exists()
841
-
842
- def pointer(self) -> Path:
843
- """Return release directory path that was generated from model data.
844
-
845
- :rtype: Path
846
- """
847
- return dynamic(
848
- "audit_path", extras=self.extras
849
- ) / self.filename_fmt.format(name=self.name, release=self.release)
850
-
851
- def save(self, excluded: Optional[list[str]] = None) -> Self:
852
- """Save logging data that receive a context data from a workflow
853
- execution result.
854
-
855
- :param excluded: An excluded list of key name that want to pass in the
856
- model_dump method.
857
-
858
- :rtype: Self
859
- """
860
- trace: TraceModel = get_trace(
861
- self.run_id,
862
- parent_run_id=self.parent_run_id,
863
- extras=self.extras,
864
- )
865
-
866
- # NOTE: Check environ variable was set for real writing.
867
- if not dynamic("enable_write_audit", extras=self.extras):
868
- trace.debug("[AUDIT]: Skip writing log cause config was set")
869
- return self
870
-
871
- log_file: Path = (
872
- self.pointer() / f"{self.parent_run_id or self.run_id}.log"
873
- )
874
- log_file.write_text(
875
- json.dumps(
876
- self.model_dump(exclude=excluded),
877
- default=str,
878
- indent=2,
879
- ),
880
- encoding="utf-8",
881
- )
882
- return self
883
-
884
-
885
- class SQLiteAudit(BaseAudit): # pragma: no cov
886
- """SQLite Audit Pydantic Model."""
887
-
888
- table_name: ClassVar[str] = "audits"
889
- schemas: ClassVar[
890
- str
891
- ] = """
892
- workflow str,
893
- release int,
894
- type str,
895
- context json,
896
- parent_run_id int,
897
- run_id int,
898
- update datetime
899
- primary key ( run_id )
900
- """
901
-
902
- @classmethod
903
- def is_pointed(
904
- cls,
905
- name: str,
906
- release: datetime,
907
- *,
908
- extras: Optional[DictData] = None,
909
- ) -> bool: ...
910
-
911
- @classmethod
912
- def find_audits(
913
- cls, name: str, *, extras: Optional[DictData] = None
914
- ) -> Iterator[Self]: ...
915
-
916
- @classmethod
917
- def find_audit_with_release(
918
- cls,
919
- name: str,
920
- release: Optional[datetime] = None,
921
- *,
922
- extras: Optional[DictData] = None,
923
- ) -> Self: ...
924
-
925
- def save(self, excluded: Optional[list[str]]) -> SQLiteAudit:
926
- """Save logging data that receive a context data from a workflow
927
- execution result.
928
- """
929
- trace: TraceModel = get_trace(
930
- self.run_id,
931
- parent_run_id=self.parent_run_id,
932
- extras=self.extras,
933
- )
934
-
935
- # NOTE: Check environ variable was set for real writing.
936
- if not dynamic("enable_write_audit", extras=self.extras):
937
- trace.debug("[AUDIT]: Skip writing log cause config was set")
938
- return self
939
-
940
- raise NotImplementedError("SQLiteAudit does not implement yet.")
941
-
942
-
943
- Audit = TypeVar("Audit", bound=BaseAudit)
944
- AuditModel = Union[
945
- FileAudit,
946
- SQLiteAudit,
947
- ]
948
-
949
-
950
- def get_audit(
951
- extras: Optional[DictData] = None,
952
- ) -> type[AuditModel]: # pragma: no cov
953
- """Get an audit class that dynamic base on the config audit path value.
954
-
955
- :param extras: An extra parameter that want to override the core config.
956
-
957
- :rtype: type[Audit]
958
- """
959
- if dynamic("audit_path", extras=extras).is_file():
960
- return SQLiteAudit
961
- return FileAudit