logger-36 2025.12__py3-none-any.whl → 2025.14__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/catalog/handler/console.py +22 -49
- logger_36/catalog/handler/console_rich.py +73 -87
- logger_36/catalog/handler/file.py +24 -55
- logger_36/catalog/handler/generic.py +67 -68
- logger_36/catalog/logger/gpu.py +1 -1
- logger_36/catalog/logger/system.py +1 -1
- logger_36/config/message.py +1 -0
- logger_36/{api/handler.py → constant/html.py} +18 -6
- logger_36/extension/exception.py +4 -2
- logger_36/extension/line.py +83 -0
- logger_36/task/format/memory.py +1 -5
- logger_36/task/format/message.py +60 -1
- logger_36/task/format/rule.py +3 -2
- logger_36/task/inspection.py +1 -1
- logger_36/task/storage.py +29 -8
- logger_36/type/handler.py +101 -106
- logger_36/type/logger.py +92 -60
- logger_36/type/message.py +90 -0
- logger_36/version.py +1 -1
- {logger_36-2025.12.dist-info → logger_36-2025.14.dist-info}/METADATA +2 -2
- {logger_36-2025.12.dist-info → logger_36-2025.14.dist-info}/RECORD +23 -22
- {logger_36-2025.12.dist-info → logger_36-2025.14.dist-info}/WHEEL +1 -1
- logger_36/task/handling.py +0 -196
- {logger_36-2025.12.dist-info → logger_36-2025.14.dist-info}/top_level.txt +0 -0
@@ -4,12 +4,24 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
|
|
4
4
|
SEE COPYRIGHT NOTICE BELOW
|
5
5
|
"""
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
TITLE_PLACEHOLDER = "TITLE"
|
8
|
+
BODY_PLACEHOLDER = "BODY"
|
9
|
+
|
10
|
+
MINIMAL_HTML = f"""
|
11
|
+
<!DOCTYPE html>
|
12
|
+
<html lang="en">
|
13
|
+
<head>
|
14
|
+
<meta charset="utf-8">
|
15
|
+
<title>{TITLE_PLACEHOLDER}</title>
|
16
|
+
</head>
|
17
|
+
<body>
|
18
|
+
<pre>
|
19
|
+
{BODY_PLACEHOLDER}
|
20
|
+
</pre>
|
21
|
+
</body>
|
22
|
+
</html>
|
23
|
+
"""
|
24
|
+
|
13
25
|
|
14
26
|
"""
|
15
27
|
COPYRIGHT NOTICE
|
logger_36/extension/exception.py
CHANGED
@@ -79,7 +79,9 @@ def _HandleException(
|
|
79
79
|
variables = map(
|
80
80
|
lambda _: f"{_:{longest}} = {all_variables[_]}", sorted(found_names)
|
81
81
|
)
|
82
|
-
variables =
|
82
|
+
variables = (
|
83
|
+
2 * _INDENTATION + f"\n{2 * _INDENTATION}".join(variables) + "\n"
|
84
|
+
)
|
83
85
|
else:
|
84
86
|
variables = ""
|
85
87
|
|
@@ -103,7 +105,7 @@ def _HandleException(
|
|
103
105
|
f"{variables}"
|
104
106
|
f"{message}"
|
105
107
|
f"{_INDENTATION}{REPORT_COLOR}Full report at: file://{document.name}"
|
106
|
-
f"{MONOCHROME}{OPTIONAL_NEWLINE}"
|
108
|
+
f"{MONOCHROME}{OPTIONAL_NEWLINE}"
|
107
109
|
)
|
108
110
|
|
109
111
|
lines = tcbk.format_exception(exception)
|
@@ -0,0 +1,83 @@
|
|
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
|
+
|
8
|
+
def WrappedLines(lines: list[str], message_width: int, /) -> list[str]:
|
9
|
+
""""""
|
10
|
+
output = []
|
11
|
+
|
12
|
+
for line in lines:
|
13
|
+
while line.__len__() > message_width:
|
14
|
+
if all(
|
15
|
+
_elm != " " for _elm in line[(message_width - 1) : (message_width + 1)]
|
16
|
+
):
|
17
|
+
if line[message_width - 2] == " ":
|
18
|
+
piece, line = (
|
19
|
+
line[: (message_width - 2)].rstrip(),
|
20
|
+
line[(message_width - 1) :],
|
21
|
+
)
|
22
|
+
else:
|
23
|
+
piece, line = (
|
24
|
+
line[: (message_width - 1)] + "-",
|
25
|
+
line[(message_width - 1) :],
|
26
|
+
)
|
27
|
+
else:
|
28
|
+
piece, line = (
|
29
|
+
line[:message_width].rstrip(),
|
30
|
+
line[message_width:].lstrip(),
|
31
|
+
)
|
32
|
+
output.append(piece)
|
33
|
+
|
34
|
+
output.append(line)
|
35
|
+
|
36
|
+
return output
|
37
|
+
|
38
|
+
|
39
|
+
"""
|
40
|
+
COPYRIGHT NOTICE
|
41
|
+
|
42
|
+
This software is governed by the CeCILL license under French law and
|
43
|
+
abiding by the rules of distribution of free software. You can use,
|
44
|
+
modify and/ or redistribute the software under the terms of the CeCILL
|
45
|
+
license as circulated by CEA, CNRS and INRIA at the following URL
|
46
|
+
"http://www.cecill.info".
|
47
|
+
|
48
|
+
As a counterpart to the access to the source code and rights to copy,
|
49
|
+
modify and redistribute granted by the license, users are provided only
|
50
|
+
with a limited warranty and the software's author, the holder of the
|
51
|
+
economic rights, and the successive licensors have only limited
|
52
|
+
liability.
|
53
|
+
|
54
|
+
In this respect, the user's attention is drawn to the risks associated
|
55
|
+
with loading, using, modifying and/or developing or reproducing the
|
56
|
+
software by the user in light of its specific status of free software,
|
57
|
+
that may mean that it is complicated to manipulate, and that also
|
58
|
+
therefore means that it is reserved for developers and experienced
|
59
|
+
professionals having in-depth computer knowledge. Users are therefore
|
60
|
+
encouraged to load and test the software's suitability as regards their
|
61
|
+
requirements in conditions enabling the security of their systems and/or
|
62
|
+
data to be ensured and, more generally, to use and operate it in the
|
63
|
+
same conditions as regards security.
|
64
|
+
|
65
|
+
The fact that you are presently reading this means that you have had
|
66
|
+
knowledge of the CeCILL license and that you accept its terms.
|
67
|
+
|
68
|
+
SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
|
69
|
+
|
70
|
+
This software is being developed by Eric Debreuve, a CNRS employee and
|
71
|
+
member of team Morpheme.
|
72
|
+
Team Morpheme is a joint team between Inria, CNRS, and UniCA.
|
73
|
+
It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
|
74
|
+
I3S, and Laboratory iBV.
|
75
|
+
|
76
|
+
CNRS: https://www.cnrs.fr/index.php/en
|
77
|
+
Inria: https://www.inria.fr/en/
|
78
|
+
UniCA: https://univ-cotedazur.eu/
|
79
|
+
Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
|
80
|
+
I3S: https://www.i3s.unice.fr/en/
|
81
|
+
iBV: http://ibv.unice.fr/
|
82
|
+
Team Morpheme: https://team.inria.fr/morpheme/
|
83
|
+
"""
|
logger_36/task/format/memory.py
CHANGED
@@ -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
|
logger_36/task/format/message.py
CHANGED
@@ -5,10 +5,69 @@ SEE COPYRIGHT NOTICE BELOW
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import difflib as diff
|
8
|
+
import logging as l
|
8
9
|
import typing as h
|
9
10
|
|
11
|
+
from logger_36.config.message import (
|
12
|
+
LEVEL_CLOSING,
|
13
|
+
LEVEL_OPENING,
|
14
|
+
MESSAGE_MARKER,
|
15
|
+
WHERE_SEPARATOR,
|
16
|
+
)
|
10
17
|
from logger_36.constant.generic import NOT_PASSED
|
11
|
-
from logger_36.constant.message import expected_op_h
|
18
|
+
from logger_36.constant.message import NEXT_LINE_PROLOGUE, expected_op_h
|
19
|
+
from logger_36.constant.record import SHOW_W_RULE_ATTR
|
20
|
+
from logger_36.extension.line import WrappedLines
|
21
|
+
from logger_36.type.message import RuleWithText_h
|
22
|
+
|
23
|
+
|
24
|
+
def MessageFromRecord(
|
25
|
+
record: l.LogRecord,
|
26
|
+
RuleWithText: RuleWithText_h,
|
27
|
+
/,
|
28
|
+
*,
|
29
|
+
line_width: int = 0,
|
30
|
+
color: str | None = None,
|
31
|
+
PreProcessed: h.Callable[[str], str] | None = None,
|
32
|
+
) -> tuple[str, bool]:
|
33
|
+
"""
|
34
|
+
See logger_36.catalog.handler.README.txt.
|
35
|
+
|
36
|
+
The second returned value is is_not_a_rule.
|
37
|
+
"""
|
38
|
+
message = record.msg
|
39
|
+
|
40
|
+
if hasattr(record, SHOW_W_RULE_ATTR):
|
41
|
+
return RuleWithText(message, color), False
|
42
|
+
|
43
|
+
if PreProcessed is not None:
|
44
|
+
message = PreProcessed(message)
|
45
|
+
if (line_width <= 0) or (message.__len__() <= line_width):
|
46
|
+
if "\n" in message:
|
47
|
+
message = NEXT_LINE_PROLOGUE.join(message.splitlines())
|
48
|
+
else:
|
49
|
+
if "\n" in message:
|
50
|
+
lines = WrappedLines(message.splitlines(), line_width)
|
51
|
+
else:
|
52
|
+
lines = WrappedLines([message], line_width)
|
53
|
+
message = NEXT_LINE_PROLOGUE.join(lines)
|
54
|
+
|
55
|
+
when_or_elapsed = getattr(record, "when_or_elapsed", None)
|
56
|
+
if when_or_elapsed is None:
|
57
|
+
return message, True
|
58
|
+
|
59
|
+
level_first_letter = getattr(record, "level_first_letter", "")
|
60
|
+
|
61
|
+
if (where := getattr(record, "where", None)) is None:
|
62
|
+
where = ""
|
63
|
+
else:
|
64
|
+
where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
|
65
|
+
|
66
|
+
return (
|
67
|
+
f"{when_or_elapsed}"
|
68
|
+
f"{LEVEL_OPENING}{level_first_letter}{LEVEL_CLOSING} "
|
69
|
+
f"{MESSAGE_MARKER} {message}{where}"
|
70
|
+
), True
|
12
71
|
|
13
72
|
|
14
73
|
def MessageWithActualExpected(
|
logger_36/task/format/rule.py
CHANGED
@@ -7,7 +7,7 @@ SEE COPYRIGHT NOTICE BELOW
|
|
7
7
|
from logger_36.catalog.config.optional import RICH_IS_AVAILABLE
|
8
8
|
|
9
9
|
|
10
|
-
def RuleAsText(text: str | None, /) -> str:
|
10
|
+
def RuleAsText(text: str | None, _: None, /) -> str:
|
11
11
|
""""""
|
12
12
|
if text is None:
|
13
13
|
return "---- ---- ---- ---- ---- ---- ---- ---- ----"
|
@@ -27,7 +27,8 @@ 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
|
-
|
30
|
+
rule_t = text_t = None
|
31
|
+
Rule = lambda _, __: RuleAsText(_, None)
|
31
32
|
|
32
33
|
"""
|
33
34
|
COPYRIGHT NOTICE
|
logger_36/task/inspection.py
CHANGED
@@ -51,7 +51,7 @@ def Modules(
|
|
51
51
|
|
52
52
|
if formatted:
|
53
53
|
max_length += 4
|
54
|
-
AlignedInColumns = lambda
|
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
@@ -8,6 +8,7 @@ import logging as l
|
|
8
8
|
from io import IOBase as io_base_t
|
9
9
|
from pathlib import Path as path_t
|
10
10
|
|
11
|
+
from logger_36.constant.html import BODY_PLACEHOLDER, MINIMAL_HTML, TITLE_PLACEHOLDER
|
11
12
|
from logger_36.instance.logger import L
|
12
13
|
|
13
14
|
|
@@ -35,19 +36,39 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
|
|
35
36
|
path = path_t(path)
|
36
37
|
|
37
38
|
actual_file = isinstance(path, path_t)
|
39
|
+
|
38
40
|
if actual_file and path.exists():
|
39
41
|
L.warning(f'{cannot_save}: File "{path}" already exists.')
|
40
42
|
return
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
44
|
+
body = "\n".join(map(_HighlightedEvent, L.recorded))
|
45
|
+
html = MINIMAL_HTML.replace(TITLE_PLACEHOLDER, L.name).replace(
|
46
|
+
BODY_PLACEHOLDER, body
|
47
|
+
)
|
48
|
+
if actual_file:
|
49
|
+
with open(path, "w") as accessor:
|
50
|
+
accessor.write(html)
|
45
51
|
else:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
path.write(html)
|
53
|
+
|
54
|
+
|
55
|
+
def _HighlightedEvent(event: tuple[int, str], /) -> str:
|
56
|
+
""""""
|
57
|
+
level, message = event
|
58
|
+
if level == l.DEBUG:
|
59
|
+
color = "BlueViolet"
|
60
|
+
elif level == l.INFO:
|
61
|
+
color = "black"
|
62
|
+
elif level == l.WARNING:
|
63
|
+
color = "gold"
|
64
|
+
elif level == l.ERROR:
|
65
|
+
color = "orange"
|
66
|
+
elif level == l.CRITICAL:
|
67
|
+
color = "red"
|
68
|
+
else:
|
69
|
+
color = "black"
|
70
|
+
|
71
|
+
return f'<span style="color:{color}">{message}</span>'
|
51
72
|
|
52
73
|
|
53
74
|
"""
|
logger_36/type/handler.py
CHANGED
@@ -4,57 +4,38 @@ 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
|
-
)
|
12
|
+
from logger_36.config.message import FALLBACK_MESSAGE_WIDTH
|
18
13
|
from logger_36.constant.error import MEMORY_MEASURE_ERROR
|
19
14
|
from logger_36.constant.handler import HANDLER_KINDS
|
20
|
-
from logger_36.
|
21
|
-
from logger_36.task.format.message import MessageWithActualExpected
|
15
|
+
from logger_36.task.format.message import MessageFromRecord, MessageWithActualExpected
|
22
16
|
from logger_36.task.measure.chronos import TimeStamp
|
23
17
|
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
|
18
|
+
from logger_36.type.message import MessageFromRecord_h, RuleWithText_h
|
40
19
|
|
41
20
|
_MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
|
42
21
|
|
43
22
|
|
44
|
-
|
45
|
-
|
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)
|
23
|
+
class _base_t:
|
24
|
+
kind: h.ClassVar[str] = "" # See logger_36.constant.handler.handler_codes_h.
|
50
25
|
|
51
|
-
|
52
|
-
|
53
|
-
formatter: d.InitVar[l.Formatter | None] = None
|
54
|
-
|
55
|
-
def __post_init__(
|
56
|
-
self, handler: l.Handler | None, level: int, formatter: l.Formatter | None
|
26
|
+
def __init__(
|
27
|
+
self, name: str | None, should_store_memory_usage: bool, message_width: int
|
57
28
|
) -> None:
|
29
|
+
""""""
|
30
|
+
self.name = name
|
31
|
+
self.should_store_memory_usage = should_store_memory_usage
|
32
|
+
self.message_width = message_width
|
33
|
+
#
|
34
|
+
self.MessageFromRecord: MessageFromRecord_h | None = None
|
35
|
+
|
36
|
+
self.__post_init__()
|
37
|
+
|
38
|
+
def __post_init__(self) -> None:
|
58
39
|
""""""
|
59
40
|
global _MEMORY_MEASURE_ERROR
|
60
41
|
|
@@ -76,81 +57,95 @@ class handler_extension_t:
|
|
76
57
|
s.__stderr__.write(_MEMORY_MEASURE_ERROR + "\n")
|
77
58
|
_MEMORY_MEASURE_ERROR = None
|
78
59
|
|
79
|
-
|
60
|
+
if 0 < self.message_width < FALLBACK_MESSAGE_WIDTH:
|
61
|
+
self.message_width = FALLBACK_MESSAGE_WIDTH
|
80
62
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
self.MessageFromRecord = handler.formatter.format
|
63
|
+
@classmethod
|
64
|
+
def New(cls, **kwargs) -> h.Self:
|
65
|
+
"""
|
66
|
+
Interest: default arguments, no prescribed argument order, variable argument list.
|
67
|
+
"""
|
68
|
+
raise NotImplementedError
|
88
69
|
|
89
|
-
def
|
90
|
-
self,
|
91
|
-
record: l.LogRecord,
|
92
|
-
/,
|
93
|
-
*,
|
94
|
-
PreProcessed: h.Callable[[str], str] | None = None,
|
95
|
-
) -> str:
|
70
|
+
def LogAsIs(self, message: str, /) -> None:
|
96
71
|
"""
|
97
|
-
See
|
72
|
+
See documentation of
|
73
|
+
logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
|
98
74
|
"""
|
99
|
-
|
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)
|
75
|
+
raise NotImplementedError
|
150
76
|
|
151
|
-
|
77
|
+
def DisplayRule(self, /, *, text: str | None = None, color: str = "black") -> None:
|
78
|
+
""""""
|
79
|
+
raise NotImplementedError
|
152
80
|
|
153
|
-
|
81
|
+
|
82
|
+
class handler_t(l.Handler, _base_t):
|
83
|
+
def __init__(
|
84
|
+
self,
|
85
|
+
name: str | None,
|
86
|
+
should_store_memory_usage: bool,
|
87
|
+
message_width: int,
|
88
|
+
level: int,
|
89
|
+
formatter: l.Formatter | None,
|
90
|
+
*_,
|
91
|
+
) -> None:
|
92
|
+
""""""
|
93
|
+
l.Handler.__init__(self)
|
94
|
+
_base_t.__init__(self, name, should_store_memory_usage, message_width)
|
95
|
+
__post_init__(self, level, formatter)
|
96
|
+
|
97
|
+
|
98
|
+
class file_handler_t(l.FileHandler, _base_t):
|
99
|
+
def __init__(
|
100
|
+
self,
|
101
|
+
name: str | None,
|
102
|
+
should_store_memory_usage: bool,
|
103
|
+
message_width: int,
|
104
|
+
level: int,
|
105
|
+
formatter: l.Formatter | None,
|
106
|
+
path: str | path_t | None,
|
107
|
+
*_,
|
108
|
+
) -> None:
|
109
|
+
""""""
|
110
|
+
if path is None:
|
111
|
+
raise ValueError("Missing file or folder.")
|
112
|
+
if isinstance(path, str):
|
113
|
+
path = path_t(path)
|
114
|
+
if path.exists():
|
115
|
+
raise ValueError(f"File or folder already exists: {path}.")
|
116
|
+
|
117
|
+
l.FileHandler.__init__(self, path)
|
118
|
+
_base_t.__init__(self, name, should_store_memory_usage, message_width)
|
119
|
+
__post_init__(self, level, formatter)
|
120
|
+
|
121
|
+
|
122
|
+
any_handler_t = handler_t | file_handler_t
|
123
|
+
|
124
|
+
|
125
|
+
def __post_init__(
|
126
|
+
handler: any_handler_t, level: int, formatter: l.Formatter | None
|
127
|
+
) -> None:
|
128
|
+
""""""
|
129
|
+
handler.setLevel(level)
|
130
|
+
|
131
|
+
if formatter is None:
|
132
|
+
handler.MessageFromRecord = MessageFromRecord
|
133
|
+
else:
|
134
|
+
handler.setFormatter(formatter)
|
135
|
+
_MessageFromRecordRaw = handler.formatter.format
|
136
|
+
|
137
|
+
def _MessageFromRecord(
|
138
|
+
record: l.LogRecord,
|
139
|
+
_: RuleWithText_h,
|
140
|
+
/,
|
141
|
+
*,
|
142
|
+
line_width: int = 0,
|
143
|
+
PreProcessed: h.Callable[[str], str] | None = None,
|
144
|
+
) -> tuple[str, bool]:
|
145
|
+
#
|
146
|
+
return _MessageFromRecordRaw(record), False
|
147
|
+
|
148
|
+
handler.MessageFromRecord = _MessageFromRecord
|
154
149
|
|
155
150
|
|
156
151
|
"""
|