logger-36 2025.27__tar.gz → 2025.29__tar.gz

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.
Files changed (62) hide show
  1. {logger_36-2025.27 → logger_36-2025.29}/PKG-INFO +1 -1
  2. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/logger/chronos.py +34 -6
  3. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/task/measure/chronos.py +7 -2
  4. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/type/logger.py +37 -18
  5. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/version.py +1 -1
  6. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36.egg-info/PKG-INFO +1 -1
  7. {logger_36-2025.27 → logger_36-2025.29}/MANIFEST.in +0 -0
  8. {logger_36-2025.27 → logger_36-2025.29}/README-COPYRIGHT-utf8.txt +0 -0
  9. {logger_36-2025.27 → logger_36-2025.29}/README-LICENCE-utf8.txt +0 -0
  10. {logger_36-2025.27 → logger_36-2025.29}/README.rst +0 -0
  11. {logger_36-2025.27 → logger_36-2025.29}/documentation/wiki/description.asciidoc +0 -0
  12. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/__init__.py +0 -0
  13. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/api/chronos.py +0 -0
  14. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/api/logger.py +0 -0
  15. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/api/memory.py +0 -0
  16. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/api/message.py +0 -0
  17. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/api/storage.py +0 -0
  18. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/config/console_rich.py +0 -0
  19. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/config/optional.py +0 -0
  20. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/handler/console.py +0 -0
  21. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/handler/console_rich.py +0 -0
  22. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/handler/file.py +0 -0
  23. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/handler/generic.py +0 -0
  24. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/handler/memory.py +0 -0
  25. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/logger/gpu.py +0 -0
  26. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/logger/memory.py +0 -0
  27. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/catalog/logger/system.py +0 -0
  28. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/config/issue.py +0 -0
  29. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/config/memory.py +0 -0
  30. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/config/message.py +0 -0
  31. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/config/rule.py +0 -0
  32. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/config/system.py +0 -0
  33. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/chronos.py +0 -0
  34. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/error.py +0 -0
  35. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/html.py +0 -0
  36. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/issue.py +0 -0
  37. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/logger.py +0 -0
  38. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/memory.py +0 -0
  39. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/message.py +0 -0
  40. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/path.py +0 -0
  41. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/record.py +0 -0
  42. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/rule.py +0 -0
  43. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/constant/system.py +0 -0
  44. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/extension/file.py +0 -0
  45. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/extension/inspection.py +0 -0
  46. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/extension/line.py +0 -0
  47. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/extension/sentinel.py +0 -0
  48. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/instance/logger.py +0 -0
  49. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/instance/loggers.py +0 -0
  50. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/task/format/memory.py +0 -0
  51. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/task/format/message.py +0 -0
  52. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/task/measure/memory.py +0 -0
  53. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/task/storage.py +0 -0
  54. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/type/handler.py +0 -0
  55. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/type/issue.py +0 -0
  56. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36/type/loggers.py +0 -0
  57. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36.egg-info/SOURCES.txt +0 -0
  58. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36.egg-info/dependency_links.txt +0 -0
  59. {logger_36-2025.27 → logger_36-2025.29}/package/logger_36.egg-info/top_level.txt +0 -0
  60. {logger_36-2025.27 → logger_36-2025.29}/pyproject.toml +0 -0
  61. {logger_36-2025.27 → logger_36-2025.29}/setup.cfg +0 -0
  62. {logger_36-2025.27 → logger_36-2025.29}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logger-36
3
- Version: 2025.27
3
+ Version: 2025.29
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
@@ -6,20 +6,48 @@ SEE COPYRIGHT NOTICE BELOW
6
6
 
7
7
  from datetime import datetime as date_time_t
8
8
 
9
- from logger_36.constant.chronos import FORMATTED_START_DATE_TIME
10
- from logger_36.constant.record import SHOW_WHEN_ATTR
9
+ from logger_36.constant.chronos import FORMATTED_START_DATE_TIME, START_DATE_TIME
10
+ from logger_36.constant.record import SHOW_WHEN_ATTR, SHOW_WHERE_ATTR
11
11
  from logger_36.instance.logger import L
12
12
  from logger_36.task.measure.chronos import FormattedElapsedTime
13
13
  from logger_36.type.logger import logger_t
14
14
 
15
15
 
16
+ _START_NAME = "START"
17
+ _START_PLACEHOLDER = "..." # Must not be longer than _START_NAME.
18
+ _END_NAME = "END"
19
+
20
+
16
21
  def LogElapsedTime(*, logger: logger_t = L) -> None:
17
22
  """"""
18
- logger.info(
19
- f"Elapsed Time: {FormattedElapsedTime(date_time_t.now())[1:]} "
20
- f"(since {FORMATTED_START_DATE_TIME})",
21
- extra={SHOW_WHEN_ATTR: False},
23
+ now = date_time_t.now()
24
+
25
+ message = (
26
+ f"Elapsed Time: {FormattedElapsedTime(now, with_separator=False)} "
27
+ f"(since {FORMATTED_START_DATE_TIME})"
22
28
  )
29
+ if logger.intermediate_times.__len__() > 0:
30
+ intermediate_e_times = []
31
+ for (start_name, start_time), (end_name, end_time) in zip(
32
+ [(_START_NAME, START_DATE_TIME)] + logger.intermediate_times,
33
+ logger.intermediate_times + [(_END_NAME, now)],
34
+ strict=True,
35
+ ):
36
+ if start_name != _START_NAME:
37
+ start_name = _START_PLACEHOLDER
38
+ e_time = FormattedElapsedTime(
39
+ end_time, reference=start_time, with_separator=False
40
+ )
41
+ intermediate_e_times.append((start_name, end_name, e_time))
42
+ max_length_end = max(map(len, (_[1] for _ in intermediate_e_times)))
43
+ intermediate_e_times = "\n ".join(
44
+ f"{_: <{_START_NAME.__len__()}} → {__: <{max_length_end}} +{___}"
45
+ for _, __, ___ in intermediate_e_times
46
+ )
47
+ message += "\n " + intermediate_e_times
48
+ logger.intermediate_times.clear()
49
+
50
+ logger.info(message, extra={SHOW_WHEN_ATTR: False, SHOW_WHERE_ATTR: False})
23
51
 
24
52
 
25
53
  """
@@ -24,7 +24,11 @@ def TimeStamp(*, precision: str = "microseconds") -> str:
24
24
 
25
25
 
26
26
  def FormattedElapsedTime(
27
- now: date_time_t, /, *, reference: date_time_t = START_DATE_TIME
27
+ now: date_time_t,
28
+ /,
29
+ *,
30
+ reference: date_time_t = START_DATE_TIME,
31
+ with_separator: bool = True,
28
32
  ) -> str:
29
33
  """"""
30
34
  output = str(now - reference)
@@ -36,7 +40,8 @@ def FormattedElapsedTime(
36
40
  if output[0] == "0":
37
41
  output = output[1:]
38
42
 
39
- output = ELAPSED_TIME_SEPARATOR + output
43
+ if with_separator:
44
+ output = ELAPSED_TIME_SEPARATOR + output
40
45
 
41
46
  if output.__len__() > TIME_LENGTH:
42
47
  return output[:TIME_LENGTH]
@@ -92,6 +92,9 @@ class logger_t(base_t):
92
92
 
93
93
  history: dict[date_time_t, str] = d.field(init=False, default_factory=dict)
94
94
  n_events: dict[int, int] = d.field(init=False, default_factory=dict)
95
+ intermediate_times: list[tuple[str, date_time_t]] = d.field(
96
+ init=False, default_factory=list
97
+ )
95
98
 
96
99
  last_message_now: date_time_t = d.field(init=False, default=DATE_TIME_ORIGIN)
97
100
  last_message_date: date_t = d.field(init=False, default=DATE_ORIGIN)
@@ -109,6 +112,7 @@ class logger_t(base_t):
109
112
  # Used only until the first handler is added (see AddHandler).
110
113
  _should_activate_log_interceptions: bool = d.field(init=False, default=False)
111
114
 
115
+ server_handlers: tuple[l.Handler,...]|None = d.field(init=False, default=None)
112
116
  log_server: log_server_t | None = d.field(init=False, default=None)
113
117
 
114
118
  name_: d.InitVar[str | None] = None
@@ -429,9 +433,12 @@ class logger_t(base_t):
429
433
  handler). If handlers are added passed this call, execution might freeze or
430
434
  crash.
431
435
  """
432
- assert self.log_server is None
436
+ assert prll.current_process().name == MAIN_PROCESS_NAME
433
437
  assert self.hasHandlers()
434
438
 
439
+ if self.log_server is not None:
440
+ return
441
+
435
442
  handlers = tuple(self.handlers) # Making a copy is necessary.
436
443
  for handler in handlers:
437
444
  self.removeHandler(handler)
@@ -440,9 +447,31 @@ class logger_t(base_t):
440
447
 
441
448
  self.addHandler(queue_handler_t(queue))
442
449
 
443
- self.log_server = log_server_t(queue, *handlers)
450
+ self.server_handlers = handlers
451
+ self.log_server = log_server_t(queue, *handlers, respect_handler_level=True)
444
452
  self.log_server.start()
445
453
 
454
+ def RemoveMultiSafety(self) -> None:
455
+ """
456
+ Calling this method automatically as an atexit-registered function does not
457
+ flush the enqueued log records. It is probably too late then for this. Hence, if
458
+ MakeMultiSafe has been called, this method should also be called before the
459
+ execution ends.
460
+ """
461
+ assert prll.current_process().name == MAIN_PROCESS_NAME
462
+
463
+ if self.log_server is None:
464
+ return
465
+
466
+ self.log_server.stop()
467
+
468
+ for handler in self.handlers: # There is only the QueueHandler, actually.
469
+ self.removeHandler(handler)
470
+ for handler in self.server_handlers:
471
+ self.addHandler(handler)
472
+
473
+ self.server_handlers = self.log_server = None
474
+
446
475
  def __call__(self, *args, **kwargs) -> None:
447
476
  """
448
477
  For a print-like calling for print-based debugging.
@@ -510,7 +539,7 @@ class logger_t(base_t):
510
539
  message = "\n".join(lines[:1] + lines[2:])
511
540
  else:
512
541
  # TODO: Explain:
513
- # - Why it's not: "\n".join(lines)?
542
+ # - Why is it not: "\n".join(lines)?
514
543
  # - Why adding exception name here and not when removing caller?
515
544
  formatted = "".join(lines)
516
545
  message = f"Exception of type {type(exception).__name__}\n----\n{formatted}"
@@ -541,11 +570,6 @@ class logger_t(base_t):
541
570
  }
542
571
  )
543
572
  base_t.handle(self, record)
544
- # emit_rule_name = handler_extension_t.EmitRule.__name__
545
- # for handler in self.handlers:
546
- # EmitRule = getattr(handler, emit_rule_name, None)
547
- # if EmitRule is not None:
548
- # EmitRule(text=message, color=color)
549
573
 
550
574
  def AddContextLevel(self, new_level: str, /) -> None:
551
575
  """"""
@@ -670,9 +694,13 @@ class logger_t(base_t):
670
694
  del extra[HAS_ACTUAL_EXPECTED_ATTR]
671
695
  self.staged_issues.clear()
672
696
 
697
+ def SetInstantName(self, name: str, /) -> None:
698
+ """"""
699
+ self.intermediate_times.append((name, date_time_t.now()))
700
+
673
701
  def StoragePath(self, suffix: str, /) -> path_t:
674
702
  """
675
- Use as staticmethod if needed.
703
+ Use as staticmethod if needed, passing a logger as self.
676
704
  """
677
705
  for handler in self.handlers:
678
706
  if (path := getattr(handler, "baseFilename", None)) is not None:
@@ -699,15 +727,6 @@ class logger_t(base_t):
699
727
  _ = self.context_levels.pop()
700
728
  return False
701
729
 
702
- def __del__(self) -> None:
703
- """"""
704
- if (
705
- (prll.current_process is not None)
706
- and (prll.current_process().name == MAIN_PROCESS_NAME)
707
- and (self.log_server is not None)
708
- ):
709
- self.log_server.stop()
710
-
711
730
 
712
731
  def _HandleForWarnings(interceptor: base_t, /) -> logger_handle_h:
713
732
  """"""
@@ -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.27"
7
+ __version__ = "2025.29"
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.27
3
+ Version: 2025.29
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
File without changes
File without changes
File without changes
File without changes
File without changes