logger-36 2025.11__py3-none-any.whl → 2025.13__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.
@@ -16,11 +16,7 @@ _BLOCK_FULL = "▉"
16
16
 
17
17
 
18
18
  def FormattedUsage(
19
- usage: int,
20
- /,
21
- *,
22
- unit: storage_units_h | None = "a",
23
- decimals: int | None = None,
19
+ usage: int, /, *, unit: storage_units_h | None = "a", decimals: int | None = None
24
20
  ) -> tuple[int | float, str]:
25
21
  """
26
22
  unit: b or None=bytes, k=kilo, m=mega, g=giga, a=auto
@@ -39,7 +39,12 @@ def MessageWithActualExpected(
39
39
  else:
40
40
  dot = ""
41
41
 
42
- return f"{message}: Actual={actual}:{type(actual).__name__}; {expected}{dot}"
42
+ if isinstance(actual, type):
43
+ actual = actual.__name__
44
+ else:
45
+ actual = f"{actual}:{type(actual).__name__}"
46
+
47
+ return f"{message}: Actual={actual}; {expected}{dot}"
43
48
 
44
49
 
45
50
  def _FormattedExpected(
@@ -54,14 +59,17 @@ def _FormattedExpected(
54
59
  else:
55
60
  expected = ", ".join(map(str, expected))
56
61
  return f"Valid values: {expected}"
62
+
63
+ if isinstance(expected, type):
64
+ return f"Expected{operator}{expected.__name__}"
65
+
66
+ if operator == "=":
67
+ stripe = f":{type(expected).__name__}"
57
68
  else:
58
- if operator == "=":
59
- stripe = f":{type(expected).__name__}"
60
- else:
61
- stripe = ""
62
- if operator == ":":
63
- operator = ": "
64
- return f"Expected{operator}{expected}{stripe}"
69
+ stripe = ""
70
+ if operator == ":":
71
+ operator = ": "
72
+ return f"Expected{operator}{expected}{stripe}"
65
73
 
66
74
 
67
75
  """
@@ -27,7 +27,7 @@ if RICH_IS_AVAILABLE:
27
27
  return rule_t(title=text_t(text, style=f"bold {color}"), style=color)
28
28
 
29
29
  else:
30
- Rule = lambda _txt, _: RuleAsText(_txt)
30
+ Rule = lambda _, __: RuleAsText(_)
31
31
 
32
32
  """
33
33
  COPYRIGHT NOTICE
@@ -51,7 +51,7 @@ def Modules(
51
51
 
52
52
  if formatted:
53
53
  max_length += 4
54
- AlignedInColumns = lambda _str: f"{_str:{max_length}}" if _str != "\n" else "\n"
54
+ AlignedInColumns = lambda _: f"{_:{max_length}}" if _ != "\n" else "\n"
55
55
  output = map(AlignedInColumns, output)
56
56
  output = "".join(output).rstrip()
57
57
 
logger_36/task/storage.py CHANGED
@@ -39,7 +39,7 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
39
39
  L.warning(f'{cannot_save}: File "{path}" already exists.')
40
40
  return
41
41
 
42
- html = L.past_logs_as_HTML
42
+ html = "\n".join(L.recorded)
43
43
  if html is None:
44
44
  L.warning(f"{cannot_save}: No handler could provide an HTML output.")
45
45
  else:
logger_36/type/handler.py CHANGED
@@ -4,57 +4,37 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- import dataclasses as d
8
7
  import logging as l
9
8
  import sys as s
10
9
  import typing as h
10
+ from pathlib import Path as path_t
11
11
 
12
- from logger_36.config.message import (
13
- LEVEL_CLOSING,
14
- LEVEL_OPENING,
15
- MESSAGE_MARKER,
16
- WHERE_SEPARATOR,
17
- )
18
12
  from logger_36.constant.error import MEMORY_MEASURE_ERROR
19
13
  from logger_36.constant.handler import HANDLER_KINDS
20
- from logger_36.constant.message import NEXT_LINE_PROLOGUE
21
14
  from logger_36.task.format.message import MessageWithActualExpected
22
15
  from logger_36.task.measure.chronos import TimeStamp
23
16
  from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
24
-
25
- MessageFromRecordRaw_h = h.Callable[[l.LogRecord], str]
26
-
27
-
28
- @h.runtime_checkable
29
- class MessageFromRecordPreprocessed_p(h.Protocol):
30
- def __call__(
31
- self,
32
- record: l.LogRecord,
33
- /,
34
- *,
35
- PreProcessed: h.Callable[[str], str] | None = None,
36
- ) -> str: ...
37
-
38
-
39
- MessageFromRecord_h = MessageFromRecordRaw_h | MessageFromRecordPreprocessed_p
17
+ from logger_36.type.message import MessageFromRecord, MessageFromRecord_h
40
18
 
41
19
  _MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
42
20
 
43
21
 
44
- @d.dataclass(slots=True, repr=False, eq=False)
45
- class handler_extension_t:
46
- name: str | None = None
47
- should_store_memory_usage: bool = False
48
- message_width: int = -1
49
- MessageFromRecord: MessageFromRecord_h = d.field(init=False)
50
-
51
- handler: d.InitVar[l.Handler | None] = None
52
- level: d.InitVar[int] = l.NOTSET
53
- formatter: d.InitVar[l.Formatter | None] = None
22
+ class _base_t:
23
+ kind: h.ClassVar[str] = "" # See logger_36.constant.handler.handler_codes_h.
54
24
 
55
- def __post_init__(
56
- self, handler: l.Handler | None, level: int, formatter: l.Formatter | None
25
+ def __init__(
26
+ self, name: str | None, should_store_memory_usage: bool, message_width: int
57
27
  ) -> None:
28
+ """"""
29
+ self.name = name
30
+ self.should_store_memory_usage = should_store_memory_usage
31
+ self.message_width = message_width
32
+ #
33
+ self.MessageFromRecord: MessageFromRecord_h | None = None
34
+
35
+ self.__post_init__()
36
+
37
+ def __post_init__(self) -> None:
58
38
  """"""
59
39
  global _MEMORY_MEASURE_ERROR
60
40
 
@@ -76,81 +56,81 @@ class handler_extension_t:
76
56
  s.__stderr__.write(_MEMORY_MEASURE_ERROR + "\n")
77
57
  _MEMORY_MEASURE_ERROR = None
78
58
 
79
- handler.setLevel(level)
80
-
81
59
  if 0 < self.message_width < 5:
82
60
  self.message_width = 5
83
- if formatter is None:
84
- self.MessageFromRecord = self._MessageFromRecord
85
- else:
86
- handler.setFormatter(formatter)
87
- self.MessageFromRecord = handler.formatter.format
88
61
 
89
- def _MessageFromRecord(
90
- self,
91
- record: l.LogRecord,
92
- /,
93
- *,
94
- PreProcessed: h.Callable[[str], str] | None = None,
95
- ) -> str:
62
+ @classmethod
63
+ def New(cls, **kwargs) -> h.Self:
96
64
  """
97
- See logger_36.catalog.handler.README.txt.
65
+ Interest: default arguments, no prescribed argument order, variable argument list.
98
66
  """
99
- message = record.msg
100
-
101
- if PreProcessed is not None:
102
- message = PreProcessed(message)
103
- if (self.message_width <= 0) or (message.__len__() <= self.message_width):
104
- if "\n" in message:
105
- message = NEXT_LINE_PROLOGUE.join(message.splitlines())
106
- else:
107
- if "\n" in message:
108
- lines = _WrappedLines(message.splitlines(), self.message_width)
109
- else:
110
- lines = _WrappedLines([message], self.message_width)
111
- message = NEXT_LINE_PROLOGUE.join(lines)
112
-
113
- if (where := getattr(record, "where", None)) is None:
114
- where = ""
115
- else:
116
- where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
117
-
118
- return (
119
- f"{record.when_or_elapsed}"
120
- f"{LEVEL_OPENING}{record.level_first_letter}{LEVEL_CLOSING} "
121
- f"{MESSAGE_MARKER} {message}{where}"
122
- )
123
-
124
-
125
- def _WrappedLines(lines: list[str], message_width: int, /) -> list[str]:
126
- """"""
127
- output = []
128
-
129
- for line in lines:
130
- while line.__len__() > message_width:
131
- if all(
132
- _elm != " " for _elm in line[(message_width - 1) : (message_width + 1)]
133
- ):
134
- if line[message_width - 2] == " ":
135
- piece, line = (
136
- line[: (message_width - 2)].rstrip(),
137
- line[(message_width - 1) :],
138
- )
139
- else:
140
- piece, line = (
141
- line[: (message_width - 1)] + "-",
142
- line[(message_width - 1) :],
143
- )
144
- else:
145
- piece, line = (
146
- line[:message_width].rstrip(),
147
- line[message_width:].lstrip(),
148
- )
149
- output.append(piece)
67
+ raise NotImplementedError
150
68
 
151
- output.append(line)
152
69
 
153
- return output
70
+ class handler_t(l.Handler, _base_t):
71
+ def __init__(
72
+ self,
73
+ name: str | None,
74
+ should_store_memory_usage: bool,
75
+ message_width: int,
76
+ level: int,
77
+ formatter: l.Formatter | None,
78
+ ) -> None:
79
+ """"""
80
+ l.Handler.__init__(self)
81
+ _base_t.__init__(self, name, should_store_memory_usage, message_width)
82
+ _HandlerPostInit(self, level, formatter)
83
+
84
+
85
+ class file_handler_t(l.FileHandler, _base_t):
86
+ def __init__(
87
+ self,
88
+ name: str | None,
89
+ should_store_memory_usage: bool,
90
+ message_width: int,
91
+ level: int,
92
+ formatter: l.Formatter | None,
93
+ path: str | path_t | None,
94
+ ) -> None:
95
+ """"""
96
+ if path is None:
97
+ raise ValueError("Missing file or folder.")
98
+ if isinstance(path, str):
99
+ path = path_t(path)
100
+ if path.exists():
101
+ raise ValueError(f"File or folder already exists: {path}.")
102
+
103
+ l.FileHandler.__init__(self, path)
104
+ _base_t.__init__(self, name, should_store_memory_usage, message_width)
105
+ _HandlerPostInit(self, level, formatter)
106
+
107
+
108
+ any_handler_t = handler_t | file_handler_t
109
+
110
+
111
+ def _HandlerPostInit(
112
+ handler: any_handler_t, level: int, formatter: l.Formatter | None
113
+ ) -> None:
114
+ """"""
115
+ handler.setLevel(level)
116
+
117
+ if formatter is None:
118
+ handler.MessageFromRecord = MessageFromRecord
119
+ else:
120
+ handler.setFormatter(formatter)
121
+ _MessageFromRecordRaw = handler.formatter.format
122
+
123
+ def _MessageFromRecord(
124
+ record: l.LogRecord,
125
+ /,
126
+ *,
127
+ line_width: int = 0,
128
+ PreProcessed: h.Callable[[str], str] | None = None,
129
+ ) -> str:
130
+ #
131
+ return _MessageFromRecordRaw(record)
132
+
133
+ handler.MessageFromRecord = _MessageFromRecord
154
134
 
155
135
 
156
136
  """
logger_36/type/logger.py CHANGED
@@ -17,6 +17,9 @@ from os import sep as FOLDER_SEPARATOR
17
17
  from pathlib import Path as path_t
18
18
  from traceback import TracebackException as traceback_t
19
19
 
20
+ from logger_36.catalog.config.optional import MISSING_RICH_MESSAGE, RICH_IS_AVAILABLE
21
+ from logger_36.catalog.handler.console import console_handler_t
22
+ from logger_36.catalog.handler.file import file_handler_t
20
23
  from logger_36.config.issue import ISSUE_CONTEXT_END, ISSUE_CONTEXT_SEPARATOR
21
24
  from logger_36.config.message import (
22
25
  DATE_FORMAT,
@@ -40,13 +43,21 @@ from logger_36.constant.record import (
40
43
  SHOW_W_RULE_ATTR,
41
44
  STORE_MEMORY_ATTR,
42
45
  )
43
- from logger_36.exception import OverrideExceptionFormat
44
- from logger_36.handler import AddConsoleHandler, AddFileHandler, AddRichConsoleHandler
46
+ from logger_36.extension.exception import OverrideExceptionFormat
45
47
  from logger_36.task.format.message import MessageWithActualExpected
46
48
  from logger_36.task.format.rule import RuleAsText
47
49
  from logger_36.task.measure.chronos import ElapsedTime
48
50
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
51
+ from logger_36.type.handler import any_handler_t as base_handler_t
49
52
  from logger_36.type.issue import NewIssue, issue_t
53
+ from logger_36.type.message import MessageFromRecord
54
+
55
+ if RICH_IS_AVAILABLE:
56
+ from logger_36.catalog.handler.console_rich import console_rich_handler_t
57
+ else:
58
+ from logger_36.catalog.handler.console import (
59
+ console_handler_t as console_rich_handler_t,
60
+ )
50
61
 
51
62
  base_t = l.Logger
52
63
 
@@ -74,10 +85,12 @@ class logger_t(base_t):
74
85
 
75
86
  # Must not be False until at least one handler has been added.
76
87
  should_hold_messages: bool = True
88
+ should_record_messages: bool = False
77
89
  exit_on_error: bool = False # Implies exit_on_critical.
78
90
  exit_on_critical: bool = False
79
91
 
80
92
  on_hold: list[l.LogRecord] = d.field(init=False, default_factory=list)
93
+ recorded: list[str] = d.field(init=False, default_factory=list)
81
94
  events: dict[int, int] = d.field(init=False, default_factory=dict)
82
95
  last_message_now: date_time_t = d.field(init=False, default=_DATE_TIME_ORIGIN)
83
96
  last_message_date: date_t = d.field(init=False, default=_DATE_ORIGIN)
@@ -112,16 +125,9 @@ class logger_t(base_t):
112
125
  return self.staged_issues.__len__() > 0
113
126
 
114
127
  @property
115
- def past_logs_as_HTML(self) -> str | None:
116
- """
117
- From the first handler found with the given functionality, if any.
118
- """
119
- for handler in self.handlers:
120
- past_logs_as_HTML = getattr(handler, "past_logs_as_HTML", None)
121
- if past_logs_as_HTML is not None:
122
- return past_logs_as_HTML
123
-
124
- return None
128
+ def n_staged_issues(self) -> int:
129
+ """"""
130
+ return self.staged_issues.__len__()
125
131
 
126
132
  @property
127
133
  def max_memory_usage(self) -> int:
@@ -172,6 +178,8 @@ class logger_t(base_t):
172
178
 
173
179
  if (self.on_hold.__len__() > 0) and not self.should_hold_messages:
174
180
  for held in self.on_hold:
181
+ if self.should_record_messages:
182
+ self.recorded.append(MessageFromRecord(held))
175
183
  base_t.handle(self, held)
176
184
  self.on_hold.clear()
177
185
 
@@ -189,6 +197,8 @@ class logger_t(base_t):
189
197
  if self.should_hold_messages:
190
198
  self.on_hold.append(date_record)
191
199
  else:
200
+ if self.should_record_messages:
201
+ self.recorded.append(MessageFromRecord(date_record))
192
202
  base_t.handle(self, date_record)
193
203
 
194
204
  # When.
@@ -231,6 +241,8 @@ class logger_t(base_t):
231
241
  if self.should_hold_messages:
232
242
  self.on_hold.append(record)
233
243
  else:
244
+ if self.should_record_messages:
245
+ self.recorded.append(MessageFromRecord(record))
234
246
  base_t.handle(self, record)
235
247
 
236
248
  if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
@@ -245,13 +257,7 @@ class logger_t(base_t):
245
257
  if should_store_where:
246
258
  self.memory_usages.append((where, CurrentMemoryUsage()))
247
259
 
248
- def SetLevel(
249
- self,
250
- level: int,
251
- /,
252
- *,
253
- which: handler_codes_h | str = "a",
254
- ) -> None:
260
+ def SetLevel(self, level: int, /, *, which: handler_codes_h | str = "a") -> None:
255
261
  """
256
262
  Set level of handlers, but the logger level is not modified.
257
263
 
@@ -281,17 +287,24 @@ class logger_t(base_t):
281
287
  def MakeMonochrome(self) -> None:
282
288
  """"""
283
289
  OverrideExceptionFormat()
284
- AddConsoleHandler(self)
290
+ self.AddHandler(console_handler_t)
285
291
 
286
292
  def MakeRich(self, *, alternating_logs: int = 0) -> None:
287
293
  """"""
294
+ if MISSING_RICH_MESSAGE is not None:
295
+ s.__stderr__.write(MISSING_RICH_MESSAGE + "\n")
296
+
288
297
  OverrideExceptionFormat()
289
- AddRichConsoleHandler(self, alternating_logs=alternating_logs)
298
+ if console_rich_handler_t is console_handler_t:
299
+ handler_kwargs = {}
300
+ else:
301
+ handler_kwargs = {"alternating_logs": alternating_logs}
302
+ self.AddHandler(console_rich_handler_t, **handler_kwargs)
290
303
 
291
304
  def MakePermanent(self, path: str | path_t, /) -> None:
292
305
  """"""
293
306
  OverrideExceptionFormat()
294
- AddFileHandler(self, path)
307
+ self.AddHandler(file_handler_t, path=path)
295
308
 
296
309
  def ResetEventCounts(self) -> None:
297
310
  """"""
@@ -355,14 +368,32 @@ class logger_t(base_t):
355
368
  self.info("Log Interception: OFF")
356
369
 
357
370
  def AddHandler(
358
- self, handler: l.Handler, /, *, should_hold_messages: bool = False
371
+ self,
372
+ handler_t: type[base_handler_t],
373
+ /,
374
+ *,
375
+ name: str | None = None,
376
+ level: int = l.INFO,
377
+ should_store_memory_usage: bool = False,
378
+ message_width: int = -1,
379
+ formatter: l.Formatter | None = None,
380
+ should_still_hold_messages: bool = False,
381
+ **kwargs,
359
382
  ) -> None:
360
383
  """"""
361
- if (not should_hold_messages) and self.should_activate_log_interceptions:
384
+ if (not should_still_hold_messages) and self.should_activate_log_interceptions:
362
385
  self.ToggleLogInterceptions(True)
363
386
  self.should_activate_log_interceptions = False
364
387
 
365
- self.should_hold_messages = should_hold_messages
388
+ self.should_hold_messages = should_still_hold_messages
389
+ handler = handler_t.New(
390
+ name=name,
391
+ should_store_memory_usage=should_store_memory_usage,
392
+ message_width=message_width,
393
+ level=level,
394
+ formatter=formatter,
395
+ **kwargs,
396
+ )
366
397
  base_t.addHandler(self, handler)
367
398
 
368
399
  extension = getattr(handler, "extension", None)
@@ -381,7 +412,7 @@ class logger_t(base_t):
381
412
 
382
413
  self.info(
383
414
  f'New handler "{name}" of type "{type(handler).__name__}" and '
384
- f"level {handler.level}={l.getLevelName(handler.level)}{path}",
415
+ f"level {handler.level}={l.getLevelName(handler.level)}{path}"
385
416
  )
386
417
 
387
418
  def Log(
@@ -497,12 +528,29 @@ class logger_t(base_t):
497
528
  )
498
529
  self.staged_issues.append(issue)
499
530
 
531
+ def PopIssues(self, /, *, should_remove_context: bool = False) -> list[str]:
532
+ """"""
533
+ if not self.has_staged_issues:
534
+ return []
535
+
536
+ output = []
537
+
538
+ if should_remove_context:
539
+ separator = ISSUE_CONTEXT_END
540
+ else:
541
+ separator = ISSUE_LEVEL_SEPARATOR
542
+ separator_length = separator.__len__()
543
+ for issue in self.staged_issues:
544
+ start_idx = issue.find(separator)
545
+ issue = issue[(start_idx + separator_length) :]
546
+ output.append(issue)
547
+
548
+ self.staged_issues.clear()
549
+
550
+ return output
551
+
500
552
  def CommitIssues(
501
- self,
502
- /,
503
- *,
504
- order: order_h = "when",
505
- unified: bool = False,
553
+ self, /, *, order: order_h = "when", unified: bool = False
506
554
  ) -> None:
507
555
  """
508
556
  Note that issues after an issue with a level triggering process exit will not be
@@ -523,7 +571,7 @@ class logger_t(base_t):
523
571
  if order == "when":
524
572
  issues = self.staged_issues
525
573
  else: # order == "context"
526
- issues = sorted(self.staged_issues, key=lambda _elm: _elm.context)
574
+ issues = sorted(self.staged_issues, key=lambda _: _.context)
527
575
  """
528
576
  Format issues as an exception:
529
577
  try:
@@ -0,0 +1,121 @@
1
+ """
2
+ Copyright CNRS/Inria/UniCA
3
+ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
+ SEE COPYRIGHT NOTICE BELOW
5
+ """
6
+
7
+ import logging as l
8
+ import typing as h
9
+
10
+ from logger_36.config.message import (
11
+ LEVEL_CLOSING,
12
+ LEVEL_OPENING,
13
+ MESSAGE_MARKER,
14
+ WHERE_SEPARATOR,
15
+ )
16
+ from logger_36.constant.message import NEXT_LINE_PROLOGUE
17
+ from logger_36.extension.line import WrappedLines
18
+
19
+
20
+ @h.runtime_checkable
21
+ class _MessageFromRecordPreprocessed_p(h.Protocol):
22
+ def __call__(
23
+ self,
24
+ record: l.LogRecord,
25
+ /,
26
+ *,
27
+ line_width: int = 0,
28
+ PreProcessed: h.Callable[[str], str] | None = None,
29
+ ) -> str: ...
30
+
31
+
32
+ MessageFromRecord_h = _MessageFromRecordPreprocessed_p
33
+
34
+
35
+ def MessageFromRecord(
36
+ record: l.LogRecord,
37
+ /,
38
+ *,
39
+ line_width: int = 0,
40
+ PreProcessed: h.Callable[[str], str] | None = None,
41
+ ) -> str:
42
+ """
43
+ See logger_36.catalog.handler.README.txt.
44
+ """
45
+ message = record.msg
46
+
47
+ if PreProcessed is not None:
48
+ message = PreProcessed(message)
49
+ if (line_width <= 0) or (message.__len__() <= line_width):
50
+ if "\n" in message:
51
+ message = NEXT_LINE_PROLOGUE.join(message.splitlines())
52
+ else:
53
+ if "\n" in message:
54
+ lines = WrappedLines(message.splitlines(), line_width)
55
+ else:
56
+ lines = WrappedLines([message], line_width)
57
+ message = NEXT_LINE_PROLOGUE.join(lines)
58
+
59
+ when_or_elapsed = getattr(record, "when_or_elapsed", None)
60
+ if when_or_elapsed is None:
61
+ return message
62
+
63
+ level_first_letter = getattr(record, "level_first_letter", "")
64
+
65
+ if (where := getattr(record, "where", None)) is None:
66
+ where = ""
67
+ else:
68
+ where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
69
+
70
+ return (
71
+ f"{when_or_elapsed}"
72
+ f"{LEVEL_OPENING}{level_first_letter}{LEVEL_CLOSING} "
73
+ f"{MESSAGE_MARKER} {message}{where}"
74
+ )
75
+
76
+
77
+ """
78
+ COPYRIGHT NOTICE
79
+
80
+ This software is governed by the CeCILL license under French law and
81
+ abiding by the rules of distribution of free software. You can use,
82
+ modify and/ or redistribute the software under the terms of the CeCILL
83
+ license as circulated by CEA, CNRS and INRIA at the following URL
84
+ "http://www.cecill.info".
85
+
86
+ As a counterpart to the access to the source code and rights to copy,
87
+ modify and redistribute granted by the license, users are provided only
88
+ with a limited warranty and the software's author, the holder of the
89
+ economic rights, and the successive licensors have only limited
90
+ liability.
91
+
92
+ In this respect, the user's attention is drawn to the risks associated
93
+ with loading, using, modifying and/or developing or reproducing the
94
+ software by the user in light of its specific status of free software,
95
+ that may mean that it is complicated to manipulate, and that also
96
+ therefore means that it is reserved for developers and experienced
97
+ professionals having in-depth computer knowledge. Users are therefore
98
+ encouraged to load and test the software's suitability as regards their
99
+ requirements in conditions enabling the security of their systems and/or
100
+ data to be ensured and, more generally, to use and operate it in the
101
+ same conditions as regards security.
102
+
103
+ The fact that you are presently reading this means that you have had
104
+ knowledge of the CeCILL license and that you accept its terms.
105
+
106
+ SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
107
+
108
+ This software is being developed by Eric Debreuve, a CNRS employee and
109
+ member of team Morpheme.
110
+ Team Morpheme is a joint team between Inria, CNRS, and UniCA.
111
+ It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
112
+ I3S, and Laboratory iBV.
113
+
114
+ CNRS: https://www.cnrs.fr/index.php/en
115
+ Inria: https://www.inria.fr/en/
116
+ UniCA: https://univ-cotedazur.eu/
117
+ Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
118
+ I3S: https://www.i3s.unice.fr/en/
119
+ iBV: http://ibv.unice.fr/
120
+ Team Morpheme: https://team.inria.fr/morpheme/
121
+ """
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.11"
7
+ __version__ = "2025.13"
8
8
 
9
9
  """
10
10
  COPYRIGHT NOTICE
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: logger-36
3
- Version: 2025.11
3
+ Version: 2025.13
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