logger-36 2025.21__py3-none-any.whl → 2025.22__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,11 +8,20 @@ try:
8
8
  import rich # noqa
9
9
  except ModuleNotFoundError:
10
10
  RICH_IS_AVAILABLE = False
11
- from logger_36.constant.error import MISSING_RICH_MESSAGE
11
+ from logger_36.constant.error import MISSING_RICH_MESSAGE # noqa
12
12
  else:
13
13
  RICH_IS_AVAILABLE = True
14
14
  MISSING_RICH_MESSAGE = None
15
15
 
16
+ from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
17
+
18
+ if CanCheckMemoryUsage():
19
+ MEMORY_MEASURE_IS_AVAILABLE = True
20
+ MEMORY_MEASURE_ERROR = None
21
+ else:
22
+ MEMORY_MEASURE_IS_AVAILABLE = False
23
+ from logger_36.constant.error import MEMORY_MEASURE_ERROR # noqa
24
+
16
25
  """
17
26
  COPYRIGHT NOTICE
18
27
 
@@ -29,7 +29,7 @@ class console_handler_t(base_t):
29
29
  """"""
30
30
  s.__stdout__.write(self.MessageFromRecord(record)[0] + "\n")
31
31
 
32
- def EmitAsIs(self, message: str, /) -> None:
32
+ def EmitMessage(self, message: str, /) -> None:
33
33
  """"""
34
34
  s.__stdout__.write(message + "\n")
35
35
 
@@ -96,7 +96,7 @@ class console_rich_handler_t(base_t):
96
96
  """"""
97
97
  return cls(name, message_width, level, kwargs)
98
98
 
99
- def Rule(self, text: str | None, /, *, color: str = "black") -> str | rule_t:
99
+ def Rule(self, /, *, text: str | None = None, color: str = "black") -> str | rule_t:
100
100
  """"""
101
101
  if text is None:
102
102
  return rule_t(style=color)
@@ -116,7 +116,7 @@ class console_rich_handler_t(base_t):
116
116
  self.console.print(message, crop=False, overflow="ignore")
117
117
  self._log_parity = not self._log_parity
118
118
 
119
- def EmitAsIs(self, message: str | renderable_t, /) -> None:
119
+ def EmitMessage(self, message: str | renderable_t, /) -> None:
120
120
  """"""
121
121
  self.console.print(message, crop=False, overflow="ignore")
122
122
 
@@ -32,7 +32,7 @@ class file_handler_t(base_t):
32
32
  self.stream.write(output[0] + "\n")
33
33
  self.stream.flush()
34
34
 
35
- def EmitAsIs(self, message: str, /) -> None:
35
+ def EmitMessage(self, message: str, /) -> None:
36
36
  """"""
37
37
  self.stream.write(message + "\n")
38
38
  self.stream.flush()
@@ -38,10 +38,10 @@ class generic_handler_t(base_t):
38
38
  self, name: str | None, message_width: int, level: int, kwargs
39
39
  ) -> None:
40
40
  """
41
- EmitAsIs: By definition, the generic handler does not know how to output
41
+ EmitMessage: By definition, the generic handler does not know how to output
42
42
  messages. If not passed, it defaults to output-ing messages in the console.
43
43
  """
44
- EmitAsIs = kwargs.pop("EmitAsIs", None)
44
+ EmitMessage = kwargs.pop(base_t.EmitMessage.__name__, None)
45
45
  alternating_logs = kwargs.pop("alternating_logs", 0)
46
46
  supports_html = kwargs.pop("supports_html", False)
47
47
 
@@ -49,8 +49,8 @@ class generic_handler_t(base_t):
49
49
 
50
50
  base_t.__init__(self, name, message_width, None, level, kwargs)
51
51
 
52
- if EmitAsIs is not None:
53
- self.EmitAsIs = EmitAsIs
52
+ if EmitMessage is not None:
53
+ self.EmitMessage = EmitMessage
54
54
  self.is_rich = False
55
55
  self.console = None # console_t | None.
56
56
  self.console_options = None # rich.console.ConsoleOptions | None.
@@ -82,14 +82,14 @@ class generic_handler_t(base_t):
82
82
  """"""
83
83
  return cls(name, message_width, level, kwargs)
84
84
 
85
- def Rule(self, text: str | None, /, *, color: str = "black") -> str | rule_t:
85
+ def Rule(self, /, *, text: str | None = None, color: str = "black") -> str | rule_t:
86
86
  """"""
87
87
  if self.is_rich:
88
88
  if text is None:
89
89
  return rule_t(style=color)
90
90
  return rule_t(title=text_t(text, style=f"bold {color}"), style=color)
91
91
 
92
- return base_t.Rule(self, text, color=color)
92
+ return base_t.Rule(self, text=text, color=color)
93
93
 
94
94
  def emit(self, record: l.LogRecord, /) -> None:
95
95
  """"""
@@ -131,7 +131,7 @@ class generic_handler_t(base_t):
131
131
  else:
132
132
  message = self.MessageFromRecord(record)[0]
133
133
 
134
- self.EmitAsIs(message)
134
+ self.EmitMessage(message)
135
135
  self._log_parity = not self._log_parity
136
136
 
137
137
 
@@ -4,8 +4,11 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- SHOW_W_RULE_ATTR = "should_show_w_rule"
7
+ INTERNAL_INFO_ATTR = "internal_info"
8
8
  SHOW_WHERE_ATTR = "should_show_where"
9
+ SHOW_W_RULE_ATTR = "should_show_w_rule"
10
+ WHEN_OR_ELAPSED_ATTR = "when_or_elapsed"
11
+ WHERE_ATTR = "where"
9
12
 
10
13
  """
11
14
  COPYRIGHT NOTICE
logger_36/type/handler.py CHANGED
@@ -16,7 +16,7 @@ from logger_36.config.message import (
16
16
  WHERE_SEPARATOR,
17
17
  )
18
18
  from logger_36.constant.message import NEXT_LINE_PROLOGUE
19
- from logger_36.constant.record import SHOW_W_RULE_ATTR
19
+ from logger_36.constant.record import SHOW_W_RULE_ATTR, WHEN_OR_ELAPSED_ATTR, WHERE_ATTR
20
20
  from logger_36.constant.rule import (
21
21
  DEFAULT_RULE,
22
22
  DEFAULT_RULE_LENGTH,
@@ -66,7 +66,7 @@ class extension_t:
66
66
  message = self.PreProcessedMessage(message)
67
67
 
68
68
  if hasattr(record, SHOW_W_RULE_ATTR):
69
- return self.Rule(message, color=rule_color), False
69
+ return self.Rule(text=message, color=rule_color), False
70
70
 
71
71
  if (self.message_width <= 0) or (message.__len__() <= self.message_width):
72
72
  if "\n" in message:
@@ -78,11 +78,11 @@ class extension_t:
78
78
  lines = WrappedLines([message], self.message_width)
79
79
  message = NEXT_LINE_PROLOGUE.join(lines)
80
80
 
81
- when_or_elapsed = getattr(record, "when_or_elapsed", None)
81
+ when_or_elapsed = getattr(record, WHEN_OR_ELAPSED_ATTR, None)
82
82
  if when_or_elapsed is None:
83
83
  return message, True
84
84
 
85
- if (where := getattr(record, "where", None)) is None:
85
+ if (where := getattr(record, WHERE_ATTR, None)) is None:
86
86
  where = ""
87
87
  else:
88
88
  where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
@@ -93,7 +93,7 @@ class extension_t:
93
93
  f"{MESSAGE_MARKER} {message}{where}"
94
94
  ), True
95
95
 
96
- def Rule(self, text: str | None, /, *, color: str = "black") -> str | h.Any:
96
+ def Rule(self, /, *, text: str | None = None, color: str = "black") -> str | h.Any:
97
97
  """
98
98
  Return type hint h.Any: For Rich, for example.
99
99
  """
@@ -113,13 +113,13 @@ class extension_t:
113
113
 
114
114
  return f"{half_rule} {text} {half_rule}"
115
115
 
116
- def EmitAsIs(self, message: str, /) -> None:
116
+ def EmitMessage(self, message: str, /) -> None:
117
117
  """"""
118
118
  raise NotImplementedError
119
119
 
120
120
  def EmitRule(self, /, *, text: str | None = None, color: str = "black") -> None:
121
121
  """"""
122
- self.EmitAsIs(self.Rule(text, color=color))
122
+ self.EmitMessage(self.Rule(text=text, color=color))
123
123
 
124
124
 
125
125
  class handler_t(l.Handler, extension_t):
logger_36/type/logger.py CHANGED
@@ -19,7 +19,12 @@ from os import sep as FOLDER_SEPARATOR
19
19
  from pathlib import Path as path_t
20
20
  from traceback import TracebackException as traceback_t
21
21
 
22
- from logger_36.catalog.config.optional import MISSING_RICH_MESSAGE, RICH_IS_AVAILABLE
22
+ from logger_36.catalog.config.optional import (
23
+ MEMORY_MEASURE_ERROR,
24
+ MEMORY_MEASURE_IS_AVAILABLE,
25
+ MISSING_RICH_MESSAGE,
26
+ RICH_IS_AVAILABLE,
27
+ )
23
28
  from logger_36.catalog.handler.console import console_handler_t
24
29
  from logger_36.catalog.handler.file import file_handler_t
25
30
  from logger_36.config.issue import ISSUE_CONTEXT_END, ISSUE_CONTEXT_SEPARATOR
@@ -30,17 +35,19 @@ from logger_36.config.message import (
30
35
  TIME_FORMAT,
31
36
  WHERE_SEPARATOR,
32
37
  )
33
- from logger_36.constant.error import MEMORY_MEASURE_ERROR
34
38
  from logger_36.constant.generic import NOT_PASSED
35
39
  from logger_36.constant.issue import ISSUE_LEVEL_SEPARATOR, ORDER, order_h
36
40
  from logger_36.constant.logger import WARNING_LOGGER_NAME, WARNING_TYPE_COMPILED_PATTERN
37
41
  from logger_36.constant.memory import UNKNOWN_MEMORY_USAGE
38
42
  from logger_36.constant.message import LINE_INDENT, TIME_LENGTH_m_1, expected_op_h
39
43
  from logger_36.constant.path import PROJECT_FILE_RELATIVE, USER_FOLDER
40
- from logger_36.constant.record import SHOW_W_RULE_ATTR, SHOW_WHERE_ATTR
44
+ from logger_36.constant.record import (
45
+ INTERNAL_INFO_ATTR,
46
+ SHOW_W_RULE_ATTR,
47
+ SHOW_WHERE_ATTR,
48
+ )
41
49
  from logger_36.task.format.message import MessageWithActualExpected
42
50
  from logger_36.task.measure.chronos import ElapsedTime
43
- from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
44
51
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
45
52
  from logger_36.type.handler import any_handler_t as base_handler_t
46
53
  from logger_36.type.handler import extension_t as handler_extension_t
@@ -61,7 +68,6 @@ logger_handle_h = logger_handle_raw_h | logger_handle_with_self_h
61
68
 
62
69
  _DATE_TIME_ORIGIN = date_time_t.fromtimestamp(1970, None)
63
70
  _DATE_ORIGIN = _DATE_TIME_ORIGIN.date()
64
- _MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
65
71
 
66
72
 
67
73
  @d.dataclass(slots=True, repr=False, eq=False)
@@ -74,16 +80,17 @@ class logger_t(base_t):
74
80
  will be missed by an early call of ToggleLogInterceptions. Therefore, passing True
75
81
  for activate_log_interceptions only sets _should_activate_log_interceptions to True,
76
82
  which is later checked in AddHandler to effectively call ToggleLogInterceptions if
77
- _should_hold_messages is False (which normally indicates that the handler about to
83
+ _warming_up is False (which normally indicates that the handler about to
78
84
  be added is the last one).
79
85
 
80
- _should_hold_messages: Must not be False until at least one handler has been added.
86
+ _warming_up: Must not be False until at least one handler has been added.
81
87
  """
82
88
 
83
89
  should_record_messages: bool = False
84
- should_watch_memory_usage: bool = False
90
+ should_monitor_memory_usage: bool = False
85
91
  exit_on_error: bool = False # Implies exit_on_critical.
86
92
  exit_on_critical: bool = False
93
+ verbose: bool = False
87
94
 
88
95
  events: dict[int, int] = d.field(init=False, default_factory=dict)
89
96
  recorded: list[tuple[int, str]] = d.field(init=False, default_factory=list)
@@ -98,11 +105,13 @@ class logger_t(base_t):
98
105
  init=False, default_factory=dict
99
106
  )
100
107
  intercepts_exceptions: bool = d.field(init=False, default=False)
101
- _should_hold_messages: bool = d.field(init=False, default=True)
102
- _should_activate_log_interceptions: bool = d.field(init=False, default=False)
103
- _on_hold: list[l.LogRecord] = d.field(init=False, default_factory=list)
104
108
  _recording_handler: handler_extension_t | None = d.field(init=False, default=None)
105
109
 
110
+ # Used only until the last handler is added (see AddHandler).
111
+ _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
+
106
115
  name_: d.InitVar[str | None] = None
107
116
  level_: d.InitVar[int] = l.NOTSET
108
117
  activate_wrn_interceptions: d.InitVar[bool] = True
@@ -156,11 +165,10 @@ class logger_t(base_t):
156
165
  activate_exc_interceptions: bool,
157
166
  ) -> None:
158
167
  """"""
159
- global _MEMORY_MEASURE_ERROR
160
-
161
168
  if name_ is None:
162
169
  name_ = f"{type(self).__name__}:{hex(id(self))[2:]}"
163
170
 
171
+ self.handle = self._HandleWarmUp
164
172
  base_t.__init__(self, name_)
165
173
  self.setLevel(level_)
166
174
  self.propagate = False # Part of base_t.
@@ -174,7 +182,10 @@ class logger_t(base_t):
174
182
  if self.should_record_messages:
175
183
  self.ActivateMessageRecording()
176
184
 
177
- self.info(f'New logger "{self.name}" for "{PROJECT_FILE_RELATIVE}"')
185
+ self.info(
186
+ f'New logger "{self.name}" for "{PROJECT_FILE_RELATIVE}"',
187
+ extra={INTERNAL_INFO_ATTR: True},
188
+ )
178
189
 
179
190
  if activate_wrn_interceptions:
180
191
  self.ToggleWarningInterceptions(True)
@@ -183,14 +194,32 @@ class logger_t(base_t):
183
194
  if activate_exc_interceptions:
184
195
  self.ToggleExceptionInterceptions(True)
185
196
 
186
- if self.should_watch_memory_usage and not CanCheckMemoryUsage():
187
- self.should_watch_memory_usage = False
188
- if _MEMORY_MEASURE_ERROR is not None:
189
- s.__stderr__.write(_MEMORY_MEASURE_ERROR + "\n")
190
- _MEMORY_MEASURE_ERROR = None
197
+ if self.should_monitor_memory_usage:
198
+ self.ActivateMemoryUsageMonitoring()
191
199
 
192
- def handle(self, record: l.LogRecord, /) -> None:
200
+ def _HandleWarmUp(self, record: l.LogRecord, /) -> None:
193
201
  """"""
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
+
194
223
  elapsed_time, now = ElapsedTime(should_return_now=True)
195
224
 
196
225
  if (date := now.date()) != self.last_message_date:
@@ -206,19 +235,8 @@ class logger_t(base_t):
206
235
 
207
236
  # Where.
208
237
  should_show_where = getattr(record, SHOW_WHERE_ATTR, record.levelno != l.INFO)
209
- if should_show_where or self.should_watch_memory_usage:
210
- module = path_t(record.pathname)
211
- for path in s.path:
212
- if module.is_relative_to(path):
213
- module = module.relative_to(path).with_suffix("")
214
- module = str(module).replace(FOLDER_SEPARATOR, ".")
215
- break
216
- else:
217
- if module.is_relative_to(USER_FOLDER):
218
- module = module.relative_to(USER_FOLDER)
219
- where = f"{module}:{record.funcName}:{record.lineno}"
220
- if should_show_where:
221
- record.where = where
238
+ if should_show_where or self.should_monitor_memory_usage:
239
+ where = _RecordLocation(record, should_show_where)
222
240
  else:
223
241
  where = None
224
242
 
@@ -226,13 +244,9 @@ class logger_t(base_t):
226
244
  if not isinstance(record.msg, str):
227
245
  record.msg = str(record.msg)
228
246
 
229
- if self._should_hold_messages:
230
- self._on_hold.append(record)
231
- else:
232
- self._HandleRecord(record)
233
- self.events[record.levelno] += 1
247
+ self._HandleRaw(record)
234
248
 
235
- if self.should_watch_memory_usage:
249
+ if self.should_monitor_memory_usage:
236
250
  self.memory_usages.append((where, CurrentMemoryUsage()))
237
251
 
238
252
  if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
@@ -242,12 +256,28 @@ class logger_t(base_t):
242
256
  # __post_init__ set self.exit_on_critical if self.exit_on_error.
243
257
  s.exit(1)
244
258
 
245
- def _FlushRecordsOnHold(self) -> None:
259
+ def _HandleRaw(self, record: l.LogRecord, /) -> None:
246
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
+ """
247
272
  for held in self._on_hold:
248
- self._HandleRecord(held)
273
+ if self.verbose or not getattr(held, INTERNAL_INFO_ATTR, False):
274
+ self._HandleRaw(held)
249
275
 
250
276
  self._on_hold.clear()
277
+ self._on_hold = None
278
+ self._warming_up = False
279
+
280
+ self.handle = self._HandleRoutine
251
281
 
252
282
  def _AcknowledgeDateChange(self, date: date_t, /) -> None:
253
283
  """"""
@@ -262,24 +292,32 @@ class logger_t(base_t):
262
292
  }
263
293
  )
264
294
 
265
- if self._should_hold_messages:
295
+ if self._warming_up:
266
296
  self._on_hold.append(record)
267
297
  else:
268
- self._HandleRecord(record)
269
-
270
- def _HandleRecord(self, record: l.LogRecord, /) -> None:
271
- """"""
272
- if self.should_record_messages:
273
- message = self._recording_handler.MessageFromRecord(record)[0]
274
- self.recorded.append((record.levelno, message))
275
-
276
- base_t.handle(self, record)
298
+ self._HandleRaw(record)
277
299
 
278
300
  def ActivateMessageRecording(self) -> None:
279
301
  """"""
280
302
  self._recording_handler = handler_extension_t("recording_handler", 0, None)
281
303
  self.should_record_messages = True # Useless if called from __post_init__.
282
- self.info(f'Message recording activated for logger "{self.name}"')
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)
283
321
 
284
322
  def ResetEventCounts(self) -> None:
285
323
  """"""
@@ -297,7 +335,7 @@ class logger_t(base_t):
297
335
  logger.handle = t.MethodType(_HandleForWarnings(self), logger)
298
336
 
299
337
  l.captureWarnings(True)
300
- self.info("Warning Interception: ON")
338
+ self.info("Warning Interception: ON", extra={INTERNAL_INFO_ATTR: True})
301
339
  else:
302
340
  if not self.intercepts_warnings:
303
341
  return
@@ -307,7 +345,7 @@ class logger_t(base_t):
307
345
  self.intercepted_wrn_handle = None
308
346
 
309
347
  l.captureWarnings(False)
310
- self.info("Warning Interception: OFF")
348
+ self.info("Warning Interception: OFF", extra={INTERNAL_INFO_ATTR: True})
311
349
 
312
350
  def ToggleLogInterceptions(self, state: bool, /) -> None:
313
351
  """"""
@@ -331,7 +369,10 @@ class logger_t(base_t):
331
369
  intercepted = sorted(self.intercepted_log_handles.keys())
332
370
  if intercepted.__len__() > 0:
333
371
  as_str = ", ".join(intercepted)
334
- self.info(f"Now Intercepting LOGs from: {as_str}")
372
+ self.info(
373
+ f"Now Intercepting LOGs from: {as_str}",
374
+ extra={INTERNAL_INFO_ATTR: True},
375
+ )
335
376
  else:
336
377
  if not self.intercepts_logs:
337
378
  return
@@ -340,7 +381,7 @@ class logger_t(base_t):
340
381
  logger = l.getLogger(name)
341
382
  logger.handle = handle
342
383
  self.intercepted_log_handles.clear()
343
- self.info("Log Interception: OFF")
384
+ self.info("Log Interception: OFF", extra={INTERNAL_INFO_ATTR: True})
344
385
 
345
386
  def ToggleExceptionInterceptions(self, state: bool, /) -> None:
346
387
  """"""
@@ -351,7 +392,7 @@ class logger_t(base_t):
351
392
  s.excepthook = self.DealWithException
352
393
  thrd.excepthook = self.DealWithExceptionInThread
353
394
  self.intercepts_exceptions = True
354
- self.info("Exception Interception: ON")
395
+ self.info("Exception Interception: ON", extra={INTERNAL_INFO_ATTR: True})
355
396
  else:
356
397
  if not self.intercepts_exceptions:
357
398
  return
@@ -359,7 +400,7 @@ class logger_t(base_t):
359
400
  s.excepthook = s.__excepthook__
360
401
  thrd.excepthook = thrd.__excepthook__
361
402
  self.intercepts_exceptions = False
362
- self.info("Exception Interception: OFF")
403
+ self.info("Exception Interception: OFF", extra={INTERNAL_INFO_ATTR: True})
363
404
 
364
405
  def AddHandler(
365
406
  self,
@@ -372,7 +413,7 @@ class logger_t(base_t):
372
413
  name: str | None = None,
373
414
  level: int = l.INFO,
374
415
  message_width: int = -1,
375
- should_still_hold_messages: bool = False,
416
+ this_is_last_handler: bool = True,
376
417
  **kwargs,
377
418
  ) -> None:
378
419
  """
@@ -380,12 +421,11 @@ class logger_t(base_t):
380
421
  """
381
422
  should_flush_on_hold = False
382
423
  new_handler_warning = ""
383
- if self._should_hold_messages and not should_still_hold_messages:
424
+ if self._warming_up and this_is_last_handler:
384
425
  if self._should_activate_log_interceptions:
385
426
  self.ToggleLogInterceptions(True)
386
427
  self._should_activate_log_interceptions = False
387
428
 
388
- self._should_hold_messages = False
389
429
  should_flush_on_hold = True
390
430
  new_handler_warning = "\n(Handlers added from now on will miss above logs.)"
391
431
 
@@ -397,32 +437,33 @@ class logger_t(base_t):
397
437
  handler = handler_t_or_handler
398
438
  base_t.addHandler(self, handler)
399
439
 
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
+
400
451
  # Wait until after the handler has been added to flush messages on hold.
401
452
  if should_flush_on_hold:
402
453
  self._FlushRecordsOnHold()
403
454
 
404
- path = getattr(handler, "baseFilename", "")
405
- if isinstance(path, path_t) or (path.__len__() > 0):
406
- path = f"\nPath: {path}"
407
- self.info(
408
- f'New handler "{handler.name}" of type "{type(handler).__name__}" and '
409
- f"level {handler.level}={l.getLevelName(handler.level)}{path}"
410
- f"{new_handler_warning}"
411
- )
412
-
413
455
  def MakeMonochrome(self) -> None:
414
456
  """"""
415
457
  self.AddHandler(console_handler_t)
416
458
 
417
459
  def MakeRich(self, *, alternating_logs: int = 0) -> None:
418
460
  """"""
419
- if MISSING_RICH_MESSAGE is not None:
420
- s.__stderr__.write(MISSING_RICH_MESSAGE + "\n")
421
-
422
- if console_rich_handler_t is console_handler_t:
423
- handler_kwargs = {}
424
- else:
461
+ if RICH_IS_AVAILABLE:
425
462
  handler_kwargs = {"alternating_logs": alternating_logs}
463
+ else:
464
+ handler_kwargs = {}
465
+ self.error(MISSING_RICH_MESSAGE)
466
+
426
467
  self.AddHandler(console_rich_handler_t, **handler_kwargs)
427
468
 
428
469
  def MakePermanent(self, path: str | path_t, /) -> None:
@@ -509,8 +550,11 @@ class logger_t(base_t):
509
550
  message = text.indent(message, LINE_INDENT)
510
551
 
511
552
  for handler in self.handlers:
512
- if (EmitAsIs := getattr(handler, "EmitAsIs", None)) is not None:
513
- EmitAsIs(message)
553
+ EmitMessage = getattr(
554
+ handler, handler_extension_t.EmitMessage.__name__, None
555
+ )
556
+ if EmitMessage is not None:
557
+ EmitMessage(message)
514
558
 
515
559
  info_raw = LogAsIs # To follow the convention of the logging methods info, error...
516
560
 
@@ -519,7 +563,8 @@ class logger_t(base_t):
519
563
  ) -> None:
520
564
  """"""
521
565
  for handler in self.handlers:
522
- if (EmitRule := getattr(handler, "EmitRule", None)) is not None:
566
+ EmitRule = getattr(handler, handler_extension_t.EmitRule.__name__, None)
567
+ if EmitRule is not None:
523
568
  EmitRule(text=message, color=color)
524
569
 
525
570
  def AddContextLevel(self, new_level: str, /) -> None:
@@ -647,6 +692,26 @@ class logger_t(base_t):
647
692
  return False
648
693
 
649
694
 
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
+
650
715
  def _HandleForWarnings(interceptor: base_t, /) -> logger_handle_h:
651
716
  """"""
652
717
 
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.21"
7
+ __version__ = "2025.22"
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.21
3
+ Version: 2025.22
4
4
  Summary: Simple logger with a catalog of handlers
5
5
  Home-page: https://src.koda.cnrs.fr/eric.debreuve/logger-36/
6
6
  Author: Eric Debreuve
@@ -1,5 +1,5 @@
1
1
  logger_36/__init__.py,sha256=mK6AD0eWI2Sk42oxleTvsxzYJ28FbHK5WNkpLgAhnNE,2129
2
- logger_36/version.py,sha256=uq-1uJ3Ttvm7hNAwg0S3T103Mh6UIamgGGIWFao8YLI,1680
2
+ logger_36/version.py,sha256=WM8W-3pzA_JWd1DQWpdL-uJTAxV5u8_nbyWIlGaizjg,1680
3
3
  logger_36/api/content.py,sha256=DuT4UX4r_1DTXzuuRD-tvsTZk5X-Nj11loBKhuWOMw0,1791
4
4
  logger_36/api/gpu.py,sha256=NNs1IvQ7bh8Dppm8O8K2YLWbm4rogc3Ie_-D6xzkX3g,1726
5
5
  logger_36/api/memory.py,sha256=vOY4cTTrC3u7L0OrKXdPNlsCahYjCrY4h7iqpGZv9kU,2217
@@ -8,11 +8,11 @@ logger_36/api/system.py,sha256=h-3GfhZPwawv0UKBWKkT1LzxSCZwpA2VIsy03lLYi6w,1725
8
8
  logger_36/api/time.py,sha256=JG0vgzPSRZ7UWQyoihnVu4sjPC-okFIKA3ZyNh2GaZo,1798
9
9
  logger_36/api/type.py,sha256=eLZ2yuH-sYeh4Z2KnAwTRJEbmkmgzBPMncdqXfFUTG8,1760
10
10
  logger_36/catalog/config/console_rich.py,sha256=t9p9-AkSgPiLAsm1evAdbz77g7JcVLePhUJ1FzNi3cY,2330
11
- logger_36/catalog/config/optional.py,sha256=5vabOlEQIFxoT_y4AjP19rpOjBuUJplpuBkLoCIKImA,1872
12
- logger_36/catalog/handler/console.py,sha256=OKtRG3PseT06_UwZBtbragNIKvCqa7ZsLphYEsgeq2c,2308
13
- logger_36/catalog/handler/console_rich.py,sha256=b3B_pQefv0IeQFEj-tZ5-tH8RrcessZI8sI4Tyf17bg,6180
14
- logger_36/catalog/handler/file.py,sha256=JReN8KruN8eKhBfG9sj72kkHfukHK4-8mdSxNpgFhLU,2455
15
- logger_36/catalog/handler/generic.py,sha256=0Z4Vm-AAc92szghYEzihkm9CBRzCvqgdUTXH_p98S4o,6314
11
+ 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
14
+ 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
16
  logger_36/catalog/logger/chronos.py,sha256=S4m9TMPQy_Ju500mpE1jNzu2gZG-QKdVuvX9RVRKHR8,1911
17
17
  logger_36/catalog/logger/gpu.py,sha256=Py5YY0nD_pqJzJsEKQYoOGHcPqyNVJ3J2noOS3hDL6g,2890
18
18
  logger_36/catalog/logger/memory.py,sha256=J0ZGKO7j1FZA_aDGxpABtvzDy1RjCDiDmWYh4U98fEI,4253
@@ -29,7 +29,7 @@ logger_36/constant/logger.py,sha256=ZQYX9JiPsoivwRgYNtdEqRKCagSKD88lRqvxP8MX1ZE,
29
29
  logger_36/constant/memory.py,sha256=Q_E5tTWa-cGaNwrE_xmKa3BxQG6oJO6DHczrxc_M4sE,1817
30
30
  logger_36/constant/message.py,sha256=YJOEzdI0ZjUOdHo3CsiS56FVPhrfNoQYvXuUkprH61g,2312
31
31
  logger_36/constant/path.py,sha256=OfLh70Jyc8po9Ls34nQh_bRr3PXyQ3kF9ciR9QPhiqI,2213
32
- logger_36/constant/record.py,sha256=gQCGLxq8Vs789Ty_qaRNKy18mqlyMT_4kyN-T9r_rGE,1734
32
+ logger_36/constant/record.py,sha256=IGWnKA4TGv1oU6ezzNrGocZlB7Ie7nTr4l3IOtwdWOg,1833
33
33
  logger_36/constant/rule.py,sha256=tBKQgPTt6G_p5eInDdWoEEAvQFz4WMSt5THsS5jvk14,1779
34
34
  logger_36/constant/system.py,sha256=pLlLXG5sepQlSUOo3TphaGrHg8xzJBp-GxpL2NPP47k,1904
35
35
  logger_36/extension/html_.py,sha256=W9SyiYsaaYHUrHLGAAN2wiJGXUlwOBJ5gzdjmEcnF18,3342
@@ -42,11 +42,11 @@ logger_36/task/format/memory.py,sha256=J1Oy3jw8wjSp2kuiRUm_VFpzXOHX2FOc7nuRrCyrs
42
42
  logger_36/task/format/message.py,sha256=Rm6zymVEEGcgKfmxMPXP7q3PtwZJKlXGhqZ5tnvlwxA,3502
43
43
  logger_36/task/measure/chronos.py,sha256=7ijMZgP4EP18HbLV2yCxpNpRS9724Wyk523f-nkbhUM,2529
44
44
  logger_36/task/measure/memory.py,sha256=kkPHEIUTUhkCOLrAt01eLJLnsnkl0nFPNhFZdIB_JAw,1991
45
- logger_36/type/handler.py,sha256=ChxP1j9PXLmoiNcsOdxI4bYVdr75v7HeCWp_iYJ2WNY,6602
45
+ logger_36/type/handler.py,sha256=4LCFZzI4OEH6lVBrBQ09ZIvF0nq-_j7OOVnujBDyDWA,6665
46
46
  logger_36/type/issue.py,sha256=QHAYf7QgrjJUtF2D46z6X630qTgeP_0FE5hIwf54RsE,2688
47
- logger_36/type/logger.py,sha256=-cEc56RRpYkXaoDjI7PF7q14Mamdc4zu3DmQ-lRjIn8,25823
47
+ logger_36/type/logger.py,sha256=b2F21M5z1hwPdDsyHgBIlC06X9-_7zsjqyGEsVaIJgM,27536
48
48
  logger_36/type/loggers.py,sha256=7EX7Sg_RlduBjdfFlNZmUfNeDloH1xU30Rdkg_-rXh8,3172
49
- logger_36-2025.21.dist-info/METADATA,sha256=bTX5e3Xn8lko_GnzpFd7XnE60aNUv6V_9JyoLwzE2G4,6529
50
- logger_36-2025.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
- logger_36-2025.21.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
52
- logger_36-2025.21.dist-info/RECORD,,
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,,