logger-36 2025.13__py3-none-any.whl → 2025.14__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.
@@ -8,7 +8,6 @@ import logging as l
8
8
  import sys as s
9
9
  import typing as h
10
10
 
11
- from logger_36.constant.record import SHOW_W_RULE_ATTR
12
11
  from logger_36.task.format.rule import RuleAsText
13
12
  from logger_36.type.handler import handler_t as base_t
14
13
 
@@ -26,29 +25,25 @@ class console_handler_t(base_t):
26
25
  message_width: int = -1,
27
26
  level: int = l.NOTSET,
28
27
  formatter: l.Formatter | None = None,
29
- **unused,
28
+ **_,
30
29
  ) -> h.Self:
31
30
  """"""
32
31
  return cls(name, should_store_memory_usage, message_width, level, formatter)
33
32
 
34
33
  def emit(self, record: l.LogRecord, /) -> None:
35
34
  """"""
36
- if hasattr(record, SHOW_W_RULE_ATTR):
37
- message = RuleAsText(record.msg)
38
- else:
39
- message = self.MessageFromRecord(record, line_width=self.message_width)
40
- s.__stdout__.write(message + "\n")
35
+ message = self.MessageFromRecord(
36
+ record, RuleAsText, line_width=self.message_width
37
+ )
38
+ s.__stdout__.write(message[0] + "\n")
41
39
 
42
40
  def LogAsIs(self, message: str, /) -> None:
43
- """
44
- See documentation of
45
- logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
46
- """
41
+ """"""
47
42
  s.__stdout__.write(message + "\n")
48
43
 
49
- def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
44
+ def DisplayRule(self, /, *, text: str | None = None, color: str = "black") -> None:
50
45
  """"""
51
- self.LogAsIs(RuleAsText(text))
46
+ self.LogAsIs(RuleAsText(text, None))
52
47
 
53
48
 
54
49
  """
@@ -25,7 +25,6 @@ from logger_36.catalog.config.console_rich import (
25
25
  )
26
26
  from logger_36.config.message import ACTUAL_PATTERNS, EXPECTED_PATTERNS, WHERE_SEPARATOR
27
27
  from logger_36.constant.message import CONTEXT_LENGTH
28
- from logger_36.constant.record import SHOW_W_RULE_ATTR
29
28
  from logger_36.task.format.rule import Rule
30
29
  from logger_36.type.handler import handler_t as base_t
31
30
 
@@ -53,33 +52,36 @@ class console_rich_handler_t(base_t):
53
52
 
54
53
  kind: h.ClassVar[str] = "c"
55
54
 
56
- @classmethod
57
- def New(
58
- cls,
59
- /,
60
- *,
61
- name: str | None = None,
62
- should_store_memory_usage: bool = False,
63
- message_width: int = -1,
64
- level: int = l.NOTSET,
65
- formatter: l.Formatter | None = None,
66
- **kwargs,
67
- ) -> h.Self:
55
+ def __init__(
56
+ self,
57
+ name: str | None,
58
+ should_store_memory_usage: bool,
59
+ message_width: int,
60
+ level: int,
61
+ formatter: l.Formatter | None,
62
+ kwargs,
63
+ ) -> None:
68
64
  """"""
69
65
  alternating_logs = kwargs.pop("alternating_logs", 0)
70
66
  should_install_traceback = kwargs.pop("should_install_traceback", False)
71
67
 
72
68
  assert alternating_logs in (0, 1, 2)
73
69
 
74
- output = cls(name, should_store_memory_usage, message_width, level, formatter)
70
+ base_t.__init__(
71
+ self,
72
+ name,
73
+ should_store_memory_usage,
74
+ message_width,
75
+ level,
76
+ formatter,
77
+ kwargs,
78
+ )
75
79
 
76
- output.console = None # console_t | None.
77
- output.alternating_logs = alternating_logs
78
- output.log_parity = False
80
+ self.console = None # console_t | None.
81
+ self.alternating_logs = alternating_logs
82
+ self._log_parity = False
79
83
 
80
- output.__post_init_local__(should_install_traceback, **kwargs)
81
-
82
- return output
84
+ self.__post_init_local__(should_install_traceback, **kwargs)
83
85
 
84
86
  def __post_init_local__(self, should_install_traceback: bool, **kwargs) -> None:
85
87
  """"""
@@ -97,32 +99,48 @@ class console_rich_handler_t(base_t):
97
99
  traceback_kwargs["console"] = self.console
98
100
  InstallTracebackHandler(**traceback_kwargs)
99
101
 
102
+ @classmethod
103
+ def New(
104
+ cls,
105
+ /,
106
+ *,
107
+ name: str | None = None,
108
+ should_store_memory_usage: bool = False,
109
+ message_width: int = -1,
110
+ level: int = l.NOTSET,
111
+ formatter: l.Formatter | None = None,
112
+ **kwargs,
113
+ ) -> h.Self:
114
+ """"""
115
+ return cls(
116
+ name, should_store_memory_usage, message_width, level, formatter, kwargs
117
+ )
118
+
100
119
  def emit(self, record: l.LogRecord, /) -> None:
101
120
  """"""
102
- if hasattr(record, SHOW_W_RULE_ATTR):
103
- richer = Rule(record.msg, DATE_TIME_COLOR)
104
- else:
105
- message = self.MessageFromRecord(
106
- record, line_width=self.message_width, PreProcessed=EscapedVersion
107
- )
108
- richer = HighlightedVersion(
121
+ message, is_not_a_rule = self.MessageFromRecord(
122
+ record,
123
+ Rule,
124
+ line_width=self.message_width,
125
+ color=DATE_TIME_COLOR,
126
+ PreProcessed=EscapedVersion,
127
+ )
128
+ if is_not_a_rule:
129
+ message = HighlightedVersion(
109
130
  self.console,
110
131
  message,
111
132
  record.levelno,
112
133
  self.alternating_logs,
113
- self.log_parity,
134
+ self._log_parity,
114
135
  )
115
- self.console.print(richer, crop=False, overflow="ignore")
116
- self.log_parity = not self.log_parity
136
+ self.console.print(message, crop=False, overflow="ignore")
137
+ self._log_parity = not self._log_parity
117
138
 
118
139
  def LogAsIs(self, message: str | renderable_t, /) -> None:
119
- """
120
- See documentation of
121
- logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
122
- """
140
+ """"""
123
141
  self.console.print(message, crop=False, overflow="ignore")
124
142
 
125
- def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
143
+ def DisplayRule(self, /, *, text: str | None = None, color: str = "black") -> None:
126
144
  """"""
127
145
  self.LogAsIs(Rule(text, color))
128
146
 
@@ -8,7 +8,6 @@ import logging as l
8
8
  import typing as h
9
9
  from pathlib import Path as path_t
10
10
 
11
- from logger_36.constant.record import SHOW_W_RULE_ATTR
12
11
  from logger_36.task.format.rule import RuleAsText
13
12
  from logger_36.type.handler import file_handler_t as base_t
14
13
 
@@ -27,7 +26,7 @@ class file_handler_t(base_t):
27
26
  level: int = l.NOTSET,
28
27
  formatter: l.Formatter | None = None,
29
28
  path: str | path_t | None = None,
30
- **unused,
29
+ **_,
31
30
  ) -> h.Self:
32
31
  """"""
33
32
  return cls(
@@ -36,24 +35,20 @@ class file_handler_t(base_t):
36
35
 
37
36
  def emit(self, record: l.LogRecord, /) -> None:
38
37
  """"""
39
- if hasattr(record, SHOW_W_RULE_ATTR):
40
- message = RuleAsText(record.msg)
41
- else:
42
- message = self.MessageFromRecord(record, line_width=self.message_width)
43
- self.stream.write(message + "\n")
38
+ message = self.MessageFromRecord(
39
+ record, RuleAsText, line_width=self.message_width
40
+ )
41
+ self.stream.write(message[0] + "\n")
44
42
  self.stream.flush()
45
43
 
46
44
  def LogAsIs(self, message: str, /) -> None:
47
- """
48
- See documentation of
49
- logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
50
- """
45
+ """"""
51
46
  self.stream.write(message + "\n")
52
47
  self.stream.flush()
53
48
 
54
- def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
49
+ def DisplayRule(self, /, *, text: str | None = None, color: str = "black") -> None:
55
50
  """"""
56
- self.LogAsIs(RuleAsText(text))
51
+ self.LogAsIs(RuleAsText(text, None))
57
52
 
58
53
 
59
54
  """
@@ -22,8 +22,7 @@ else:
22
22
  EscapedForRich
23
23
  ) = DEFAULT_TERMINAL_THEME = None
24
24
 
25
- from logger_36.constant.record import SHOW_W_RULE_ATTR
26
- from logger_36.task.format.rule import Rule, RuleAsText
25
+ from logger_36.task.format.rule import Rule, RuleAsText, rule_t
27
26
  from logger_36.type.handler import handler_t as base_t
28
27
 
29
28
  LogAsIs_h = h.Callable[[str | h.Any], None]
@@ -54,18 +53,15 @@ class generic_handler_t(base_t):
54
53
 
55
54
  kind: h.ClassVar[str] = "g"
56
55
 
57
- @classmethod
58
- def New(
59
- cls,
60
- /,
61
- *,
62
- name: str | None = None,
63
- should_store_memory_usage: bool = False,
64
- message_width: int = -1,
65
- level: int = l.NOTSET,
66
- formatter: l.Formatter | None = None,
67
- **kwargs,
68
- ) -> h.Self:
56
+ def __init__(
57
+ self,
58
+ name: str | None,
59
+ should_store_memory_usage: bool,
60
+ message_width: int,
61
+ level: int,
62
+ formatter: l.Formatter | None,
63
+ kwargs,
64
+ ) -> None:
69
65
  """"""
70
66
  alternating_logs = kwargs.pop("alternating_logs", 0)
71
67
  LogAsIs = kwargs.pop("LogAsIs", lambda _, indented=False: print(_))
@@ -73,18 +69,24 @@ class generic_handler_t(base_t):
73
69
 
74
70
  assert alternating_logs in (0, 1, 2)
75
71
 
76
- output = cls(name, should_store_memory_usage, message_width, level, formatter)
77
-
78
- output.LogAsIs = LogAsIs
79
- output.console = None # console_t | None.
80
- output.console_options = None # console_options_t | None.
81
- output.alternating_logs = alternating_logs
82
- output.log_parity = False
83
- output.DisplayRule = None # DisplayRule_p | None.
84
-
85
- output.__post_init_local__(supports_html)
86
-
87
- return output
72
+ base_t.__init__(
73
+ self,
74
+ name,
75
+ should_store_memory_usage,
76
+ message_width,
77
+ level,
78
+ formatter,
79
+ kwargs,
80
+ )
81
+
82
+ self.LogAsIs = LogAsIs
83
+ self.DisplayRule = None # DisplayRule_p | None.
84
+ self.console = None # console_t | None.
85
+ self.console_options = None # console_options_t | None.
86
+ self.alternating_logs = alternating_logs
87
+ self._log_parity = False
88
+
89
+ self.__post_init_local__(supports_html)
88
90
 
89
91
  def __post_init_local__(self, supports_html: bool) -> None:
90
92
  """"""
@@ -97,28 +99,46 @@ class generic_handler_t(base_t):
97
99
  else:
98
100
  self.DisplayRule = self._DisplayRuleAsText
99
101
 
102
+ @classmethod
103
+ def New(
104
+ cls,
105
+ /,
106
+ *,
107
+ name: str | None = None,
108
+ should_store_memory_usage: bool = False,
109
+ message_width: int = -1,
110
+ level: int = l.NOTSET,
111
+ formatter: l.Formatter | None = None,
112
+ **kwargs,
113
+ ) -> h.Self:
114
+ """"""
115
+ return cls(
116
+ name, should_store_memory_usage, message_width, level, formatter, kwargs
117
+ )
118
+
100
119
  def emit(self, record: l.LogRecord, /) -> None:
101
120
  """"""
102
121
  if self.console is None:
103
- if hasattr(record, SHOW_W_RULE_ATTR):
104
- message = RuleAsText(record.msg)
105
- else:
106
- message = self.MessageFromRecord(record, line_width=self.message_width)
122
+ message = self.MessageFromRecord(
123
+ record, RuleAsText, line_width=self.message_width
124
+ )[0]
107
125
  else:
108
- if hasattr(record, SHOW_W_RULE_ATTR):
109
- richer = Rule(record.msg, DATE_TIME_COLOR)
110
- else:
111
- message = self.MessageFromRecord(
112
- record, line_width=self.message_width, PreProcessed=EscapedForRich
113
- )
114
- richer = HighlightedVersion(
126
+ message, is_not_a_rule = self.MessageFromRecord(
127
+ record,
128
+ Rule,
129
+ line_width=self.message_width,
130
+ color=DATE_TIME_COLOR,
131
+ PreProcessed=EscapedForRich,
132
+ )
133
+ if is_not_a_rule:
134
+ message = HighlightedVersion(
115
135
  self.console,
116
136
  message,
117
137
  record.levelno,
118
138
  self.alternating_logs,
119
- self.log_parity,
139
+ self._log_parity,
120
140
  )
121
- segments = self.console.render(richer, options=self.console_options)
141
+ segments = self.console.render(message, options=self.console_options)
122
142
 
123
143
  # Inspired from the code of: rich.console.export_html.
124
144
  html_segments = []
@@ -143,13 +163,13 @@ class generic_handler_t(base_t):
143
163
  )
144
164
 
145
165
  self.LogAsIs(message)
146
- self.log_parity = not self.log_parity
166
+ self._log_parity = not self._log_parity
147
167
 
148
168
  def _DisplayRuleAsText(
149
169
  self, /, *, text: str | None = None, color: str = "white"
150
170
  ) -> None:
151
171
  """"""
152
- self.LogAsIs(RuleAsText(text))
172
+ self.LogAsIs(RuleAsText(text, None))
153
173
 
154
174
  def _DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
155
175
  """"""
@@ -11,6 +11,7 @@ LEVEL_CLOSING = ")"
11
11
  MESSAGE_MARKER = "|"
12
12
  WHERE_SEPARATOR = "@"
13
13
  ELAPSED_TIME_SEPARATOR = "+"
14
+ FALLBACK_MESSAGE_WIDTH = 5
14
15
 
15
16
  DATE_FORMAT = "%Y-%m-%d"
16
17
  TIME_FORMAT = "%H:%M:%S"
@@ -0,0 +1,70 @@
1
+ """
2
+ Copyright CNRS/Inria/UniCA
3
+ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
+ SEE COPYRIGHT NOTICE BELOW
5
+ """
6
+
7
+ TITLE_PLACEHOLDER = "TITLE"
8
+ BODY_PLACEHOLDER = "BODY"
9
+
10
+ MINIMAL_HTML = f"""
11
+ <!DOCTYPE html>
12
+ <html lang="en">
13
+ <head>
14
+ <meta charset="utf-8">
15
+ <title>{TITLE_PLACEHOLDER}</title>
16
+ </head>
17
+ <body>
18
+ <pre>
19
+ {BODY_PLACEHOLDER}
20
+ </pre>
21
+ </body>
22
+ </html>
23
+ """
24
+
25
+
26
+ """
27
+ COPYRIGHT NOTICE
28
+
29
+ This software is governed by the CeCILL license under French law and
30
+ abiding by the rules of distribution of free software. You can use,
31
+ modify and/ or redistribute the software under the terms of the CeCILL
32
+ license as circulated by CEA, CNRS and INRIA at the following URL
33
+ "http://www.cecill.info".
34
+
35
+ As a counterpart to the access to the source code and rights to copy,
36
+ modify and redistribute granted by the license, users are provided only
37
+ with a limited warranty and the software's author, the holder of the
38
+ economic rights, and the successive licensors have only limited
39
+ liability.
40
+
41
+ In this respect, the user's attention is drawn to the risks associated
42
+ with loading, using, modifying and/or developing or reproducing the
43
+ software by the user in light of its specific status of free software,
44
+ that may mean that it is complicated to manipulate, and that also
45
+ therefore means that it is reserved for developers and experienced
46
+ professionals having in-depth computer knowledge. Users are therefore
47
+ encouraged to load and test the software's suitability as regards their
48
+ requirements in conditions enabling the security of their systems and/or
49
+ data to be ensured and, more generally, to use and operate it in the
50
+ same conditions as regards security.
51
+
52
+ The fact that you are presently reading this means that you have had
53
+ knowledge of the CeCILL license and that you accept its terms.
54
+
55
+ SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
56
+
57
+ This software is being developed by Eric Debreuve, a CNRS employee and
58
+ member of team Morpheme.
59
+ Team Morpheme is a joint team between Inria, CNRS, and UniCA.
60
+ It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
61
+ I3S, and Laboratory iBV.
62
+
63
+ CNRS: https://www.cnrs.fr/index.php/en
64
+ Inria: https://www.inria.fr/en/
65
+ UniCA: https://univ-cotedazur.eu/
66
+ Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
67
+ I3S: https://www.i3s.unice.fr/en/
68
+ iBV: http://ibv.unice.fr/
69
+ Team Morpheme: https://team.inria.fr/morpheme/
70
+ """
@@ -5,10 +5,69 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  import difflib as diff
8
+ import logging as l
8
9
  import typing as h
9
10
 
11
+ from logger_36.config.message import (
12
+ LEVEL_CLOSING,
13
+ LEVEL_OPENING,
14
+ MESSAGE_MARKER,
15
+ WHERE_SEPARATOR,
16
+ )
10
17
  from logger_36.constant.generic import NOT_PASSED
11
- from logger_36.constant.message import expected_op_h
18
+ from logger_36.constant.message import NEXT_LINE_PROLOGUE, expected_op_h
19
+ from logger_36.constant.record import SHOW_W_RULE_ATTR
20
+ from logger_36.extension.line import WrappedLines
21
+ from logger_36.type.message import RuleWithText_h
22
+
23
+
24
+ def MessageFromRecord(
25
+ record: l.LogRecord,
26
+ RuleWithText: RuleWithText_h,
27
+ /,
28
+ *,
29
+ line_width: int = 0,
30
+ color: str | None = None,
31
+ PreProcessed: h.Callable[[str], str] | None = None,
32
+ ) -> tuple[str, bool]:
33
+ """
34
+ See logger_36.catalog.handler.README.txt.
35
+
36
+ The second returned value is is_not_a_rule.
37
+ """
38
+ message = record.msg
39
+
40
+ if hasattr(record, SHOW_W_RULE_ATTR):
41
+ return RuleWithText(message, color), False
42
+
43
+ if PreProcessed is not None:
44
+ message = PreProcessed(message)
45
+ if (line_width <= 0) or (message.__len__() <= line_width):
46
+ if "\n" in message:
47
+ message = NEXT_LINE_PROLOGUE.join(message.splitlines())
48
+ else:
49
+ if "\n" in message:
50
+ lines = WrappedLines(message.splitlines(), line_width)
51
+ else:
52
+ lines = WrappedLines([message], line_width)
53
+ message = NEXT_LINE_PROLOGUE.join(lines)
54
+
55
+ when_or_elapsed = getattr(record, "when_or_elapsed", None)
56
+ if when_or_elapsed is None:
57
+ return message, True
58
+
59
+ level_first_letter = getattr(record, "level_first_letter", "")
60
+
61
+ if (where := getattr(record, "where", None)) is None:
62
+ where = ""
63
+ else:
64
+ where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
65
+
66
+ return (
67
+ f"{when_or_elapsed}"
68
+ f"{LEVEL_OPENING}{level_first_letter}{LEVEL_CLOSING} "
69
+ f"{MESSAGE_MARKER} {message}{where}"
70
+ ), True
12
71
 
13
72
 
14
73
  def MessageWithActualExpected(
@@ -7,7 +7,7 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  from logger_36.catalog.config.optional import RICH_IS_AVAILABLE
8
8
 
9
9
 
10
- def RuleAsText(text: str | None, /) -> str:
10
+ def RuleAsText(text: str | None, _: None, /) -> str:
11
11
  """"""
12
12
  if text is None:
13
13
  return "---- ---- ---- ---- ---- ---- ---- ---- ----"
@@ -27,7 +27,8 @@ if RICH_IS_AVAILABLE:
27
27
  return rule_t(title=text_t(text, style=f"bold {color}"), style=color)
28
28
 
29
29
  else:
30
- Rule = lambda _, __: RuleAsText(_)
30
+ rule_t = text_t = None
31
+ Rule = lambda _, __: RuleAsText(_, None)
31
32
 
32
33
  """
33
34
  COPYRIGHT NOTICE
logger_36/task/storage.py CHANGED
@@ -8,6 +8,7 @@ import logging as l
8
8
  from io import IOBase as io_base_t
9
9
  from pathlib import Path as path_t
10
10
 
11
+ from logger_36.constant.html import BODY_PLACEHOLDER, MINIMAL_HTML, TITLE_PLACEHOLDER
11
12
  from logger_36.instance.logger import L
12
13
 
13
14
 
@@ -35,19 +36,39 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
35
36
  path = path_t(path)
36
37
 
37
38
  actual_file = isinstance(path, path_t)
39
+
38
40
  if actual_file and path.exists():
39
41
  L.warning(f'{cannot_save}: File "{path}" already exists.')
40
42
  return
41
43
 
42
- html = "\n".join(L.recorded)
43
- if html is None:
44
- L.warning(f"{cannot_save}: No handler could provide an HTML output.")
44
+ body = "\n".join(map(_HighlightedEvent, L.recorded))
45
+ html = MINIMAL_HTML.replace(TITLE_PLACEHOLDER, L.name).replace(
46
+ BODY_PLACEHOLDER, body
47
+ )
48
+ if actual_file:
49
+ with open(path, "w") as accessor:
50
+ accessor.write(html)
45
51
  else:
46
- if actual_file:
47
- with open(path, "w") as accessor:
48
- accessor.write(html)
49
- else:
50
- path.write(html)
52
+ path.write(html)
53
+
54
+
55
+ def _HighlightedEvent(event: tuple[int, str], /) -> str:
56
+ """"""
57
+ level, message = event
58
+ if level == l.DEBUG:
59
+ color = "BlueViolet"
60
+ elif level == l.INFO:
61
+ color = "black"
62
+ elif level == l.WARNING:
63
+ color = "gold"
64
+ elif level == l.ERROR:
65
+ color = "orange"
66
+ elif level == l.CRITICAL:
67
+ color = "red"
68
+ else:
69
+ color = "black"
70
+
71
+ return f'<span style="color:{color}">{message}</span>'
51
72
 
52
73
 
53
74
  """
logger_36/type/handler.py CHANGED
@@ -9,12 +9,13 @@ import sys as s
9
9
  import typing as h
10
10
  from pathlib import Path as path_t
11
11
 
12
+ from logger_36.config.message import FALLBACK_MESSAGE_WIDTH
12
13
  from logger_36.constant.error import MEMORY_MEASURE_ERROR
13
14
  from logger_36.constant.handler import HANDLER_KINDS
14
- from logger_36.task.format.message import MessageWithActualExpected
15
+ from logger_36.task.format.message import MessageFromRecord, MessageWithActualExpected
15
16
  from logger_36.task.measure.chronos import TimeStamp
16
17
  from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
17
- from logger_36.type.message import MessageFromRecord, MessageFromRecord_h
18
+ from logger_36.type.message import MessageFromRecord_h, RuleWithText_h
18
19
 
19
20
  _MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
20
21
 
@@ -56,8 +57,8 @@ class _base_t:
56
57
  s.__stderr__.write(_MEMORY_MEASURE_ERROR + "\n")
57
58
  _MEMORY_MEASURE_ERROR = None
58
59
 
59
- if 0 < self.message_width < 5:
60
- self.message_width = 5
60
+ if 0 < self.message_width < FALLBACK_MESSAGE_WIDTH:
61
+ self.message_width = FALLBACK_MESSAGE_WIDTH
61
62
 
62
63
  @classmethod
63
64
  def New(cls, **kwargs) -> h.Self:
@@ -66,6 +67,17 @@ class _base_t:
66
67
  """
67
68
  raise NotImplementedError
68
69
 
70
+ def LogAsIs(self, message: str, /) -> None:
71
+ """
72
+ See documentation of
73
+ logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
74
+ """
75
+ raise NotImplementedError
76
+
77
+ def DisplayRule(self, /, *, text: str | None = None, color: str = "black") -> None:
78
+ """"""
79
+ raise NotImplementedError
80
+
69
81
 
70
82
  class handler_t(l.Handler, _base_t):
71
83
  def __init__(
@@ -75,11 +87,12 @@ class handler_t(l.Handler, _base_t):
75
87
  message_width: int,
76
88
  level: int,
77
89
  formatter: l.Formatter | None,
90
+ *_,
78
91
  ) -> None:
79
92
  """"""
80
93
  l.Handler.__init__(self)
81
94
  _base_t.__init__(self, name, should_store_memory_usage, message_width)
82
- _HandlerPostInit(self, level, formatter)
95
+ __post_init__(self, level, formatter)
83
96
 
84
97
 
85
98
  class file_handler_t(l.FileHandler, _base_t):
@@ -91,6 +104,7 @@ class file_handler_t(l.FileHandler, _base_t):
91
104
  level: int,
92
105
  formatter: l.Formatter | None,
93
106
  path: str | path_t | None,
107
+ *_,
94
108
  ) -> None:
95
109
  """"""
96
110
  if path is None:
@@ -102,13 +116,13 @@ class file_handler_t(l.FileHandler, _base_t):
102
116
 
103
117
  l.FileHandler.__init__(self, path)
104
118
  _base_t.__init__(self, name, should_store_memory_usage, message_width)
105
- _HandlerPostInit(self, level, formatter)
119
+ __post_init__(self, level, formatter)
106
120
 
107
121
 
108
122
  any_handler_t = handler_t | file_handler_t
109
123
 
110
124
 
111
- def _HandlerPostInit(
125
+ def __post_init__(
112
126
  handler: any_handler_t, level: int, formatter: l.Formatter | None
113
127
  ) -> None:
114
128
  """"""
@@ -122,13 +136,14 @@ def _HandlerPostInit(
122
136
 
123
137
  def _MessageFromRecord(
124
138
  record: l.LogRecord,
139
+ _: RuleWithText_h,
125
140
  /,
126
141
  *,
127
142
  line_width: int = 0,
128
143
  PreProcessed: h.Callable[[str], str] | None = None,
129
- ) -> str:
144
+ ) -> tuple[str, bool]:
130
145
  #
131
- return _MessageFromRecordRaw(record)
146
+ return _MessageFromRecordRaw(record), False
132
147
 
133
148
  handler.MessageFromRecord = _MessageFromRecord
134
149
 
logger_36/type/logger.py CHANGED
@@ -44,13 +44,12 @@ from logger_36.constant.record import (
44
44
  STORE_MEMORY_ATTR,
45
45
  )
46
46
  from logger_36.extension.exception import OverrideExceptionFormat
47
- from logger_36.task.format.message import MessageWithActualExpected
47
+ from logger_36.task.format.message import MessageFromRecord, MessageWithActualExpected
48
48
  from logger_36.task.format.rule import RuleAsText
49
49
  from logger_36.task.measure.chronos import ElapsedTime
50
50
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
51
51
  from logger_36.type.handler import any_handler_t as base_handler_t
52
52
  from logger_36.type.issue import NewIssue, issue_t
53
- from logger_36.type.message import MessageFromRecord
54
53
 
55
54
  if RICH_IS_AVAILABLE:
56
55
  from logger_36.catalog.handler.console_rich import console_rich_handler_t
@@ -75,23 +74,23 @@ class logger_t(base_t):
75
74
  intercepted_wrn_handle: When warning interception is on, this stores the original
76
75
  "handle" method of the Python warning logger.
77
76
 
78
- should_activate_log_interceptions: Loggers instantiated after a logger_t logger will
77
+ _should_activate_log_interceptions: Loggers instantiated after a logger_t logger will
79
78
  be missed by an early call of ToggleLogInterceptions. Therefore, passing True for
80
- activate_log_interceptions only sets should_activate_log_interceptions to True,
79
+ activate_log_interceptions only sets _should_activate_log_interceptions to True,
81
80
  which is later checked in AddHandler to effectively call ToggleLogInterceptions if
82
- should_hold_messages is False (which normally indicates that the handler about to be
81
+ _should_hold_messages is False (which normally indicates that the handler about to be
83
82
  added is the last one).
83
+
84
+ _should_hold_messages: Must not be False until at least one handler has been added.
84
85
  """
85
86
 
86
- # Must not be False until at least one handler has been added.
87
- should_hold_messages: bool = True
88
87
  should_record_messages: bool = False
89
88
  exit_on_error: bool = False # Implies exit_on_critical.
90
89
  exit_on_critical: bool = False
91
90
 
92
- on_hold: list[l.LogRecord] = d.field(init=False, default_factory=list)
93
- recorded: list[str] = d.field(init=False, default_factory=list)
94
91
  events: dict[int, int] = d.field(init=False, default_factory=dict)
92
+ recorded: list[tuple[int, str]] = d.field(init=False, default_factory=list)
93
+
95
94
  last_message_now: date_time_t = d.field(init=False, default=_DATE_TIME_ORIGIN)
96
95
  last_message_date: date_t = d.field(init=False, default=_DATE_ORIGIN)
97
96
  any_handler_stores_memory: bool = d.field(init=False, default=False)
@@ -102,7 +101,9 @@ class logger_t(base_t):
102
101
  intercepted_log_handles: dict[str, logger_handle_h] = d.field(
103
102
  init=False, default_factory=dict
104
103
  )
105
- should_activate_log_interceptions: bool = False
104
+ _on_hold: list[l.LogRecord] = d.field(init=False, default_factory=list)
105
+ _should_hold_messages: bool = d.field(init=False, default=True)
106
+ _should_activate_log_interceptions: bool = d.field(init=False, default=False)
106
107
 
107
108
  name_: d.InitVar[str] = LOGGER_NAME
108
109
  level_: d.InitVar[int] = l.NOTSET
@@ -167,7 +168,7 @@ class logger_t(base_t):
167
168
  if activate_wrn_interceptions:
168
169
  self.ToggleWarningInterceptions(True)
169
170
  if activate_log_interceptions:
170
- self.should_activate_log_interceptions = True
171
+ self._should_activate_log_interceptions = True
171
172
 
172
173
  if self.exit_on_error:
173
174
  self.exit_on_critical = True
@@ -176,12 +177,14 @@ class logger_t(base_t):
176
177
  """"""
177
178
  elapsed_time, now = ElapsedTime(should_return_now=True)
178
179
 
179
- if (self.on_hold.__len__() > 0) and not self.should_hold_messages:
180
- for held in self.on_hold:
180
+ if (self._on_hold.__len__() > 0) and not self._should_hold_messages:
181
+ for held in self._on_hold:
181
182
  if self.should_record_messages:
182
- self.recorded.append(MessageFromRecord(held))
183
+ self.recorded.append(
184
+ (held.levelno, MessageFromRecord(held, RuleAsText)[0])
185
+ )
183
186
  base_t.handle(self, held)
184
- self.on_hold.clear()
187
+ self._on_hold.clear()
185
188
 
186
189
  if (date := now.date()) != self.last_message_date:
187
190
  self.last_message_date = date
@@ -194,11 +197,16 @@ class logger_t(base_t):
194
197
  SHOW_W_RULE_ATTR: True,
195
198
  }
196
199
  )
197
- if self.should_hold_messages:
198
- self.on_hold.append(date_record)
200
+ if self._should_hold_messages:
201
+ self._on_hold.append(date_record)
199
202
  else:
200
203
  if self.should_record_messages:
201
- self.recorded.append(MessageFromRecord(date_record))
204
+ self.recorded.append(
205
+ (
206
+ date_record.levelno,
207
+ MessageFromRecord(date_record, RuleAsText)[0],
208
+ )
209
+ )
202
210
  base_t.handle(self, date_record)
203
211
 
204
212
  # When.
@@ -238,11 +246,13 @@ class logger_t(base_t):
238
246
  if not isinstance(record.msg, str):
239
247
  record.msg = str(record.msg)
240
248
 
241
- if self.should_hold_messages:
242
- self.on_hold.append(record)
249
+ if self._should_hold_messages:
250
+ self._on_hold.append(record)
243
251
  else:
244
252
  if self.should_record_messages:
245
- self.recorded.append(MessageFromRecord(record))
253
+ self.recorded.append(
254
+ (record.levelno, MessageFromRecord(record, RuleAsText)[0])
255
+ )
246
256
  base_t.handle(self, record)
247
257
 
248
258
  if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
@@ -381,11 +391,11 @@ class logger_t(base_t):
381
391
  **kwargs,
382
392
  ) -> None:
383
393
  """"""
384
- if (not should_still_hold_messages) and self.should_activate_log_interceptions:
394
+ if (not should_still_hold_messages) and self._should_activate_log_interceptions:
385
395
  self.ToggleLogInterceptions(True)
386
- self.should_activate_log_interceptions = False
396
+ self._should_activate_log_interceptions = False
387
397
 
388
- self.should_hold_messages = should_still_hold_messages
398
+ self._should_hold_messages = should_still_hold_messages
389
399
  handler = handler_t.New(
390
400
  name=name,
391
401
  should_store_memory_usage=should_store_memory_usage,
@@ -415,6 +425,13 @@ class logger_t(base_t):
415
425
  f"level {handler.level}={l.getLevelName(handler.level)}{path}"
416
426
  )
417
427
 
428
+ def __call__(self, *args, **kwargs) -> None:
429
+ """
430
+ For a print-like calling.
431
+ """
432
+ separator = kwargs.get("separator", " ")
433
+ self.info(separator.join(map(str, args)))
434
+
418
435
  def Log(
419
436
  self,
420
437
  message: str,
@@ -463,10 +480,7 @@ class logger_t(base_t):
463
480
  self.log(level, message)
464
481
 
465
482
  def LogAsIs(self, message: str, /, *, indented: bool = False) -> None:
466
- """
467
- See documentation of
468
- logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
469
- """
483
+ """"""
470
484
  if indented:
471
485
  message = text.indent(message, LINE_INDENT)
472
486
 
@@ -476,7 +490,7 @@ class logger_t(base_t):
476
490
  else:
477
491
  LogAsIs(message)
478
492
 
479
- raw_info = LogAsIs # To follow the convention of the logging methods info, error...
493
+ info_raw = LogAsIs # To follow the convention of the logging methods info, error...
480
494
 
481
495
  def DisplayRule(
482
496
  self, /, *, message: str | None = None, color: str = "white"
@@ -484,7 +498,7 @@ class logger_t(base_t):
484
498
  """"""
485
499
  for handler in self.handlers:
486
500
  if (DisplayRule := getattr(handler, "DisplayRule", None)) is None:
487
- self.info(RuleAsText(message))
501
+ self.info(RuleAsText(message, None))
488
502
  else:
489
503
  DisplayRule(text=message, color=color)
490
504
 
logger_36/type/message.py CHANGED
@@ -7,14 +7,24 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  import logging as l
8
8
  import typing as h
9
9
 
10
- from logger_36.config.message import (
11
- LEVEL_CLOSING,
12
- LEVEL_OPENING,
13
- MESSAGE_MARKER,
14
- WHERE_SEPARATOR,
15
- )
16
- from logger_36.constant.message import NEXT_LINE_PROLOGUE
17
- from logger_36.extension.line import WrappedLines
10
+ from logger_36.catalog.config.optional import RICH_IS_AVAILABLE
11
+
12
+
13
+ @h.runtime_checkable
14
+ class _RuleAsText_p(h.Protocol):
15
+ def __call__(self, text: str | None, color: None, /) -> str: ...
16
+
17
+
18
+ if RICH_IS_AVAILABLE:
19
+ from rich.rule import Rule as rule_t # noqa
20
+
21
+ @h.runtime_checkable
22
+ class _Rule_p(h.Protocol):
23
+ def __call__(self, text: str | None, color: str, /) -> rule_t | str: ...
24
+ else:
25
+ _Rule_p = None
26
+
27
+ RuleWithText_h = _RuleAsText_p | _Rule_p
18
28
 
19
29
 
20
30
  @h.runtime_checkable
@@ -22,58 +32,17 @@ class _MessageFromRecordPreprocessed_p(h.Protocol):
22
32
  def __call__(
23
33
  self,
24
34
  record: l.LogRecord,
35
+ RuleWithText: RuleWithText_h,
25
36
  /,
26
37
  *,
27
38
  line_width: int = 0,
28
39
  PreProcessed: h.Callable[[str], str] | None = None,
29
- ) -> str: ...
40
+ ) -> tuple[str, bool]: ...
30
41
 
31
42
 
32
43
  MessageFromRecord_h = _MessageFromRecordPreprocessed_p
33
44
 
34
45
 
35
- def MessageFromRecord(
36
- record: l.LogRecord,
37
- /,
38
- *,
39
- line_width: int = 0,
40
- PreProcessed: h.Callable[[str], str] | None = None,
41
- ) -> str:
42
- """
43
- See logger_36.catalog.handler.README.txt.
44
- """
45
- message = record.msg
46
-
47
- if PreProcessed is not None:
48
- message = PreProcessed(message)
49
- if (line_width <= 0) or (message.__len__() <= line_width):
50
- if "\n" in message:
51
- message = NEXT_LINE_PROLOGUE.join(message.splitlines())
52
- else:
53
- if "\n" in message:
54
- lines = WrappedLines(message.splitlines(), line_width)
55
- else:
56
- lines = WrappedLines([message], line_width)
57
- message = NEXT_LINE_PROLOGUE.join(lines)
58
-
59
- when_or_elapsed = getattr(record, "when_or_elapsed", None)
60
- if when_or_elapsed is None:
61
- return message
62
-
63
- level_first_letter = getattr(record, "level_first_letter", "")
64
-
65
- if (where := getattr(record, "where", None)) is None:
66
- where = ""
67
- else:
68
- where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
69
-
70
- return (
71
- f"{when_or_elapsed}"
72
- f"{LEVEL_OPENING}{level_first_letter}{LEVEL_CLOSING} "
73
- f"{MESSAGE_MARKER} {message}{where}"
74
- )
75
-
76
-
77
46
  """
78
47
  COPYRIGHT NOTICE
79
48
 
logger_36/version.py CHANGED
@@ -4,7 +4,7 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- __version__ = "2025.13"
7
+ __version__ = "2025.14"
8
8
 
9
9
  """
10
10
  COPYRIGHT NOTICE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logger-36
3
- Version: 2025.13
3
+ Version: 2025.14
4
4
  Summary: Simple logger with a catalog of handlers
5
5
  Home-page: https://src.koda.cnrs.fr/eric.debreuve/logger-36/
6
6
  Author: Eric Debreuve
@@ -1,5 +1,5 @@
1
1
  logger_36/__init__.py,sha256=UhKxuQLS1Pfgt5H0K_7BaDAPejOUR8byD5BYRCnHQMQ,2655
2
- logger_36/version.py,sha256=8Q08zB-7T6p-P0RQlBl_Y5-Y1kdpyPxTtxJ5OyfjwMU,2206
2
+ logger_36/version.py,sha256=puslblEld2sLwTmqWreV3vMyhNnL0uc4l3B-pcUa2hg,2206
3
3
  logger_36/api/content.py,sha256=clHYYUKa8n4qef6PVlUV4mFHRRf6fnm9wEd2fu9oagA,2381
4
4
  logger_36/api/exception.py,sha256=QKIkNJA0N6FvVHLTApiH3ymhVQoSYU08t2RkyufQPIw,2291
5
5
  logger_36/api/gpu.py,sha256=BOumedCAPWvCo7J-KJ3XE-jr5S0KSmgcFv_S4QKRPO8,2252
@@ -10,21 +10,22 @@ logger_36/api/time.py,sha256=Uw1jQtY1njsRuIPRAXX44v4nPOo84MSBu_WK_YCRzQs,2324
10
10
  logger_36/api/type.py,sha256=4m5fZGI6LOQvFakEStFv6HTP4FY9nyFpNNlK34rCfQw,2286
11
11
  logger_36/catalog/config/console_rich.py,sha256=lAa5Ev5BhXvmQzfIt1FNihMNUQJFlXaIzNanAMdgtd0,2861
12
12
  logger_36/catalog/config/optional.py,sha256=HaN6mbx7gHBBppNvUw1ckhYTOrlYqb-b_r0mzPcHPjM,2398
13
- logger_36/catalog/handler/console.py,sha256=2scmBhoZdTVEiepgnfcgrLwhm11mjIQuVYPmQqDKKvk,3516
14
- logger_36/catalog/handler/console_rich.py,sha256=7F7qyztTMuQt5zy9ml3BZFFdi3WoGbJ_S9E58TKsLF8,6903
15
- logger_36/catalog/handler/file.py,sha256=FjnkppbFgsieULfoWD1tRD5jIoC1qZLKE3gtSdNYseE,3661
16
- logger_36/catalog/handler/generic.py,sha256=AaGnnfiuKgzsTyMTtY7UYkzthXQ20Vyv_cnaMAPXefo,7946
13
+ logger_36/catalog/handler/console.py,sha256=5F_JFsGm5ERFh0_1rh8Jae51VwAJMg7d7bRLZkOK4DY,3283
14
+ logger_36/catalog/handler/console_rich.py,sha256=U4hD8qF88dpDALKkeOvwnrJpUbUpG5egq1JfImU4pFg,7168
15
+ logger_36/catalog/handler/file.py,sha256=4Du9f-UNJsZrcNV9jCVLLCklA0T4HYX_jKjxSie-deM,3428
16
+ logger_36/catalog/handler/generic.py,sha256=8xQA0ETIMmdhmip0wcaD21h4ZGz-52gk0hgoUJYxbn8,8258
17
17
  logger_36/catalog/logger/chronos.py,sha256=ocY13f98EfknU7wZCv0FS9Xb7pTNaWCPSusXFIEvEd4,2437
18
18
  logger_36/catalog/logger/gpu.py,sha256=ybn7Q8exiqoigvNpzEhg0Zp027WogypuNJwfsQ1pRY4,3416
19
19
  logger_36/catalog/logger/memory.py,sha256=CWhr2J4BqArJxzH6tS-ZThr-rYPAQGtuLn0pP7Iryfg,4685
20
20
  logger_36/catalog/logger/system.py,sha256=3VWbceSAknZwmRhEfd8pkuLwU2B8zPhCGNGQE0h5KLo,3065
21
21
  logger_36/config/issue.py,sha256=G-i5p6lhZCLAOa-VTMyL9ZonvGCvhdoQ5KZdSWgP-FU,2267
22
22
  logger_36/config/memory.py,sha256=yCX5phsB_KJMr5xHpVUeOHFhAA7p_8yahP3X28VndOY,2217
23
- logger_36/config/message.py,sha256=yfbMO_Jk1IbWvT6Lp6hVpID2Tr99cuiJ-ZaMBesIFXw,2527
23
+ logger_36/config/message.py,sha256=mgPcMS7qWBuqP2w5NoHw1df32kcVToVhisozb32_EII,2554
24
24
  logger_36/config/system.py,sha256=HD8ZuwsXhEAExeZrww8YoDkQGMs4T5RDqQMb1W4qVgc,2477
25
25
  logger_36/constant/error.py,sha256=LzsS_P1IoH3ct_ifNWi9LzJ-X_Y5DN1naTLwwIFzDQA,2827
26
26
  logger_36/constant/generic.py,sha256=t6aRb66_NHwMhR1p7BZ4QXTU2jpLz-H5YAL4PuMtKx8,2244
27
27
  logger_36/constant/handler.py,sha256=PQUehMK9Yg0_rBDcMc8xpUbAsCauCLy_eS_ntiWew1Y,2378
28
+ logger_36/constant/html.py,sha256=-m1CDyDN0kkurloEtJeqBsyxy9nXCImIMGLwEIF33M0,2515
28
29
  logger_36/constant/issue.py,sha256=01l8itRPWGS5F6gXtsXUJgGR-4lS1Eu3_YeKC-khKLw,2315
29
30
  logger_36/constant/logger.py,sha256=2qRkteblpbHrq9x0aiw9MPquyXrSRd6_yMQnPEhFp2U,2468
30
31
  logger_36/constant/memory.py,sha256=ZL1MwbdtNsrCrOwzEyfTsfOoOsRBTJtbbf3otHGnxXo,2343
@@ -38,18 +39,18 @@ logger_36/extension/line.py,sha256=3MJ3B5PXJn18RHxBUcWnNBLEYzb7VTcEAufn7ULdYfY,3
38
39
  logger_36/instance/logger.py,sha256=oTw5svRzKRJKvGrrZUtutJIOjp5UISft3fl0Ze7DOBE,2241
39
40
  logger_36/instance/loggers.py,sha256=RCWpC1NPAf6vXnFc9NqsSALv-x-FEzcH6k_OlxTxeQk,2251
40
41
  logger_36/task/inspection.py,sha256=VR9ESSa2iAAiQggwzHzV2A3-rRr6VgMqR1HvclhK2Xc,5010
41
- logger_36/task/storage.py,sha256=mBUzUw884tSPcWLjlNPjDmwxJ6QXV2tnrfjKDZLQwJg,3504
42
+ logger_36/task/storage.py,sha256=Y88934cVcen1i8qruskmCMQYA5-dl1wI8EaJFCv49Q0,4042
42
43
  logger_36/task/format/memory.py,sha256=ZipL1f-Bqv4ugFvXZfNrj49rdIMXjEKcVz1OvgMaZXI,4249
43
- logger_36/task/format/message.py,sha256=siQ3crsaw0846-lEKEC1bc29Exmxw7qMBdBF91fncpc,4028
44
- logger_36/task/format/rule.py,sha256=1cm1d1h9fEv15A-tYVwXy5XzKmkFnXF5kKXFBdNCmNc,2868
44
+ logger_36/task/format/message.py,sha256=5mR9CZaARy9q-JtIX68IyY-DKTepkxyRV7juByqBH7c,5771
45
+ logger_36/task/format/rule.py,sha256=CtR7d2X-pZFKdqG6Y2_3b5AMKg_J274Jq69kht0N6xs,2910
45
46
  logger_36/task/measure/chronos.py,sha256=1kVhu6jZlNAtNWQQh8ZVuRwZIAC9gGz3_ul1tn0t4Yw,3055
46
47
  logger_36/task/measure/memory.py,sha256=OjU5EYFH8SnzlCQKAoiXvauUlwQYOrH34jFXTVYF0jE,2517
47
- logger_36/type/handler.py,sha256=saGN7S1XgADzbgwbuycJwjdaar-bRgsfW5gMM2JA2PA,6040
48
+ logger_36/type/handler.py,sha256=6oxW-Y_kuAEfoXsMr-KXspv0czYiwe8rDzMAMrshFmw,6567
48
49
  logger_36/type/issue.py,sha256=2rGsFqaQJCbeml9xN08mN_nK79L8qscaS_0ws36Y0bI,3214
49
- logger_36/type/logger.py,sha256=mflRwDwvo1sZhAwJJ5DhSvM-Oqij3DUmE0B7mXQRYkE,24789
50
+ logger_36/type/logger.py,sha256=ISMUQBZ-KhLBi_tZjCAYtss5pAwFW3psTQu5k9_ZbEw,25287
50
51
  logger_36/type/loggers.py,sha256=znqxWBnfQxvkg3VUfbTUvt3S6Kq0DAzWWepxQDt9suI,2871
51
- logger_36/type/message.py,sha256=1TA41C-jk-zlfVwv66am6BDvhHKD_4VJoafc2nXEgE8,3969
52
- logger_36-2025.13.dist-info/METADATA,sha256=7FsKH9oTSxEdfppbTtwRuwqAm9RGAl8dqqcHEA5Auag,6506
53
- logger_36-2025.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
54
- logger_36-2025.13.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
55
- logger_36-2025.13.dist-info/RECORD,,
52
+ logger_36/type/message.py,sha256=zKME5p87ynsXte_b5usXV3VHaj34Uezs9Gg_WVWfaeY,3063
53
+ logger_36-2025.14.dist-info/METADATA,sha256=GkEE-L8P0ShhL0XxKOu-A5p3yZD0mJzAcGax90FiUw8,6506
54
+ logger_36-2025.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
+ logger_36-2025.14.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
56
+ logger_36-2025.14.dist-info/RECORD,,