logger-36 2025.6__py3-none-any.whl → 2025.7__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 +5 -10
- logger_36/catalog/handler/console_rich.py +7 -13
- logger_36/catalog/handler/file.py +5 -10
- logger_36/catalog/handler/generic.py +10 -13
- logger_36/constant/path.py +69 -0
- logger_36/exception.py +4 -3
- logger_36/handler.py +3 -3
- logger_36/type/handler.py +4 -7
- logger_36/type/logger.py +199 -130
- logger_36/version.py +1 -1
- {logger_36-2025.6.dist-info → logger_36-2025.7.dist-info}/METADATA +1 -1
- {logger_36-2025.6.dist-info → logger_36-2025.7.dist-info}/RECORD +14 -13
- {logger_36-2025.6.dist-info → logger_36-2025.7.dist-info}/WHEEL +0 -0
- {logger_36-2025.6.dist-info → logger_36-2025.7.dist-info}/top_level.txt +0 -0
@@ -6,14 +6,11 @@ SEE COPYRIGHT NOTICE BELOW
|
|
6
6
|
|
7
7
|
import dataclasses as d
|
8
8
|
import logging as l
|
9
|
-
import textwrap as txt_
|
10
9
|
import typing as h
|
11
10
|
|
12
|
-
from logger_36.constant.message import LINE_INDENT
|
13
11
|
from logger_36.constant.record import SHOW_W_RULE_ATTR
|
14
12
|
from logger_36.task.format.rule import RuleAsText
|
15
|
-
from logger_36.type.handler import handler_extension_t
|
16
|
-
from logger_36.type.handler import message_from_record_raw_p as message_from_record_p
|
13
|
+
from logger_36.type.handler import MessageFromRecordRaw_h, handler_extension_t
|
17
14
|
|
18
15
|
|
19
16
|
@d.dataclass(slots=True, repr=False, eq=False)
|
@@ -25,7 +22,7 @@ class console_handler_t(l.Handler):
|
|
25
22
|
kind: h.ClassVar[str] = "c"
|
26
23
|
|
27
24
|
extension: handler_extension_t = d.field(init=False)
|
28
|
-
MessageFromRecord:
|
25
|
+
MessageFromRecord: MessageFromRecordRaw_h = d.field(init=False)
|
29
26
|
|
30
27
|
name: d.InitVar[str | None] = None
|
31
28
|
level: d.InitVar[int] = l.NOTSET
|
@@ -63,18 +60,16 @@ class console_handler_t(l.Handler):
|
|
63
60
|
message = self.MessageFromRecord(record)
|
64
61
|
print(message)
|
65
62
|
|
66
|
-
def
|
63
|
+
def LogAsIs(self, message: str, /) -> None:
|
67
64
|
"""
|
68
65
|
See documentation of
|
69
|
-
logger_36.catalog.handler.generic.generic_handler_t.
|
66
|
+
logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
|
70
67
|
"""
|
71
|
-
if indented:
|
72
|
-
message = txt_.indent(message, LINE_INDENT)
|
73
68
|
print(message)
|
74
69
|
|
75
70
|
def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
|
76
71
|
""""""
|
77
|
-
self.
|
72
|
+
self.LogAsIs(RuleAsText(text))
|
78
73
|
|
79
74
|
|
80
75
|
"""
|
@@ -6,7 +6,6 @@ SEE COPYRIGHT NOTICE BELOW
|
|
6
6
|
|
7
7
|
import dataclasses as d
|
8
8
|
import logging as l
|
9
|
-
import textwrap as txt_
|
10
9
|
import typing as h
|
11
10
|
|
12
11
|
from logger_36.catalog.config.console_rich import (
|
@@ -20,13 +19,10 @@ from logger_36.catalog.config.console_rich import (
|
|
20
19
|
WHITE_COLOR,
|
21
20
|
)
|
22
21
|
from logger_36.config.message import ACTUAL_PATTERNS, EXPECTED_PATTERNS, WHERE_SEPARATOR
|
23
|
-
from logger_36.constant.message import CONTEXT_LENGTH
|
22
|
+
from logger_36.constant.message import CONTEXT_LENGTH
|
24
23
|
from logger_36.constant.record import SHOW_W_RULE_ATTR
|
25
|
-
from logger_36.task.format.rule import Rule
|
26
|
-
from logger_36.type.handler import handler_extension_t
|
27
|
-
from logger_36.type.handler import (
|
28
|
-
message_from_record_preprocessed_p as message_from_record_p,
|
29
|
-
)
|
24
|
+
from logger_36.task.format.rule import Rule
|
25
|
+
from logger_36.type.handler import MessageFromRecordPreprocessed_p, handler_extension_t
|
30
26
|
from rich.console import Console as console_t # noqa
|
31
27
|
from rich.console import RenderableType as renderable_t # noqa
|
32
28
|
from rich.markup import escape as EscapedVersion # noqa
|
@@ -64,7 +60,7 @@ class console_rich_handler_t(l.Handler):
|
|
64
60
|
|
65
61
|
extension: handler_extension_t = d.field(init=False)
|
66
62
|
console: console_t = d.field(init=False)
|
67
|
-
MessageFromRecord:
|
63
|
+
MessageFromRecord: MessageFromRecordPreprocessed_p = d.field(init=False)
|
68
64
|
alternating_lines: int = 0
|
69
65
|
background_is_light: bool = True
|
70
66
|
|
@@ -152,18 +148,16 @@ class console_rich_handler_t(l.Handler):
|
|
152
148
|
)
|
153
149
|
self.console.print(richer, crop=False, overflow="ignore")
|
154
150
|
|
155
|
-
def
|
151
|
+
def LogAsIs(self, message: str | renderable_t, /) -> None:
|
156
152
|
"""
|
157
153
|
See documentation of
|
158
|
-
logger_36.catalog.handler.generic.generic_handler_t.
|
154
|
+
logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
|
159
155
|
"""
|
160
|
-
if isinstance(message, str) and indented:
|
161
|
-
message = txt_.indent(message, LINE_INDENT)
|
162
156
|
self.console.print(message, crop=False, overflow="ignore")
|
163
157
|
|
164
158
|
def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
|
165
159
|
""""""
|
166
|
-
self.
|
160
|
+
self.LogAsIs(Rule(text, color))
|
167
161
|
|
168
162
|
|
169
163
|
def HighlightedVersion(
|
@@ -6,15 +6,12 @@ SEE COPYRIGHT NOTICE BELOW
|
|
6
6
|
|
7
7
|
import dataclasses as d
|
8
8
|
import logging as l
|
9
|
-
import textwrap as txt_
|
10
9
|
import typing as h
|
11
10
|
from pathlib import Path as path_t
|
12
11
|
|
13
|
-
from logger_36.constant.message import LINE_INDENT
|
14
12
|
from logger_36.constant.record import SHOW_W_RULE_ATTR
|
15
13
|
from logger_36.task.format.rule import RuleAsText
|
16
|
-
from logger_36.type.handler import handler_extension_t
|
17
|
-
from logger_36.type.handler import message_from_record_raw_p as message_from_record_p
|
14
|
+
from logger_36.type.handler import MessageFromRecordRaw_h, handler_extension_t
|
18
15
|
|
19
16
|
|
20
17
|
@d.dataclass(slots=True, repr=False, eq=False)
|
@@ -26,7 +23,7 @@ class file_handler_t(l.FileHandler):
|
|
26
23
|
kind: h.ClassVar[str] = "f"
|
27
24
|
|
28
25
|
extension: handler_extension_t = d.field(init=False)
|
29
|
-
MessageFromRecord:
|
26
|
+
MessageFromRecord: MessageFromRecordRaw_h = d.field(init=False)
|
30
27
|
|
31
28
|
name: d.InitVar[str | None] = None
|
32
29
|
level: d.InitVar[int] = l.NOTSET
|
@@ -72,19 +69,17 @@ class file_handler_t(l.FileHandler):
|
|
72
69
|
print(message, file=self.stream)
|
73
70
|
self.stream.flush()
|
74
71
|
|
75
|
-
def
|
72
|
+
def LogAsIs(self, message: str, /) -> None:
|
76
73
|
"""
|
77
74
|
See documentation of
|
78
|
-
logger_36.catalog.handler.generic.generic_handler_t.
|
75
|
+
logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
|
79
76
|
"""
|
80
|
-
if indented:
|
81
|
-
message = txt_.indent(message, LINE_INDENT)
|
82
77
|
print(message, file=self.stream)
|
83
78
|
self.stream.flush()
|
84
79
|
|
85
80
|
def DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
|
86
81
|
""""""
|
87
|
-
self.
|
82
|
+
self.LogAsIs(RuleAsText(text))
|
88
83
|
|
89
84
|
|
90
85
|
"""
|
@@ -24,16 +24,13 @@ else:
|
|
24
24
|
|
25
25
|
from logger_36.constant.record import SHOW_W_RULE_ATTR
|
26
26
|
from logger_36.task.format.rule import Rule, RuleAsText
|
27
|
-
from logger_36.type.handler import
|
27
|
+
from logger_36.type.handler import MessageFromRecord_h, handler_extension_t
|
28
28
|
|
29
|
-
|
30
|
-
@h.runtime_checkable
|
31
|
-
class show_message_p(h.Protocol):
|
32
|
-
def __call__(self, message: str, /, *, indented: bool = False) -> None: ...
|
29
|
+
LogAsIs_h = h.Callable[[str | h.Any], None]
|
33
30
|
|
34
31
|
|
35
32
|
@h.runtime_checkable
|
36
|
-
class
|
33
|
+
class DisplayRule_p(h.Protocol):
|
37
34
|
def __call__(self, /, *, text: str | None = None, color: str = "white") -> None: ...
|
38
35
|
|
39
36
|
|
@@ -49,7 +46,7 @@ class generic_handler_t(l.Handler):
|
|
49
46
|
- anything else: disabled
|
50
47
|
- Runtime value: 0/1=do not/do highlight next time.
|
51
48
|
|
52
|
-
|
49
|
+
LogAsIs:
|
53
50
|
Log a message as is, i.e. without formatting. If this is a method, it should
|
54
51
|
contain the same call(s) as the final ones in the emit methods that are used to
|
55
52
|
output the formatted log messages. This means that there is some code
|
@@ -62,16 +59,16 @@ class generic_handler_t(l.Handler):
|
|
62
59
|
|
63
60
|
kind: h.ClassVar[str] = "g"
|
64
61
|
|
65
|
-
|
62
|
+
LogAsIs: LogAsIs_h
|
66
63
|
# "None -> h.Any" (twice below) since None | None is invalid.
|
67
64
|
console: console_t | h.Any = None
|
68
65
|
console_options: console_options_t | h.Any = None
|
69
66
|
alternating_lines: int = 0
|
70
67
|
background_is_light: bool = True
|
71
68
|
|
72
|
-
DisplayRule:
|
69
|
+
DisplayRule: DisplayRule_p = d.field(init=False)
|
73
70
|
extension: handler_extension_t = d.field(init=False)
|
74
|
-
MessageFromRecord:
|
71
|
+
MessageFromRecord: MessageFromRecord_h = d.field(init=False)
|
75
72
|
|
76
73
|
name: d.InitVar[str | None] = None
|
77
74
|
level: d.InitVar[int] = l.NOTSET
|
@@ -178,17 +175,17 @@ class generic_handler_t(l.Handler):
|
|
178
175
|
"<pre style='margin-bottom:0px'>" + "".join(html_segments) + "</pre>"
|
179
176
|
)
|
180
177
|
|
181
|
-
self.
|
178
|
+
self.LogAsIs(message)
|
182
179
|
|
183
180
|
def _DisplayRuleAsText(
|
184
181
|
self, /, *, text: str | None = None, color: str = "white"
|
185
182
|
) -> None:
|
186
183
|
""""""
|
187
|
-
self.
|
184
|
+
self.LogAsIs(RuleAsText(text))
|
188
185
|
|
189
186
|
def _DisplayRule(self, /, *, text: str | None = None, color: str = "white") -> None:
|
190
187
|
""""""
|
191
|
-
self.
|
188
|
+
self.LogAsIs(Rule(text, color))
|
192
189
|
|
193
190
|
|
194
191
|
"""
|
@@ -0,0 +1,69 @@
|
|
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 inspect as nspt
|
8
|
+
import tempfile as tmps
|
9
|
+
from pathlib import Path as path_t
|
10
|
+
|
11
|
+
USER_FOLDER = path_t.home()
|
12
|
+
|
13
|
+
frame = nspt.stack()[-1]
|
14
|
+
if path_t(frame.filename).exists():
|
15
|
+
PROJECT_FILE = path_t(frame.filename)
|
16
|
+
if PROJECT_FILE.is_relative_to(USER_FOLDER):
|
17
|
+
PROJECT_FILE_RELATIVE = path_t("~") / PROJECT_FILE.relative_to(USER_FOLDER)
|
18
|
+
else:
|
19
|
+
PROJECT_FILE_RELATIVE = PROJECT_FILE
|
20
|
+
PROJECT_FOLDER = PROJECT_FILE.parent
|
21
|
+
else:
|
22
|
+
PROJECT_FILE = PROJECT_FILE_RELATIVE = "<unknown>"
|
23
|
+
PROJECT_FOLDER = path_t(tmps.mkdtemp())
|
24
|
+
|
25
|
+
"""
|
26
|
+
COPYRIGHT NOTICE
|
27
|
+
|
28
|
+
This software is governed by the CeCILL license under French law and
|
29
|
+
abiding by the rules of distribution of free software. You can use,
|
30
|
+
modify and/ or redistribute the software under the terms of the CeCILL
|
31
|
+
license as circulated by CEA, CNRS and INRIA at the following URL
|
32
|
+
"http://www.cecill.info".
|
33
|
+
|
34
|
+
As a counterpart to the access to the source code and rights to copy,
|
35
|
+
modify and redistribute granted by the license, users are provided only
|
36
|
+
with a limited warranty and the software's author, the holder of the
|
37
|
+
economic rights, and the successive licensors have only limited
|
38
|
+
liability.
|
39
|
+
|
40
|
+
In this respect, the user's attention is drawn to the risks associated
|
41
|
+
with loading, using, modifying and/or developing or reproducing the
|
42
|
+
software by the user in light of its specific status of free software,
|
43
|
+
that may mean that it is complicated to manipulate, and that also
|
44
|
+
therefore means that it is reserved for developers and experienced
|
45
|
+
professionals having in-depth computer knowledge. Users are therefore
|
46
|
+
encouraged to load and test the software's suitability as regards their
|
47
|
+
requirements in conditions enabling the security of their systems and/or
|
48
|
+
data to be ensured and, more generally, to use and operate it in the
|
49
|
+
same conditions as regards security.
|
50
|
+
|
51
|
+
The fact that you are presently reading this means that you have had
|
52
|
+
knowledge of the CeCILL license and that you accept its terms.
|
53
|
+
|
54
|
+
SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
|
55
|
+
|
56
|
+
This software is being developed by Eric Debreuve, a CNRS employee and
|
57
|
+
member of team Morpheme.
|
58
|
+
Team Morpheme is a joint team between Inria, CNRS, and UniCA.
|
59
|
+
It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
|
60
|
+
I3S, and Laboratory iBV.
|
61
|
+
|
62
|
+
CNRS: https://www.cnrs.fr/index.php/en
|
63
|
+
Inria: https://www.inria.fr/en/
|
64
|
+
UniCA: https://univ-cotedazur.eu/
|
65
|
+
Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
|
66
|
+
I3S: https://www.i3s.unice.fr/en/
|
67
|
+
iBV: http://ibv.unice.fr/
|
68
|
+
Team Morpheme: https://team.inria.fr/morpheme/
|
69
|
+
"""
|
logger_36/exception.py
CHANGED
@@ -11,6 +11,8 @@ import traceback as tcbk
|
|
11
11
|
import types as t
|
12
12
|
from pathlib import Path as path_t
|
13
13
|
|
14
|
+
from logger_36.constant.path import USER_FOLDER
|
15
|
+
|
14
16
|
_ORIGINAL_EXCEPTION_HANDLER = s.excepthook
|
15
17
|
|
16
18
|
|
@@ -38,9 +40,8 @@ def _HandleException(
|
|
38
40
|
line_content = module.read_text().splitlines()[line_number - 1].strip()
|
39
41
|
|
40
42
|
# Format module.
|
41
|
-
|
42
|
-
|
43
|
-
module = path_t("~") / module.relative_to(home)
|
43
|
+
if module.is_relative_to(USER_FOLDER):
|
44
|
+
module = path_t("~") / module.relative_to(USER_FOLDER)
|
44
45
|
|
45
46
|
# Format line content.
|
46
47
|
if line_content.startswith("raise "):
|
logger_36/handler.py
CHANGED
@@ -11,7 +11,7 @@ from pathlib import Path as path_t
|
|
11
11
|
from logger_36.catalog.config.optional import MISSING_RICH_MESSAGE, RICH_IS_AVAILABLE
|
12
12
|
from logger_36.catalog.handler.console import console_handler_t
|
13
13
|
from logger_36.catalog.handler.file import file_handler_t
|
14
|
-
from logger_36.catalog.handler.generic import
|
14
|
+
from logger_36.catalog.handler.generic import LogAsIs_h, generic_handler_t
|
15
15
|
|
16
16
|
if RICH_IS_AVAILABLE:
|
17
17
|
from logger_36.catalog.handler.console_rich import console_rich_handler_t
|
@@ -24,7 +24,7 @@ _MISSING_RICH_MESSAGE = MISSING_RICH_MESSAGE
|
|
24
24
|
|
25
25
|
def AddGenericHandler(
|
26
26
|
logger: l.Logger,
|
27
|
-
|
27
|
+
LogAsIs: LogAsIs_h,
|
28
28
|
/,
|
29
29
|
*,
|
30
30
|
name: str | None = None,
|
@@ -49,7 +49,7 @@ def AddGenericHandler(
|
|
49
49
|
alternating_lines=alternating_lines,
|
50
50
|
should_record=should_record,
|
51
51
|
rich_kwargs=kwargs,
|
52
|
-
|
52
|
+
LogAsIs=LogAsIs,
|
53
53
|
)
|
54
54
|
logger.AddHandler(handler, should_hold_messages=should_hold_messages)
|
55
55
|
|
logger_36/type/handler.py
CHANGED
@@ -22,14 +22,11 @@ from logger_36.task.format.message import MessageWithActualExpected
|
|
22
22
|
from logger_36.task.measure.chronos import TimeStamp
|
23
23
|
from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
|
24
24
|
|
25
|
-
|
26
|
-
@h.runtime_checkable
|
27
|
-
class message_from_record_raw_p(h.Protocol):
|
28
|
-
def __call__(self, record: l.LogRecord, /) -> str: ...
|
25
|
+
MessageFromRecordRaw_h = h.Callable[[l.LogRecord], str]
|
29
26
|
|
30
27
|
|
31
28
|
@h.runtime_checkable
|
32
|
-
class
|
29
|
+
class MessageFromRecordPreprocessed_p(h.Protocol):
|
33
30
|
def __call__(
|
34
31
|
self,
|
35
32
|
record: l.LogRecord,
|
@@ -39,7 +36,7 @@ class message_from_record_preprocessed_p(h.Protocol):
|
|
39
36
|
) -> str: ...
|
40
37
|
|
41
38
|
|
42
|
-
|
39
|
+
MessageFromRecord_h = MessageFromRecordRaw_h | MessageFromRecordPreprocessed_p
|
43
40
|
|
44
41
|
_MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
|
45
42
|
|
@@ -49,7 +46,7 @@ class handler_extension_t:
|
|
49
46
|
name: str | None = None
|
50
47
|
should_store_memory_usage: bool = False
|
51
48
|
message_width: int = -1
|
52
|
-
MessageFromRecord:
|
49
|
+
MessageFromRecord: MessageFromRecord_h = d.field(init=False)
|
53
50
|
|
54
51
|
handler: d.InitVar[l.Handler | None] = None
|
55
52
|
level: d.InitVar[int] = l.NOTSET
|
logger_36/type/logger.py
CHANGED
@@ -7,6 +7,7 @@ SEE COPYRIGHT NOTICE BELOW
|
|
7
7
|
import dataclasses as d
|
8
8
|
import logging as l
|
9
9
|
import sys as s
|
10
|
+
import textwrap as text
|
10
11
|
import traceback as tcbk
|
11
12
|
import types as t
|
12
13
|
import typing as h
|
@@ -32,15 +33,17 @@ from logger_36.constant.logger import (
|
|
32
33
|
WARNING_TYPE_COMPILED_PATTERN,
|
33
34
|
)
|
34
35
|
from logger_36.constant.memory import UNKNOWN_MEMORY_USAGE
|
35
|
-
from logger_36.constant.message import TIME_LENGTH_m_1, expected_op_h
|
36
|
+
from logger_36.constant.message import LINE_INDENT, TIME_LENGTH_m_1, expected_op_h
|
37
|
+
from logger_36.constant.path import PROJECT_FILE_RELATIVE
|
36
38
|
from logger_36.constant.record import (
|
37
39
|
HIDE_WHERE_ATTR,
|
38
40
|
SHOW_W_RULE_ATTR,
|
39
41
|
STORE_MEMORY_ATTR,
|
40
42
|
)
|
41
43
|
from logger_36.exception import OverrideExceptionFormat
|
42
|
-
from logger_36.handler import AddRichConsoleHandler
|
44
|
+
from logger_36.handler import AddConsoleHandler, AddFileHandler, AddRichConsoleHandler
|
43
45
|
from logger_36.task.format.message import MessageWithActualExpected
|
46
|
+
from logger_36.task.format.rule import RuleAsText
|
44
47
|
from logger_36.task.measure.chronos import ElapsedTime
|
45
48
|
from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
|
46
49
|
from logger_36.type.issue import NewIssue, issue_t
|
@@ -84,6 +87,39 @@ class logger_t(base_t):
|
|
84
87
|
init=False, default_factory=dict
|
85
88
|
)
|
86
89
|
|
90
|
+
@property
|
91
|
+
def intercepts_warnings(self) -> bool:
|
92
|
+
""""""
|
93
|
+
return self.intercepted_wrn_handle is not None
|
94
|
+
|
95
|
+
@property
|
96
|
+
def intercepts_logs(self) -> bool:
|
97
|
+
""""""
|
98
|
+
return self.intercepted_log_handles.__len__() > 0
|
99
|
+
|
100
|
+
@property
|
101
|
+
def has_staged_issues(self) -> bool:
|
102
|
+
""""""
|
103
|
+
return self.staged_issues.__len__() > 0
|
104
|
+
|
105
|
+
@property
|
106
|
+
def max_memory_usage(self) -> int:
|
107
|
+
""""""
|
108
|
+
if self.memory_usages.__len__() > 0:
|
109
|
+
return max(tuple(zip(*self.memory_usages))[1])
|
110
|
+
return UNKNOWN_MEMORY_USAGE
|
111
|
+
|
112
|
+
@property
|
113
|
+
def max_memory_usage_full(self) -> tuple[str, int]:
|
114
|
+
""""""
|
115
|
+
if self.memory_usages.__len__() > 0:
|
116
|
+
where_s, usages = zip(*self.memory_usages)
|
117
|
+
max_usage = max(usages)
|
118
|
+
|
119
|
+
return where_s[usages.index(max_usage)], max_usage
|
120
|
+
|
121
|
+
return "?", UNKNOWN_MEMORY_USAGE
|
122
|
+
|
87
123
|
def __post_init__(
|
88
124
|
self, name_: str, level_: int, activate_wrn_interceptions: bool
|
89
125
|
) -> None:
|
@@ -95,11 +131,92 @@ class logger_t(base_t):
|
|
95
131
|
for level in l.getLevelNamesMapping().values():
|
96
132
|
self.events[level] = 0
|
97
133
|
|
134
|
+
self.info(f'New logger "{self.name}" for "{PROJECT_FILE_RELATIVE}"')
|
135
|
+
|
98
136
|
if activate_wrn_interceptions:
|
99
|
-
self.
|
137
|
+
self.ToggleWarningInterceptions(True)
|
100
138
|
if self.exit_on_error:
|
101
139
|
self.exit_on_critical = True
|
102
140
|
|
141
|
+
def handle(self, record: l.LogRecord, /) -> None:
|
142
|
+
""""""
|
143
|
+
elapsed_time, now = ElapsedTime(should_return_now=True)
|
144
|
+
|
145
|
+
if (self.on_hold.__len__() > 0) and not self.should_hold_messages:
|
146
|
+
for held in self.on_hold:
|
147
|
+
base_t.handle(self, held)
|
148
|
+
self.on_hold.clear()
|
149
|
+
|
150
|
+
if (date := now.date()) != self.last_message_date:
|
151
|
+
self.last_message_date = date
|
152
|
+
# levelno: Added for management by logging.Logger.handle.
|
153
|
+
date_record = l.makeLogRecord(
|
154
|
+
{
|
155
|
+
"name": self.name,
|
156
|
+
"levelno": l.INFO,
|
157
|
+
"msg": f"DATE: {date.strftime(DATE_FORMAT)}",
|
158
|
+
SHOW_W_RULE_ATTR: True,
|
159
|
+
}
|
160
|
+
)
|
161
|
+
if self.should_hold_messages:
|
162
|
+
self.on_hold.append(date_record)
|
163
|
+
else:
|
164
|
+
base_t.handle(self, date_record)
|
165
|
+
|
166
|
+
# When.
|
167
|
+
if now - self.last_message_now > LONG_ENOUGH:
|
168
|
+
record.when_or_elapsed = now.strftime(TIME_FORMAT)
|
169
|
+
else:
|
170
|
+
record.when_or_elapsed = (
|
171
|
+
f"{ELAPSED_TIME_SEPARATOR}{elapsed_time:.<{TIME_LENGTH_m_1}}"
|
172
|
+
)
|
173
|
+
self.last_message_now = now
|
174
|
+
|
175
|
+
# Where.
|
176
|
+
# Memory usage is also stored if there are no handlers yet, just in case.
|
177
|
+
should_store_where = self.any_handler_stores_memory or not self.hasHandlers()
|
178
|
+
should_show_where = (record.levelno != l.INFO) and not hasattr(
|
179
|
+
record, HIDE_WHERE_ATTR
|
180
|
+
)
|
181
|
+
if should_store_where or should_show_where:
|
182
|
+
module = path_t(record.pathname)
|
183
|
+
for path in s.path:
|
184
|
+
if module.is_relative_to(path):
|
185
|
+
module = module.relative_to(path).with_suffix("")
|
186
|
+
module = str(module).replace(FOLDER_SEPARATOR, ".")
|
187
|
+
break
|
188
|
+
else:
|
189
|
+
module = record.module
|
190
|
+
where = f"{module}:{record.funcName}:{record.lineno}"
|
191
|
+
if should_show_where:
|
192
|
+
record.where = where
|
193
|
+
else:
|
194
|
+
where = None
|
195
|
+
|
196
|
+
# How.
|
197
|
+
record.level_first_letter = record.levelname[0]
|
198
|
+
|
199
|
+
# What.
|
200
|
+
if not isinstance(record.msg, str):
|
201
|
+
record.msg = str(record.msg)
|
202
|
+
|
203
|
+
if self.should_hold_messages:
|
204
|
+
self.on_hold.append(record)
|
205
|
+
else:
|
206
|
+
base_t.handle(self, record)
|
207
|
+
|
208
|
+
if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
|
209
|
+
self.exit_on_error and (record.levelno is l.ERROR)
|
210
|
+
):
|
211
|
+
# Also works if self.exit_on_error and record.levelno is l.CRITICAL since
|
212
|
+
# __post_init__ set self.exit_on_critical if self.exit_on_error.
|
213
|
+
s.exit(1)
|
214
|
+
|
215
|
+
self.events[record.levelno] += 1
|
216
|
+
|
217
|
+
if should_store_where:
|
218
|
+
self.memory_usages.append((where, CurrentMemoryUsage()))
|
219
|
+
|
103
220
|
def SetLevel(
|
104
221
|
self,
|
105
222
|
level: int,
|
@@ -133,32 +250,82 @@ class logger_t(base_t):
|
|
133
250
|
)
|
134
251
|
)
|
135
252
|
|
136
|
-
def
|
253
|
+
def MakeMonochrome(
|
254
|
+
self,
|
255
|
+
*,
|
256
|
+
should_intercept_logs: bool = True,
|
257
|
+
should_override_exceptions: bool = True,
|
258
|
+
) -> None:
|
259
|
+
""""""
|
260
|
+
self._MakePreamble(
|
261
|
+
should_intercept_logs=should_intercept_logs,
|
262
|
+
should_override_exceptions=should_override_exceptions,
|
263
|
+
)
|
264
|
+
AddConsoleHandler(self)
|
265
|
+
|
266
|
+
def MakeRich(
|
267
|
+
self,
|
268
|
+
*,
|
269
|
+
alternating_lines: int = 2,
|
270
|
+
should_intercept_logs: bool = True,
|
271
|
+
should_override_exceptions: bool = True,
|
272
|
+
) -> None:
|
137
273
|
""""""
|
138
|
-
|
274
|
+
self._MakePreamble(
|
275
|
+
should_intercept_logs=should_intercept_logs,
|
276
|
+
should_override_exceptions=should_override_exceptions,
|
277
|
+
)
|
139
278
|
AddRichConsoleHandler(self, alternating_lines=alternating_lines)
|
140
279
|
|
280
|
+
def MakePermanent(
|
281
|
+
self,
|
282
|
+
path: str | path_t,
|
283
|
+
/,
|
284
|
+
*,
|
285
|
+
should_intercept_logs: bool = True,
|
286
|
+
should_override_exceptions: bool = True,
|
287
|
+
) -> None:
|
288
|
+
""""""
|
289
|
+
self._MakePreamble(
|
290
|
+
should_intercept_logs=should_intercept_logs,
|
291
|
+
should_override_exceptions=should_override_exceptions,
|
292
|
+
)
|
293
|
+
AddFileHandler(self, path)
|
294
|
+
|
295
|
+
def _MakePreamble(
|
296
|
+
self,
|
297
|
+
*,
|
298
|
+
should_intercept_logs: bool = True,
|
299
|
+
should_override_exceptions: bool = True,
|
300
|
+
) -> None:
|
301
|
+
""""""
|
302
|
+
if should_override_exceptions:
|
303
|
+
OverrideExceptionFormat()
|
304
|
+
if should_intercept_logs:
|
305
|
+
self.ToggleLogInterceptions(True)
|
306
|
+
|
141
307
|
def ResetEventCounts(self) -> None:
|
142
308
|
""""""
|
143
309
|
for level in self.events:
|
144
310
|
self.events[level] = 0
|
145
311
|
|
146
|
-
def
|
312
|
+
def ToggleWarningInterceptions(self, state: bool, /) -> None:
|
147
313
|
"""
|
148
314
|
The log message will not appear if called from __post_init__ since there are no
|
149
315
|
handlers yet.
|
150
316
|
"""
|
151
|
-
if
|
317
|
+
if state:
|
318
|
+
assert not self.intercepts_warnings
|
319
|
+
|
152
320
|
logger = l.getLogger(WARNING_LOGGER_NAME)
|
153
321
|
self.intercepted_wrn_handle = logger.handle
|
154
322
|
logger.handle = t.MethodType(_HandleForWarnings(self), logger)
|
155
323
|
|
156
324
|
l.captureWarnings(True)
|
157
325
|
self.info("Warning Interception: ON")
|
326
|
+
else:
|
327
|
+
assert self.intercepts_warnings
|
158
328
|
|
159
|
-
def _DeactivateWarningInterceptions(self) -> None:
|
160
|
-
""""""
|
161
|
-
if self.intercepted_wrn_handle is not None:
|
162
329
|
logger = l.getLogger(WARNING_LOGGER_NAME)
|
163
330
|
logger.handle = self.intercepted_wrn_handle
|
164
331
|
self.intercepted_wrn_handle = None
|
@@ -166,17 +333,10 @@ class logger_t(base_t):
|
|
166
333
|
l.captureWarnings(False)
|
167
334
|
self.info("Warning Interception: OFF")
|
168
335
|
|
169
|
-
def ToggleWarningInterceptions(self, state: bool, /) -> None:
|
170
|
-
""""""
|
171
|
-
if state:
|
172
|
-
self._ActivateWarningInterceptions()
|
173
|
-
else:
|
174
|
-
self._DeactivateWarningInterceptions()
|
175
|
-
|
176
336
|
def ToggleLogInterceptions(self, state: bool, /) -> None:
|
177
337
|
""""""
|
178
338
|
if state:
|
179
|
-
self.
|
339
|
+
assert not self.intercepts_logs
|
180
340
|
|
181
341
|
all_loggers = [l.getLogger()] + [
|
182
342
|
l.getLogger(_nme)
|
@@ -193,31 +353,15 @@ class logger_t(base_t):
|
|
193
353
|
if intercepted.__len__() > 0:
|
194
354
|
as_str = ", ".join(intercepted)
|
195
355
|
self.info(f"Now Intercepting LOGs from: {as_str}")
|
196
|
-
|
356
|
+
else:
|
357
|
+
assert self.intercepts_logs
|
358
|
+
|
197
359
|
for name, handle in self.intercepted_log_handles.items():
|
198
360
|
logger = l.getLogger(name)
|
199
361
|
logger.handle = handle
|
200
362
|
self.intercepted_log_handles.clear()
|
201
363
|
self.info("Log Interception: OFF")
|
202
364
|
|
203
|
-
@property
|
204
|
-
def max_memory_usage(self) -> int:
|
205
|
-
""""""
|
206
|
-
if self.memory_usages.__len__() > 0:
|
207
|
-
return max(tuple(zip(*self.memory_usages))[1])
|
208
|
-
return UNKNOWN_MEMORY_USAGE
|
209
|
-
|
210
|
-
@property
|
211
|
-
def max_memory_usage_full(self) -> tuple[str, int]:
|
212
|
-
""""""
|
213
|
-
if self.memory_usages.__len__() > 0:
|
214
|
-
where_s, usages = zip(*self.memory_usages)
|
215
|
-
max_usage = max(usages)
|
216
|
-
|
217
|
-
return where_s[usages.index(max_usage)], max_usage
|
218
|
-
|
219
|
-
return "?", UNKNOWN_MEMORY_USAGE
|
220
|
-
|
221
365
|
def AddHandler(
|
222
366
|
self, handler: l.Handler, /, *, should_hold_messages: bool = False
|
223
367
|
) -> None:
|
@@ -244,85 +388,6 @@ class logger_t(base_t):
|
|
244
388
|
f"level {handler.level}={l.getLevelName(handler.level)}{path}",
|
245
389
|
)
|
246
390
|
|
247
|
-
def handle(self, record: l.LogRecord, /) -> None:
|
248
|
-
""""""
|
249
|
-
elapsed_time, now = ElapsedTime(should_return_now=True)
|
250
|
-
|
251
|
-
if (self.on_hold.__len__() > 0) and not self.should_hold_messages:
|
252
|
-
for held in self.on_hold:
|
253
|
-
base_t.handle(self, held)
|
254
|
-
self.on_hold.clear()
|
255
|
-
|
256
|
-
if (date := now.date()) != self.last_message_date:
|
257
|
-
self.last_message_date = date
|
258
|
-
# levelno: Added for management by logging.Logger.handle.
|
259
|
-
date_record = l.makeLogRecord(
|
260
|
-
{
|
261
|
-
"name": self.name,
|
262
|
-
"levelno": l.INFO,
|
263
|
-
"msg": f"DATE: {date.strftime(DATE_FORMAT)}",
|
264
|
-
SHOW_W_RULE_ATTR: True,
|
265
|
-
}
|
266
|
-
)
|
267
|
-
if self.should_hold_messages:
|
268
|
-
self.on_hold.append(date_record)
|
269
|
-
else:
|
270
|
-
base_t.handle(self, date_record)
|
271
|
-
|
272
|
-
# When.
|
273
|
-
if now - self.last_message_now > LONG_ENOUGH:
|
274
|
-
record.when_or_elapsed = now.strftime(TIME_FORMAT)
|
275
|
-
else:
|
276
|
-
record.when_or_elapsed = (
|
277
|
-
f"{ELAPSED_TIME_SEPARATOR}{elapsed_time:.<{TIME_LENGTH_m_1}}"
|
278
|
-
)
|
279
|
-
self.last_message_now = now
|
280
|
-
|
281
|
-
# Where.
|
282
|
-
# Memory usage is also stored if there are no handlers yet, just in case.
|
283
|
-
should_store_where = self.any_handler_stores_memory or not self.hasHandlers()
|
284
|
-
should_show_where = (record.levelno != l.INFO) and not hasattr(
|
285
|
-
record, HIDE_WHERE_ATTR
|
286
|
-
)
|
287
|
-
if should_store_where or should_show_where:
|
288
|
-
module = path_t(record.pathname)
|
289
|
-
for path in s.path:
|
290
|
-
if module.is_relative_to(path):
|
291
|
-
module = module.relative_to(path).with_suffix("")
|
292
|
-
module = str(module).replace(FOLDER_SEPARATOR, ".")
|
293
|
-
break
|
294
|
-
else:
|
295
|
-
module = record.module
|
296
|
-
where = f"{module}:{record.funcName}:{record.lineno}"
|
297
|
-
if should_show_where:
|
298
|
-
record.where = where
|
299
|
-
else:
|
300
|
-
where = None
|
301
|
-
|
302
|
-
# How.
|
303
|
-
record.level_first_letter = record.levelname[0]
|
304
|
-
|
305
|
-
# What.
|
306
|
-
if not isinstance(record.msg, str):
|
307
|
-
record.msg = str(record.msg)
|
308
|
-
|
309
|
-
if self.should_hold_messages:
|
310
|
-
self.on_hold.append(record)
|
311
|
-
else:
|
312
|
-
base_t.handle(self, record)
|
313
|
-
|
314
|
-
if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
|
315
|
-
self.exit_on_error and (record.levelno is l.ERROR)
|
316
|
-
):
|
317
|
-
# Also works if self.exit_on_error and record.levelno is l.CRITICAL since
|
318
|
-
# __post_init__ set self.exit_on_critical if self.exit_on_error.
|
319
|
-
s.exit(1)
|
320
|
-
|
321
|
-
self.events[record.levelno] += 1
|
322
|
-
|
323
|
-
if should_store_where:
|
324
|
-
self.memory_usages.append((where, CurrentMemoryUsage()))
|
325
|
-
|
326
391
|
def Log(
|
327
392
|
self,
|
328
393
|
message: str,
|
@@ -370,22 +435,31 @@ class logger_t(base_t):
|
|
370
435
|
message = f"{type(exception).__name__}:\n{formatted}"
|
371
436
|
self.log(level, message)
|
372
437
|
|
373
|
-
def
|
438
|
+
def LogAsIs(self, message: str, /, *, indented: bool = False) -> None:
|
374
439
|
"""
|
375
440
|
See documentation of
|
376
|
-
logger_36.catalog.handler.generic.generic_handler_t.
|
441
|
+
logger_36.catalog.handler.generic.generic_handler_t.LogAsIs.
|
377
442
|
"""
|
443
|
+
if indented:
|
444
|
+
message = text.indent(message, LINE_INDENT)
|
445
|
+
|
378
446
|
for handler in self.handlers:
|
379
|
-
|
380
|
-
|
381
|
-
|
447
|
+
if (LogAsIs := getattr(handler, "LogAsIs", None)) is None:
|
448
|
+
self.info(message)
|
449
|
+
else:
|
450
|
+
LogAsIs(message)
|
451
|
+
|
452
|
+
raw_info = LogAsIs # To follow the convention of the logging methods info, error...
|
382
453
|
|
383
|
-
def DisplayRule(
|
454
|
+
def DisplayRule(
|
455
|
+
self, /, *, message: str | None = None, color: str = "white"
|
456
|
+
) -> None:
|
384
457
|
""""""
|
385
458
|
for handler in self.handlers:
|
386
|
-
DisplayRule
|
387
|
-
|
388
|
-
|
459
|
+
if (DisplayRule := getattr(handler, "DisplayRule", None)) is None:
|
460
|
+
self.info(RuleAsText(message))
|
461
|
+
else:
|
462
|
+
DisplayRule(text=message, color=color)
|
389
463
|
|
390
464
|
def AddContextLevel(self, new_level: str, /) -> None:
|
391
465
|
""""""
|
@@ -427,11 +501,6 @@ class logger_t(base_t):
|
|
427
501
|
)
|
428
502
|
self.staged_issues.append(issue)
|
429
503
|
|
430
|
-
@property
|
431
|
-
def has_staged_issues(self) -> bool:
|
432
|
-
""""""
|
433
|
-
return self.staged_issues.__len__() > 0
|
434
|
-
|
435
504
|
def CommitIssues(
|
436
505
|
self,
|
437
506
|
/,
|
logger_36/version.py
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
logger_36/__init__.py,sha256=3BtAgxFb14e9zzC5fXwqSQxstsd3BO0b_KVu3_wbLwg,2592
|
2
2
|
logger_36/content.py,sha256=clHYYUKa8n4qef6PVlUV4mFHRRf6fnm9wEd2fu9oagA,2381
|
3
|
-
logger_36/exception.py,sha256=
|
3
|
+
logger_36/exception.py,sha256=BQm9maX9CMjpqN26cEsArPCcX37LqYtQSrUZ3fN8tbU,4613
|
4
4
|
logger_36/gpu.py,sha256=BOumedCAPWvCo7J-KJ3XE-jr5S0KSmgcFv_S4QKRPO8,2252
|
5
|
-
logger_36/handler.py,sha256=
|
5
|
+
logger_36/handler.py,sha256=pIwunW-_aSB-SrdlvVmq61nOTH03deKIVcJa4Sz_hkc,6304
|
6
6
|
logger_36/memory.py,sha256=szJVk4UTXsbYv3B-W9LFttf1F3j86GXHsKgEUOsXKl4,2743
|
7
7
|
logger_36/storage.py,sha256=sCxkHQH4xMaseweK1p2M1j0j2PxNPpy9MytPdg1sKiQ,2239
|
8
8
|
logger_36/system.py,sha256=cgOMF_OneYeIJDMbIbIDx96EZss2uAdkk8QofOC7O1U,2251
|
9
9
|
logger_36/time.py,sha256=Uw1jQtY1njsRuIPRAXX44v4nPOo84MSBu_WK_YCRzQs,2324
|
10
|
-
logger_36/version.py,sha256=
|
10
|
+
logger_36/version.py,sha256=QWIy4Jr-I6cIfY2uS0OZkwf54A1Oh3Rxgb14HI6W6bE,2205
|
11
11
|
logger_36/api/logger.py,sha256=TE3ATbymeWX-wBKBFkVz2FxUyJnaqY7vzFwAONVsp2o,2233
|
12
12
|
logger_36/api/storage.py,sha256=KT52AGR37nsMrhKTVfG8R-Dc7lmCXjWML18cOqqCXZY,2239
|
13
13
|
logger_36/catalog/config/console_rich.py,sha256=lAa5Ev5BhXvmQzfIt1FNihMNUQJFlXaIzNanAMdgtd0,2861
|
14
14
|
logger_36/catalog/config/optional.py,sha256=HaN6mbx7gHBBppNvUw1ckhYTOrlYqb-b_r0mzPcHPjM,2398
|
15
|
-
logger_36/catalog/handler/console.py,sha256=
|
16
|
-
logger_36/catalog/handler/console_rich.py,sha256=
|
17
|
-
logger_36/catalog/handler/file.py,sha256=
|
18
|
-
logger_36/catalog/handler/generic.py,sha256=
|
15
|
+
logger_36/catalog/handler/console.py,sha256=s2DBcDK9To-wVS228RsDDPmOPxlIbVnQbZINfIK2TP0,4150
|
16
|
+
logger_36/catalog/handler/console_rich.py,sha256=pI0OE0c5I19-ycvOJjdG5vdtDXTZadNNRbKCJD-2oL4,8347
|
17
|
+
logger_36/catalog/handler/file.py,sha256=2qbsI3UHxqEm9WiCMkAm20hA2qXth2wKnakazVbwrBs,4613
|
18
|
+
logger_36/catalog/handler/generic.py,sha256=y-f6HY5xppoHYYnej0qOQT3BI0Gam_0W1_bIHCk5nn0,9212
|
19
19
|
logger_36/catalog/logger/chronos.py,sha256=ocY13f98EfknU7wZCv0FS9Xb7pTNaWCPSusXFIEvEd4,2437
|
20
20
|
logger_36/catalog/logger/gpu.py,sha256=lzrkqrMnXsszRB_TiHFqnNNI7JhNat8qL2OSlnHDe5c,3412
|
21
21
|
logger_36/catalog/logger/memory.py,sha256=CWhr2J4BqArJxzH6tS-ZThr-rYPAQGtuLn0pP7Iryfg,4685
|
@@ -31,6 +31,7 @@ logger_36/constant/issue.py,sha256=01l8itRPWGS5F6gXtsXUJgGR-4lS1Eu3_YeKC-khKLw,2
|
|
31
31
|
logger_36/constant/logger.py,sha256=2qRkteblpbHrq9x0aiw9MPquyXrSRd6_yMQnPEhFp2U,2468
|
32
32
|
logger_36/constant/memory.py,sha256=ZL1MwbdtNsrCrOwzEyfTsfOoOsRBTJtbbf3otHGnxXo,2343
|
33
33
|
logger_36/constant/message.py,sha256=Ys_CAyhENlT8Z3rr-AxO4hjdl1jLsKzVSPQ8wqLOCPQ,2838
|
34
|
+
logger_36/constant/path.py,sha256=fKJn2vGj012BU5DFRetDFus_tKMty2q_WL0J2KrXdCo,2731
|
34
35
|
logger_36/constant/record.py,sha256=9Q28lVH_s0og4v74delgwIPAJ9G28I5rBM-brXcoY80,2308
|
35
36
|
logger_36/constant/system.py,sha256=G2mzBTxRXoJMxb53TnmBaceMJC_q3WonoCG7y6nC_R8,2430
|
36
37
|
logger_36/instance/logger.py,sha256=oTw5svRzKRJKvGrrZUtutJIOjp5UISft3fl0Ze7DOBE,2241
|
@@ -42,11 +43,11 @@ logger_36/task/format/message.py,sha256=T2V2gUlUQqSojyRrz4I4uAHwNe6eBEsuAe6V-LTy
|
|
42
43
|
logger_36/task/format/rule.py,sha256=vkf-HivFb4VqV2GeOPVqMAp99krtziI-kXhox3UVnzw,2873
|
43
44
|
logger_36/task/measure/chronos.py,sha256=1kVhu6jZlNAtNWQQh8ZVuRwZIAC9gGz3_ul1tn0t4Yw,3055
|
44
45
|
logger_36/task/measure/memory.py,sha256=OjU5EYFH8SnzlCQKAoiXvauUlwQYOrH34jFXTVYF0jE,2517
|
45
|
-
logger_36/type/handler.py,sha256
|
46
|
+
logger_36/type/handler.py,sha256=-myl7uBMOzkwCs1u4ehuYlQa9F6909jmnL2v_eQN5ag,6819
|
46
47
|
logger_36/type/issue.py,sha256=2rGsFqaQJCbeml9xN08mN_nK79L8qscaS_0ws36Y0bI,3214
|
47
|
-
logger_36/type/logger.py,sha256=
|
48
|
+
logger_36/type/logger.py,sha256=nGNWJ5xcyA79E45qcZ-gZ3ZuY7j1aDg5NXk7VwbQVFU,22287
|
48
49
|
logger_36/type/loggers.py,sha256=znqxWBnfQxvkg3VUfbTUvt3S6Kq0DAzWWepxQDt9suI,2871
|
49
|
-
logger_36-2025.
|
50
|
-
logger_36-2025.
|
51
|
-
logger_36-2025.
|
52
|
-
logger_36-2025.
|
50
|
+
logger_36-2025.7.dist-info/METADATA,sha256=E708bS17nu3wkJZ7YTNfvezYSBUbfwXgUCwiiXwOkf0,6505
|
51
|
+
logger_36-2025.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
52
|
+
logger_36-2025.7.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
|
53
|
+
logger_36-2025.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|