ddeutil-workflow 0.0.85__py3-none-any.whl → 0.0.86__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.
@@ -17,6 +17,7 @@ Functions:
17
17
  set_logging: Configure logger with custom formatting.
18
18
  get_trace: Factory function for trace instances.
19
19
  """
20
+ import contextlib
20
21
  import json
21
22
  import logging
22
23
  import os
@@ -38,6 +39,7 @@ from typing import (
38
39
  Optional,
39
40
  TypeVar,
40
41
  Union,
42
+ cast,
41
43
  )
42
44
  from zoneinfo import ZoneInfo
43
45
 
@@ -110,21 +112,23 @@ PrefixType = Literal[
110
112
  "audit",
111
113
  ]
112
114
  PREFIX_LOGS: Final[dict[str, dict]] = {
113
- "CALLER": {
115
+ "caller": {
114
116
  "emoji": "⚙️",
115
117
  "desc": "logs from any usage from custom caller function.",
116
118
  },
117
- "NESTED": {"emoji": "⛓️", "desc": "logs from stages module."},
118
- "STAGE": {"emoji": "🔗", "desc": "logs from stages module."},
119
- "JOB": {"emoji": "🏗", "desc": "logs from job module."},
120
- "WORKFLOW": {"emoji": "👟", "desc": "logs from workflow module."},
121
- "RELEASE": {"emoji": "📅", "desc": "logs from release workflow method."},
122
- "SCHEDULE": {"emoji": "⏰", "desc": "logs from poke workflow method."},
123
- "AUDIT": {"emoji": "📌", "desc": "logs from audit model."},
119
+ "nested": {"emoji": "⛓️", "desc": "logs from stages module."},
120
+ "stage": {"emoji": "🔗", "desc": "logs from stages module."},
121
+ "job": {"emoji": "🏗", "desc": "logs from job module."},
122
+ "workflow": {"emoji": "👟", "desc": "logs from workflow module."},
123
+ "release": {"emoji": "📅", "desc": "logs from release workflow method."},
124
+ "schedule": {"emoji": "⏰", "desc": "logs from poke workflow method."},
125
+ "audit": {"emoji": "📌", "desc": "logs from audit model."},
124
126
  } # pragma: no cov
125
- PREFIX_DEFAULT: Final[str] = "CALLER"
127
+ PREFIX_LOGS_UPPER: Final[Iterator[str]] = (p.upper() for p in PREFIX_LOGS)
128
+ PREFIX_DEFAULT: Final[Literal["caller"]] = "caller"
129
+ PREFIX_EMOJI_DEFAULT: Final[str] = "⚙️"
126
130
  PREFIX_LOGS_REGEX: Final[re.Pattern[str]] = re.compile(
127
- rf"(^\[(?P<name>{'|'.join(PREFIX_LOGS)})]:\s?)?(?P<message>.*)",
131
+ rf"(^\[(?P<module>{'|'.join(PREFIX_LOGS_UPPER)})]:\s?)?(?P<message>.*)",
128
132
  re.MULTILINE | re.DOTALL | re.ASCII | re.VERBOSE,
129
133
  ) # pragma: no cov
130
134
 
@@ -136,42 +140,52 @@ class Message(BaseModel):
136
140
  with emoji support and categorization.
137
141
  """
138
142
 
139
- name: Optional[str] = Field(
140
- default=None, description="A prefix name of message."
143
+ module: Optional[PrefixType] = Field(
144
+ default=None,
145
+ description="A prefix module of message it allow to be None.",
141
146
  )
142
147
  message: Optional[str] = Field(default=None, description="A message.")
143
148
 
149
+ @field_validator("module", mode="before", json_schema_input_type=str)
150
+ def __prepare_module(cls, data: Optional[str]) -> Optional[str]:
151
+ return data.lower() if data is not None else data
152
+
144
153
  @classmethod
145
- def from_str(cls, msg: str) -> Self:
154
+ def from_str(cls, msg: str, module: Optional[PrefixType] = None) -> Self:
146
155
  """Extract message prefix from an input message.
147
156
 
148
157
  Args:
149
- msg: A message that want to extract.
158
+ msg (str): A message that want to extract.
159
+ module (PrefixType, default None): A prefix module type.
150
160
 
151
161
  Returns:
152
162
  Message: The validated model from a string message.
153
163
  """
154
- return Message.model_validate(
155
- obj=PREFIX_LOGS_REGEX.search(msg).groupdict()
156
- )
164
+ msg = cls.model_validate(PREFIX_LOGS_REGEX.search(msg).groupdict())
165
+ if msg.module is None and module:
166
+ msg.module = module
167
+ return msg
157
168
 
158
169
  def prepare(self, extras: Optional[DictData] = None) -> str:
159
170
  """Prepare message with force add prefix before writing trace log.
160
171
 
161
172
  Args:
162
- extras: An extra parameter that want to get the
163
- `log_add_emoji` flag.
173
+ extras (DictData, default None): An extra parameter that want to
174
+ get the `log_add_emoji` flag.
164
175
 
165
176
  Returns:
166
177
  str: The prepared message with prefix and optional emoji.
167
178
  """
168
- name: str = self.name or PREFIX_DEFAULT
179
+ module = cast(PrefixType, self.module or PREFIX_DEFAULT)
180
+ module_data: dict[str, str] = PREFIX_LOGS.get(
181
+ module, {"emoji": PREFIX_EMOJI_DEFAULT}
182
+ )
169
183
  emoji: str = (
170
- f"{PREFIX_LOGS[name]['emoji']} "
184
+ f"{module_data['emoji']} "
171
185
  if (extras or {}).get("log_add_emoji", True)
172
186
  else ""
173
187
  )
174
- return f"{emoji}[{name}]: {self.message}"
188
+ return f"{emoji}[{module.upper()}]: {self.message}"
175
189
 
176
190
 
177
191
  class Metric(BaseModel): # pragma: no cov
@@ -195,6 +209,9 @@ class Metadata(BaseModel): # pragma: no cov
195
209
  )
196
210
  process: int = Field(description="A process ID.")
197
211
  thread: int = Field(description="A thread ID.")
212
+ module: Optional[PrefixType] = Field(
213
+ default=None, description="A prefix module type."
214
+ )
198
215
  message: str = Field(description="A message log.")
199
216
  cut_id: Optional[str] = Field(
200
217
  default=None, description="A cutting of running ID."
@@ -274,6 +291,7 @@ class Metadata(BaseModel): # pragma: no cov
274
291
  parent_run_id: Optional[str],
275
292
  *,
276
293
  metric: Optional[DictData] = None,
294
+ module: Optional[PrefixType] = None,
277
295
  extras: Optional[DictData] = None,
278
296
  ) -> Self:
279
297
  """Make the current metric for contract this Metadata model instance.
@@ -289,6 +307,7 @@ class Metadata(BaseModel): # pragma: no cov
289
307
  run_id:
290
308
  parent_run_id:
291
309
  metric:
310
+ module:
292
311
  extras: An extra parameter that want to override core
293
312
  config values.
294
313
 
@@ -333,6 +352,7 @@ class Metadata(BaseModel): # pragma: no cov
333
352
  ),
334
353
  process=os.getpid(),
335
354
  thread=get_ident(),
355
+ module=module,
336
356
  message=message,
337
357
  cut_id=cutting_id,
338
358
  run_id=run_id,
@@ -1679,8 +1699,8 @@ class BaseEmit(ABC):
1679
1699
  msg: str,
1680
1700
  level: Level,
1681
1701
  *,
1682
- module: Optional[str] = None,
1683
1702
  metric: Optional[DictData] = None,
1703
+ module: Optional[PrefixType] = None,
1684
1704
  ) -> None:
1685
1705
  """Write trace log with append mode and logging this message with any
1686
1706
  logging level.
@@ -1690,62 +1710,73 @@ class BaseEmit(ABC):
1690
1710
  level: A logging level.
1691
1711
  metric (DictData, default None): A metric data that want to export
1692
1712
  to each target handler.
1693
- module (str, default None): A module name that use for adding prefix
1694
- at the message value.
1713
+ module (PrefixType, default None): A module name that use for adding
1714
+ prefix at the message value.
1695
1715
  """
1696
1716
  raise NotImplementedError(
1697
1717
  "Emit action should be implement for making trace log."
1698
1718
  )
1699
1719
 
1700
- def debug(self, msg: str, module: Optional[str] = None):
1720
+ def debug(self, msg: str, module: Optional[PrefixType] = None):
1701
1721
  """Write trace log with append mode and logging this message with the
1702
1722
  DEBUG level.
1703
1723
 
1704
1724
  Args:
1705
1725
  msg: A message that want to log.
1706
- module (str, default None): A module name that use for adding prefix
1707
- at the message value.
1726
+ module (PrefixType, default None): A module name that use for adding
1727
+ prefix at the message value.
1708
1728
  """
1709
1729
  self.emit(msg, level="debug", module=module)
1710
1730
 
1711
- def info(self, msg: str) -> None:
1731
+ def info(self, msg: str, module: Optional[PrefixType] = None) -> None:
1712
1732
  """Write trace log with append mode and logging this message with the
1713
1733
  INFO level.
1714
1734
 
1715
1735
  Args:
1716
1736
  msg: A message that want to log.
1737
+ module (PrefixType, default None): A module name that use for adding
1738
+ prefix at the message value.
1717
1739
  """
1718
- self.emit(msg, level="info")
1740
+ self.emit(msg, level="info", module=module)
1719
1741
 
1720
- def warning(self, msg: str) -> None:
1742
+ def warning(self, msg: str, module: Optional[PrefixType] = None) -> None:
1721
1743
  """Write trace log with append mode and logging this message with the
1722
1744
  WARNING level.
1723
1745
 
1724
1746
  Args:
1725
1747
  msg: A message that want to log.
1748
+ module (PrefixType, default None): A module name that use for adding
1749
+ prefix at the message value.
1726
1750
  """
1727
- self.emit(msg, level="warning")
1751
+ self.emit(msg, level="warning", module=module)
1728
1752
 
1729
- def error(self, msg: str) -> None:
1753
+ def error(self, msg: str, module: Optional[PrefixType] = None) -> None:
1730
1754
  """Write trace log with append mode and logging this message with the
1731
1755
  ERROR level.
1732
1756
 
1733
1757
  Args:
1734
1758
  msg: A message that want to log.
1759
+ module (PrefixType, default None): A module name that use for adding
1760
+ prefix at the message value.
1735
1761
  """
1736
- self.emit(msg, level="error")
1762
+ self.emit(msg, level="error", module=module)
1737
1763
 
1738
- def exception(self, msg: str) -> None:
1764
+ def exception(self, msg: str, module: Optional[PrefixType] = None) -> None:
1739
1765
  """Write trace log with append mode and logging this message with the
1740
1766
  EXCEPTION level.
1741
1767
 
1742
1768
  Args:
1743
1769
  msg: A message that want to log.
1770
+ module (PrefixType, default None): A module name that use for adding
1771
+ prefix at the message value.
1744
1772
  """
1745
- self.emit(msg, level="exception")
1773
+ self.emit(msg, level="exception", module=module)
1746
1774
 
1747
1775
 
1748
1776
  class BaseAsyncEmit(ABC):
1777
+ """Base Async Emit Abstract class for mixin `amit` method and async
1778
+ logging that will use prefix with `a` charactor.
1779
+ """
1749
1780
 
1750
1781
  @abstractmethod
1751
1782
  async def amit(
@@ -1754,6 +1785,7 @@ class BaseAsyncEmit(ABC):
1754
1785
  level: Level,
1755
1786
  *,
1756
1787
  metric: Optional[DictData] = None,
1788
+ module: Optional[PrefixType] = None,
1757
1789
  ) -> None:
1758
1790
  """Async write trace log with append mode and logging this message with
1759
1791
  any logging level.
@@ -1763,55 +1795,77 @@ class BaseAsyncEmit(ABC):
1763
1795
  level (Mode): A logging level.
1764
1796
  metric (DictData, default None): A metric data that want to export
1765
1797
  to each target handler.
1798
+ module (PrefixType, default None): A module name that use for adding
1799
+ prefix at the message value.
1766
1800
  """
1767
1801
  raise NotImplementedError(
1768
1802
  "Async Logging action should be implement for making trace log."
1769
1803
  )
1770
1804
 
1771
- async def adebug(self, msg: str) -> None: # pragma: no cov
1805
+ async def adebug(
1806
+ self, msg: str, module: Optional[PrefixType] = None
1807
+ ) -> None: # pragma: no cov
1772
1808
  """Async write trace log with append mode and logging this message with
1773
1809
  the DEBUG level.
1774
1810
 
1775
1811
  Args:
1776
1812
  msg: A message that want to log.
1813
+ module (PrefixType, default None): A module name that use for adding
1814
+ prefix at the message value.
1777
1815
  """
1778
- await self.amit(msg, level="debug")
1816
+ await self.amit(msg, level="debug", module=module)
1779
1817
 
1780
- async def ainfo(self, msg: str) -> None: # pragma: no cov
1818
+ async def ainfo(
1819
+ self, msg: str, module: Optional[PrefixType] = None
1820
+ ) -> None: # pragma: no cov
1781
1821
  """Async write trace log with append mode and logging this message with
1782
1822
  the INFO level.
1783
1823
 
1784
1824
  Args:
1785
1825
  msg: A message that want to log.
1826
+ module (PrefixType, default None): A module name that use for adding
1827
+ prefix at the message value.
1786
1828
  """
1787
- await self.amit(msg, level="info")
1829
+ await self.amit(msg, level="info", module=module)
1788
1830
 
1789
- async def awarning(self, msg: str) -> None: # pragma: no cov
1831
+ async def awarning(
1832
+ self, msg: str, module: Optional[PrefixType] = None
1833
+ ) -> None: # pragma: no cov
1790
1834
  """Async write trace log with append mode and logging this message with
1791
1835
  the WARNING level.
1792
1836
 
1793
1837
  Args:
1794
1838
  msg: A message that want to log.
1839
+ module (PrefixType, default None): A module name that use for adding
1840
+ prefix at the message value.
1795
1841
  """
1796
- await self.amit(msg, level="warning")
1842
+ await self.amit(msg, level="warning", module=module)
1797
1843
 
1798
- async def aerror(self, msg: str) -> None: # pragma: no cov
1844
+ async def aerror(
1845
+ self, msg: str, module: Optional[PrefixType] = None
1846
+ ) -> None: # pragma: no cov
1799
1847
  """Async write trace log with append mode and logging this message with
1800
1848
  the ERROR level.
1801
1849
 
1802
1850
  Args:
1803
1851
  msg: A message that want to log.
1852
+ module (PrefixType, default None): A module name that use for adding
1853
+ prefix at the message value.
1804
1854
  """
1805
- await self.amit(msg, level="error")
1855
+ await self.amit(msg, level="error", module=module)
1806
1856
 
1807
- async def aexception(self, msg: str) -> None: # pragma: no cov
1857
+ async def aexception(
1858
+ self, msg: str, module: Optional[PrefixType] = None
1859
+ ) -> None: # pragma: no cov
1808
1860
  """Async write trace log with append mode and logging this message with
1809
1861
  the EXCEPTION level.
1810
1862
 
1811
1863
  Args:
1812
1864
  msg: A message that want to log.
1865
+ module (PrefixType, default None): A module name that use for adding
1866
+ prefix at the message value.
1813
1867
  """
1814
- await self.amit(msg, level="exception")
1868
+ await self.amit(msg, level="exception", module=module)
1815
1869
 
1816
1870
 
1817
1871
  class Trace(BaseModel, BaseEmit, BaseAsyncEmit):
@@ -1857,24 +1911,13 @@ class Trace(BaseModel, BaseEmit, BaseAsyncEmit):
1857
1911
  cut_parent_run_id: str = cut_id(self.parent_run_id)
1858
1912
  return f"{cut_parent_run_id} -> {cut_run_id}"
1859
1913
 
1860
- def make_message(self, msg: str) -> str:
1861
- """Prepare and Make a message before write and log steps.
1862
-
1863
- Args:
1864
- msg: A message that want to prepare and make before.
1865
-
1866
- Returns:
1867
- str: The prepared message.
1868
- """
1869
- return prepare_newline(Message.from_str(msg).prepare(self.extras))
1870
-
1871
1914
  def emit(
1872
1915
  self,
1873
1916
  msg: str,
1874
1917
  level: Level,
1875
1918
  *,
1876
- module: Optional[str] = None,
1877
1919
  metric: Optional[DictData] = None,
1920
+ module: Optional[PrefixType] = None,
1878
1921
  ) -> None:
1879
1922
  """Emit a trace log to all handler. This will use synchronise process.
1880
1923
 
@@ -1883,14 +1926,15 @@ class Trace(BaseModel, BaseEmit, BaseAsyncEmit):
1883
1926
  level (Level): A tracing level.
1884
1927
  metric (DictData, default None): A metric data that want to export
1885
1928
  to each target handler.
1886
- module (str, default None): A module name that use for adding prefix
1887
- at the message value.
1929
+ module (PrefixType, default None): A module name that use for adding
1930
+ prefix at the message value.
1888
1931
  """
1889
- _msg: str = self.make_message(msg)
1932
+ _msg: Message = Message.from_str(msg, module=module)
1890
1933
  metadata: Metadata = Metadata.make(
1891
1934
  error_flag=(level in ("error", "exception")),
1892
1935
  level=level,
1893
- message=_msg,
1936
+ module=_msg.module,
1937
+ message=prepare_newline(_msg.prepare(self.extras)),
1894
1938
  cutting_id=self.cut_id,
1895
1939
  run_id=self.run_id,
1896
1940
  parent_run_id=self.parent_run_id,
@@ -1915,7 +1959,12 @@ class Trace(BaseModel, BaseEmit, BaseAsyncEmit):
1915
1959
  self._buffer.clear()
1916
1960
 
1917
1961
  async def amit(
1918
- self, msg: str, level: Level, *, metric: Optional[DictData] = None
1962
+ self,
1963
+ msg: str,
1964
+ level: Level,
1965
+ *,
1966
+ metric: Optional[DictData] = None,
1967
+ module: Optional[PrefixType] = None,
1919
1968
  ) -> None:
1920
1969
  """Async write trace log with append mode and logging this message with
1921
1970
  any logging level.
@@ -1925,12 +1974,15 @@ class Trace(BaseModel, BaseEmit, BaseAsyncEmit):
1925
1974
  level (Level): A logging mode.
1926
1975
  metric (DictData, default None): A metric data that want to export
1927
1976
  to each target handler.
1977
+ module (PrefixType, default None): A module name that use for adding
1978
+ prefix at the message value.
1928
1979
  """
1929
- _msg: str = self.make_message(msg)
1980
+ _msg: Message = Message.from_str(msg, module=module)
1930
1981
  metadata: Metadata = Metadata.make(
1931
1982
  error_flag=(level in ("error", "exception")),
1932
1983
  level=level,
1933
- message=_msg,
1984
+ module=_msg.module,
1985
+ message=prepare_newline(_msg.prepare(self.extras)),
1934
1986
  cutting_id=self.cut_id,
1935
1987
  run_id=self.run_id,
1936
1988
  parent_run_id=self.parent_run_id,
@@ -1942,35 +1994,40 @@ class Trace(BaseModel, BaseEmit, BaseAsyncEmit):
1942
1994
  for handler in self.handlers:
1943
1995
  await handler.amit(metadata, extra=self.extras)
1944
1996
 
1945
- def __enter__(self):
1997
+ @contextlib.contextmanager
1998
+ def buffer(self, module: Optional[PrefixType] = None) -> Iterator[Self]:
1946
1999
  """Enter the trace for catching the logs that run so fast. It will use
1947
2000
  buffer strategy to flush the logs instead emit.
2001
+
2002
+ Args:
2003
+ module (PrefixType, default None): A module name that use for adding
2004
+ prefix at the message value.
2005
+
2006
+ Yields:
2007
+ Self: Itself instance.
1948
2008
  """
1949
2009
  self._enable_buffer = True
1950
- return self
1951
-
1952
- def __exit__(self, exc_type, exc_val, exc_tb):
1953
- """Exit the trace that will clear all log in the buffer."""
1954
- if exc_type:
1955
- _msg: str = self.make_message(str(exc_val))
2010
+ try:
2011
+ yield self
2012
+ except Exception as err:
2013
+ _msg: Message = Message.from_str(str(err), module=module)
1956
2014
  metadata: Metadata = Metadata.make(
1957
2015
  error_flag=True,
1958
2016
  level="error",
1959
- message=_msg,
2017
+ module=_msg.module,
2018
+ message=prepare_newline(_msg.prepare(self.extras)),
1960
2019
  cutting_id=self.cut_id,
1961
2020
  run_id=self.run_id,
1962
2021
  parent_run_id=self.parent_run_id,
1963
2022
  extras=self.extras,
1964
2023
  )
1965
2024
  self._buffer.append(metadata)
1966
-
1967
- if self._buffer:
1968
- for handler in self.handlers:
1969
- handler.flush(self._buffer, extra=self.extras)
1970
- self._buffer.clear()
1971
-
1972
- # NOTE: Re-raise the exception if one occurred
1973
- return False
2025
+ raise
2026
+ finally:
2027
+ if self._buffer:
2028
+ for handler in self.handlers:
2029
+ handler.flush(self._buffer, extra=self.extras)
2030
+ self._buffer.clear()
1974
2031
 
1975
2032
 
1976
2033
  def get_trace(
@@ -1979,23 +2036,24 @@ def get_trace(
1979
2036
  handlers: list[Union[DictData, Handler]] = None,
1980
2037
  parent_run_id: Optional[str] = None,
1981
2038
  extras: Optional[DictData] = None,
1982
- auto_pre_process: bool = True,
2039
+ pre_process: bool = False,
1983
2040
  ) -> Trace:
1984
- """Get dynamic Trace instance from the core config.
2041
+ """Get dynamic Trace instance from the core config. This function will use
2042
+ for start some process, and it wants to generated trace object.
1985
2043
 
1986
- This factory function returns the appropriate trace implementation based on
1987
- configuration. It can be overridden by extras argument and accepts running ID
1988
- and parent running ID.
2044
+ This factory function returns the appropriate trace implementation based
2045
+ on configuration. It can be overridden by extras argument and accepts
2046
+ running ID and parent running ID.
1989
2047
 
1990
2048
  Args:
1991
2049
  run_id (str): A running ID.
1992
- parent_run_id (str | None, default None): A parent running ID.
2050
+ parent_run_id (str, default None): A parent running ID.
1993
2051
  handlers (list[DictData | Handler], default None): A list of handler or
1994
2052
  mapping of handler data that want to direct pass instead use
1995
2053
  environment variable config.
1996
2054
  extras (DictData, default None): An extra parameter that want to
1997
2055
  override the core config values.
1998
- auto_pre_process (bool, default False) A flag that will auto call pre
2056
+ pre_process (bool, default False) A flag that will auto call pre
1999
2057
  method after validate a trace model.
2000
2058
 
2001
2059
  Returns:
@@ -2010,7 +2068,7 @@ def get_trace(
2010
2068
  }
2011
2069
  )
2012
2070
  # NOTE: Start pre-process when start create trace.
2013
- if auto_pre_process:
2071
+ if pre_process:
2014
2072
  for handler in trace.handlers:
2015
2073
  handler.pre()
2016
2074
  return trace