logger-36 2025.6__py3-none-any.whl → 2025.8__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.
logger_36/api/storage.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
- from logger_36.task.storage import html_reader_t # noqa
7
+ from logger_36.extension.html_ import html_content_t # noqa
8
8
 
9
9
  """
10
10
  COPYRIGHT NOTICE
@@ -6,14 +6,11 @@ SEE COPYRIGHT NOTICE BELOW
6
6
 
7
7
  import dataclasses as d
8
8
  import logging as l
9
- import textwrap as txt_
10
9
  import typing as h
11
10
 
12
- from logger_36.constant.message import LINE_INDENT
13
11
  from logger_36.constant.record import SHOW_W_RULE_ATTR
14
12
  from logger_36.task.format.rule import RuleAsText
15
- from logger_36.type.handler import handler_extension_t
16
- from logger_36.type.handler import message_from_record_raw_p as message_from_record_p
13
+ from logger_36.type.handler import MessageFromRecordRaw_h, handler_extension_t
17
14
 
18
15
 
19
16
  @d.dataclass(slots=True, repr=False, eq=False)
@@ -25,7 +22,7 @@ class console_handler_t(l.Handler):
25
22
  kind: h.ClassVar[str] = "c"
26
23
 
27
24
  extension: handler_extension_t = d.field(init=False)
28
- MessageFromRecord: message_from_record_p = d.field(init=False)
25
+ MessageFromRecord: MessageFromRecordRaw_h = d.field(init=False)
29
26
 
30
27
  name: d.InitVar[str | None] = None
31
28
  level: d.InitVar[int] = l.NOTSET
@@ -63,18 +60,16 @@ class console_handler_t(l.Handler):
63
60
  message = self.MessageFromRecord(record)
64
61
  print(message)
65
62
 
66
- def ShowMessage(self, message: str, /, *, indented: bool = False) -> None:
63
+ def LogAsIs(self, message: str, /) -> None:
67
64
  """
68
65
  See documentation of
69
- logger_36.catalog.handler.generic.generic_handler_t.ShowMessage.
66
+ logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
70
67
  """
71
- if indented:
72
- message = txt_.indent(message, LINE_INDENT)
73
68
  print(message)
74
69
 
75
70
  def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
76
71
  """"""
77
- self.ShowMessage(RuleAsText(text))
72
+ self.LogAsIs(RuleAsText(text))
78
73
 
79
74
 
80
75
  """
@@ -6,7 +6,6 @@ SEE COPYRIGHT NOTICE BELOW
6
6
 
7
7
  import dataclasses as d
8
8
  import logging as l
9
- import textwrap as txt_
10
9
  import typing as h
11
10
 
12
11
  from logger_36.catalog.config.console_rich import (
@@ -20,13 +19,10 @@ from logger_36.catalog.config.console_rich import (
20
19
  WHITE_COLOR,
21
20
  )
22
21
  from logger_36.config.message import ACTUAL_PATTERNS, EXPECTED_PATTERNS, WHERE_SEPARATOR
23
- from logger_36.constant.message import CONTEXT_LENGTH, LINE_INDENT
22
+ from logger_36.constant.message import CONTEXT_LENGTH
24
23
  from logger_36.constant.record import SHOW_W_RULE_ATTR
25
- from logger_36.task.format.rule import Rule, rule_t
26
- from logger_36.type.handler import handler_extension_t
27
- from logger_36.type.handler import (
28
- message_from_record_preprocessed_p as message_from_record_p,
29
- )
24
+ from logger_36.task.format.rule import Rule
25
+ from logger_36.type.handler import MessageFromRecordPreprocessed_p, handler_extension_t
30
26
  from rich.console import Console as console_t # noqa
31
27
  from rich.console import RenderableType as renderable_t # noqa
32
28
  from rich.markup import escape as EscapedVersion # noqa
@@ -64,7 +60,7 @@ class console_rich_handler_t(l.Handler):
64
60
 
65
61
  extension: handler_extension_t = d.field(init=False)
66
62
  console: console_t = d.field(init=False)
67
- MessageFromRecord: message_from_record_p = d.field(init=False)
63
+ MessageFromRecord: MessageFromRecordPreprocessed_p = d.field(init=False)
68
64
  alternating_lines: int = 0
69
65
  background_is_light: bool = True
70
66
 
@@ -78,6 +74,15 @@ class console_rich_handler_t(l.Handler):
78
74
 
79
75
  rich_kwargs: d.InitVar[dict[str, h.Any] | None] = None
80
76
 
77
+ @property
78
+ def past_logs_as_HTML(self) -> str | None:
79
+ """"""
80
+ console = self.console
81
+ if console.record:
82
+ return console.export_html()
83
+
84
+ return None
85
+
81
86
  def __post_init__(
82
87
  self,
83
88
  name: str | None,
@@ -152,18 +157,16 @@ class console_rich_handler_t(l.Handler):
152
157
  )
153
158
  self.console.print(richer, crop=False, overflow="ignore")
154
159
 
155
- def ShowMessage(self, message: str | rule_t, /, *, indented: bool = False) -> None:
160
+ def LogAsIs(self, message: str | renderable_t, /) -> None:
156
161
  """
157
162
  See documentation of
158
- logger_36.catalog.handler.generic.generic_handler_t.ShowMessage.
163
+ logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
159
164
  """
160
- if isinstance(message, str) and indented:
161
- message = txt_.indent(message, LINE_INDENT)
162
165
  self.console.print(message, crop=False, overflow="ignore")
163
166
 
164
167
  def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
165
168
  """"""
166
- self.ShowMessage(Rule(text, color))
169
+ self.LogAsIs(Rule(text, color))
167
170
 
168
171
 
169
172
  def HighlightedVersion(
@@ -6,15 +6,12 @@ SEE COPYRIGHT NOTICE BELOW
6
6
 
7
7
  import dataclasses as d
8
8
  import logging as l
9
- import textwrap as txt_
10
9
  import typing as h
11
10
  from pathlib import Path as path_t
12
11
 
13
- from logger_36.constant.message import LINE_INDENT
14
12
  from logger_36.constant.record import SHOW_W_RULE_ATTR
15
13
  from logger_36.task.format.rule import RuleAsText
16
- from logger_36.type.handler import handler_extension_t
17
- from logger_36.type.handler import message_from_record_raw_p as message_from_record_p
14
+ from logger_36.type.handler import MessageFromRecordRaw_h, handler_extension_t
18
15
 
19
16
 
20
17
  @d.dataclass(slots=True, repr=False, eq=False)
@@ -26,7 +23,7 @@ class file_handler_t(l.FileHandler):
26
23
  kind: h.ClassVar[str] = "f"
27
24
 
28
25
  extension: handler_extension_t = d.field(init=False)
29
- MessageFromRecord: message_from_record_p = d.field(init=False)
26
+ MessageFromRecord: MessageFromRecordRaw_h = d.field(init=False)
30
27
 
31
28
  name: d.InitVar[str | None] = None
32
29
  level: d.InitVar[int] = l.NOTSET
@@ -72,19 +69,17 @@ class file_handler_t(l.FileHandler):
72
69
  print(message, file=self.stream)
73
70
  self.stream.flush()
74
71
 
75
- def ShowMessage(self, message: str, /, *, indented: bool = False) -> None:
72
+ def LogAsIs(self, message: str, /) -> None:
76
73
  """
77
74
  See documentation of
78
- logger_36.catalog.handler.generic.generic_handler_t.ShowMessage.
75
+ logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
79
76
  """
80
- if indented:
81
- message = txt_.indent(message, LINE_INDENT)
82
77
  print(message, file=self.stream)
83
78
  self.stream.flush()
84
79
 
85
80
  def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
86
81
  """"""
87
- self.ShowMessage(RuleAsText(text))
82
+ self.LogAsIs(RuleAsText(text))
88
83
 
89
84
 
90
85
  """
@@ -24,16 +24,13 @@ else:
24
24
 
25
25
  from logger_36.constant.record import SHOW_W_RULE_ATTR
26
26
  from logger_36.task.format.rule import Rule, RuleAsText
27
- from logger_36.type.handler import handler_extension_t, message_from_record_h
27
+ from logger_36.type.handler import MessageFromRecord_h, handler_extension_t
28
28
 
29
-
30
- @h.runtime_checkable
31
- class show_message_p(h.Protocol):
32
- def __call__(self, message: str, /, *, indented: bool = False) -> None: ...
29
+ LogAsIs_h = h.Callable[[str | h.Any], None]
33
30
 
34
31
 
35
32
  @h.runtime_checkable
36
- class display_rule_p(h.Protocol):
33
+ class DisplayRule_p(h.Protocol):
37
34
  def __call__(self, /, *, text: str | None = None, color: str = "white") -> None: ...
38
35
 
39
36
 
@@ -49,7 +46,7 @@ class generic_handler_t(l.Handler):
49
46
  - anything else: disabled
50
47
  - Runtime value: 0/1=do not/do highlight next time.
51
48
 
52
- ShowMessage:
49
+ LogAsIs:
53
50
  Log a message as is, i.e. without formatting. If this is a method, it should
54
51
  contain the same call(s) as the final ones in the emit methods that are used to
55
52
  output the formatted log messages. This means that there is some code
@@ -62,16 +59,16 @@ class generic_handler_t(l.Handler):
62
59
 
63
60
  kind: h.ClassVar[str] = "g"
64
61
 
65
- ShowMessage: show_message_p
62
+ LogAsIs: LogAsIs_h
66
63
  # "None -> h.Any" (twice below) since None | None is invalid.
67
64
  console: console_t | h.Any = None
68
65
  console_options: console_options_t | h.Any = None
69
66
  alternating_lines: int = 0
70
67
  background_is_light: bool = True
71
68
 
72
- DisplayRule: display_rule_p = d.field(init=False)
69
+ DisplayRule: DisplayRule_p = d.field(init=False)
73
70
  extension: handler_extension_t = d.field(init=False)
74
- MessageFromRecord: message_from_record_h = d.field(init=False)
71
+ MessageFromRecord: MessageFromRecord_h = d.field(init=False)
75
72
 
76
73
  name: d.InitVar[str | None] = None
77
74
  level: d.InitVar[int] = l.NOTSET
@@ -178,17 +175,17 @@ class generic_handler_t(l.Handler):
178
175
  "<pre style='margin-bottom:0px'>" + "".join(html_segments) + "</pre>"
179
176
  )
180
177
 
181
- self.ShowMessage(message)
178
+ self.LogAsIs(message)
182
179
 
183
180
  def _DisplayRuleAsText(
184
181
  self, /, *, text: str | None = None, color: str = "white"
185
182
  ) -> None:
186
183
  """"""
187
- self.ShowMessage(RuleAsText(text))
184
+ self.LogAsIs(RuleAsText(text))
188
185
 
189
186
  def _DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
190
187
  """"""
191
- self.ShowMessage(Rule(text, color))
188
+ self.LogAsIs(Rule(text, color))
192
189
 
193
190
 
194
191
  """
@@ -0,0 +1,69 @@
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
+ import inspect as nspt
8
+ import tempfile as tmps
9
+ from pathlib import Path as path_t
10
+
11
+ USER_FOLDER = path_t.home()
12
+
13
+ frame = nspt.stack()[-1]
14
+ if path_t(frame.filename).exists():
15
+ PROJECT_FILE = path_t(frame.filename)
16
+ if PROJECT_FILE.is_relative_to(USER_FOLDER):
17
+ PROJECT_FILE_RELATIVE = path_t("~") / PROJECT_FILE.relative_to(USER_FOLDER)
18
+ else:
19
+ PROJECT_FILE_RELATIVE = PROJECT_FILE
20
+ PROJECT_FOLDER = PROJECT_FILE.parent
21
+ else:
22
+ PROJECT_FILE = PROJECT_FILE_RELATIVE = "<unknown>"
23
+ PROJECT_FOLDER = path_t(tmps.mkdtemp())
24
+
25
+ """
26
+ COPYRIGHT NOTICE
27
+
28
+ This software is governed by the CeCILL license under French law and
29
+ abiding by the rules of distribution of free software. You can use,
30
+ modify and/ or redistribute the software under the terms of the CeCILL
31
+ license as circulated by CEA, CNRS and INRIA at the following URL
32
+ "http://www.cecill.info".
33
+
34
+ As a counterpart to the access to the source code and rights to copy,
35
+ modify and redistribute granted by the license, users are provided only
36
+ with a limited warranty and the software's author, the holder of the
37
+ economic rights, and the successive licensors have only limited
38
+ liability.
39
+
40
+ In this respect, the user's attention is drawn to the risks associated
41
+ with loading, using, modifying and/or developing or reproducing the
42
+ software by the user in light of its specific status of free software,
43
+ that may mean that it is complicated to manipulate, and that also
44
+ therefore means that it is reserved for developers and experienced
45
+ professionals having in-depth computer knowledge. Users are therefore
46
+ encouraged to load and test the software's suitability as regards their
47
+ requirements in conditions enabling the security of their systems and/or
48
+ data to be ensured and, more generally, to use and operate it in the
49
+ same conditions as regards security.
50
+
51
+ The fact that you are presently reading this means that you have had
52
+ knowledge of the CeCILL license and that you accept its terms.
53
+
54
+ SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
55
+
56
+ This software is being developed by Eric Debreuve, a CNRS employee and
57
+ member of team Morpheme.
58
+ Team Morpheme is a joint team between Inria, CNRS, and UniCA.
59
+ It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
60
+ I3S, and Laboratory iBV.
61
+
62
+ CNRS: https://www.cnrs.fr/index.php/en
63
+ Inria: https://www.inria.fr/en/
64
+ UniCA: https://univ-cotedazur.eu/
65
+ Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
66
+ I3S: https://www.i3s.unice.fr/en/
67
+ iBV: http://ibv.unice.fr/
68
+ Team Morpheme: https://team.inria.fr/morpheme/
69
+ """
logger_36/exception.py CHANGED
@@ -11,6 +11,8 @@ import traceback as tcbk
11
11
  import types as t
12
12
  from pathlib import Path as path_t
13
13
 
14
+ from logger_36.constant.path import USER_FOLDER
15
+
14
16
  _ORIGINAL_EXCEPTION_HANDLER = s.excepthook
15
17
 
16
18
 
@@ -38,9 +40,8 @@ def _HandleException(
38
40
  line_content = module.read_text().splitlines()[line_number - 1].strip()
39
41
 
40
42
  # Format module.
41
- home = path_t.home()
42
- if module.is_relative_to(home):
43
- module = path_t("~") / module.relative_to(home)
43
+ if module.is_relative_to(USER_FOLDER):
44
+ module = path_t("~") / module.relative_to(USER_FOLDER)
44
45
 
45
46
  # Format line content.
46
47
  if line_content.startswith("raise "):
@@ -0,0 +1,107 @@
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
+ import dataclasses as d
8
+ import re as r
9
+ from html.parser import HTMLParser as base_t
10
+
11
+ _BODY_END_PATTERN = r"</[bB][oO][dD][yY]>(.|\n)*$"
12
+
13
+
14
+ @d.dataclass(slots=True, repr=False, eq=False)
15
+ class html_content_t(base_t):
16
+ source: str = ""
17
+ inside_body: bool = d.field(init=False, default=False)
18
+ body_position_start: tuple[int, int] = d.field(init=False, default=(-1, -1))
19
+ body_position_end: tuple[int, int] = d.field(init=False, default=(-1, -1))
20
+ pieces: list[str] = d.field(init=False, default_factory=list)
21
+
22
+ @property
23
+ def body(self) -> str:
24
+ """"""
25
+ output = self.source.splitlines()
26
+ output = "\n".join(
27
+ output[self.body_position_start[0] : (self.body_position_end[0] + 1)]
28
+ )
29
+ output = output[self.body_position_start[1] :]
30
+ output = r.sub(_BODY_END_PATTERN, "", output, count=1)
31
+
32
+ return output.strip()
33
+
34
+ @property
35
+ def body_as_text(self) -> str:
36
+ """"""
37
+ return "".join(self.pieces).strip()
38
+
39
+ def __post_init__(self) -> None:
40
+ """"""
41
+ base_t.__init__(self)
42
+ self.source = self.source.strip()
43
+ self.feed(self.source)
44
+
45
+ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]], /) -> None:
46
+ """"""
47
+ if tag == "body":
48
+ self.body_position_start = self.getpos()
49
+ self.inside_body = True
50
+
51
+ def handle_endtag(self, tag: str, /) -> None:
52
+ """"""
53
+ if tag == "body":
54
+ self.body_position_end = self.getpos()
55
+ self.inside_body = False
56
+
57
+ def handle_data(self, data: str, /) -> None:
58
+ """"""
59
+ if self.inside_body:
60
+ self.pieces.append(data)
61
+
62
+
63
+ """
64
+ COPYRIGHT NOTICE
65
+
66
+ This software is governed by the CeCILL license under French law and
67
+ abiding by the rules of distribution of free software. You can use,
68
+ modify and/ or redistribute the software under the terms of the CeCILL
69
+ license as circulated by CEA, CNRS and INRIA at the following URL
70
+ "http://www.cecill.info".
71
+
72
+ As a counterpart to the access to the source code and rights to copy,
73
+ modify and redistribute granted by the license, users are provided only
74
+ with a limited warranty and the software's author, the holder of the
75
+ economic rights, and the successive licensors have only limited
76
+ liability.
77
+
78
+ In this respect, the user's attention is drawn to the risks associated
79
+ with loading, using, modifying and/or developing or reproducing the
80
+ software by the user in light of its specific status of free software,
81
+ that may mean that it is complicated to manipulate, and that also
82
+ therefore means that it is reserved for developers and experienced
83
+ professionals having in-depth computer knowledge. Users are therefore
84
+ encouraged to load and test the software's suitability as regards their
85
+ requirements in conditions enabling the security of their systems and/or
86
+ data to be ensured and, more generally, to use and operate it in the
87
+ same conditions as regards security.
88
+
89
+ The fact that you are presently reading this means that you have had
90
+ knowledge of the CeCILL license and that you accept its terms.
91
+
92
+ SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
93
+
94
+ This software is being developed by Eric Debreuve, a CNRS employee and
95
+ member of team Morpheme.
96
+ Team Morpheme is a joint team between Inria, CNRS, and UniCA.
97
+ It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
98
+ I3S, and Laboratory iBV.
99
+
100
+ CNRS: https://www.cnrs.fr/index.php/en
101
+ Inria: https://www.inria.fr/en/
102
+ UniCA: https://univ-cotedazur.eu/
103
+ Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
104
+ I3S: https://www.i3s.unice.fr/en/
105
+ iBV: http://ibv.unice.fr/
106
+ Team Morpheme: https://team.inria.fr/morpheme/
107
+ """
logger_36/handler.py CHANGED
@@ -11,7 +11,7 @@ from pathlib import Path as path_t
11
11
  from logger_36.catalog.config.optional import MISSING_RICH_MESSAGE, RICH_IS_AVAILABLE
12
12
  from logger_36.catalog.handler.console import console_handler_t
13
13
  from logger_36.catalog.handler.file import file_handler_t
14
- from logger_36.catalog.handler.generic import generic_handler_t, show_message_p
14
+ from logger_36.catalog.handler.generic import LogAsIs_h, generic_handler_t
15
15
 
16
16
  if RICH_IS_AVAILABLE:
17
17
  from logger_36.catalog.handler.console_rich import console_rich_handler_t
@@ -24,7 +24,7 @@ _MISSING_RICH_MESSAGE = MISSING_RICH_MESSAGE
24
24
 
25
25
  def AddGenericHandler(
26
26
  logger: l.Logger,
27
- ShowMessage: show_message_p,
27
+ LogAsIs: LogAsIs_h,
28
28
  /,
29
29
  *,
30
30
  name: str | None = None,
@@ -49,7 +49,7 @@ def AddGenericHandler(
49
49
  alternating_lines=alternating_lines,
50
50
  should_record=should_record,
51
51
  rich_kwargs=kwargs,
52
- ShowMessage=ShowMessage,
52
+ LogAsIs=LogAsIs,
53
53
  )
54
54
  logger.AddHandler(handler, should_hold_messages=should_hold_messages)
55
55
 
logger_36/task/storage.py CHANGED
@@ -4,73 +4,12 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- import dataclasses as d
8
7
  import logging as l
9
- import re as r
10
- from html.parser import HTMLParser as html_parser_t
11
8
  from io import IOBase as io_base_t
12
9
  from pathlib import Path as path_t
13
10
 
14
- from logger_36.catalog.config.optional import RICH_IS_AVAILABLE
15
11
  from logger_36.instance.logger import L
16
12
 
17
- if RICH_IS_AVAILABLE:
18
- from rich.console import Console as console_t # noqa
19
- else:
20
- console_t = None
21
-
22
-
23
- _BODY_END_PATTERN = r"</[bB][oO][dD][yY]>(.|\n)*$"
24
-
25
-
26
- @d.dataclass(slots=True, repr=False, eq=False)
27
- class html_reader_t(html_parser_t):
28
- source: str = ""
29
- inside_body: bool = d.field(init=False, default=False)
30
- body_position_start: tuple[int, int] = d.field(init=False, default=(-1, -1))
31
- body_position_end: tuple[int, int] = d.field(init=False, default=(-1, -1))
32
- pieces: list[str] = d.field(init=False, default_factory=list)
33
-
34
- def __post_init__(self) -> None:
35
- """"""
36
- html_parser_t.__init__(self)
37
- self.source = self.source.strip()
38
- self.feed(self.source)
39
-
40
- def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]], /) -> None:
41
- """"""
42
- if tag == "body":
43
- self.body_position_start = self.getpos()
44
- self.inside_body = True
45
-
46
- def handle_endtag(self, tag: str, /) -> None:
47
- """"""
48
- if tag == "body":
49
- self.body_position_end = self.getpos()
50
- self.inside_body = False
51
-
52
- def handle_data(self, data: str, /) -> None:
53
- """"""
54
- if self.inside_body:
55
- self.pieces.append(data)
56
-
57
- @property
58
- def body(self) -> str:
59
- """"""
60
- output = self.source.splitlines()
61
- output = "\n".join(
62
- output[self.body_position_start[0] : (self.body_position_end[0] + 1)]
63
- )
64
- output = output[self.body_position_start[1] :]
65
- output = r.sub(_BODY_END_PATTERN, "", output, count=1)
66
-
67
- return output.strip()
68
-
69
- @property
70
- def body_as_text(self) -> str:
71
- """"""
72
- return "".join(self.pieces).strip()
73
-
74
13
 
75
14
  def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
76
15
  """
@@ -78,18 +17,15 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
78
17
  """
79
18
  cannot_save = "Cannot save logging record as HTML"
80
19
 
81
- if console_t is None:
82
- L.warning(f"{cannot_save}: The Rich console cannot be imported.")
83
- return
84
-
85
20
  if path is None:
86
21
  for handler in L.handlers:
87
22
  if isinstance(handler, l.FileHandler):
88
23
  path = path_t(handler.baseFilename).with_suffix(".htm")
89
24
  break
90
- if path is None:
25
+ else:
91
26
  L.warning(f"{cannot_save}: No file handler to build a filename from.")
92
27
  return
28
+
93
29
  if path.exists():
94
30
  L.warning(
95
31
  f'{cannot_save}: Automatically generated path "{path}" already exists.'
@@ -103,18 +39,15 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
103
39
  L.warning(f'{cannot_save}: File "{path}" already exists.')
104
40
  return
105
41
 
106
- for handler in L.handlers:
107
- console = getattr(handler, "console", None)
108
- if isinstance(console, console_t) and console.record:
109
- html = console.export_html()
110
- if actual_file:
111
- with open(path, "w") as accessor:
112
- accessor.write(html)
113
- else:
114
- path.write(html)
115
- break
42
+ html = L.past_logs_as_HTML
43
+ if html is None:
44
+ L.warning(f"{cannot_save}: No handler could provide an HTML output.")
116
45
  else:
117
- L.warning(f"{cannot_save}: No handler has a RICH console with recording ON.")
46
+ if actual_file:
47
+ with open(path, "w") as accessor:
48
+ accessor.write(html)
49
+ else:
50
+ path.write(html)
118
51
 
119
52
 
120
53
  """
logger_36/type/handler.py CHANGED
@@ -22,14 +22,11 @@ from logger_36.task.format.message import MessageWithActualExpected
22
22
  from logger_36.task.measure.chronos import TimeStamp
23
23
  from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
24
24
 
25
-
26
- @h.runtime_checkable
27
- class message_from_record_raw_p(h.Protocol):
28
- def __call__(self, record: l.LogRecord, /) -> str: ...
25
+ MessageFromRecordRaw_h = h.Callable[[l.LogRecord], str]
29
26
 
30
27
 
31
28
  @h.runtime_checkable
32
- class message_from_record_preprocessed_p(h.Protocol):
29
+ class MessageFromRecordPreprocessed_p(h.Protocol):
33
30
  def __call__(
34
31
  self,
35
32
  record: l.LogRecord,
@@ -39,7 +36,7 @@ class message_from_record_preprocessed_p(h.Protocol):
39
36
  ) -> str: ...
40
37
 
41
38
 
42
- message_from_record_h = message_from_record_raw_p | message_from_record_preprocessed_p
39
+ MessageFromRecord_h = MessageFromRecordRaw_h | MessageFromRecordPreprocessed_p
43
40
 
44
41
  _MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
45
42
 
@@ -49,7 +46,7 @@ class handler_extension_t:
49
46
  name: str | None = None
50
47
  should_store_memory_usage: bool = False
51
48
  message_width: int = -1
52
- MessageFromRecord: message_from_record_h = d.field(init=False)
49
+ MessageFromRecord: MessageFromRecord_h = d.field(init=False)
53
50
 
54
51
  handler: d.InitVar[l.Handler | None] = None
55
52
  level: d.InitVar[int] = l.NOTSET
logger_36/type/logger.py CHANGED
@@ -7,6 +7,7 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  import dataclasses as d
8
8
  import logging as l
9
9
  import sys as s
10
+ import textwrap as text
10
11
  import traceback as tcbk
11
12
  import types as t
12
13
  import typing as h
@@ -32,15 +33,17 @@ from logger_36.constant.logger import (
32
33
  WARNING_TYPE_COMPILED_PATTERN,
33
34
  )
34
35
  from logger_36.constant.memory import UNKNOWN_MEMORY_USAGE
35
- from logger_36.constant.message import TIME_LENGTH_m_1, expected_op_h
36
+ from logger_36.constant.message import LINE_INDENT, TIME_LENGTH_m_1, expected_op_h
37
+ from logger_36.constant.path import PROJECT_FILE_RELATIVE
36
38
  from logger_36.constant.record import (
37
39
  HIDE_WHERE_ATTR,
38
40
  SHOW_W_RULE_ATTR,
39
41
  STORE_MEMORY_ATTR,
40
42
  )
41
43
  from logger_36.exception import OverrideExceptionFormat
42
- from logger_36.handler import AddRichConsoleHandler
44
+ from logger_36.handler import AddConsoleHandler, AddFileHandler, AddRichConsoleHandler
43
45
  from logger_36.task.format.message import MessageWithActualExpected
46
+ from logger_36.task.format.rule import RuleAsText
44
47
  from logger_36.task.measure.chronos import ElapsedTime
45
48
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
46
49
  from logger_36.type.issue import NewIssue, issue_t
@@ -84,6 +87,51 @@ class logger_t(base_t):
84
87
  init=False, default_factory=dict
85
88
  )
86
89
 
90
+ @property
91
+ def intercepts_warnings(self) -> bool:
92
+ """"""
93
+ return self.intercepted_wrn_handle is not None
94
+
95
+ @property
96
+ def intercepts_logs(self) -> bool:
97
+ """"""
98
+ return self.intercepted_log_handles.__len__() > 0
99
+
100
+ @property
101
+ def has_staged_issues(self) -> bool:
102
+ """"""
103
+ return self.staged_issues.__len__() > 0
104
+
105
+ @property
106
+ def past_logs_as_HTML(self) -> str | None:
107
+ """
108
+ From the first handler found with the given functionality, if any.
109
+ """
110
+ for handler in self.handlers:
111
+ past_logs_as_HTML = getattr(handler, "past_logs_as_HTML", None)
112
+ if past_logs_as_HTML is not None:
113
+ return past_logs_as_HTML
114
+
115
+ return None
116
+
117
+ @property
118
+ def max_memory_usage(self) -> int:
119
+ """"""
120
+ if self.memory_usages.__len__() > 0:
121
+ return max(tuple(zip(*self.memory_usages))[1])
122
+ return UNKNOWN_MEMORY_USAGE
123
+
124
+ @property
125
+ def max_memory_usage_full(self) -> tuple[str, int]:
126
+ """"""
127
+ if self.memory_usages.__len__() > 0:
128
+ where_s, usages = zip(*self.memory_usages)
129
+ max_usage = max(usages)
130
+
131
+ return where_s[usages.index(max_usage)], max_usage
132
+
133
+ return "?", UNKNOWN_MEMORY_USAGE
134
+
87
135
  def __post_init__(
88
136
  self, name_: str, level_: int, activate_wrn_interceptions: bool
89
137
  ) -> None:
@@ -95,11 +143,92 @@ class logger_t(base_t):
95
143
  for level in l.getLevelNamesMapping().values():
96
144
  self.events[level] = 0
97
145
 
146
+ self.info(f'New logger "{self.name}" for "{PROJECT_FILE_RELATIVE}"')
147
+
98
148
  if activate_wrn_interceptions:
99
- self._ActivateWarningInterceptions()
149
+ self.ToggleWarningInterceptions(True)
100
150
  if self.exit_on_error:
101
151
  self.exit_on_critical = True
102
152
 
153
+ def handle(self, record: l.LogRecord, /) -> None:
154
+ """"""
155
+ elapsed_time, now = ElapsedTime(should_return_now=True)
156
+
157
+ if (self.on_hold.__len__() > 0) and not self.should_hold_messages:
158
+ for held in self.on_hold:
159
+ base_t.handle(self, held)
160
+ self.on_hold.clear()
161
+
162
+ if (date := now.date()) != self.last_message_date:
163
+ self.last_message_date = date
164
+ # levelno: Added for management by logging.Logger.handle.
165
+ date_record = l.makeLogRecord(
166
+ {
167
+ "name": self.name,
168
+ "levelno": l.INFO,
169
+ "msg": f"DATE: {date.strftime(DATE_FORMAT)}",
170
+ SHOW_W_RULE_ATTR: True,
171
+ }
172
+ )
173
+ if self.should_hold_messages:
174
+ self.on_hold.append(date_record)
175
+ else:
176
+ base_t.handle(self, date_record)
177
+
178
+ # When.
179
+ if now - self.last_message_now > LONG_ENOUGH:
180
+ record.when_or_elapsed = now.strftime(TIME_FORMAT)
181
+ else:
182
+ record.when_or_elapsed = (
183
+ f"{ELAPSED_TIME_SEPARATOR}{elapsed_time:.<{TIME_LENGTH_m_1}}"
184
+ )
185
+ self.last_message_now = now
186
+
187
+ # Where.
188
+ # Memory usage is also stored if there are no handlers yet, just in case.
189
+ should_store_where = self.any_handler_stores_memory or not self.hasHandlers()
190
+ should_show_where = (record.levelno != l.INFO) and not hasattr(
191
+ record, HIDE_WHERE_ATTR
192
+ )
193
+ if should_store_where or should_show_where:
194
+ module = path_t(record.pathname)
195
+ for path in s.path:
196
+ if module.is_relative_to(path):
197
+ module = module.relative_to(path).with_suffix("")
198
+ module = str(module).replace(FOLDER_SEPARATOR, ".")
199
+ break
200
+ else:
201
+ module = record.module
202
+ where = f"{module}:{record.funcName}:{record.lineno}"
203
+ if should_show_where:
204
+ record.where = where
205
+ else:
206
+ where = None
207
+
208
+ # How.
209
+ record.level_first_letter = record.levelname[0]
210
+
211
+ # What.
212
+ if not isinstance(record.msg, str):
213
+ record.msg = str(record.msg)
214
+
215
+ if self.should_hold_messages:
216
+ self.on_hold.append(record)
217
+ else:
218
+ base_t.handle(self, record)
219
+
220
+ if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
221
+ self.exit_on_error and (record.levelno is l.ERROR)
222
+ ):
223
+ # Also works if self.exit_on_error and record.levelno is l.CRITICAL since
224
+ # __post_init__ set self.exit_on_critical if self.exit_on_error.
225
+ s.exit(1)
226
+
227
+ self.events[record.levelno] += 1
228
+
229
+ if should_store_where:
230
+ self.memory_usages.append((where, CurrentMemoryUsage()))
231
+
103
232
  def SetLevel(
104
233
  self,
105
234
  level: int,
@@ -133,32 +262,82 @@ class logger_t(base_t):
133
262
  )
134
263
  )
135
264
 
136
- def MakeRich(self, *, alternating_lines: int = 2) -> None:
265
+ def MakeMonochrome(
266
+ self,
267
+ *,
268
+ should_intercept_logs: bool = True,
269
+ should_override_exceptions: bool = True,
270
+ ) -> None:
271
+ """"""
272
+ self._MakePreamble(
273
+ should_intercept_logs=should_intercept_logs,
274
+ should_override_exceptions=should_override_exceptions,
275
+ )
276
+ AddConsoleHandler(self)
277
+
278
+ def MakeRich(
279
+ self,
280
+ *,
281
+ alternating_lines: int = 2,
282
+ should_intercept_logs: bool = True,
283
+ should_override_exceptions: bool = True,
284
+ ) -> None:
137
285
  """"""
138
- OverrideExceptionFormat()
286
+ self._MakePreamble(
287
+ should_intercept_logs=should_intercept_logs,
288
+ should_override_exceptions=should_override_exceptions,
289
+ )
139
290
  AddRichConsoleHandler(self, alternating_lines=alternating_lines)
140
291
 
292
+ def MakePermanent(
293
+ self,
294
+ path: str | path_t,
295
+ /,
296
+ *,
297
+ should_intercept_logs: bool = True,
298
+ should_override_exceptions: bool = True,
299
+ ) -> None:
300
+ """"""
301
+ self._MakePreamble(
302
+ should_intercept_logs=should_intercept_logs,
303
+ should_override_exceptions=should_override_exceptions,
304
+ )
305
+ AddFileHandler(self, path)
306
+
307
+ def _MakePreamble(
308
+ self,
309
+ *,
310
+ should_intercept_logs: bool = True,
311
+ should_override_exceptions: bool = True,
312
+ ) -> None:
313
+ """"""
314
+ if should_override_exceptions:
315
+ OverrideExceptionFormat()
316
+ if should_intercept_logs:
317
+ self.ToggleLogInterceptions(True)
318
+
141
319
  def ResetEventCounts(self) -> None:
142
320
  """"""
143
321
  for level in self.events:
144
322
  self.events[level] = 0
145
323
 
146
- def _ActivateWarningInterceptions(self) -> None:
324
+ def ToggleWarningInterceptions(self, state: bool, /) -> None:
147
325
  """
148
326
  The log message will not appear if called from __post_init__ since there are no
149
327
  handlers yet.
150
328
  """
151
- if self.intercepted_wrn_handle is None:
329
+ if state:
330
+ assert not self.intercepts_warnings
331
+
152
332
  logger = l.getLogger(WARNING_LOGGER_NAME)
153
333
  self.intercepted_wrn_handle = logger.handle
154
334
  logger.handle = t.MethodType(_HandleForWarnings(self), logger)
155
335
 
156
336
  l.captureWarnings(True)
157
337
  self.info("Warning Interception: ON")
338
+ else:
339
+ assert self.intercepts_warnings
158
340
 
159
- def _DeactivateWarningInterceptions(self) -> None:
160
- """"""
161
- if self.intercepted_wrn_handle is not None:
162
341
  logger = l.getLogger(WARNING_LOGGER_NAME)
163
342
  logger.handle = self.intercepted_wrn_handle
164
343
  self.intercepted_wrn_handle = None
@@ -166,18 +345,12 @@ class logger_t(base_t):
166
345
  l.captureWarnings(False)
167
346
  self.info("Warning Interception: OFF")
168
347
 
169
- def ToggleWarningInterceptions(self, state: bool, /) -> None:
170
- """"""
171
- if state:
172
- self._ActivateWarningInterceptions()
173
- else:
174
- self._DeactivateWarningInterceptions()
175
-
176
348
  def ToggleLogInterceptions(self, state: bool, /) -> None:
177
349
  """"""
178
350
  if state:
179
- self.ToggleLogInterceptions(False)
351
+ assert not self.intercepts_logs
180
352
 
353
+ # Note: Alternative to self.manager is logging.root.manager.
181
354
  all_loggers = [l.getLogger()] + [
182
355
  l.getLogger(_nme)
183
356
  for _nme in self.manager.loggerDict
@@ -193,31 +366,15 @@ class logger_t(base_t):
193
366
  if intercepted.__len__() > 0:
194
367
  as_str = ", ".join(intercepted)
195
368
  self.info(f"Now Intercepting LOGs from: {as_str}")
196
- elif self.intercepted_log_handles.__len__() > 0:
369
+ else:
370
+ assert self.intercepts_logs
371
+
197
372
  for name, handle in self.intercepted_log_handles.items():
198
373
  logger = l.getLogger(name)
199
374
  logger.handle = handle
200
375
  self.intercepted_log_handles.clear()
201
376
  self.info("Log Interception: OFF")
202
377
 
203
- @property
204
- def max_memory_usage(self) -> int:
205
- """"""
206
- if self.memory_usages.__len__() > 0:
207
- return max(tuple(zip(*self.memory_usages))[1])
208
- return UNKNOWN_MEMORY_USAGE
209
-
210
- @property
211
- def max_memory_usage_full(self) -> tuple[str, int]:
212
- """"""
213
- if self.memory_usages.__len__() > 0:
214
- where_s, usages = zip(*self.memory_usages)
215
- max_usage = max(usages)
216
-
217
- return where_s[usages.index(max_usage)], max_usage
218
-
219
- return "?", UNKNOWN_MEMORY_USAGE
220
-
221
378
  def AddHandler(
222
379
  self, handler: l.Handler, /, *, should_hold_messages: bool = False
223
380
  ) -> None:
@@ -244,85 +401,6 @@ class logger_t(base_t):
244
401
  f"level {handler.level}={l.getLevelName(handler.level)}{path}",
245
402
  )
246
403
 
247
- def handle(self, record: l.LogRecord, /) -> None:
248
- """"""
249
- elapsed_time, now = ElapsedTime(should_return_now=True)
250
-
251
- if (self.on_hold.__len__() > 0) and not self.should_hold_messages:
252
- for held in self.on_hold:
253
- base_t.handle(self, held)
254
- self.on_hold.clear()
255
-
256
- if (date := now.date()) != self.last_message_date:
257
- self.last_message_date = date
258
- # levelno: Added for management by logging.Logger.handle.
259
- date_record = l.makeLogRecord(
260
- {
261
- "name": self.name,
262
- "levelno": l.INFO,
263
- "msg": f"DATE: {date.strftime(DATE_FORMAT)}",
264
- SHOW_W_RULE_ATTR: True,
265
- }
266
- )
267
- if self.should_hold_messages:
268
- self.on_hold.append(date_record)
269
- else:
270
- base_t.handle(self, date_record)
271
-
272
- # When.
273
- if now - self.last_message_now > LONG_ENOUGH:
274
- record.when_or_elapsed = now.strftime(TIME_FORMAT)
275
- else:
276
- record.when_or_elapsed = (
277
- f"{ELAPSED_TIME_SEPARATOR}{elapsed_time:.<{TIME_LENGTH_m_1}}"
278
- )
279
- self.last_message_now = now
280
-
281
- # Where.
282
- # Memory usage is also stored if there are no handlers yet, just in case.
283
- should_store_where = self.any_handler_stores_memory or not self.hasHandlers()
284
- should_show_where = (record.levelno != l.INFO) and not hasattr(
285
- record, HIDE_WHERE_ATTR
286
- )
287
- if should_store_where or should_show_where:
288
- module = path_t(record.pathname)
289
- for path in s.path:
290
- if module.is_relative_to(path):
291
- module = module.relative_to(path).with_suffix("")
292
- module = str(module).replace(FOLDER_SEPARATOR, ".")
293
- break
294
- else:
295
- module = record.module
296
- where = f"{module}:{record.funcName}:{record.lineno}"
297
- if should_show_where:
298
- record.where = where
299
- else:
300
- where = None
301
-
302
- # How.
303
- record.level_first_letter = record.levelname[0]
304
-
305
- # What.
306
- if not isinstance(record.msg, str):
307
- record.msg = str(record.msg)
308
-
309
- if self.should_hold_messages:
310
- self.on_hold.append(record)
311
- else:
312
- base_t.handle(self, record)
313
-
314
- if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
315
- self.exit_on_error and (record.levelno is l.ERROR)
316
- ):
317
- # Also works if self.exit_on_error and record.levelno is l.CRITICAL since
318
- # __post_init__ set self.exit_on_critical if self.exit_on_error.
319
- s.exit(1)
320
-
321
- self.events[record.levelno] += 1
322
-
323
- if should_store_where:
324
- self.memory_usages.append((where, CurrentMemoryUsage()))
325
-
326
404
  def Log(
327
405
  self,
328
406
  message: str,
@@ -370,22 +448,31 @@ class logger_t(base_t):
370
448
  message = f"{type(exception).__name__}:\n{formatted}"
371
449
  self.log(level, message)
372
450
 
373
- def ShowMessage(self, message: str, /, *, indented: bool = False) -> None:
451
+ def LogAsIs(self, message: str, /, *, indented: bool = False) -> None:
374
452
  """
375
453
  See documentation of
376
- logger_36.catalog.handler.generic.generic_handler_t.ShowMessage.
454
+ logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
377
455
  """
456
+ if indented:
457
+ message = text.indent(message, LINE_INDENT)
458
+
378
459
  for handler in self.handlers:
379
- ShowMessage = getattr(handler, "ShowMessage", None)
380
- if ShowMessage is not None:
381
- ShowMessage(message, indented=indented)
460
+ if (LogAsIs := getattr(handler, "LogAsIs", None)) is None:
461
+ self.info(message)
462
+ else:
463
+ LogAsIs(message)
382
464
 
383
- def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
465
+ raw_info = LogAsIs # To follow the convention of the logging methods info, error...
466
+
467
+ def DisplayRule(
468
+ self, /, *, message: str | None = None, color: str = "white"
469
+ ) -> None:
384
470
  """"""
385
471
  for handler in self.handlers:
386
- DisplayRule = getattr(handler, "DisplayRule", None)
387
- if DisplayRule is not None:
388
- DisplayRule(text=text, color=color)
472
+ if (DisplayRule := getattr(handler, "DisplayRule", None)) is None:
473
+ self.info(RuleAsText(message))
474
+ else:
475
+ DisplayRule(text=message, color=color)
389
476
 
390
477
  def AddContextLevel(self, new_level: str, /) -> None:
391
478
  """"""
@@ -427,11 +514,6 @@ class logger_t(base_t):
427
514
  )
428
515
  self.staged_issues.append(issue)
429
516
 
430
- @property
431
- def has_staged_issues(self) -> bool:
432
- """"""
433
- return self.staged_issues.__len__() > 0
434
-
435
517
  def CommitIssues(
436
518
  self,
437
519
  /,
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.6"
7
+ __version__ = "2025.8"
8
8
 
9
9
  """
10
10
  COPYRIGHT NOTICE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: logger-36
3
- Version: 2025.6
3
+ Version: 2025.8
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,21 +1,21 @@
1
1
  logger_36/__init__.py,sha256=3BtAgxFb14e9zzC5fXwqSQxstsd3BO0b_KVu3_wbLwg,2592
2
2
  logger_36/content.py,sha256=clHYYUKa8n4qef6PVlUV4mFHRRf6fnm9wEd2fu9oagA,2381
3
- logger_36/exception.py,sha256=YNT4S_gvz7WQUSWILFAwI01h5-BVw1d0oT97zjnw3dU,4575
3
+ logger_36/exception.py,sha256=BQm9maX9CMjpqN26cEsArPCcX37LqYtQSrUZ3fN8tbU,4613
4
4
  logger_36/gpu.py,sha256=BOumedCAPWvCo7J-KJ3XE-jr5S0KSmgcFv_S4QKRPO8,2252
5
- logger_36/handler.py,sha256=hEKhfsKHhg8UqSrBTVe7w0lubf9SSkuGMi81oXwjPoo,6326
5
+ logger_36/handler.py,sha256=pIwunW-_aSB-SrdlvVmq61nOTH03deKIVcJa4Sz_hkc,6304
6
6
  logger_36/memory.py,sha256=szJVk4UTXsbYv3B-W9LFttf1F3j86GXHsKgEUOsXKl4,2743
7
7
  logger_36/storage.py,sha256=sCxkHQH4xMaseweK1p2M1j0j2PxNPpy9MytPdg1sKiQ,2239
8
8
  logger_36/system.py,sha256=cgOMF_OneYeIJDMbIbIDx96EZss2uAdkk8QofOC7O1U,2251
9
9
  logger_36/time.py,sha256=Uw1jQtY1njsRuIPRAXX44v4nPOo84MSBu_WK_YCRzQs,2324
10
- logger_36/version.py,sha256=eAXepMLNtMw0muvMucofZ1LJ6BBOIZKujqvtOs93Qho,2205
10
+ logger_36/version.py,sha256=dIKiLoaPWfEeTZKKzEklIyLlE8cFbHUq2h72W5GmOiI,2205
11
11
  logger_36/api/logger.py,sha256=TE3ATbymeWX-wBKBFkVz2FxUyJnaqY7vzFwAONVsp2o,2233
12
- logger_36/api/storage.py,sha256=KT52AGR37nsMrhKTVfG8R-Dc7lmCXjWML18cOqqCXZY,2239
12
+ logger_36/api/storage.py,sha256=v1iywLEItJCz18F_nJ20OnlpCpLdA-7EhlvqdLu42js,2243
13
13
  logger_36/catalog/config/console_rich.py,sha256=lAa5Ev5BhXvmQzfIt1FNihMNUQJFlXaIzNanAMdgtd0,2861
14
14
  logger_36/catalog/config/optional.py,sha256=HaN6mbx7gHBBppNvUw1ckhYTOrlYqb-b_r0mzPcHPjM,2398
15
- logger_36/catalog/handler/console.py,sha256=tLMroj95xt_IC9Vra4M5TCDRKrU4mSkfvUg9GliQ_l8,4402
16
- logger_36/catalog/handler/console_rich.py,sha256=gkRnbhwyFFrS4nGvgUn9lElh7YNCAaC2-7dAex5bHzI,8592
17
- logger_36/catalog/handler/file.py,sha256=hCb21GahjeRbBJGtPsa2PPme7LmlZ8ftF3Q_BMKRiA8,4865
18
- logger_36/catalog/handler/generic.py,sha256=Y6rxfKkWe9cA196ntt6p0_rcjEeESGFCKWVlo1WIzW0,9335
15
+ logger_36/catalog/handler/console.py,sha256=s2DBcDK9To-wVS228RsDDPmOPxlIbVnQbZINfIK2TP0,4150
16
+ logger_36/catalog/handler/console_rich.py,sha256=uYCoxPBPypaioSibC68Vw9r1XoY8AB5pAq2-RV1a4wQ,8544
17
+ logger_36/catalog/handler/file.py,sha256=2qbsI3UHxqEm9WiCMkAm20hA2qXth2wKnakazVbwrBs,4613
18
+ logger_36/catalog/handler/generic.py,sha256=y-f6HY5xppoHYYnej0qOQT3BI0Gam_0W1_bIHCk5nn0,9212
19
19
  logger_36/catalog/logger/chronos.py,sha256=ocY13f98EfknU7wZCv0FS9Xb7pTNaWCPSusXFIEvEd4,2437
20
20
  logger_36/catalog/logger/gpu.py,sha256=lzrkqrMnXsszRB_TiHFqnNNI7JhNat8qL2OSlnHDe5c,3412
21
21
  logger_36/catalog/logger/memory.py,sha256=CWhr2J4BqArJxzH6tS-ZThr-rYPAQGtuLn0pP7Iryfg,4685
@@ -31,22 +31,24 @@ logger_36/constant/issue.py,sha256=01l8itRPWGS5F6gXtsXUJgGR-4lS1Eu3_YeKC-khKLw,2
31
31
  logger_36/constant/logger.py,sha256=2qRkteblpbHrq9x0aiw9MPquyXrSRd6_yMQnPEhFp2U,2468
32
32
  logger_36/constant/memory.py,sha256=ZL1MwbdtNsrCrOwzEyfTsfOoOsRBTJtbbf3otHGnxXo,2343
33
33
  logger_36/constant/message.py,sha256=Ys_CAyhENlT8Z3rr-AxO4hjdl1jLsKzVSPQ8wqLOCPQ,2838
34
+ logger_36/constant/path.py,sha256=fKJn2vGj012BU5DFRetDFus_tKMty2q_WL0J2KrXdCo,2731
34
35
  logger_36/constant/record.py,sha256=9Q28lVH_s0og4v74delgwIPAJ9G28I5rBM-brXcoY80,2308
35
36
  logger_36/constant/system.py,sha256=G2mzBTxRXoJMxb53TnmBaceMJC_q3WonoCG7y6nC_R8,2430
37
+ logger_36/extension/html_.py,sha256=J9EX8-Rotq9i8bZ9U-dIpXv5gKLLnLmWqdDy4XayT1Q,3868
36
38
  logger_36/instance/logger.py,sha256=oTw5svRzKRJKvGrrZUtutJIOjp5UISft3fl0Ze7DOBE,2241
37
39
  logger_36/instance/loggers.py,sha256=RCWpC1NPAf6vXnFc9NqsSALv-x-FEzcH6k_OlxTxeQk,2251
38
40
  logger_36/task/inspection.py,sha256=KZzmQyREQ6VmBWCLyNIYIOOISW9C_fC9TWTSX90zGDk,5019
39
- logger_36/task/storage.py,sha256=2B4OU7RqpUe98-pY9fadfnW8aFwxtsLSRGKkBtGWn-k,5686
41
+ logger_36/task/storage.py,sha256=T96JZT5Tmrt_-Kqf_WKweTvJYPX6lmPZZkJzCqyVPcI,3502
40
42
  logger_36/task/format/memory.py,sha256=jpQS8tAdxy7GM_FzqEIJUU3m-6O9iX-jiyO7gx5YwR8,4266
41
43
  logger_36/task/format/message.py,sha256=T2V2gUlUQqSojyRrz4I4uAHwNe6eBEsuAe6V-LTyx0k,3867
42
44
  logger_36/task/format/rule.py,sha256=vkf-HivFb4VqV2GeOPVqMAp99krtziI-kXhox3UVnzw,2873
43
45
  logger_36/task/measure/chronos.py,sha256=1kVhu6jZlNAtNWQQh8ZVuRwZIAC9gGz3_ul1tn0t4Yw,3055
44
46
  logger_36/task/measure/memory.py,sha256=OjU5EYFH8SnzlCQKAoiXvauUlwQYOrH34jFXTVYF0jE,2517
45
- logger_36/type/handler.py,sha256=7M8f8U-zedzo-1AR7iIxHnzRHsuDj9IM0WtrqG2GMY0,6902
47
+ logger_36/type/handler.py,sha256=-myl7uBMOzkwCs1u4ehuYlQa9F6909jmnL2v_eQN5ag,6819
46
48
  logger_36/type/issue.py,sha256=2rGsFqaQJCbeml9xN08mN_nK79L8qscaS_0ws36Y0bI,3214
47
- logger_36/type/logger.py,sha256=4cCa-u1ktDdlj96aOWz6OsDJ_WhuN7eKEEALIHDS9NY,20451
49
+ logger_36/type/logger.py,sha256=J6J87-RkH0RwiROeDQBnoTxLO5eXz32-GKjtxqyRXwk,22743
48
50
  logger_36/type/loggers.py,sha256=znqxWBnfQxvkg3VUfbTUvt3S6Kq0DAzWWepxQDt9suI,2871
49
- logger_36-2025.6.dist-info/METADATA,sha256=AQ37dDMn1ZG7E3X5x29OR4_GaeG_FWcVkjew0B9hbzw,6505
50
- logger_36-2025.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
51
- logger_36-2025.6.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
52
- logger_36-2025.6.dist-info/RECORD,,
51
+ logger_36-2025.8.dist-info/METADATA,sha256=R6yfIOmsKd4dJgf9EwdP7Mv1TtSlLD04g5liwcKXPOU,6505
52
+ logger_36-2025.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
53
+ logger_36-2025.8.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
54
+ logger_36-2025.8.dist-info/RECORD,,