logger-36 2025.23__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):
@@ -8,9 +8,9 @@ import json
8
8
  import logging as l
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
- formats_h = h.Literal["json", "message", "raw"]
13
+ formats_h = h.Literal["dict", "json", "message", "raw"]
14
14
 
15
15
 
16
16
  class memory_handler_t(base_t):
@@ -45,11 +45,14 @@ class memory_handler_t(base_t):
45
45
 
46
46
  if self.format_ == "raw":
47
47
  is_not_a_rule = True
48
- elif self.format_ == "message":
49
- record, is_not_a_rule = self.MessageFromRecord(record)
50
- else:
48
+ elif self.format_ == "dict":
49
+ record = dict(record.__dict__)
50
+ is_not_a_rule = True
51
+ elif self.format_ == "json":
51
52
  record = json.dumps(record.__dict__)
52
53
  is_not_a_rule = True
54
+ else:
55
+ record, is_not_a_rule = self.MessageFromRecord(record)
53
56
 
54
57
  self.records.append((level, record, is_not_a_rule))
55
58
 
@@ -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,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,6 +5,8 @@ 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
 
@@ -41,8 +43,15 @@ def SaveLOGasHTML(
41
43
  actual_file = isinstance(path, path_t)
42
44
 
43
45
  if actual_file and path.exists():
44
- logger.warning(f'{cannot_save}: File "{path}" already exists.')
45
- return
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
+ )
46
55
 
47
56
  for handler in logger.handlers:
48
57
  records = getattr(handler, "records", None)
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,18 +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
43
+ from logger_36.constant.path import USER_FOLDER, LAUNCH_ROOT_FILE_relative
44
44
  from logger_36.constant.record import SHOW_W_RULE_ATTR, SHOW_WHERE_ATTR
45
+ from logger_36.extension.record import RecordLocation
45
46
  from logger_36.task.format.message import MessageWithActualExpected
46
47
  from logger_36.task.measure.chronos import ElapsedTime
47
48
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
48
- from logger_36.type.handler import any_handler_t as base_handler_t
49
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
50
51
  from logger_36.type.issue import NewIssue, issue_t
51
52
 
52
53
  if RICH_IS_AVAILABLE:
@@ -62,9 +63,6 @@ logger_handle_raw_h = h.Callable[[l.LogRecord], None]
62
63
  logger_handle_with_self_h = h.Callable[[l.Logger, l.LogRecord], None]
63
64
  logger_handle_h = logger_handle_raw_h | logger_handle_with_self_h
64
65
 
65
- _DATE_TIME_ORIGIN = date_time_t.fromtimestamp(1970, None)
66
- _DATE_ORIGIN = _DATE_TIME_ORIGIN.date()
67
-
68
66
 
69
67
  @d.dataclass(slots=True, repr=False, eq=False)
70
68
  class logger_t(base_t):
@@ -85,8 +83,8 @@ class logger_t(base_t):
85
83
  history: dict[date_time_t, str] = d.field(init=False, default_factory=dict)
86
84
  n_events: dict[int, int] = d.field(init=False, default_factory=dict)
87
85
 
88
- last_message_now: date_time_t = d.field(init=False, default=_DATE_TIME_ORIGIN)
89
- 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)
90
88
  memory_usages: list[tuple[str, int]] = d.field(init=False, default_factory=list)
91
89
  context_levels: list[str] = d.field(init=False, default_factory=list)
92
90
  staged_issues: list[issue_t] = d.field(init=False, default_factory=list)
@@ -96,7 +94,7 @@ class logger_t(base_t):
96
94
  )
97
95
  intercepts_exceptions: bool = d.field(init=False, default=False)
98
96
 
99
- # Used only until the last handler is added (see AddHandler).
97
+ # Used only until the first handler is added (see AddHandler).
100
98
  _should_activate_log_interceptions: bool = d.field(init=False, default=False)
101
99
 
102
100
  name_: d.InitVar[str | None] = None
@@ -105,6 +103,12 @@ class logger_t(base_t):
105
103
  activate_log_interceptions: d.InitVar[bool] = True
106
104
  activate_exc_interceptions: d.InitVar[bool] = True
107
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
+
108
112
  @property
109
113
  def intercepts_warnings(self) -> bool:
110
114
  """"""
@@ -155,6 +159,10 @@ class logger_t(base_t):
155
159
  if name_ is None:
156
160
  name_ = f"{type(self).__name__}:{hex(id(self))[2:]}"
157
161
 
162
+ self.history[date_time_t.now()] = (
163
+ f'Logger "{name_}" instantiation for "{LAUNCH_ROOT_FILE_relative}"'
164
+ )
165
+
158
166
  base_t.__init__(self, name_)
159
167
  self.setLevel(level_)
160
168
  self.propagate = False # Part of base_t.
@@ -175,10 +183,6 @@ class logger_t(base_t):
175
183
  if self.should_monitor_memory_usage:
176
184
  self.ActivateMemoryUsageMonitoring()
177
185
 
178
- self.history[date_time_t.now()] = (
179
- f'Logger "{self.name}" instantiation for "{PROJECT_FILE_RELATIVE}"'
180
- )
181
-
182
186
  def handle(self, record: l.LogRecord, /) -> None:
183
187
  """"""
184
188
  elapsed_time, now = ElapsedTime(should_return_now=True)
@@ -197,7 +201,7 @@ class logger_t(base_t):
197
201
  # Where.
198
202
  should_show_where = getattr(record, SHOW_WHERE_ATTR, record.levelno != l.INFO)
199
203
  if should_show_where or self.should_monitor_memory_usage:
200
- where = _RecordLocation(record, should_show_where)
204
+ where = RecordLocation(record, should_show_where)
201
205
  else:
202
206
  where = None
203
207
 
@@ -332,8 +336,8 @@ class logger_t(base_t):
332
336
 
333
337
  def AddHandler(
334
338
  self,
335
- handler_t_or_handler: type[base_handler_t]
336
- | base_handler_t
339
+ handler_t_or_handler: type[base_handler_h]
340
+ | base_handler_h
337
341
  | l.Handler
338
342
  | l.FileHandler,
339
343
  /,
@@ -426,6 +430,20 @@ class logger_t(base_t):
426
430
  )
427
431
  self.log(level, message)
428
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
+
429
447
  def LogException(
430
448
  self,
431
449
  exception: Exception,
@@ -460,20 +478,6 @@ class logger_t(base_t):
460
478
  """"""
461
479
  self.DealWithException(exc_type, exc_value, exc_traceback)
462
480
 
463
- def LogAsIs(self, message: str, /, *, indented: bool = False) -> None:
464
- """"""
465
- if indented:
466
- message = text.indent(message, LINE_INDENT)
467
-
468
- for handler in self.handlers:
469
- EmitMessage = getattr(
470
- handler, handler_extension_t.EmitMessage.__name__, None
471
- )
472
- if EmitMessage is not None:
473
- EmitMessage(message)
474
-
475
- info_raw = LogAsIs # To follow the convention of the logging methods info, error...
476
-
477
481
  def DisplayRule(
478
482
  self, /, *, message: str | None = None, color: str = "white"
479
483
  ) -> None:
@@ -608,26 +612,6 @@ class logger_t(base_t):
608
612
  return False
609
613
 
610
614
 
611
- def _RecordLocation(record: l.LogRecord, should_also_store: bool, /) -> str:
612
- """"""
613
- module = path_t(record.pathname)
614
- for path in s.path:
615
- if module.is_relative_to(path):
616
- module = module.relative_to(path).with_suffix("")
617
- module = str(module).replace(FOLDER_SEPARATOR, ".")
618
- break
619
- else:
620
- if module.is_relative_to(USER_FOLDER):
621
- module = module.relative_to(USER_FOLDER)
622
-
623
- output = f"{module}:{record.funcName}:{record.lineno}"
624
-
625
- if should_also_store:
626
- record.where = output
627
-
628
- return output
629
-
630
-
631
615
  def _HandleForWarnings(interceptor: base_t, /) -> logger_handle_h:
632
616
  """"""
633
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.23"
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.23
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,27 +1,27 @@
1
1
  logger_36/__init__.py,sha256=mK6AD0eWI2Sk42oxleTvsxzYJ28FbHK5WNkpLgAhnNE,2129
2
- logger_36/version.py,sha256=3y8_8Te0bVUofCcWg9yoGDEx2HlTct92Ol2Pk3NTA8w,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
16
- logger_36/catalog/handler/memory.py,sha256=-31hTXgGImC6if4wJF6eegvwYhsYAQsDTkdoXIUxZB8,3224
13
+ logger_36/catalog/handler/generic.py,sha256=Asz0yfs0cn6GTxE2bmrUEhmScOxjfm4KIZGhB4FBc5I,6370
14
+ logger_36/catalog/handler/memory.py,sha256=V7itZkDXXjdUEunl8aCI3xYc07XtupgFSE7usvd0nSQ,3351
17
15
  logger_36/catalog/logger/chronos.py,sha256=S4m9TMPQy_Ju500mpE1jNzu2gZG-QKdVuvX9RVRKHR8,1911
18
16
  logger_36/catalog/logger/gpu.py,sha256=Py5YY0nD_pqJzJsEKQYoOGHcPqyNVJ3J2noOS3hDL6g,2890
19
17
  logger_36/catalog/logger/memory.py,sha256=J0ZGKO7j1FZA_aDGxpABtvzDy1RjCDiDmWYh4U98fEI,4253
20
- logger_36/catalog/logger/system.py,sha256=b7GnhHTDCQsclMyiZshzjASdk8eJaR7FkwDK-vt6IJg,2539
18
+ logger_36/catalog/logger/system.py,sha256=zEbHirATqZAVYFmHLd0pppeuRhka1ucWwyHRq0afQNE,2593
21
19
  logger_36/config/issue.py,sha256=QOkVRPSLZC_2mfcFpad-pcSXJXfLHdWUAXiMbTWlZTg,1741
22
20
  logger_36/config/memory.py,sha256=bZmNYsD2goVdkraS1v_t2OqAJo86jKMtP311kIVURDk,1691
23
21
  logger_36/config/message.py,sha256=3PvvFNT6tzZu6lOkO3xEoyMFnJonx_CMBDwQAbrXD38,2028
22
+ logger_36/config/rule.py,sha256=BqOb4SWKgT0YQZ-DtOzkLNZNWZVZkl5GN3lMoWoPrKw,1702
24
23
  logger_36/config/system.py,sha256=YRSa2eN_SoTnTXWUXAcpKt4JXifabzMR0eXwjUYlA_A,1951
24
+ logger_36/constant/date_time.py,sha256=fUqiTekMtpJc6aV6x4_2B6excMERCKlR0766bDNAV-0,1981
25
25
  logger_36/constant/error.py,sha256=CGisFxb1AWjKbxIKuGqAR7gPKUaD6KkLlY-XmQuIfXg,2301
26
26
  logger_36/constant/generic.py,sha256=SQgkQiRcTIjCRvbxiOb6TEm59BC0FNMcjYoIShpcwLo,1718
27
27
  logger_36/constant/html.py,sha256=eCp6kumL8bvF30lBjz1iPqw1apzM1rG8rxjNr81aJbA,1989
@@ -29,25 +29,25 @@ logger_36/constant/issue.py,sha256=0EmcsRmSxktFUJR0qOU0PnKG-gfbLDOULH6sSRHFOcc,1
29
29
  logger_36/constant/logger.py,sha256=ZQYX9JiPsoivwRgYNtdEqRKCagSKD88lRqvxP8MX1ZE,1942
30
30
  logger_36/constant/memory.py,sha256=Q_E5tTWa-cGaNwrE_xmKa3BxQG6oJO6DHczrxc_M4sE,1817
31
31
  logger_36/constant/message.py,sha256=YJOEzdI0ZjUOdHo3CsiS56FVPhrfNoQYvXuUkprH61g,2312
32
- logger_36/constant/path.py,sha256=OfLh70Jyc8po9Ls34nQh_bRr3PXyQ3kF9ciR9QPhiqI,2213
32
+ logger_36/constant/path.py,sha256=r-vx5ztGhcpYfg37kw0oaxBYdTSkWOJuToTmexaW8tE,2265
33
33
  logger_36/constant/record.py,sha256=VMqGxVTbtHpRiDOsYiXEoyYOocYFm8vtcRY-7zXzJrk,1796
34
- logger_36/constant/rule.py,sha256=tBKQgPTt6G_p5eInDdWoEEAvQFz4WMSt5THsS5jvk14,1779
34
+ logger_36/constant/rule.py,sha256=ul-MqOdHBGBC5Nwn05EUnz2T__7VEs82qiH7Fzs5qCk,1804
35
35
  logger_36/constant/system.py,sha256=pLlLXG5sepQlSUOo3TphaGrHg8xzJBp-GxpL2NPP47k,1904
36
- logger_36/extension/html_.py,sha256=W9SyiYsaaYHUrHLGAAN2wiJGXUlwOBJ5gzdjmEcnF18,3342
36
+ logger_36/extension/inspection.py,sha256=LoHXi4wsIgHKrq_7GYkVcfJ9rnBG16pLKMpAoHNwJiY,3922
37
37
  logger_36/extension/line.py,sha256=joeojQX1bZJM53333mOEU3s-YC5ExGOrN9Cu9Lh5-FU,2617
38
+ logger_36/extension/record.py,sha256=-8nJMkGCRKGcSIV9aYsIDrLJSAYDIJuZ8drScNCCEcw,2459
38
39
  logger_36/instance/logger.py,sha256=X_U10RYU1h2Aa70D8hBnmFyJZtRILK16KN-GB4xkHMU,1782
39
40
  logger_36/instance/loggers.py,sha256=inBk4KKrQ-z3szaopQ29-qQwh1iSc842sWo5J6zJoiM,1725
40
- logger_36/task/inspection.py,sha256=ZgPcrPo3h_kEnM-UpHDLg7PAFfB2fqsLFdfmi6hlPVA,4484
41
- logger_36/task/storage.py,sha256=a50M628062iGEymuj5d9iKo7Ad01OXCZrEQnfNqy0gY,4486
41
+ logger_36/task/storage.py,sha256=7aXX--1WNYgVLp1Enbi_TcSAjA_bVZO287hFi20661Y,4692
42
42
  logger_36/task/format/memory.py,sha256=J1Oy3jw8wjSp2kuiRUm_VFpzXOHX2FOc7nuRrCyrskw,3723
43
43
  logger_36/task/format/message.py,sha256=Rm6zymVEEGcgKfmxMPXP7q3PtwZJKlXGhqZ5tnvlwxA,3502
44
- logger_36/task/measure/chronos.py,sha256=7ijMZgP4EP18HbLV2yCxpNpRS9724Wyk523f-nkbhUM,2529
44
+ logger_36/task/measure/chronos.py,sha256=azY6eBR-RiNyQBt-8FkTpnxo01tQMw1ZSVuD-1riGD0,2402
45
45
  logger_36/task/measure/memory.py,sha256=kkPHEIUTUhkCOLrAt01eLJLnsnkl0nFPNhFZdIB_JAw,1991
46
- logger_36/type/handler.py,sha256=4LCFZzI4OEH6lVBrBQ09ZIvF0nq-_j7OOVnujBDyDWA,6665
46
+ logger_36/type/handler.py,sha256=KWPHdNd_unNvU_IS-kEloZ7L5fppcR6o0kYiOt7h7KQ,6683
47
47
  logger_36/type/issue.py,sha256=QHAYf7QgrjJUtF2D46z6X630qTgeP_0FE5hIwf54RsE,2688
48
- logger_36/type/logger.py,sha256=nh7EuJvdkaDrtdHPWIjot2a2hGfAzwufMWm9_2KoDXM,24645
48
+ logger_36/type/logger.py,sha256=8Rt12lUALDt3vbxQ9dUS1_XD8VeN6gZDPpWFbQK1iKo,24262
49
49
  logger_36/type/loggers.py,sha256=7EX7Sg_RlduBjdfFlNZmUfNeDloH1xU30Rdkg_-rXh8,3172
50
- logger_36-2025.23.dist-info/METADATA,sha256=d94bUT9zjDr1Yjny2bsSobExZ1W0MXcIv2hX_DLLGC8,6529
51
- logger_36-2025.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
- logger_36-2025.23.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
53
- logger_36-2025.23.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,,
@@ -1,93 +0,0 @@
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 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
- """
File without changes
File without changes