logger-36 2025.22__py3-none-any.whl → 2025.24__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/memory.py CHANGED
@@ -4,15 +4,6 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- from logger_36.catalog.logger.memory import ( # noqa
8
- LogMaximumMemoryUsage,
9
- LogMemoryUsages,
10
- )
11
- from logger_36.task.format.memory import FormattedUsage as FormattedMemoryUsage # noqa
12
- from logger_36.task.format.memory import ( # noqa
13
- FormattedUsageWithAutoUnit as FormattedMemoryUsageWithAutoUnit,
14
- )
15
- from logger_36.task.format.memory import UsageBar as MemoryUsageBar # noqa
16
7
  from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage # noqa
17
8
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage # noqa
18
9
 
logger_36/api/storage.py CHANGED
@@ -4,7 +4,6 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- from logger_36.extension.html_ import html_content_t # noqa
8
7
  from logger_36.task.storage import SaveLOGasHTML # noqa
9
8
 
10
9
  """
logger_36/api/time.py CHANGED
@@ -4,7 +4,6 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- from logger_36.catalog.logger.chronos import LogElapsedTime # noqa
8
7
  from logger_36.task.measure.chronos import ElapsedTime, TimeStamp # noqa
9
8
 
10
9
  """
@@ -8,7 +8,7 @@ import logging as l
8
8
  import sys as s
9
9
  import typing as h
10
10
 
11
- from logger_36.type.handler import handler_t as base_t
11
+ from logger_36.type.handler import non_file_handler_t as base_t
12
12
 
13
13
 
14
14
  class console_handler_t(base_t):
@@ -26,7 +26,7 @@ from logger_36.catalog.config.console_rich import (
26
26
  )
27
27
  from logger_36.config.message import ACTUAL_PATTERNS, EXPECTED_PATTERNS, WHERE_SEPARATOR
28
28
  from logger_36.constant.message import CONTEXT_LENGTH
29
- from logger_36.type.handler import handler_t as base_t
29
+ from logger_36.type.handler import non_file_handler_t as base_t
30
30
 
31
31
  _COMMON_TRACEBACK_ARGUMENTS = ("theme", "width")
32
32
  _EXCLUSIVE_TRACEBACK_ARGUMENTS = (
@@ -23,7 +23,7 @@ else:
23
23
  RULE_COLOR
24
24
  ) = HighlightedVersion = None
25
25
 
26
- from logger_36.type.handler import handler_t as base_t
26
+ from logger_36.type.handler import non_file_handler_t as base_t
27
27
 
28
28
 
29
29
  class generic_handler_t(base_t):
@@ -4,60 +4,68 @@ 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
- import re as r
9
- from html.parser import HTMLParser as base_t
7
+ import json
8
+ import logging as l
9
+ import typing as h
10
10
 
11
- _BODY_END_PATTERN = r"</[bB][oO][dD][yY]>(.|\n)*$"
11
+ from logger_36.type.handler import non_file_handler_t as base_t
12
12
 
13
+ formats_h = h.Literal["dict", "json", "message", "raw"]
13
14
 
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
15
 
22
- @property
23
- def body(self) -> str:
16
+ class memory_handler_t(base_t):
17
+ def __init__(
18
+ self, name: str | None, message_width: int, level: int, format_: formats_h
19
+ ) -> None:
24
20
  """"""
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:
21
+ assert format_ in h.get_args(formats_h)
22
+
23
+ base_t.__init__(self, name, message_width, None, level)
24
+
25
+ self.format_ = format_
26
+ self.records = []
27
+
28
+ @classmethod
29
+ def New(
30
+ cls,
31
+ /,
32
+ *,
33
+ name: str | None = None,
34
+ message_width: int = -1,
35
+ level: int = l.NOTSET,
36
+ format_: formats_h = "message",
37
+ **_,
38
+ ) -> h.Self:
36
39
  """"""
37
- return "".join(self.pieces).strip()
40
+ return cls(name, message_width, level, format_)
38
41
 
39
- def __post_init__(self) -> None:
42
+ def emit(self, record: l.LogRecord, /) -> None:
40
43
  """"""
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:
44
+ level = record.levelno
45
+
46
+ if self.format_ == "raw":
47
+ is_not_a_rule = True
48
+ elif self.format_ == "dict":
49
+ record = dict(record.__dict__)
50
+ is_not_a_rule = True
51
+ elif self.format_ == "json":
52
+ record = json.dumps(record.__dict__)
53
+ is_not_a_rule = True
54
+ else:
55
+ record, is_not_a_rule = self.MessageFromRecord(record)
56
+
57
+ self.records.append((level, record, is_not_a_rule))
58
+
59
+ def EmitMessage(self, message: str, /) -> None:
46
60
  """"""
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
+ if self.format_ == "message":
62
+ message_key = "msg"
63
+ else:
64
+ message_key = "message"
65
+ record = l.makeLogRecord(
66
+ {"name": "<UNKNOWN LOGGER>", "levelno": l.INFO, message_key: message}
67
+ )
68
+ self.emit(record)
61
69
 
62
70
 
63
71
  """
@@ -5,16 +5,17 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  from logger_36.constant.system import MAX_DETAIL_NAME_LENGTH, SYSTEM_DETAILS_AS_DICT
8
+ from logger_36.extension.inspection import Modules
8
9
  from logger_36.instance.logger import L
9
- from logger_36.task.inspection import Modules
10
10
  from logger_36.type.logger import logger_t
11
11
 
12
12
 
13
13
  def LogSystemDetails(
14
14
  *,
15
+ should_restrict_modules_to_loaded: bool = True,
15
16
  modules_with_version: bool = True,
16
17
  modules_formatted: bool = True,
17
- should_restrict_modules_to_loaded: bool = True,
18
+ indent: int = 4,
18
19
  logger: logger_t = L,
19
20
  ) -> None:
20
21
  """"""
@@ -23,10 +24,10 @@ def LogSystemDetails(
23
24
  for _key, _vle in SYSTEM_DETAILS_AS_DICT.items()
24
25
  )
25
26
  modules = Modules(
26
- modules_with_version,
27
- modules_formatted,
28
27
  only_loaded=should_restrict_modules_to_loaded,
29
- indent=4,
28
+ with_version=modules_with_version,
29
+ formatted=modules_formatted,
30
+ indent=indent,
30
31
  )
31
32
 
32
33
  logger.info(
@@ -4,7 +4,8 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- from logger_36.catalog.logger.system import LogSystemDetails # noqa
7
+ RULE_CHARACTER = "-"
8
+ DEFAULT_RULE_LENGTH = 50
8
9
 
9
10
  """
10
11
  COPYRIGHT NOTICE
@@ -4,7 +4,14 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- from logger_36.catalog.logger.gpu import LogGPURelatedDetails # noqa
7
+ from datetime import datetime as date_time_t
8
+
9
+ # This module is imported early. Therefore, the current date and time should be close
10
+ # enough to the real start date and time of the main script.
11
+ START_DATE_TIME = date_time_t.now()
12
+
13
+ DATE_TIME_ORIGIN = date_time_t.fromtimestamp(1970, None)
14
+ DATE_ORIGIN = DATE_TIME_ORIGIN.date()
8
15
 
9
16
  """
10
17
  COPYRIGHT NOTICE
@@ -12,15 +12,16 @@ USER_FOLDER = path_t.home()
12
12
 
13
13
  frame = e.stack(context=0)[-1] # -1=root caller.
14
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 = PROJECT_FILE.relative_to(USER_FOLDER)
15
+ LAUNCH_ROOT_FILE = path_t(frame.filename)
16
+ LAUNCH_FOLDER = LAUNCH_ROOT_FILE.parent
17
+
18
+ if LAUNCH_ROOT_FILE.is_relative_to(USER_FOLDER):
19
+ LAUNCH_ROOT_FILE_relative = LAUNCH_ROOT_FILE.relative_to(USER_FOLDER)
18
20
  else:
19
- PROJECT_FILE_RELATIVE = PROJECT_FILE
20
- PROJECT_FOLDER = PROJECT_FILE.parent
21
+ LAUNCH_ROOT_FILE_relative = LAUNCH_ROOT_FILE
21
22
  else:
22
- PROJECT_FILE = PROJECT_FILE_RELATIVE = "<unknown>"
23
- PROJECT_FOLDER = path_t(tmps.mkdtemp())
23
+ LAUNCH_ROOT_FILE = LAUNCH_ROOT_FILE_relative = "<unknown launch root file>"
24
+ LAUNCH_FOLDER = path_t(tmps.mkdtemp())
24
25
 
25
26
  """
26
27
  COPYRIGHT NOTICE
@@ -4,7 +4,6 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- INTERNAL_INFO_ATTR = "internal_info"
8
7
  SHOW_WHERE_ATTR = "should_show_where"
9
8
  SHOW_W_RULE_ATTR = "should_show_w_rule"
10
9
  WHEN_OR_ELAPSED_ATTR = "when_or_elapsed"
@@ -4,9 +4,9 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- RULE_CHARACTER = "-"
7
+ from logger_36.config.rule import DEFAULT_RULE_LENGTH, RULE_CHARACTER
8
+
8
9
  MIN_HALF_RULE_LENGTH = 4
9
- DEFAULT_RULE_LENGTH = 50
10
10
  DEFAULT_RULE = DEFAULT_RULE_LENGTH * RULE_CHARACTER
11
11
 
12
12
  """
@@ -4,14 +4,16 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- import importlib.metadata as mprt
8
7
  import pkgutil as pkgs
9
8
  import sys as s
10
- from types import FunctionType, MethodType
11
9
 
12
10
 
13
11
  def Modules(
14
- with_version: bool, formatted: bool, /, *, only_loaded: bool = True, indent: int = 0
12
+ *,
13
+ only_loaded: bool = True,
14
+ with_version: bool = True,
15
+ formatted: bool = False,
16
+ indent: int = 0,
15
17
  ) -> tuple[str, ...] | str:
16
18
  """"""
17
19
  output = []
@@ -61,23 +63,6 @@ def Modules(
61
63
  return tuple(output)
62
64
 
63
65
 
64
- def WhereFunction(function: FunctionType, /) -> str:
65
- """"""
66
- return f"{function.__module__}:{function.__name__}"
67
-
68
-
69
- def WhereMethod(instance: object, method: MethodType, /) -> str:
70
- """
71
- method: Could be a str instead, which would require changing method.__name__ into
72
- getattr(cls, method). But if the method name changes while forgetting to change the
73
- string in the call to WhereMethod accordingly, then an exception would be raised
74
- here.
75
- """
76
- cls = instance.__class__
77
-
78
- return f"{cls.__module__}:{cls.__name__}:{method.__name__}"
79
-
80
-
81
66
  def _ModulesUsingPkgUtil() -> tuple[str, ...]:
82
67
  """
83
68
  Returns more results than using importlib.
@@ -91,15 +76,16 @@ def _ModulesUsingPkgUtil() -> tuple[str, ...]:
91
76
  )
92
77
 
93
78
 
94
- def _ModulesUsingImportlib() -> tuple[str, ...]:
95
- """"""
96
- return tuple(
97
- sorted(
98
- _elm
99
- for _elm in mprt.packages_distributions()
100
- if (_elm[0] != "_") and ("__" not in _elm) and ("/" not in _elm)
101
- )
102
- )
79
+ # import importlib.metadata as mprt
80
+ # def _ModulesUsingImportlib() -> tuple[str, ...]:
81
+ # """"""
82
+ # return tuple(
83
+ # sorted(
84
+ # _elm
85
+ # for _elm in mprt.packages_distributions()
86
+ # if (_elm[0] != "_") and ("__" not in _elm) and ("/" not in _elm)
87
+ # )
88
+ # )
103
89
 
104
90
 
105
91
  """
@@ -0,0 +1,66 @@
1
+ """
2
+ Copyright CNRS (https://www.cnrs.fr/index.php/en)
3
+ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
+ SEE COPYRIGHT NOTICE BELOW
5
+ """
6
+
7
+ import logging as l
8
+ import sys as s
9
+ from os import sep as FOLDER_SEPARATOR
10
+ from pathlib import Path as path_t
11
+
12
+ from logger_36.constant.path import USER_FOLDER
13
+ from logger_36.constant.record import WHERE_ATTR
14
+
15
+
16
+ def RecordLocation(record: l.LogRecord, should_also_store: bool, /) -> str:
17
+ """"""
18
+ module = path_t(record.pathname)
19
+ for path in s.path:
20
+ if module.is_relative_to(path):
21
+ module = module.relative_to(path).with_suffix("")
22
+ module = str(module).replace(FOLDER_SEPARATOR, ".")
23
+ break
24
+ else:
25
+ if module.is_relative_to(USER_FOLDER):
26
+ module = module.relative_to(USER_FOLDER)
27
+
28
+ output = f"{module}:{record.funcName}:{record.lineno}"
29
+
30
+ if should_also_store:
31
+ setattr(record, WHERE_ATTR, output)
32
+
33
+ return output
34
+
35
+
36
+ """
37
+ COPYRIGHT NOTICE
38
+
39
+ This software is governed by the CeCILL license under French law and
40
+ abiding by the rules of distribution of free software. You can use,
41
+ modify and/ or redistribute the software under the terms of the CeCILL
42
+ license as circulated by CEA, CNRS and INRIA at the following URL
43
+ "http://www.cecill.info".
44
+
45
+ As a counterpart to the access to the source code and rights to copy,
46
+ modify and redistribute granted by the license, users are provided only
47
+ with a limited warranty and the software's author, the holder of the
48
+ economic rights, and the successive licensors have only limited
49
+ liability.
50
+
51
+ In this respect, the user's attention is drawn to the risks associated
52
+ with loading, using, modifying and/or developing or reproducing the
53
+ software by the user in light of its specific status of free software,
54
+ that may mean that it is complicated to manipulate, and that also
55
+ therefore means that it is reserved for developers and experienced
56
+ professionals having in-depth computer knowledge. Users are therefore
57
+ encouraged to load and test the software's suitability as regards their
58
+ requirements in conditions enabling the security of their systems and/or
59
+ data to be ensured and, more generally, to use and operate it in the
60
+ same conditions as regards security.
61
+
62
+ The fact that you are presently reading this means that you have had
63
+ knowledge of the CeCILL license and that you accept its terms.
64
+
65
+ SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
66
+ """
@@ -7,9 +7,7 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  import time
8
8
  from datetime import datetime as date_time_t
9
9
 
10
- # This module is imported early. Therefore, the current date and time should be close
11
- # enough to the real start time of the main script.
12
- _START_DATE_AND_TIME = date_time_t.now()
10
+ from logger_36.constant.date_time import START_DATE_TIME
13
11
 
14
12
 
15
13
  def TimeStamp(*, precision: str = "microseconds") -> str:
@@ -25,7 +23,7 @@ def TimeStamp(*, precision: str = "microseconds") -> str:
25
23
  def ElapsedTime(*, should_return_now: bool = False) -> str | tuple[str, date_time_t]:
26
24
  """"""
27
25
  now = date_time_t.now()
28
- elapsed_seconds = (now - _START_DATE_AND_TIME).total_seconds()
26
+ elapsed_seconds = (now - START_DATE_TIME).total_seconds()
29
27
  output = time.strftime("%H:%M:%S", time.gmtime(elapsed_seconds))
30
28
  while output.startswith("00:"):
31
29
  output = output.split(sep=":", maxsplit=1)[-1]
logger_36/task/storage.py CHANGED
@@ -5,30 +5,35 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  import logging as l
8
+ import os as o
9
+ import tempfile as tmps
8
10
  from io import IOBase as io_base_t
9
11
  from pathlib import Path as path_t
10
12
 
11
13
  from logger_36.constant.html import BODY_PLACEHOLDER, MINIMAL_HTML, TITLE_PLACEHOLDER
12
14
  from logger_36.instance.logger import L
15
+ from logger_36.type.logger import logger_t
13
16
 
14
17
 
15
- def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
18
+ def SaveLOGasHTML(
19
+ path: str | path_t | io_base_t | None = None, /, *, logger: logger_t = L
20
+ ) -> None:
16
21
  """
17
22
  From first console handler found.
18
23
  """
19
24
  cannot_save = "Cannot save logging record as HTML"
20
25
 
21
26
  if path is None:
22
- for handler in L.handlers:
27
+ for handler in logger.handlers:
23
28
  if isinstance(handler, l.FileHandler):
24
29
  path = path_t(handler.baseFilename).with_suffix(".htm")
25
30
  break
26
31
  else:
27
- L.warning(f"{cannot_save}: No file handler to build a filename from.")
32
+ logger.warning(f"{cannot_save}: No file handler to build a filename from.")
28
33
  return
29
34
 
30
35
  if path.exists():
31
- L.warning(
36
+ logger.warning(
32
37
  f'{cannot_save}: Automatically generated path "{path}" already exists.'
33
38
  )
34
39
  return
@@ -38,13 +43,45 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
38
43
  actual_file = isinstance(path, path_t)
39
44
 
40
45
  if actual_file and path.exists():
41
- L.warning(f'{cannot_save}: File "{path}" already exists.')
46
+ existing = path
47
+
48
+ accessor, path = tmps.mkstemp(suffix=".htm")
49
+ o.close(accessor)
50
+ path = path_t(path)
51
+
52
+ logger.warning(
53
+ f'File "{existing}" already exists: Saving LOG as HTML in {path} instead.'
54
+ )
55
+
56
+ for handler in logger.handlers:
57
+ records = getattr(handler, "records", None)
58
+ if isinstance(records, list) and (
59
+ (records.__len__() == 0)
60
+ or all(
61
+ isinstance(_, tuple)
62
+ and isinstance(_[0], int)
63
+ and isinstance(_[1], dict | str | l.LogRecord)
64
+ and isinstance(_[2], bool)
65
+ for _ in records
66
+ )
67
+ ):
68
+ break
69
+ else:
70
+ logger.warning(f"{cannot_save}: No handlers with recording capability found.")
42
71
  return
43
72
 
44
- body = "\n".join(map(_HighlightedEvent, L.recorded))
45
- html = MINIMAL_HTML.replace(TITLE_PLACEHOLDER, L.name).replace(
73
+ if records.__len__() == 0:
74
+ return
75
+
76
+ if isinstance(records[0][1], str):
77
+ records = map(_HighlightedEvent, records)
78
+ else:
79
+ records = map(lambda _: str(_[1]), records)
80
+ body = "\n".join(records)
81
+ html = MINIMAL_HTML.replace(TITLE_PLACEHOLDER, logger.name).replace(
46
82
  BODY_PLACEHOLDER, body
47
83
  )
84
+
48
85
  if actual_file:
49
86
  with open(path, "w") as accessor:
50
87
  accessor.write(html)
@@ -52,21 +89,25 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
52
89
  path.write(html)
53
90
 
54
91
 
55
- def _HighlightedEvent(event: tuple[int, str], /) -> str:
92
+ def _HighlightedEvent(event: tuple[int, str, bool], /) -> str:
56
93
  """"""
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"
94
+ level, message, is_not_a_rule = event
95
+
96
+ if is_not_a_rule:
97
+ if level == l.DEBUG:
98
+ color = "BlueViolet"
99
+ elif level == l.INFO:
100
+ color = "black"
101
+ elif level == l.WARNING:
102
+ color = "gold"
103
+ elif level == l.ERROR:
104
+ color = "orange"
105
+ elif level == l.CRITICAL:
106
+ color = "red"
107
+ else:
108
+ color = "black"
68
109
  else:
69
- color = "black"
110
+ color = "DarkTurquoise"
70
111
 
71
112
  return f'<span style="color:{color}">{message}</span>'
72
113
 
logger_36/type/handler.py CHANGED
@@ -15,14 +15,10 @@ from logger_36.config.message import (
15
15
  MESSAGE_MARKER,
16
16
  WHERE_SEPARATOR,
17
17
  )
18
+ from logger_36.config.rule import DEFAULT_RULE_LENGTH, RULE_CHARACTER
18
19
  from logger_36.constant.message import NEXT_LINE_PROLOGUE
19
20
  from logger_36.constant.record import SHOW_W_RULE_ATTR, WHEN_OR_ELAPSED_ATTR, WHERE_ATTR
20
- from logger_36.constant.rule import (
21
- DEFAULT_RULE,
22
- DEFAULT_RULE_LENGTH,
23
- MIN_HALF_RULE_LENGTH,
24
- RULE_CHARACTER,
25
- )
21
+ from logger_36.constant.rule import DEFAULT_RULE, MIN_HALF_RULE_LENGTH
26
22
  from logger_36.extension.line import WrappedLines
27
23
 
28
24
 
@@ -56,7 +52,7 @@ class extension_t:
56
52
  raise NotImplementedError
57
53
 
58
54
  def MessageFromRecord(
59
- self, record: l.LogRecord, /, *, rule_color: str | None = None
55
+ self, record: l.LogRecord, /, *, rule_color: str = "black"
60
56
  ) -> tuple[str, bool]:
61
57
  """
62
58
  The second returned value is is_not_a_rule.
@@ -122,7 +118,7 @@ class extension_t:
122
118
  self.EmitMessage(self.Rule(text=text, color=color))
123
119
 
124
120
 
125
- class handler_t(l.Handler, extension_t):
121
+ class non_file_handler_t(l.Handler, extension_t):
126
122
  def __init__(
127
123
  self,
128
124
  name: str | None,
@@ -160,10 +156,10 @@ class file_handler_t(l.FileHandler, extension_t):
160
156
  __post_init__(self, level)
161
157
 
162
158
 
163
- any_handler_t = handler_t | file_handler_t
159
+ handler_h = non_file_handler_t | file_handler_t
164
160
 
165
161
 
166
- def __post_init__(handler: any_handler_t, level: int) -> None:
162
+ def __post_init__(handler: handler_h, level: int) -> None:
167
163
  """"""
168
164
  handler.setLevel(level)
169
165
 
logger_36/type/logger.py CHANGED
@@ -15,7 +15,6 @@ import types as t
15
15
  import typing as h
16
16
  from datetime import date as date_t
17
17
  from datetime import datetime as date_time_t
18
- from os import sep as FOLDER_SEPARATOR
19
18
  from pathlib import Path as path_t
20
19
  from traceback import TracebackException as traceback_t
21
20
 
@@ -35,22 +34,20 @@ from logger_36.config.message import (
35
34
  TIME_FORMAT,
36
35
  WHERE_SEPARATOR,
37
36
  )
37
+ from logger_36.constant.date_time import DATE_ORIGIN, DATE_TIME_ORIGIN
38
38
  from logger_36.constant.generic import NOT_PASSED
39
39
  from logger_36.constant.issue import ISSUE_LEVEL_SEPARATOR, ORDER, order_h
40
40
  from logger_36.constant.logger import WARNING_LOGGER_NAME, WARNING_TYPE_COMPILED_PATTERN
41
41
  from logger_36.constant.memory import UNKNOWN_MEMORY_USAGE
42
42
  from logger_36.constant.message import LINE_INDENT, TIME_LENGTH_m_1, expected_op_h
43
- from logger_36.constant.path import PROJECT_FILE_RELATIVE, USER_FOLDER
44
- from logger_36.constant.record import (
45
- INTERNAL_INFO_ATTR,
46
- SHOW_W_RULE_ATTR,
47
- SHOW_WHERE_ATTR,
48
- )
43
+ from logger_36.constant.path import USER_FOLDER, LAUNCH_ROOT_FILE_relative
44
+ from logger_36.constant.record import SHOW_W_RULE_ATTR, SHOW_WHERE_ATTR
45
+ from logger_36.extension.record import RecordLocation
49
46
  from logger_36.task.format.message import MessageWithActualExpected
50
47
  from logger_36.task.measure.chronos import ElapsedTime
51
48
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
52
- from logger_36.type.handler import any_handler_t as base_handler_t
53
49
  from logger_36.type.handler import extension_t as handler_extension_t
50
+ from logger_36.type.handler import handler_h as base_handler_h
54
51
  from logger_36.type.issue import NewIssue, issue_t
55
52
 
56
53
  if RICH_IS_AVAILABLE:
@@ -66,9 +63,6 @@ logger_handle_raw_h = h.Callable[[l.LogRecord], None]
66
63
  logger_handle_with_self_h = h.Callable[[l.Logger, l.LogRecord], None]
67
64
  logger_handle_h = logger_handle_raw_h | logger_handle_with_self_h
68
65
 
69
- _DATE_TIME_ORIGIN = date_time_t.fromtimestamp(1970, None)
70
- _DATE_ORIGIN = _DATE_TIME_ORIGIN.date()
71
-
72
66
 
73
67
  @d.dataclass(slots=True, repr=False, eq=False)
74
68
  class logger_t(base_t):
@@ -79,24 +73,18 @@ class logger_t(base_t):
79
73
  _should_activate_log_interceptions: Loggers instantiated after a logger_t logger
80
74
  will be missed by an early call of ToggleLogInterceptions. Therefore, passing True
81
75
  for activate_log_interceptions only sets _should_activate_log_interceptions to True,
82
- which is later checked in AddHandler to effectively call ToggleLogInterceptions if
83
- _warming_up is False (which normally indicates that the handler about to
84
- be added is the last one).
85
-
86
- _warming_up: Must not be False until at least one handler has been added.
76
+ which is later checked in AddHandler to effectively call ToggleLogInterceptions.
87
77
  """
88
78
 
89
- should_record_messages: bool = False
90
- should_monitor_memory_usage: bool = False
91
79
  exit_on_error: bool = False # Implies exit_on_critical.
92
80
  exit_on_critical: bool = False
93
- verbose: bool = False
81
+ should_monitor_memory_usage: bool = False
94
82
 
95
- events: dict[int, int] = d.field(init=False, default_factory=dict)
96
- recorded: list[tuple[int, str]] = d.field(init=False, default_factory=list)
83
+ history: dict[date_time_t, str] = d.field(init=False, default_factory=dict)
84
+ n_events: dict[int, int] = d.field(init=False, default_factory=dict)
97
85
 
98
- last_message_now: date_time_t = d.field(init=False, default=_DATE_TIME_ORIGIN)
99
- last_message_date: date_t = d.field(init=False, default=_DATE_ORIGIN)
86
+ last_message_now: date_time_t = d.field(init=False, default=DATE_TIME_ORIGIN)
87
+ last_message_date: date_t = d.field(init=False, default=DATE_ORIGIN)
100
88
  memory_usages: list[tuple[str, int]] = d.field(init=False, default_factory=list)
101
89
  context_levels: list[str] = d.field(init=False, default_factory=list)
102
90
  staged_issues: list[issue_t] = d.field(init=False, default_factory=list)
@@ -105,12 +93,9 @@ class logger_t(base_t):
105
93
  init=False, default_factory=dict
106
94
  )
107
95
  intercepts_exceptions: bool = d.field(init=False, default=False)
108
- _recording_handler: handler_extension_t | None = d.field(init=False, default=None)
109
96
 
110
- # Used only until the last handler is added (see AddHandler).
97
+ # Used only until the first handler is added (see AddHandler).
111
98
  _should_activate_log_interceptions: bool = d.field(init=False, default=False)
112
- _warming_up: bool = d.field(init=False, default=True)
113
- _on_hold: list[l.LogRecord] | None = d.field(init=False, default_factory=list)
114
99
 
115
100
  name_: d.InitVar[str | None] = None
116
101
  level_: d.InitVar[int] = l.NOTSET
@@ -118,6 +103,12 @@ class logger_t(base_t):
118
103
  activate_log_interceptions: d.InitVar[bool] = True
119
104
  activate_exc_interceptions: d.InitVar[bool] = True
120
105
 
106
+ @property
107
+ def formatted_history(self) -> str:
108
+ """"""
109
+ FormattedEntry = lambda _: f"{_[0]}: {_[1].replace('\n', '↲ ')}"
110
+ return "\n".join(map(FormattedEntry, self.history.items()))
111
+
121
112
  @property
122
113
  def intercepts_warnings(self) -> bool:
123
114
  """"""
@@ -168,7 +159,10 @@ class logger_t(base_t):
168
159
  if name_ is None:
169
160
  name_ = f"{type(self).__name__}:{hex(id(self))[2:]}"
170
161
 
171
- self.handle = self._HandleWarmUp
162
+ self.history[date_time_t.now()] = (
163
+ f'Logger "{name_}" instantiation for "{LAUNCH_ROOT_FILE_relative}"'
164
+ )
165
+
172
166
  base_t.__init__(self, name_)
173
167
  self.setLevel(level_)
174
168
  self.propagate = False # Part of base_t.
@@ -177,15 +171,7 @@ class logger_t(base_t):
177
171
  self.exit_on_critical = True
178
172
 
179
173
  for level_id in l.getLevelNamesMapping().values():
180
- self.events[level_id] = 0
181
-
182
- if self.should_record_messages:
183
- self.ActivateMessageRecording()
184
-
185
- self.info(
186
- f'New logger "{self.name}" for "{PROJECT_FILE_RELATIVE}"',
187
- extra={INTERNAL_INFO_ATTR: True},
188
- )
174
+ self.n_events[level_id] = 0
189
175
 
190
176
  if activate_wrn_interceptions:
191
177
  self.ToggleWarningInterceptions(True)
@@ -197,29 +183,8 @@ class logger_t(base_t):
197
183
  if self.should_monitor_memory_usage:
198
184
  self.ActivateMemoryUsageMonitoring()
199
185
 
200
- def _HandleWarmUp(self, record: l.LogRecord, /) -> None:
186
+ def handle(self, record: l.LogRecord, /) -> None:
201
187
  """"""
202
- now = ElapsedTime(should_return_now=True)[1]
203
- if (date := now.date()) != self.last_message_date:
204
- self._AcknowledgeDateChange(date)
205
-
206
- # When.
207
- record.when_or_elapsed = (TIME_LENGTH_m_1 + 1) * "."
208
- # Where.
209
- if record.levelno != l.INFO:
210
- _ = _RecordLocation(record, True)
211
- # What.
212
- if not isinstance(record.msg, str):
213
- record.msg = str(record.msg)
214
-
215
- self._on_hold.append(record)
216
-
217
- def _HandleRoutine(self, record: l.LogRecord, /) -> None:
218
- """"""
219
- is_internal_info = getattr(record, INTERNAL_INFO_ATTR, False)
220
- if is_internal_info and not self.verbose:
221
- return
222
-
223
188
  elapsed_time, now = ElapsedTime(should_return_now=True)
224
189
 
225
190
  if (date := now.date()) != self.last_message_date:
@@ -236,7 +201,7 @@ class logger_t(base_t):
236
201
  # Where.
237
202
  should_show_where = getattr(record, SHOW_WHERE_ATTR, record.levelno != l.INFO)
238
203
  if should_show_where or self.should_monitor_memory_usage:
239
- where = _RecordLocation(record, should_show_where)
204
+ where = RecordLocation(record, should_show_where)
240
205
  else:
241
206
  where = None
242
207
 
@@ -244,7 +209,8 @@ class logger_t(base_t):
244
209
  if not isinstance(record.msg, str):
245
210
  record.msg = str(record.msg)
246
211
 
247
- self._HandleRaw(record)
212
+ base_t.handle(self, record)
213
+ self.n_events[record.levelno] += 1
248
214
 
249
215
  if self.should_monitor_memory_usage:
250
216
  self.memory_usages.append((where, CurrentMemoryUsage()))
@@ -256,29 +222,6 @@ class logger_t(base_t):
256
222
  # __post_init__ set self.exit_on_critical if self.exit_on_error.
257
223
  s.exit(1)
258
224
 
259
- def _HandleRaw(self, record: l.LogRecord, /) -> None:
260
- """"""
261
- if self.should_record_messages:
262
- message = self._recording_handler.MessageFromRecord(record)[0]
263
- self.recorded.append((record.levelno, message))
264
-
265
- base_t.handle(self, record)
266
- self.events[record.levelno] += 1
267
-
268
- def _FlushRecordsOnHold(self) -> None:
269
- """
270
- should_record_messages and verbose must have been set by now.
271
- """
272
- for held in self._on_hold:
273
- if self.verbose or not getattr(held, INTERNAL_INFO_ATTR, False):
274
- self._HandleRaw(held)
275
-
276
- self._on_hold.clear()
277
- self._on_hold = None
278
- self._warming_up = False
279
-
280
- self.handle = self._HandleRoutine
281
-
282
225
  def _AcknowledgeDateChange(self, date: date_t, /) -> None:
283
226
  """"""
284
227
  self.last_message_date = date
@@ -291,38 +234,12 @@ class logger_t(base_t):
291
234
  SHOW_W_RULE_ATTR: True,
292
235
  }
293
236
  )
294
-
295
- if self._warming_up:
296
- self._on_hold.append(record)
297
- else:
298
- self._HandleRaw(record)
299
-
300
- def ActivateMessageRecording(self) -> None:
301
- """"""
302
- self._recording_handler = handler_extension_t("recording_handler", 0, None)
303
- self.should_record_messages = True # Useless if called from __post_init__.
304
- self.info(
305
- f'Message recording activated for logger "{self.name}"',
306
- extra={INTERNAL_INFO_ATTR: True},
307
- )
308
-
309
- def ActivateMemoryUsageMonitoring(self) -> None:
310
- """"""
311
- if MEMORY_MEASURE_IS_AVAILABLE:
312
- # Useless if called from __post_init__.
313
- self.should_monitor_memory_usage = True
314
- self.info(
315
- f'Memory usage monitoring activated for logger "{self.name}"',
316
- extra={INTERNAL_INFO_ATTR: True},
317
- )
318
- else:
319
- self.should_monitor_memory_usage = False
320
- self.error(MEMORY_MEASURE_ERROR)
237
+ base_t.handle(self, record)
321
238
 
322
239
  def ResetEventCounts(self) -> None:
323
240
  """"""
324
- for level_id in self.events:
325
- self.events[level_id] = 0
241
+ for level_id in self.n_events:
242
+ self.n_events[level_id] = 0
326
243
 
327
244
  def ToggleWarningInterceptions(self, state: bool, /) -> None:
328
245
  """"""
@@ -335,7 +252,7 @@ class logger_t(base_t):
335
252
  logger.handle = t.MethodType(_HandleForWarnings(self), logger)
336
253
 
337
254
  l.captureWarnings(True)
338
- self.info("Warning Interception: ON", extra={INTERNAL_INFO_ATTR: True})
255
+ self.history[date_time_t.now()] = "Warning Interception: ON"
339
256
  else:
340
257
  if not self.intercepts_warnings:
341
258
  return
@@ -345,12 +262,12 @@ class logger_t(base_t):
345
262
  self.intercepted_wrn_handle = None
346
263
 
347
264
  l.captureWarnings(False)
348
- self.info("Warning Interception: OFF", extra={INTERNAL_INFO_ATTR: True})
265
+ self.history[date_time_t.now()] = "Warning Interception: OFF"
349
266
 
350
267
  def ToggleLogInterceptions(self, state: bool, /) -> None:
351
268
  """"""
352
269
  if state:
353
- if self.intercepts_logs:
270
+ if self._should_activate_log_interceptions or self.intercepts_logs:
354
271
  return
355
272
 
356
273
  # Note: Alternative to self.manager is logging.root.manager.
@@ -369,11 +286,14 @@ class logger_t(base_t):
369
286
  intercepted = sorted(self.intercepted_log_handles.keys())
370
287
  if intercepted.__len__() > 0:
371
288
  as_str = ", ".join(intercepted)
372
- self.info(
373
- f"Now Intercepting LOGs from: {as_str}",
374
- extra={INTERNAL_INFO_ATTR: True},
289
+ self.history[date_time_t.now()] = (
290
+ f"Now Intercepting LOGs from: {as_str}"
375
291
  )
376
292
  else:
293
+ if self._should_activate_log_interceptions:
294
+ self._should_activate_log_interceptions = False
295
+ return
296
+
377
297
  if not self.intercepts_logs:
378
298
  return
379
299
 
@@ -381,7 +301,7 @@ class logger_t(base_t):
381
301
  logger = l.getLogger(name)
382
302
  logger.handle = handle
383
303
  self.intercepted_log_handles.clear()
384
- self.info("Log Interception: OFF", extra={INTERNAL_INFO_ATTR: True})
304
+ self.history[date_time_t.now()] = "Log Interception: OFF"
385
305
 
386
306
  def ToggleExceptionInterceptions(self, state: bool, /) -> None:
387
307
  """"""
@@ -392,7 +312,7 @@ class logger_t(base_t):
392
312
  s.excepthook = self.DealWithException
393
313
  thrd.excepthook = self.DealWithExceptionInThread
394
314
  self.intercepts_exceptions = True
395
- self.info("Exception Interception: ON", extra={INTERNAL_INFO_ATTR: True})
315
+ self.history[date_time_t.now()] = "Exception Interception: ON"
396
316
  else:
397
317
  if not self.intercepts_exceptions:
398
318
  return
@@ -400,12 +320,24 @@ class logger_t(base_t):
400
320
  s.excepthook = s.__excepthook__
401
321
  thrd.excepthook = thrd.__excepthook__
402
322
  self.intercepts_exceptions = False
403
- self.info("Exception Interception: OFF", extra={INTERNAL_INFO_ATTR: True})
323
+ self.history[date_time_t.now()] = "Exception Interception: OFF"
324
+
325
+ def ActivateMemoryUsageMonitoring(self) -> None:
326
+ """"""
327
+ if MEMORY_MEASURE_IS_AVAILABLE:
328
+ # Useless if called from __post_init__.
329
+ self.should_monitor_memory_usage = True
330
+ self.history[date_time_t.now()] = (
331
+ f'Memory usage monitoring activated for logger "{self.name}"'
332
+ )
333
+ else:
334
+ self.should_monitor_memory_usage = False
335
+ self.error(MEMORY_MEASURE_ERROR)
404
336
 
405
337
  def AddHandler(
406
338
  self,
407
- handler_t_or_handler: type[base_handler_t]
408
- | base_handler_t
339
+ handler_t_or_handler: type[base_handler_h]
340
+ | base_handler_h
409
341
  | l.Handler
410
342
  | l.FileHandler,
411
343
  /,
@@ -413,21 +345,16 @@ class logger_t(base_t):
413
345
  name: str | None = None,
414
346
  level: int = l.INFO,
415
347
  message_width: int = -1,
416
- this_is_last_handler: bool = True,
417
348
  **kwargs,
418
349
  ) -> None:
419
350
  """
420
351
  Silently ignores re-holding request after un-holding.
421
352
  """
422
- should_flush_on_hold = False
423
- new_handler_warning = ""
424
- if self._warming_up and this_is_last_handler:
425
- if self._should_activate_log_interceptions:
426
- self.ToggleLogInterceptions(True)
427
- self._should_activate_log_interceptions = False
428
-
429
- should_flush_on_hold = True
430
- new_handler_warning = "\n(Handlers added from now on will miss above logs.)"
353
+ if self._should_activate_log_interceptions:
354
+ # Turn _should_activate_log_interceptions off before calling
355
+ # ToggleLogInterceptions because it checks it.
356
+ self._should_activate_log_interceptions = False
357
+ self.ToggleLogInterceptions(True)
431
358
 
432
359
  if isinstance(handler_t_or_handler, type):
433
360
  handler = handler_t_or_handler.New(
@@ -437,20 +364,13 @@ class logger_t(base_t):
437
364
  handler = handler_t_or_handler
438
365
  base_t.addHandler(self, handler)
439
366
 
440
- if self.verbose:
441
- path = getattr(handler, "baseFilename", "")
442
- if isinstance(path, path_t) or (path.__len__() > 0):
443
- path = f"\nPath: {path}"
444
- self.info(
445
- f'New handler "{handler.name}" of type "{type(handler).__name__}" and '
446
- f"level {handler.level}={l.getLevelName(handler.level)}{path}"
447
- f"{new_handler_warning}",
448
- extra={INTERNAL_INFO_ATTR: True},
449
- )
450
-
451
- # Wait until after the handler has been added to flush messages on hold.
452
- if should_flush_on_hold:
453
- self._FlushRecordsOnHold()
367
+ path = getattr(handler, "baseFilename", "")
368
+ if isinstance(path, path_t) or (path.__len__() > 0):
369
+ path = f"\nPath: {path}"
370
+ self.history[date_time_t.now()] = (
371
+ f'New handler "{handler.name}" of type "{type(handler).__name__}" and '
372
+ f"level {handler.level}={l.getLevelName(handler.level)}{path}"
373
+ )
454
374
 
455
375
  def MakeMonochrome(self) -> None:
456
376
  """"""
@@ -510,6 +430,20 @@ class logger_t(base_t):
510
430
  )
511
431
  self.log(level, message)
512
432
 
433
+ def LogAsIs(self, message: str, /, *, indented: bool = False) -> None:
434
+ """"""
435
+ if indented:
436
+ message = text.indent(message, LINE_INDENT)
437
+
438
+ for handler in self.handlers:
439
+ EmitMessage = getattr(
440
+ handler, handler_extension_t.EmitMessage.__name__, None
441
+ )
442
+ if EmitMessage is not None:
443
+ EmitMessage(message)
444
+
445
+ info_raw = LogAsIs # To follow the convention of the logging methods info, error...
446
+
513
447
  def LogException(
514
448
  self,
515
449
  exception: Exception,
@@ -544,20 +478,6 @@ class logger_t(base_t):
544
478
  """"""
545
479
  self.DealWithException(exc_type, exc_value, exc_traceback)
546
480
 
547
- def LogAsIs(self, message: str, /, *, indented: bool = False) -> None:
548
- """"""
549
- if indented:
550
- message = text.indent(message, LINE_INDENT)
551
-
552
- for handler in self.handlers:
553
- EmitMessage = getattr(
554
- handler, handler_extension_t.EmitMessage.__name__, None
555
- )
556
- if EmitMessage is not None:
557
- EmitMessage(message)
558
-
559
- info_raw = LogAsIs # To follow the convention of the logging methods info, error...
560
-
561
481
  def DisplayRule(
562
482
  self, /, *, message: str | None = None, color: str = "white"
563
483
  ) -> None:
@@ -692,26 +612,6 @@ class logger_t(base_t):
692
612
  return False
693
613
 
694
614
 
695
- def _RecordLocation(record: l.LogRecord, should_also_store: bool, /) -> str:
696
- """"""
697
- module = path_t(record.pathname)
698
- for path in s.path:
699
- if module.is_relative_to(path):
700
- module = module.relative_to(path).with_suffix("")
701
- module = str(module).replace(FOLDER_SEPARATOR, ".")
702
- break
703
- else:
704
- if module.is_relative_to(USER_FOLDER):
705
- module = module.relative_to(USER_FOLDER)
706
-
707
- output = f"{module}:{record.funcName}:{record.lineno}"
708
-
709
- if should_also_store:
710
- record.where = output
711
-
712
- return output
713
-
714
-
715
615
  def _HandleForWarnings(interceptor: base_t, /) -> logger_handle_h:
716
616
  """"""
717
617
 
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.22"
7
+ __version__ = "2025.24"
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.22
3
+ Version: 2025.24
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,26 +1,27 @@
1
1
  logger_36/__init__.py,sha256=mK6AD0eWI2Sk42oxleTvsxzYJ28FbHK5WNkpLgAhnNE,2129
2
- logger_36/version.py,sha256=WM8W-3pzA_JWd1DQWpdL-uJTAxV5u8_nbyWIlGaizjg,1680
3
- logger_36/api/content.py,sha256=DuT4UX4r_1DTXzuuRD-tvsTZk5X-Nj11loBKhuWOMw0,1791
4
- logger_36/api/gpu.py,sha256=NNs1IvQ7bh8Dppm8O8K2YLWbm4rogc3Ie_-D6xzkX3g,1726
5
- logger_36/api/memory.py,sha256=vOY4cTTrC3u7L0OrKXdPNlsCahYjCrY4h7iqpGZv9kU,2217
6
- logger_36/api/storage.py,sha256=RYjn2TD-E1zfNTMgm4b2mbYNVtTwsCUMbuPlMbuvgP0,1774
7
- logger_36/api/system.py,sha256=h-3GfhZPwawv0UKBWKkT1LzxSCZwpA2VIsy03lLYi6w,1725
8
- logger_36/api/time.py,sha256=JG0vgzPSRZ7UWQyoihnVu4sjPC-okFIKA3ZyNh2GaZo,1798
9
- logger_36/api/type.py,sha256=eLZ2yuH-sYeh4Z2KnAwTRJEbmkmgzBPMncdqXfFUTG8,1760
2
+ logger_36/version.py,sha256=QjPDEEk8iJm0lRtofN7Pbc34N4IOYHazajxRPaje3-8,1680
3
+ logger_36/api/logger.py,sha256=eLZ2yuH-sYeh4Z2KnAwTRJEbmkmgzBPMncdqXfFUTG8,1760
4
+ logger_36/api/memory.py,sha256=U4mMEkx8WRHk9q2d3SCFGt2EJaBuflOXw2bGbAxOnSc,1828
5
+ logger_36/api/message.py,sha256=DuT4UX4r_1DTXzuuRD-tvsTZk5X-Nj11loBKhuWOMw0,1791
6
+ logger_36/api/storage.py,sha256=S1fVzrMp-_zlhg27fRPddWCFQRyvbpFwSreALOeoNFI,1713
7
+ logger_36/api/time.py,sha256=akg503WsfzSCbHS6SmMcfCUGYLdg9_TyRTFAFzLjPc8,1730
10
8
  logger_36/catalog/config/console_rich.py,sha256=t9p9-AkSgPiLAsm1evAdbz77g7JcVLePhUJ1FzNi3cY,2330
11
9
  logger_36/catalog/config/optional.py,sha256=8d8HdpE07gHfsdoL8mVAlRlh9AgLcb4z7I7h6ob7CfU,2174
12
- logger_36/catalog/handler/console.py,sha256=YEQN8fw7ra9w-5sJACNC4pktv9einm5ASP9TncU8TQo,2311
13
- logger_36/catalog/handler/console_rich.py,sha256=gPpq8ij1ZFTXaLgISaCTXDDwv7uWPivLeq2ttpiuKlk,6190
10
+ logger_36/catalog/handler/console.py,sha256=XIyO_8MrzaTlihDbFhIzRk47HjxMH4gV2Zqp0-1oMsU,2320
11
+ logger_36/catalog/handler/console_rich.py,sha256=WsxFg8HhqhQ0GsuiZ5NqU06PwZor1jd83dxnRoM-I1M,6199
14
12
  logger_36/catalog/handler/file.py,sha256=yg8GnsV6AmDsx1R5iZlsK-5idqD733gc09Syl02TG7Y,2458
15
- logger_36/catalog/handler/generic.py,sha256=L8mLc-iQlBWKBqEN3-wIKy2yk_TVgqIWYn_P1hXDsQA,6361
13
+ logger_36/catalog/handler/generic.py,sha256=Asz0yfs0cn6GTxE2bmrUEhmScOxjfm4KIZGhB4FBc5I,6370
14
+ logger_36/catalog/handler/memory.py,sha256=V7itZkDXXjdUEunl8aCI3xYc07XtupgFSE7usvd0nSQ,3351
16
15
  logger_36/catalog/logger/chronos.py,sha256=S4m9TMPQy_Ju500mpE1jNzu2gZG-QKdVuvX9RVRKHR8,1911
17
16
  logger_36/catalog/logger/gpu.py,sha256=Py5YY0nD_pqJzJsEKQYoOGHcPqyNVJ3J2noOS3hDL6g,2890
18
17
  logger_36/catalog/logger/memory.py,sha256=J0ZGKO7j1FZA_aDGxpABtvzDy1RjCDiDmWYh4U98fEI,4253
19
- logger_36/catalog/logger/system.py,sha256=b7GnhHTDCQsclMyiZshzjASdk8eJaR7FkwDK-vt6IJg,2539
18
+ logger_36/catalog/logger/system.py,sha256=zEbHirATqZAVYFmHLd0pppeuRhka1ucWwyHRq0afQNE,2593
20
19
  logger_36/config/issue.py,sha256=QOkVRPSLZC_2mfcFpad-pcSXJXfLHdWUAXiMbTWlZTg,1741
21
20
  logger_36/config/memory.py,sha256=bZmNYsD2goVdkraS1v_t2OqAJo86jKMtP311kIVURDk,1691
22
21
  logger_36/config/message.py,sha256=3PvvFNT6tzZu6lOkO3xEoyMFnJonx_CMBDwQAbrXD38,2028
22
+ logger_36/config/rule.py,sha256=BqOb4SWKgT0YQZ-DtOzkLNZNWZVZkl5GN3lMoWoPrKw,1702
23
23
  logger_36/config/system.py,sha256=YRSa2eN_SoTnTXWUXAcpKt4JXifabzMR0eXwjUYlA_A,1951
24
+ logger_36/constant/date_time.py,sha256=fUqiTekMtpJc6aV6x4_2B6excMERCKlR0766bDNAV-0,1981
24
25
  logger_36/constant/error.py,sha256=CGisFxb1AWjKbxIKuGqAR7gPKUaD6KkLlY-XmQuIfXg,2301
25
26
  logger_36/constant/generic.py,sha256=SQgkQiRcTIjCRvbxiOb6TEm59BC0FNMcjYoIShpcwLo,1718
26
27
  logger_36/constant/html.py,sha256=eCp6kumL8bvF30lBjz1iPqw1apzM1rG8rxjNr81aJbA,1989
@@ -28,25 +29,25 @@ logger_36/constant/issue.py,sha256=0EmcsRmSxktFUJR0qOU0PnKG-gfbLDOULH6sSRHFOcc,1
28
29
  logger_36/constant/logger.py,sha256=ZQYX9JiPsoivwRgYNtdEqRKCagSKD88lRqvxP8MX1ZE,1942
29
30
  logger_36/constant/memory.py,sha256=Q_E5tTWa-cGaNwrE_xmKa3BxQG6oJO6DHczrxc_M4sE,1817
30
31
  logger_36/constant/message.py,sha256=YJOEzdI0ZjUOdHo3CsiS56FVPhrfNoQYvXuUkprH61g,2312
31
- logger_36/constant/path.py,sha256=OfLh70Jyc8po9Ls34nQh_bRr3PXyQ3kF9ciR9QPhiqI,2213
32
- logger_36/constant/record.py,sha256=IGWnKA4TGv1oU6ezzNrGocZlB7Ie7nTr4l3IOtwdWOg,1833
33
- logger_36/constant/rule.py,sha256=tBKQgPTt6G_p5eInDdWoEEAvQFz4WMSt5THsS5jvk14,1779
32
+ logger_36/constant/path.py,sha256=r-vx5ztGhcpYfg37kw0oaxBYdTSkWOJuToTmexaW8tE,2265
33
+ logger_36/constant/record.py,sha256=VMqGxVTbtHpRiDOsYiXEoyYOocYFm8vtcRY-7zXzJrk,1796
34
+ logger_36/constant/rule.py,sha256=ul-MqOdHBGBC5Nwn05EUnz2T__7VEs82qiH7Fzs5qCk,1804
34
35
  logger_36/constant/system.py,sha256=pLlLXG5sepQlSUOo3TphaGrHg8xzJBp-GxpL2NPP47k,1904
35
- logger_36/extension/html_.py,sha256=W9SyiYsaaYHUrHLGAAN2wiJGXUlwOBJ5gzdjmEcnF18,3342
36
+ logger_36/extension/inspection.py,sha256=LoHXi4wsIgHKrq_7GYkVcfJ9rnBG16pLKMpAoHNwJiY,3922
36
37
  logger_36/extension/line.py,sha256=joeojQX1bZJM53333mOEU3s-YC5ExGOrN9Cu9Lh5-FU,2617
38
+ logger_36/extension/record.py,sha256=-8nJMkGCRKGcSIV9aYsIDrLJSAYDIJuZ8drScNCCEcw,2459
37
39
  logger_36/instance/logger.py,sha256=X_U10RYU1h2Aa70D8hBnmFyJZtRILK16KN-GB4xkHMU,1782
38
40
  logger_36/instance/loggers.py,sha256=inBk4KKrQ-z3szaopQ29-qQwh1iSc842sWo5J6zJoiM,1725
39
- logger_36/task/inspection.py,sha256=ZgPcrPo3h_kEnM-UpHDLg7PAFfB2fqsLFdfmi6hlPVA,4484
40
- logger_36/task/storage.py,sha256=KAILmJlF5IULxEX9QRCyXCwcalp5mpunWVh1oXuLvSs,3516
41
+ logger_36/task/storage.py,sha256=7aXX--1WNYgVLp1Enbi_TcSAjA_bVZO287hFi20661Y,4692
41
42
  logger_36/task/format/memory.py,sha256=J1Oy3jw8wjSp2kuiRUm_VFpzXOHX2FOc7nuRrCyrskw,3723
42
43
  logger_36/task/format/message.py,sha256=Rm6zymVEEGcgKfmxMPXP7q3PtwZJKlXGhqZ5tnvlwxA,3502
43
- logger_36/task/measure/chronos.py,sha256=7ijMZgP4EP18HbLV2yCxpNpRS9724Wyk523f-nkbhUM,2529
44
+ logger_36/task/measure/chronos.py,sha256=azY6eBR-RiNyQBt-8FkTpnxo01tQMw1ZSVuD-1riGD0,2402
44
45
  logger_36/task/measure/memory.py,sha256=kkPHEIUTUhkCOLrAt01eLJLnsnkl0nFPNhFZdIB_JAw,1991
45
- logger_36/type/handler.py,sha256=4LCFZzI4OEH6lVBrBQ09ZIvF0nq-_j7OOVnujBDyDWA,6665
46
+ logger_36/type/handler.py,sha256=KWPHdNd_unNvU_IS-kEloZ7L5fppcR6o0kYiOt7h7KQ,6683
46
47
  logger_36/type/issue.py,sha256=QHAYf7QgrjJUtF2D46z6X630qTgeP_0FE5hIwf54RsE,2688
47
- logger_36/type/logger.py,sha256=b2F21M5z1hwPdDsyHgBIlC06X9-_7zsjqyGEsVaIJgM,27536
48
+ logger_36/type/logger.py,sha256=8Rt12lUALDt3vbxQ9dUS1_XD8VeN6gZDPpWFbQK1iKo,24262
48
49
  logger_36/type/loggers.py,sha256=7EX7Sg_RlduBjdfFlNZmUfNeDloH1xU30Rdkg_-rXh8,3172
49
- logger_36-2025.22.dist-info/METADATA,sha256=xcPkxJFWNYEIXgspK2VCcU4LbsME8hlmFQjASmR-3KM,6529
50
- logger_36-2025.22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
- logger_36-2025.22.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
52
- logger_36-2025.22.dist-info/RECORD,,
50
+ logger_36-2025.24.dist-info/METADATA,sha256=WynTu2dmNvgjXnMqQNp5RyIM3YPCcJgnL4FAW_GYAVM,6529
51
+ logger_36-2025.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
+ logger_36-2025.24.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
53
+ logger_36-2025.24.dist-info/RECORD,,
File without changes
File without changes