logger-36 2025.24__py3-none-any.whl → 2025.25__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/{time.py → chronos.py} +1 -1
- logger_36/catalog/handler/console_rich.py +32 -21
- logger_36/catalog/handler/file.py +1 -2
- logger_36/catalog/handler/generic.py +23 -9
- logger_36/catalog/handler/memory.py +24 -2
- logger_36/catalog/logger/chronos.py +10 -2
- logger_36/catalog/logger/memory.py +1 -1
- logger_36/config/message.py +1 -4
- logger_36/constant/{date_time.py → chronos.py} +4 -0
- logger_36/constant/error.py +2 -0
- logger_36/constant/html.py +2 -0
- logger_36/constant/message.py +7 -7
- logger_36/constant/record.py +2 -0
- logger_36/extension/{record.py → file.py} +11 -21
- logger_36/extension/line.py +1 -1
- logger_36/task/format/memory.py +1 -1
- logger_36/task/format/message.py +7 -5
- logger_36/task/measure/chronos.py +21 -11
- logger_36/task/storage.py +30 -55
- logger_36/type/handler.py +21 -23
- logger_36/type/issue.py +7 -4
- logger_36/type/logger.py +136 -44
- logger_36/version.py +1 -1
- {logger_36-2025.24.dist-info → logger_36-2025.25.dist-info}/METADATA +30 -44
- logger_36-2025.25.dist-info/RECORD +53 -0
- logger_36-2025.24.dist-info/RECORD +0 -53
- /logger_36/{constant/generic.py → extension/sentinel.py} +0 -0
- {logger_36-2025.24.dist-info → logger_36-2025.25.dist-info}/WHEEL +0 -0
- {logger_36-2025.24.dist-info → logger_36-2025.25.dist-info}/top_level.txt +0 -0
logger_36/type/handler.py
CHANGED
@@ -8,15 +8,14 @@ import logging as l
|
|
8
8
|
import typing as h
|
9
9
|
from pathlib import Path as path_t
|
10
10
|
|
11
|
-
from logger_36.config.message import
|
12
|
-
FALLBACK_MESSAGE_WIDTH,
|
13
|
-
LEVEL_CLOSING,
|
14
|
-
LEVEL_OPENING,
|
15
|
-
MESSAGE_MARKER,
|
16
|
-
WHERE_SEPARATOR,
|
17
|
-
)
|
11
|
+
from logger_36.config.message import FALLBACK_MESSAGE_WIDTH
|
18
12
|
from logger_36.config.rule import DEFAULT_RULE_LENGTH, RULE_CHARACTER
|
19
|
-
from logger_36.constant.message import
|
13
|
+
from logger_36.constant.message import (
|
14
|
+
NEXT_LINE_PROLOGUE,
|
15
|
+
TIME_PLACEHOLDER,
|
16
|
+
WHERE_PROLOGUE,
|
17
|
+
CONTEXT_LENGTH_p_1,
|
18
|
+
)
|
20
19
|
from logger_36.constant.record import SHOW_W_RULE_ATTR, WHEN_OR_ELAPSED_ATTR, WHERE_ATTR
|
21
20
|
from logger_36.constant.rule import DEFAULT_RULE, MIN_HALF_RULE_LENGTH
|
22
21
|
from logger_36.extension.line import WrappedLines
|
@@ -53,41 +52,40 @@ class extension_t:
|
|
53
52
|
|
54
53
|
def MessageFromRecord(
|
55
54
|
self, record: l.LogRecord, /, *, rule_color: str = "black"
|
56
|
-
) -> tuple[str, bool]:
|
55
|
+
) -> tuple[str, bool, int | None]:
|
57
56
|
"""
|
58
|
-
|
57
|
+
Arguments from second on: is_not_a_rule, where_location.
|
59
58
|
"""
|
60
59
|
message = record.msg # See logger_36.catalog.handler.README.txt.
|
61
60
|
if self.PreProcessedMessage is not None:
|
62
61
|
message = self.PreProcessedMessage(message)
|
63
62
|
|
64
63
|
if hasattr(record, SHOW_W_RULE_ATTR):
|
65
|
-
return self.Rule(text=message, color=rule_color), False
|
64
|
+
return self.Rule(text=message, color=rule_color), False, None
|
66
65
|
|
67
66
|
if (self.message_width <= 0) or (message.__len__() <= self.message_width):
|
68
67
|
if "\n" in message:
|
69
68
|
message = NEXT_LINE_PROLOGUE.join(message.splitlines())
|
70
69
|
else:
|
71
70
|
if "\n" in message:
|
72
|
-
lines =
|
71
|
+
lines = message.splitlines()
|
73
72
|
else:
|
74
|
-
lines =
|
75
|
-
message = NEXT_LINE_PROLOGUE.join(lines)
|
76
|
-
|
77
|
-
when_or_elapsed = getattr(record, WHEN_OR_ELAPSED_ATTR, None)
|
78
|
-
if when_or_elapsed is None:
|
79
|
-
return message, True
|
73
|
+
lines = [message]
|
74
|
+
message = NEXT_LINE_PROLOGUE.join(WrappedLines(lines, self.message_width))
|
80
75
|
|
76
|
+
when_or_elapsed = getattr(record, WHEN_OR_ELAPSED_ATTR, TIME_PLACEHOLDER)
|
81
77
|
if (where := getattr(record, WHERE_ATTR, None)) is None:
|
78
|
+
where_location = None
|
82
79
|
where = ""
|
83
80
|
else:
|
84
|
-
|
81
|
+
where_location = CONTEXT_LENGTH_p_1 + message.__len__()
|
82
|
+
where = f"{WHERE_PROLOGUE}{where}"
|
85
83
|
|
86
84
|
return (
|
87
|
-
f"{when_or_elapsed}"
|
88
|
-
|
89
|
-
|
90
|
-
)
|
85
|
+
f"{when_or_elapsed}_{record.levelname[0].lower()} {message}{where}",
|
86
|
+
True,
|
87
|
+
where_location,
|
88
|
+
)
|
91
89
|
|
92
90
|
def Rule(self, /, *, text: str | None = None, color: str = "black") -> str | h.Any:
|
93
91
|
"""
|
logger_36/type/issue.py
CHANGED
@@ -8,9 +8,9 @@ import logging as l
|
|
8
8
|
import typing as h
|
9
9
|
|
10
10
|
from logger_36.config.issue import ISSUE_BASE_CONTEXT
|
11
|
-
from logger_36.constant.generic import NOT_PASSED
|
12
11
|
from logger_36.constant.issue import ISSUE_LEVEL_SEPARATOR
|
13
12
|
from logger_36.constant.message import expected_op_h
|
13
|
+
from logger_36.extension.sentinel import NOT_PASSED
|
14
14
|
from logger_36.task.format.message import MessageWithActualExpected
|
15
15
|
|
16
16
|
issue_t = str
|
@@ -28,11 +28,11 @@ def NewIssue(
|
|
28
28
|
expected_is_choices: bool = False,
|
29
29
|
expected_op: expected_op_h = "=",
|
30
30
|
with_final_dot: bool = True,
|
31
|
-
) -> issue_t:
|
31
|
+
) -> tuple[issue_t, bool]:
|
32
32
|
""""""
|
33
33
|
if context.__len__() == 0:
|
34
34
|
context = ISSUE_BASE_CONTEXT
|
35
|
-
message = MessageWithActualExpected(
|
35
|
+
message, has_actual_expected = MessageWithActualExpected(
|
36
36
|
message,
|
37
37
|
actual=actual,
|
38
38
|
expected=expected,
|
@@ -41,7 +41,10 @@ def NewIssue(
|
|
41
41
|
with_final_dot=with_final_dot,
|
42
42
|
)
|
43
43
|
|
44
|
-
return
|
44
|
+
return (
|
45
|
+
f"{level}{ISSUE_LEVEL_SEPARATOR}{context}{separator}{message}",
|
46
|
+
has_actual_expected,
|
47
|
+
)
|
45
48
|
|
46
49
|
|
47
50
|
"""
|
logger_36/type/logger.py
CHANGED
@@ -7,6 +7,7 @@ SEE COPYRIGHT NOTICE BELOW
|
|
7
7
|
import dataclasses as d
|
8
8
|
import inspect as e
|
9
9
|
import logging as l
|
10
|
+
import multiprocessing as prll
|
10
11
|
import sys as s
|
11
12
|
import textwrap as text
|
12
13
|
import threading as thrd
|
@@ -15,6 +16,8 @@ import types as t
|
|
15
16
|
import typing as h
|
16
17
|
from datetime import date as date_t
|
17
18
|
from datetime import datetime as date_time_t
|
19
|
+
from logging.handlers import QueueHandler as queue_handler_t
|
20
|
+
from logging.handlers import QueueListener as log_server_t
|
18
21
|
from pathlib import Path as path_t
|
19
22
|
from traceback import TracebackException as traceback_t
|
20
23
|
|
@@ -26,25 +29,32 @@ from logger_36.catalog.config.optional import (
|
|
26
29
|
)
|
27
30
|
from logger_36.catalog.handler.console import console_handler_t
|
28
31
|
from logger_36.catalog.handler.file import file_handler_t
|
32
|
+
from logger_36.catalog.handler.memory import memory_handler_t, records_h
|
29
33
|
from logger_36.config.issue import ISSUE_CONTEXT_END, ISSUE_CONTEXT_SEPARATOR
|
30
34
|
from logger_36.config.message import (
|
31
35
|
DATE_FORMAT,
|
32
|
-
ELAPSED_TIME_SEPARATOR,
|
33
36
|
LONG_ENOUGH,
|
34
37
|
TIME_FORMAT,
|
35
38
|
WHERE_SEPARATOR,
|
36
39
|
)
|
37
|
-
from logger_36.constant.
|
38
|
-
from logger_36.constant.generic import NOT_PASSED
|
40
|
+
from logger_36.constant.chronos import DATE_ORIGIN, DATE_TIME_ORIGIN
|
39
41
|
from logger_36.constant.issue import ISSUE_LEVEL_SEPARATOR, ORDER, order_h
|
40
42
|
from logger_36.constant.logger import WARNING_LOGGER_NAME, WARNING_TYPE_COMPILED_PATTERN
|
41
43
|
from logger_36.constant.memory import UNKNOWN_MEMORY_USAGE
|
42
|
-
from logger_36.constant.message import LINE_INDENT,
|
44
|
+
from logger_36.constant.message import LINE_INDENT, expected_op_h
|
43
45
|
from logger_36.constant.path import USER_FOLDER, LAUNCH_ROOT_FILE_relative
|
44
|
-
from logger_36.constant.record import
|
45
|
-
|
46
|
+
from logger_36.constant.record import (
|
47
|
+
HAS_ACTUAL_EXPECTED_ATTR,
|
48
|
+
SHOW_W_RULE_ATTR,
|
49
|
+
SHOW_WHEN_ATTR,
|
50
|
+
SHOW_WHERE_ATTR,
|
51
|
+
WHEN_OR_ELAPSED_ATTR,
|
52
|
+
WHERE_ATTR,
|
53
|
+
)
|
54
|
+
from logger_36.extension.file import NewTemporaryFile
|
55
|
+
from logger_36.extension.sentinel import NOT_PASSED
|
46
56
|
from logger_36.task.format.message import MessageWithActualExpected
|
47
|
-
from logger_36.task.measure.chronos import
|
57
|
+
from logger_36.task.measure.chronos import FormattedElapsedTime
|
48
58
|
from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
|
49
59
|
from logger_36.type.handler import extension_t as handler_extension_t
|
50
60
|
from logger_36.type.handler import handler_h as base_handler_h
|
@@ -63,6 +73,8 @@ logger_handle_raw_h = h.Callable[[l.LogRecord], None]
|
|
63
73
|
logger_handle_with_self_h = h.Callable[[l.Logger, l.LogRecord], None]
|
64
74
|
logger_handle_h = logger_handle_raw_h | logger_handle_with_self_h
|
65
75
|
|
76
|
+
MAIN_PROCESS_NAME = "MainProcess"
|
77
|
+
|
66
78
|
|
67
79
|
@d.dataclass(slots=True, repr=False, eq=False)
|
68
80
|
class logger_t(base_t):
|
@@ -87,7 +99,9 @@ class logger_t(base_t):
|
|
87
99
|
last_message_date: date_t = d.field(init=False, default=DATE_ORIGIN)
|
88
100
|
memory_usages: list[tuple[str, int]] = d.field(init=False, default_factory=list)
|
89
101
|
context_levels: list[str] = d.field(init=False, default_factory=list)
|
90
|
-
staged_issues: list[issue_t] = d.field(
|
102
|
+
staged_issues: list[tuple[issue_t, bool]] = d.field(
|
103
|
+
init=False, default_factory=list
|
104
|
+
)
|
91
105
|
intercepted_wrn_handle: logger_handle_h | None = d.field(init=False, default=None)
|
92
106
|
intercepted_log_handles: dict[str, logger_handle_h] = d.field(
|
93
107
|
init=False, default_factory=dict
|
@@ -97,6 +111,8 @@ class logger_t(base_t):
|
|
97
111
|
# Used only until the first handler is added (see AddHandler).
|
98
112
|
_should_activate_log_interceptions: bool = d.field(init=False, default=False)
|
99
113
|
|
114
|
+
log_server: log_server_t | None = d.field(init=False, default=None)
|
115
|
+
|
100
116
|
name_: d.InitVar[str | None] = None
|
101
117
|
level_: d.InitVar[int] = l.NOTSET
|
102
118
|
activate_wrn_interceptions: d.InitVar[bool] = True
|
@@ -109,6 +125,21 @@ class logger_t(base_t):
|
|
109
125
|
FormattedEntry = lambda _: f"{_[0]}: {_[1].replace('\n', '↲ ')}"
|
110
126
|
return "\n".join(map(FormattedEntry, self.history.items()))
|
111
127
|
|
128
|
+
@property
|
129
|
+
def records(self) -> records_h | None:
|
130
|
+
""""""
|
131
|
+
return logger_t.Records(self)
|
132
|
+
|
133
|
+
@staticmethod
|
134
|
+
def Records(logger: base_t | l.Logger, /) -> records_h | None:
|
135
|
+
""""""
|
136
|
+
for handler in logger.handlers:
|
137
|
+
output = getattr(handler, "records", None)
|
138
|
+
if memory_handler_t.AreRecords(output):
|
139
|
+
return output
|
140
|
+
|
141
|
+
return None
|
142
|
+
|
112
143
|
@property
|
113
144
|
def intercepts_warnings(self) -> bool:
|
114
145
|
""""""
|
@@ -156,6 +187,8 @@ class logger_t(base_t):
|
|
156
187
|
activate_exc_interceptions: bool,
|
157
188
|
) -> None:
|
158
189
|
""""""
|
190
|
+
assert prll.current_process().name == MAIN_PROCESS_NAME
|
191
|
+
|
159
192
|
if name_ is None:
|
160
193
|
name_ = f"{type(self).__name__}:{hex(id(self))[2:]}"
|
161
194
|
|
@@ -185,38 +218,39 @@ class logger_t(base_t):
|
|
185
218
|
|
186
219
|
def handle(self, record: l.LogRecord, /) -> None:
|
187
220
|
""""""
|
188
|
-
|
189
|
-
|
221
|
+
now = date_time_t.now()
|
190
222
|
if (date := now.date()) != self.last_message_date:
|
191
223
|
self._AcknowledgeDateChange(date)
|
192
224
|
|
225
|
+
level = record.levelno
|
226
|
+
|
193
227
|
# When.
|
194
|
-
if
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
228
|
+
if getattr(record, SHOW_WHEN_ATTR, True):
|
229
|
+
if now - self.last_message_now > LONG_ENOUGH:
|
230
|
+
w_or_e = f"{now:{TIME_FORMAT}}"
|
231
|
+
else:
|
232
|
+
w_or_e = FormattedElapsedTime(now) # or: f"{[...]:.<{TIME_LENGTH}}".
|
233
|
+
setattr(record, WHEN_OR_ELAPSED_ATTR, w_or_e)
|
199
234
|
self.last_message_now = now
|
200
235
|
|
201
236
|
# Where.
|
202
|
-
should_show_where = getattr(record, SHOW_WHERE_ATTR,
|
237
|
+
should_show_where = getattr(record, SHOW_WHERE_ATTR, level != l.INFO)
|
203
238
|
if should_show_where or self.should_monitor_memory_usage:
|
204
|
-
where =
|
205
|
-
|
206
|
-
|
239
|
+
where = f"{record.pathname}:{record.funcName}:{record.lineno}"
|
240
|
+
if should_show_where:
|
241
|
+
setattr(record, WHERE_ATTR, where)
|
242
|
+
if self.should_monitor_memory_usage:
|
243
|
+
self.memory_usages.append((where, CurrentMemoryUsage()))
|
207
244
|
|
208
245
|
# What.
|
209
246
|
if not isinstance(record.msg, str):
|
210
247
|
record.msg = str(record.msg)
|
211
248
|
|
212
249
|
base_t.handle(self, record)
|
213
|
-
self.n_events[
|
250
|
+
self.n_events[level] += 1
|
214
251
|
|
215
|
-
if self.
|
216
|
-
self.
|
217
|
-
|
218
|
-
if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
|
219
|
-
self.exit_on_error and (record.levelno is l.ERROR)
|
252
|
+
if (self.exit_on_critical and (level is l.CRITICAL)) or (
|
253
|
+
self.exit_on_error and (level is l.ERROR)
|
220
254
|
):
|
221
255
|
# Also works if self.exit_on_error and record.levelno is l.CRITICAL since
|
222
256
|
# __post_init__ set self.exit_on_critical if self.exit_on_error.
|
@@ -230,7 +264,7 @@ class logger_t(base_t):
|
|
230
264
|
{
|
231
265
|
"name": self.name,
|
232
266
|
"levelno": l.INFO, # For management by logging.Logger.handle.
|
233
|
-
"msg": f"DATE: {date
|
267
|
+
"msg": f"DATE: {date:{DATE_FORMAT}}",
|
234
268
|
SHOW_W_RULE_ATTR: True,
|
235
269
|
}
|
236
270
|
)
|
@@ -390,6 +424,27 @@ class logger_t(base_t):
|
|
390
424
|
""""""
|
391
425
|
self.AddHandler(file_handler_t, path=path)
|
392
426
|
|
427
|
+
def MakeMultiSafe(self) -> None:
|
428
|
+
"""
|
429
|
+
Should not be called until after all desired handlers have been added (as a
|
430
|
+
better-then-nothing check, it is checked that the logger has at least one
|
431
|
+
handler). If handlers are added passed this call, execution might freeze or
|
432
|
+
crash.
|
433
|
+
"""
|
434
|
+
assert self.log_server is None
|
435
|
+
assert self.hasHandlers()
|
436
|
+
|
437
|
+
handlers = tuple(self.handlers) # Making a copy is necessary.
|
438
|
+
for handler in handlers:
|
439
|
+
self.removeHandler(handler)
|
440
|
+
|
441
|
+
queue = prll.Queue()
|
442
|
+
|
443
|
+
self.addHandler(queue_handler_t(queue))
|
444
|
+
|
445
|
+
self.log_server = log_server_t(queue, *handlers)
|
446
|
+
self.log_server.start()
|
447
|
+
|
393
448
|
def __call__(self, *args, **kwargs) -> None:
|
394
449
|
"""
|
395
450
|
For a print-like calling for print-based debugging.
|
@@ -401,7 +456,7 @@ class logger_t(base_t):
|
|
401
456
|
path = path_t(details.filename)
|
402
457
|
if path.is_relative_to(USER_FOLDER):
|
403
458
|
path = path.relative_to(USER_FOLDER)
|
404
|
-
where = f"{str(path.with_suffix(''))}
|
459
|
+
where = f"{str(path.with_suffix(''))}:{details.function}:{details.lineno}"
|
405
460
|
|
406
461
|
self.info(separator.join(map(str, args)) + f"\n{WHERE_SEPARATOR} " + where)
|
407
462
|
|
@@ -420,7 +475,7 @@ class logger_t(base_t):
|
|
420
475
|
""""""
|
421
476
|
if isinstance(level, str):
|
422
477
|
level = l.getLevelNamesMapping()[level.upper()]
|
423
|
-
message = MessageWithActualExpected(
|
478
|
+
message, has_actual_expected = MessageWithActualExpected(
|
424
479
|
message,
|
425
480
|
actual=actual,
|
426
481
|
expected=expected,
|
@@ -428,7 +483,11 @@ class logger_t(base_t):
|
|
428
483
|
expected_op=expected_op,
|
429
484
|
with_final_dot=with_final_dot,
|
430
485
|
)
|
431
|
-
|
486
|
+
if has_actual_expected:
|
487
|
+
extra = {HAS_ACTUAL_EXPECTED_ATTR: True}
|
488
|
+
else:
|
489
|
+
extra = {}
|
490
|
+
self.log(level, message, extra=extra)
|
432
491
|
|
433
492
|
def LogAsIs(self, message: str, /, *, indented: bool = False) -> None:
|
434
493
|
""""""
|
@@ -472,11 +531,9 @@ class logger_t(base_t):
|
|
472
531
|
self.LogException(exception, level=l.CRITICAL)
|
473
532
|
s.exit(1)
|
474
533
|
|
475
|
-
def DealWithExceptionInThread(
|
476
|
-
self, exc_type, exc_value, exc_traceback, _, /
|
477
|
-
) -> None:
|
534
|
+
def DealWithExceptionInThread(self, args, /) -> None:
|
478
535
|
""""""
|
479
|
-
self.DealWithException(exc_type, exc_value, exc_traceback)
|
536
|
+
self.DealWithException(args.exc_type, args.exc_value, args.exc_traceback)
|
480
537
|
|
481
538
|
def DisplayRule(
|
482
539
|
self, /, *, message: str | None = None, color: str = "white"
|
@@ -527,7 +584,9 @@ class logger_t(base_t):
|
|
527
584
|
)
|
528
585
|
self.staged_issues.append(issue)
|
529
586
|
|
530
|
-
def PopIssues(
|
587
|
+
def PopIssues(
|
588
|
+
self, /, *, should_remove_context: bool = False
|
589
|
+
) -> list[tuple[str, bool]]:
|
531
590
|
""""""
|
532
591
|
if not self.has_staged_issues:
|
533
592
|
return []
|
@@ -539,10 +598,10 @@ class logger_t(base_t):
|
|
539
598
|
else:
|
540
599
|
separator = ISSUE_LEVEL_SEPARATOR
|
541
600
|
separator_length = separator.__len__()
|
542
|
-
for issue in self.staged_issues:
|
601
|
+
for issue, has_actual_expected in self.staged_issues:
|
543
602
|
start_idx = issue.find(separator)
|
544
603
|
issue = issue[(start_idx + separator_length) :]
|
545
|
-
output.append(issue)
|
604
|
+
output.append((issue, has_actual_expected))
|
546
605
|
|
547
606
|
self.staged_issues.clear()
|
548
607
|
|
@@ -564,13 +623,16 @@ class logger_t(base_t):
|
|
564
623
|
"Invalid commit order",
|
565
624
|
actual=order,
|
566
625
|
expected=f"One of {str(ORDER)[1:-1]}",
|
567
|
-
)
|
626
|
+
)[0]
|
568
627
|
)
|
569
628
|
|
570
629
|
if order == "when":
|
571
630
|
issues = self.staged_issues
|
572
631
|
else: # order == "context"
|
573
|
-
issues = sorted(
|
632
|
+
issues = sorted(
|
633
|
+
self.staged_issues,
|
634
|
+
key=lambda _: _[0].split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)[1],
|
635
|
+
)
|
574
636
|
"""
|
575
637
|
Format issues as an exception:
|
576
638
|
try:
|
@@ -582,20 +644,43 @@ class logger_t(base_t):
|
|
582
644
|
formatted = "\n".join(lines)
|
583
645
|
"""
|
584
646
|
|
585
|
-
|
647
|
+
extra = {SHOW_WHERE_ATTR: False}
|
586
648
|
if unified:
|
587
|
-
level, _ = issues[0].split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)
|
649
|
+
level, _ = issues[0][0].split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)
|
588
650
|
wo_level = []
|
589
|
-
|
651
|
+
any_has_actual_expected = False
|
652
|
+
for issue, has_actual_expected in issues:
|
590
653
|
_, issue = issue.split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)
|
654
|
+
if has_actual_expected:
|
655
|
+
any_has_actual_expected = True
|
591
656
|
wo_level.append(issue)
|
592
|
-
|
657
|
+
if any_has_actual_expected:
|
658
|
+
extra[HAS_ACTUAL_EXPECTED_ATTR] = True
|
659
|
+
self.log(int(level), "\n".join(wo_level), stacklevel=2, extra=extra)
|
593
660
|
else:
|
594
|
-
for issue in issues:
|
661
|
+
for issue, has_actual_expected in issues:
|
595
662
|
level, issue = issue.split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)
|
596
|
-
|
663
|
+
if has_actual_expected:
|
664
|
+
extra[HAS_ACTUAL_EXPECTED_ATTR] = True
|
665
|
+
self.log(int(level), issue, stacklevel=2, extra=extra)
|
666
|
+
if has_actual_expected:
|
667
|
+
del extra[HAS_ACTUAL_EXPECTED_ATTR]
|
597
668
|
self.staged_issues.clear()
|
598
669
|
|
670
|
+
def StoragePath(self, suffix: str, /) -> path_t:
|
671
|
+
"""
|
672
|
+
Use as staticmethod if needed.
|
673
|
+
"""
|
674
|
+
for handler in self.handlers:
|
675
|
+
if (path := getattr(handler, "baseFilename", None)) is not None:
|
676
|
+
output = path_t(path).with_suffix(suffix)
|
677
|
+
if output.exists():
|
678
|
+
output = NewTemporaryFile(suffix)
|
679
|
+
|
680
|
+
return output
|
681
|
+
|
682
|
+
return NewTemporaryFile(suffix)
|
683
|
+
|
599
684
|
def __enter__(self) -> None:
|
600
685
|
""""""
|
601
686
|
pass
|
@@ -611,6 +696,13 @@ class logger_t(base_t):
|
|
611
696
|
_ = self.context_levels.pop()
|
612
697
|
return False
|
613
698
|
|
699
|
+
def __del__(self) -> None:
|
700
|
+
""""""
|
701
|
+
assert prll.current_process().name == MAIN_PROCESS_NAME
|
702
|
+
|
703
|
+
if self.log_server is not None:
|
704
|
+
self.log_server.stop()
|
705
|
+
|
614
706
|
|
615
707
|
def _HandleForWarnings(interceptor: base_t, /) -> logger_handle_h:
|
616
708
|
""""""
|
logger_36/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: logger-36
|
3
|
-
Version: 2025.
|
3
|
+
Version: 2025.25
|
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
|
@@ -109,46 +109,32 @@ The code is formatted by `Black <https://github.com/psf/black/>`_, *The Uncompro
|
|
109
109
|
The imports are ordered by `isort <https://github.com/timothycrosley/isort/>`_... *your imports, so you don't have to*.
|
110
110
|
|
111
111
|
..
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
This software is being developed by Eric Debreuve, a CNRS employee and
|
143
|
-
member of team Morpheme.
|
144
|
-
Team Morpheme is a joint team between Inria, CNRS, and UniCA.
|
145
|
-
It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
|
146
|
-
I3S, and Laboratory iBV.
|
147
|
-
|
148
|
-
CNRS: https://www.cnrs.fr/index.php/en
|
149
|
-
Inria: https://www.inria.fr/en/
|
150
|
-
UniCA: https://univ-cotedazur.eu/
|
151
|
-
Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
|
152
|
-
I3S: https://www.i3s.unice.fr/en/
|
153
|
-
iBV: http://ibv.unice.fr/
|
154
|
-
Team Morpheme: https://team.inria.fr/morpheme/
|
112
|
+
COPYRIGHT NOTICE
|
113
|
+
|
114
|
+
This software is governed by the CeCILL license under French law and
|
115
|
+
abiding by the rules of distribution of free software. You can use,
|
116
|
+
modify and/ or redistribute the software under the terms of the CeCILL
|
117
|
+
license as circulated by CEA, CNRS and INRIA at the following URL
|
118
|
+
"http://www.cecill.info".
|
119
|
+
|
120
|
+
As a counterpart to the access to the source code and rights to copy,
|
121
|
+
modify and redistribute granted by the license, users are provided only
|
122
|
+
with a limited warranty and the software's author, the holder of the
|
123
|
+
economic rights, and the successive licensors have only limited
|
124
|
+
liability.
|
125
|
+
|
126
|
+
In this respect, the user's attention is drawn to the risks associated
|
127
|
+
with loading, using, modifying and/or developing or reproducing the
|
128
|
+
software by the user in light of its specific status of free software,
|
129
|
+
that may mean that it is complicated to manipulate, and that also
|
130
|
+
therefore means that it is reserved for developers and experienced
|
131
|
+
professionals having in-depth computer knowledge. Users are therefore
|
132
|
+
encouraged to load and test the software's suitability as regards their
|
133
|
+
requirements in conditions enabling the security of their systems and/or
|
134
|
+
data to be ensured and, more generally, to use and operate it in the
|
135
|
+
same conditions as regards security.
|
136
|
+
|
137
|
+
The fact that you are presently reading this means that you have had
|
138
|
+
knowledge of the CeCILL license and that you accept its terms.
|
139
|
+
|
140
|
+
SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
|
@@ -0,0 +1,53 @@
|
|
1
|
+
logger_36/__init__.py,sha256=mK6AD0eWI2Sk42oxleTvsxzYJ28FbHK5WNkpLgAhnNE,2129
|
2
|
+
logger_36/version.py,sha256=gqr9_xGceDfeDrDRv0O1e-8Rn6mXcEV_iFdCtiuTyAk,1680
|
3
|
+
logger_36/api/chronos.py,sha256=o_UMZbeExjb01aNURmzIjOMAHhI9e90nyvJJYcJR6VQ,1739
|
4
|
+
logger_36/api/logger.py,sha256=eLZ2yuH-sYeh4Z2KnAwTRJEbmkmgzBPMncdqXfFUTG8,1760
|
5
|
+
logger_36/api/memory.py,sha256=U4mMEkx8WRHk9q2d3SCFGt2EJaBuflOXw2bGbAxOnSc,1828
|
6
|
+
logger_36/api/message.py,sha256=DuT4UX4r_1DTXzuuRD-tvsTZk5X-Nj11loBKhuWOMw0,1791
|
7
|
+
logger_36/api/storage.py,sha256=S1fVzrMp-_zlhg27fRPddWCFQRyvbpFwSreALOeoNFI,1713
|
8
|
+
logger_36/catalog/config/console_rich.py,sha256=t9p9-AkSgPiLAsm1evAdbz77g7JcVLePhUJ1FzNi3cY,2330
|
9
|
+
logger_36/catalog/config/optional.py,sha256=8d8HdpE07gHfsdoL8mVAlRlh9AgLcb4z7I7h6ob7CfU,2174
|
10
|
+
logger_36/catalog/handler/console.py,sha256=XIyO_8MrzaTlihDbFhIzRk47HjxMH4gV2Zqp0-1oMsU,2320
|
11
|
+
logger_36/catalog/handler/console_rich.py,sha256=RXLu8AQxAmyec1bXUc99Mabp7BjZc-lKEfwrCzMPkZA,6662
|
12
|
+
logger_36/catalog/handler/file.py,sha256=5GR_aACDEBXuZ-pUH9P0OCaXbCf-aLPmsz-XrGCAIgE,2434
|
13
|
+
logger_36/catalog/handler/generic.py,sha256=kwr7x7GWLmWvT7niHGW7OqlMotvTQNuNEwQGfKh_nhU,7040
|
14
|
+
logger_36/catalog/handler/memory.py,sha256=pJwKOlCm8Ej8ipDI00-FfX4qJjMPXJb-DucD1ukIQOU,4057
|
15
|
+
logger_36/catalog/logger/chronos.py,sha256=MDAx_NRRcRZcQYDDjVCRcu87SDP-rPYjX0-10KNcMnk,2216
|
16
|
+
logger_36/catalog/logger/gpu.py,sha256=Py5YY0nD_pqJzJsEKQYoOGHcPqyNVJ3J2noOS3hDL6g,2890
|
17
|
+
logger_36/catalog/logger/memory.py,sha256=pa-9pkvDGdf52giwL3Zi1mjWNFE_NVzBGdjwcfeIDNc,4256
|
18
|
+
logger_36/catalog/logger/system.py,sha256=zEbHirATqZAVYFmHLd0pppeuRhka1ucWwyHRq0afQNE,2593
|
19
|
+
logger_36/config/issue.py,sha256=QOkVRPSLZC_2mfcFpad-pcSXJXfLHdWUAXiMbTWlZTg,1741
|
20
|
+
logger_36/config/memory.py,sha256=bZmNYsD2goVdkraS1v_t2OqAJo86jKMtP311kIVURDk,1691
|
21
|
+
logger_36/config/message.py,sha256=bDAW4hZdsHBTYlCA7IZbL1FxGPwjX9Khmw0d21VHGs0,1968
|
22
|
+
logger_36/config/rule.py,sha256=BqOb4SWKgT0YQZ-DtOzkLNZNWZVZkl5GN3lMoWoPrKw,1702
|
23
|
+
logger_36/config/system.py,sha256=YRSa2eN_SoTnTXWUXAcpKt4JXifabzMR0eXwjUYlA_A,1951
|
24
|
+
logger_36/constant/chronos.py,sha256=JJvBN_skqRdUgnuWhMqG0gZ3_ZkXmAz1syEixfu4lCM,2131
|
25
|
+
logger_36/constant/error.py,sha256=FinnCcwGGH2oiXX3aw2uxKYvR3d5Tkb9rlNhNjyuoF8,2354
|
26
|
+
logger_36/constant/html.py,sha256=w8gttc1XBSMpdKRolnXAVJ5x_hOAIsVsUyELCXJYdWU,2011
|
27
|
+
logger_36/constant/issue.py,sha256=0EmcsRmSxktFUJR0qOU0PnKG-gfbLDOULH6sSRHFOcc,1789
|
28
|
+
logger_36/constant/logger.py,sha256=ZQYX9JiPsoivwRgYNtdEqRKCagSKD88lRqvxP8MX1ZE,1942
|
29
|
+
logger_36/constant/memory.py,sha256=Q_E5tTWa-cGaNwrE_xmKa3BxQG6oJO6DHczrxc_M4sE,1817
|
30
|
+
logger_36/constant/message.py,sha256=TdsZXWO2UmlG3a0ia3UsUCxtVUa8GNferi69pO4A9TM,2301
|
31
|
+
logger_36/constant/path.py,sha256=r-vx5ztGhcpYfg37kw0oaxBYdTSkWOJuToTmexaW8tE,2265
|
32
|
+
logger_36/constant/record.py,sha256=70JO2LYL-1Eg-vLD3N7nUoYALp6ji7-b3DLxCHTc7iQ,1881
|
33
|
+
logger_36/constant/rule.py,sha256=ul-MqOdHBGBC5Nwn05EUnz2T__7VEs82qiH7Fzs5qCk,1804
|
34
|
+
logger_36/constant/system.py,sha256=pLlLXG5sepQlSUOo3TphaGrHg8xzJBp-GxpL2NPP47k,1904
|
35
|
+
logger_36/extension/file.py,sha256=ClY8k805DnB6Vy0LEQIhlO0MavP91Y-CEjHjFepyROE,2034
|
36
|
+
logger_36/extension/inspection.py,sha256=LoHXi4wsIgHKrq_7GYkVcfJ9rnBG16pLKMpAoHNwJiY,3922
|
37
|
+
logger_36/extension/line.py,sha256=9BgxnY6wiyc44Ari5rkvqbvz9ao4sNs39u8k7sY6vFc,2611
|
38
|
+
logger_36/extension/sentinel.py,sha256=SQgkQiRcTIjCRvbxiOb6TEm59BC0FNMcjYoIShpcwLo,1718
|
39
|
+
logger_36/instance/logger.py,sha256=X_U10RYU1h2Aa70D8hBnmFyJZtRILK16KN-GB4xkHMU,1782
|
40
|
+
logger_36/instance/loggers.py,sha256=inBk4KKrQ-z3szaopQ29-qQwh1iSc842sWo5J6zJoiM,1725
|
41
|
+
logger_36/task/storage.py,sha256=L93v5w9p_7MoiagEr3d6QSYEQxX42DxmZ-BJTCuLpgQ,3915
|
42
|
+
logger_36/task/format/memory.py,sha256=xcWwbUnl1BxH7RVBHyhp1RlbT2n370PzoFLLLd3dtlU,3726
|
43
|
+
logger_36/task/format/message.py,sha256=Q9QkyUAU47Nj606zsvq0gNyso8Vk9G9OaqG9Em5FjKg,3575
|
44
|
+
logger_36/task/measure/chronos.py,sha256=fZKK16LwlLzRRAP-gfF1uy6gFCL33gR7LZ4Xs_nXdVY,2612
|
45
|
+
logger_36/task/measure/memory.py,sha256=kkPHEIUTUhkCOLrAt01eLJLnsnkl0nFPNhFZdIB_JAw,1991
|
46
|
+
logger_36/type/handler.py,sha256=bDsCFYpevCJBV7Vc9jovttapjU-7GXI1_TDbmOf2kN4,6660
|
47
|
+
logger_36/type/issue.py,sha256=M2KeQwzDG9yqgdtbyWk5Y-ier7c71TuAKlNCf5QCGzY,2770
|
48
|
+
logger_36/type/logger.py,sha256=0OjqyMcBAyS0qUUeEzweALrs0m5rVqZF3e6R1b8B4RA,27358
|
49
|
+
logger_36/type/loggers.py,sha256=7EX7Sg_RlduBjdfFlNZmUfNeDloH1xU30Rdkg_-rXh8,3172
|
50
|
+
logger_36-2025.25.dist-info/METADATA,sha256=Uchfn7mGuqLow-qAR-TejGvwkXNns2N7XnpLbgHeGFE,5980
|
51
|
+
logger_36-2025.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
52
|
+
logger_36-2025.25.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
|
53
|
+
logger_36-2025.25.dist-info/RECORD,,
|