omlish 0.0.0.dev424__py3-none-any.whl → 0.0.0.dev426__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.
- omlish/__about__.py +3 -3
- omlish/c3.py +4 -1
- omlish/configs/processing/flattening.py +1 -1
- omlish/configs/processing/merging.py +8 -6
- omlish/dataclasses/impl/concerns/doc.py +1 -1
- omlish/dataclasses/impl/configs.py +2 -1
- omlish/diag/_pycharm/runhack.py +1 -1
- omlish/diag/procfs.py +2 -2
- omlish/formats/json/stream/lexing.py +69 -14
- omlish/formats/json/stream/parsing.py +1 -1
- omlish/formats/json/stream/utils.py +3 -0
- omlish/formats/json5/streams.py +22 -0
- omlish/formats/logfmt.py +8 -2
- omlish/funcs/genmachine.py +1 -1
- omlish/http/sse.py +1 -1
- omlish/inject/impl/injector.py +1 -1
- omlish/inject/impl/multis.py +2 -2
- omlish/inject/impl/providers.py +0 -4
- omlish/inject/impl/proxy.py +0 -2
- omlish/inject/scopes.py +0 -4
- omlish/io/buffers.py +1 -1
- omlish/lang/__init__.py +26 -14
- omlish/lang/asyncs.py +12 -0
- omlish/lang/{attrs.py → attrstorage.py} +15 -15
- omlish/lang/cached/property.py +2 -2
- omlish/lang/classes/simple.py +26 -4
- omlish/lang/collections.py +1 -1
- omlish/lang/functions.py +0 -11
- omlish/lang/iterables.py +2 -2
- omlish/lang/lazyglobals.py +27 -5
- omlish/lang/maysync.py +2 -2
- omlish/lifecycles/contextmanagers.py +1 -2
- omlish/lifecycles/controller.py +1 -2
- omlish/lite/asyncs.py +20 -0
- omlish/lite/attrops.py +332 -0
- omlish/lite/cached.py +1 -1
- omlish/lite/maybes.py +2 -0
- omlish/lite/strings.py +0 -7
- omlish/lite/timing.py +6 -3
- omlish/logs/all.py +25 -32
- omlish/logs/base.py +248 -0
- omlish/logs/callers.py +21 -15
- omlish/logs/infos.py +105 -0
- omlish/logs/levels.py +64 -0
- omlish/logs/modules.py +10 -0
- omlish/logs/protocols.py +31 -0
- omlish/logs/standard.py +12 -11
- omlish/logs/std/adapters.py +41 -0
- omlish/logs/std/configs.py +29 -0
- omlish/logs/{filters.py → std/filters.py} +1 -1
- omlish/logs/{handlers.py → std/handlers.py} +1 -1
- omlish/logs/{json.py → std/json.py} +2 -2
- omlish/logs/{proxy.py → std/proxy.py} +3 -3
- omlish/logs/std/records.py +286 -0
- omlish/logs/times.py +86 -0
- omlish/logs/typed/bindings.py +24 -0
- omlish/logs/utils.py +60 -4
- omlish/logs/warnings.py +8 -0
- omlish/manifests/loading.py +1 -1
- omlish/os/atomics.py +1 -1
- omlish/os/journald.py +3 -3
- omlish/reflect/types.py +22 -0
- omlish/testing/pytest/plugins/skips.py +0 -4
- {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/METADATA +2 -2
- {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/RECORD +72 -114
- omlish/defs.py +0 -216
- omlish/dispatch/_dispatch2.py +0 -69
- omlish/dispatch/_dispatch3.py +0 -108
- omlish/dynamic.py +0 -219
- omlish/formats/json/Json.g4 +0 -77
- omlish/formats/json/_antlr/JsonLexer.py +0 -109
- omlish/formats/json/_antlr/JsonListener.py +0 -61
- omlish/formats/json/_antlr/JsonParser.py +0 -457
- omlish/formats/json/_antlr/JsonVisitor.py +0 -42
- omlish/io/trampoline.py +0 -289
- omlish/lite/logs.py +0 -4
- omlish/lite/reprs.py +0 -85
- omlish/logs/abc.py +0 -319
- omlish/logs/color.py +0 -27
- omlish/logs/configs.py +0 -29
- omlish/logs/protocol.py +0 -218
- omlish/logs/timing.py +0 -58
- omlish/specs/irc/__init__.py +0 -0
- omlish/specs/irc/messages/__init__.py +0 -0
- omlish/specs/irc/messages/base.py +0 -49
- omlish/specs/irc/messages/formats.py +0 -92
- omlish/specs/irc/messages/messages.py +0 -774
- omlish/specs/irc/messages/parsing.py +0 -98
- omlish/specs/irc/numerics/__init__.py +0 -0
- omlish/specs/irc/numerics/formats.py +0 -97
- omlish/specs/irc/numerics/numerics.py +0 -865
- omlish/specs/irc/numerics/types.py +0 -59
- omlish/specs/irc/protocol/LICENSE +0 -11
- omlish/specs/irc/protocol/__init__.py +0 -61
- omlish/specs/irc/protocol/consts.py +0 -6
- omlish/specs/irc/protocol/errors.py +0 -30
- omlish/specs/irc/protocol/message.py +0 -21
- omlish/specs/irc/protocol/nuh.py +0 -55
- omlish/specs/irc/protocol/parsing.py +0 -158
- omlish/specs/irc/protocol/rendering.py +0 -153
- omlish/specs/irc/protocol/tags.py +0 -102
- omlish/specs/irc/protocol/utils.py +0 -30
- omlish/specs/proto/Protobuf3.g4 +0 -396
- omlish/specs/proto/__init__.py +0 -0
- omlish/specs/proto/_antlr/Protobuf3Lexer.py +0 -340
- omlish/specs/proto/_antlr/Protobuf3Listener.py +0 -448
- omlish/specs/proto/_antlr/Protobuf3Parser.py +0 -3909
- omlish/specs/proto/_antlr/Protobuf3Visitor.py +0 -257
- omlish/specs/proto/_antlr/__init__.py +0 -0
- omlish/specs/proto/nodes.py +0 -54
- omlish/specs/proto/parsing.py +0 -97
- omlish/sql/parsing/Minisql.g4 +0 -292
- omlish/sql/parsing/__init__.py +0 -0
- omlish/sql/parsing/_antlr/MinisqlLexer.py +0 -322
- omlish/sql/parsing/_antlr/MinisqlListener.py +0 -511
- omlish/sql/parsing/_antlr/MinisqlParser.py +0 -3763
- omlish/sql/parsing/_antlr/MinisqlVisitor.py +0 -292
- omlish/sql/parsing/_antlr/__init__.py +0 -0
- omlish/sql/parsing/parsing.py +0 -119
- /omlish/{.manifests.json → .omlish-manifests.json} +0 -0
- /omlish/{formats/json/_antlr → logs/std}/__init__.py +0 -0
- /omlish/logs/{noisy.py → std/noisy.py} +0 -0
- {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/top_level.txt +0 -0
omlish/logs/all.py
CHANGED
@@ -5,64 +5,57 @@ from .. import lang as _lang
|
|
5
5
|
with _lang.auto_proxy_init(globals()):
|
6
6
|
##
|
7
7
|
|
8
|
-
from .
|
9
|
-
|
8
|
+
from .std.filters import ( # noqa
|
9
|
+
TidLoggingFilter,
|
10
10
|
)
|
11
11
|
|
12
|
-
from .
|
13
|
-
|
12
|
+
from .std.handlers import ( # noqa
|
13
|
+
ListLoggingHandler,
|
14
14
|
)
|
15
15
|
|
16
|
-
from .
|
17
|
-
|
16
|
+
from .std.json import ( # noqa
|
17
|
+
JsonLoggingFormatter,
|
18
18
|
)
|
19
19
|
|
20
|
-
from .
|
21
|
-
|
20
|
+
from .std.noisy import ( # noqa
|
21
|
+
silence_noisy_loggers,
|
22
22
|
)
|
23
23
|
|
24
|
-
from .
|
25
|
-
|
24
|
+
from .std.proxy import ( # noqa
|
25
|
+
ProxyLoggingFilterer,
|
26
|
+
ProxyLoggingHandler,
|
27
|
+
)
|
28
|
+
|
29
|
+
from .callers import ( # noqa
|
30
|
+
LoggingCaller,
|
26
31
|
)
|
27
32
|
|
28
33
|
from .levels import ( # noqa
|
29
34
|
LogLevel,
|
30
|
-
)
|
31
35
|
|
32
|
-
|
33
|
-
silence_noisy_loggers,
|
36
|
+
NamedLogLevel,
|
34
37
|
)
|
35
38
|
|
36
|
-
from .
|
37
|
-
|
38
|
-
Logging,
|
39
|
-
AsyncLogging,
|
40
|
-
|
41
|
-
AnyAbstractLogging,
|
42
|
-
AbstractLogging,
|
43
|
-
AbstractAsyncLogging,
|
44
|
-
|
45
|
-
AnyNopLogging,
|
46
|
-
NopLogging,
|
47
|
-
NopAsyncLogging,
|
48
|
-
|
49
|
-
StdlibLogging,
|
39
|
+
from .modules import ( # noqa
|
40
|
+
get_module_logger,
|
50
41
|
)
|
51
42
|
|
52
|
-
from .
|
53
|
-
|
54
|
-
ProxyLogHandler,
|
43
|
+
from .protocols import ( # noqa
|
44
|
+
LoggerLike,
|
55
45
|
)
|
56
46
|
|
57
47
|
from .standard import ( # noqa
|
58
48
|
STANDARD_LOG_FORMAT_PARTS,
|
59
|
-
|
49
|
+
StandardLoggingFormatter,
|
60
50
|
|
61
|
-
|
51
|
+
StandardConfiguredLoggingHandler,
|
62
52
|
|
63
53
|
configure_standard_logging,
|
64
54
|
)
|
65
55
|
|
66
56
|
from .utils import ( # noqa
|
57
|
+
LogTimingContext,
|
58
|
+
log_timing_context,
|
59
|
+
|
67
60
|
error_logging,
|
68
61
|
)
|
omlish/logs/base.py
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007 UP045 UP046
|
2
|
+
# @omlish-lite
|
3
|
+
import abc
|
4
|
+
import sys
|
5
|
+
import time
|
6
|
+
import types
|
7
|
+
import typing as ta
|
8
|
+
|
9
|
+
from ..lite.abstract import Abstract
|
10
|
+
from .callers import LoggingCaller
|
11
|
+
from .infos import LoggingAsyncioTaskInfo
|
12
|
+
from .infos import LoggingMultiprocessingInfo
|
13
|
+
from .infos import LoggingProcessInfo
|
14
|
+
from .infos import LoggingSourceFileInfo
|
15
|
+
from .infos import LoggingThreadInfo
|
16
|
+
from .levels import NamedLogLevel
|
17
|
+
from .times import LoggingTimeFields
|
18
|
+
|
19
|
+
|
20
|
+
T = ta.TypeVar('T')
|
21
|
+
|
22
|
+
|
23
|
+
LogLevel = int # ta.TypeAlias
|
24
|
+
|
25
|
+
LoggingExcInfoTuple = ta.Tuple[ta.Type[BaseException], BaseException, ta.Optional[types.TracebackType]] # ta.TypeAlias
|
26
|
+
LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
|
27
|
+
LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
|
28
|
+
|
29
|
+
|
30
|
+
##
|
31
|
+
|
32
|
+
|
33
|
+
@ta.final
|
34
|
+
class LoggingContext:
|
35
|
+
level: NamedLogLevel
|
36
|
+
|
37
|
+
time_ns: int
|
38
|
+
|
39
|
+
exc_info: ta.Optional[LoggingExcInfo] = None
|
40
|
+
exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = None
|
41
|
+
|
42
|
+
@ta.final
|
43
|
+
class NOT_SET: # noqa
|
44
|
+
def __new__(cls, *args, **kwargs): # noqa
|
45
|
+
raise TypeError
|
46
|
+
|
47
|
+
#
|
48
|
+
|
49
|
+
def __init__(
|
50
|
+
self,
|
51
|
+
level: LogLevel,
|
52
|
+
*,
|
53
|
+
time_ns: ta.Optional[int] = None,
|
54
|
+
|
55
|
+
exc_info: LoggingExcInfoArg = False,
|
56
|
+
|
57
|
+
caller: ta.Union[LoggingCaller, ta.Type[NOT_SET], None] = NOT_SET,
|
58
|
+
stack_offset: int = 0,
|
59
|
+
stack_info: bool = False,
|
60
|
+
) -> None:
|
61
|
+
self.level = level if level.__class__ is NamedLogLevel else NamedLogLevel(level) # type: ignore[assignment]
|
62
|
+
|
63
|
+
#
|
64
|
+
|
65
|
+
if time_ns is None:
|
66
|
+
time_ns = time.time_ns()
|
67
|
+
self.time_ns: int = time_ns
|
68
|
+
|
69
|
+
#
|
70
|
+
|
71
|
+
if exc_info is True:
|
72
|
+
sys_exc_info = sys.exc_info()
|
73
|
+
if sys_exc_info[0] is not None:
|
74
|
+
exc_info = sys_exc_info
|
75
|
+
else:
|
76
|
+
exc_info = None
|
77
|
+
elif exc_info is False:
|
78
|
+
exc_info = None
|
79
|
+
|
80
|
+
if exc_info is not None:
|
81
|
+
self.exc_info = exc_info
|
82
|
+
if isinstance(exc_info, BaseException):
|
83
|
+
self.exc_info_tuple = (type(exc_info), exc_info, exc_info.__traceback__)
|
84
|
+
else:
|
85
|
+
self.exc_info_tuple = exc_info
|
86
|
+
|
87
|
+
#
|
88
|
+
|
89
|
+
if caller is not LoggingContext.NOT_SET:
|
90
|
+
self._caller = caller # type: ignore[assignment]
|
91
|
+
else:
|
92
|
+
self._stack_offset = stack_offset
|
93
|
+
self._stack_info = stack_info
|
94
|
+
|
95
|
+
#
|
96
|
+
|
97
|
+
self.thread = LoggingThreadInfo.build()
|
98
|
+
self.process = LoggingProcessInfo.build()
|
99
|
+
self.multiprocessing = LoggingMultiprocessingInfo.build()
|
100
|
+
self.asyncio_task = LoggingAsyncioTaskInfo.build()
|
101
|
+
|
102
|
+
#
|
103
|
+
|
104
|
+
_times: LoggingTimeFields
|
105
|
+
|
106
|
+
@property
|
107
|
+
def times(self) -> LoggingTimeFields:
|
108
|
+
try:
|
109
|
+
return self._times
|
110
|
+
except AttributeError:
|
111
|
+
pass
|
112
|
+
|
113
|
+
times = self._times = LoggingTimeFields.build(self.time_ns)
|
114
|
+
return times
|
115
|
+
|
116
|
+
#
|
117
|
+
|
118
|
+
def inc_stack_offset(self, ofs: int = 1) -> 'LoggingContext':
|
119
|
+
if hasattr(self, '_stack_offset'):
|
120
|
+
self._stack_offset += ofs
|
121
|
+
return self
|
122
|
+
|
123
|
+
_caller: ta.Optional[LoggingCaller]
|
124
|
+
|
125
|
+
def build_caller(self) -> ta.Optional[LoggingCaller]:
|
126
|
+
"""Must be cooperatively called only from the exact configured _stack_offset."""
|
127
|
+
|
128
|
+
try:
|
129
|
+
return self._caller
|
130
|
+
except AttributeError:
|
131
|
+
pass
|
132
|
+
|
133
|
+
caller = self._caller = LoggingCaller.find(
|
134
|
+
self._stack_offset + 1,
|
135
|
+
stack_info=self._stack_info,
|
136
|
+
)
|
137
|
+
return caller
|
138
|
+
|
139
|
+
def caller(self) -> ta.Optional[LoggingCaller]:
|
140
|
+
try:
|
141
|
+
return self._caller
|
142
|
+
except AttributeError:
|
143
|
+
return None
|
144
|
+
|
145
|
+
_source_file: ta.Optional[LoggingSourceFileInfo]
|
146
|
+
|
147
|
+
def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
|
148
|
+
try:
|
149
|
+
return self._source_file
|
150
|
+
except AttributeError:
|
151
|
+
pass
|
152
|
+
|
153
|
+
if (caller := self.caller()) is None:
|
154
|
+
return None
|
155
|
+
|
156
|
+
src_file = self._source_file = LoggingSourceFileInfo.build(caller.file_path)
|
157
|
+
return src_file
|
158
|
+
|
159
|
+
|
160
|
+
##
|
161
|
+
|
162
|
+
|
163
|
+
class AnyLogger(Abstract, ta.Generic[T]):
|
164
|
+
def is_enabled_for(self, level: LogLevel) -> bool:
|
165
|
+
return self.get_effective_level() >= level
|
166
|
+
|
167
|
+
@abc.abstractmethod
|
168
|
+
def get_effective_level(self) -> LogLevel:
|
169
|
+
raise NotImplementedError
|
170
|
+
|
171
|
+
#
|
172
|
+
|
173
|
+
@ta.final
|
174
|
+
def isEnabledFor(self, level: LogLevel) -> bool: # noqa
|
175
|
+
return self.is_enabled_for(level)
|
176
|
+
|
177
|
+
@ta.final
|
178
|
+
def getEffectiveLevel(self) -> LogLevel: # noqa
|
179
|
+
return self.get_effective_level()
|
180
|
+
|
181
|
+
#
|
182
|
+
|
183
|
+
@ta.final
|
184
|
+
def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
185
|
+
return self._log(LoggingContext(NamedLogLevel.DEBUG, stack_offset=1), msg, *args, **kwargs)
|
186
|
+
|
187
|
+
@ta.final
|
188
|
+
def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
189
|
+
return self._log(LoggingContext(NamedLogLevel.INFO, stack_offset=1), msg, *args, **kwargs)
|
190
|
+
|
191
|
+
@ta.final
|
192
|
+
def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
193
|
+
return self._log(LoggingContext(NamedLogLevel.WARNING, stack_offset=1), msg, *args, **kwargs)
|
194
|
+
|
195
|
+
@ta.final
|
196
|
+
def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
197
|
+
return self._log(LoggingContext(NamedLogLevel.ERROR, stack_offset=1), msg, *args, **kwargs)
|
198
|
+
|
199
|
+
@ta.final
|
200
|
+
def exception(self, msg: str, *args: ta.Any, exc_info: LoggingExcInfoArg = True, **kwargs: ta.Any) -> T:
|
201
|
+
return self._log(LoggingContext(NamedLogLevel.ERROR, exc_info=exc_info, stack_offset=1), msg, *args, **kwargs)
|
202
|
+
|
203
|
+
@ta.final
|
204
|
+
def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
205
|
+
return self._log(LoggingContext(NamedLogLevel.CRITICAL, stack_offset=1), msg, *args, **kwargs)
|
206
|
+
|
207
|
+
@ta.final
|
208
|
+
def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
209
|
+
return self._log(LoggingContext(level, stack_offset=1), msg, *args, **kwargs)
|
210
|
+
|
211
|
+
#
|
212
|
+
|
213
|
+
@abc.abstractmethod
|
214
|
+
def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
|
215
|
+
raise NotImplementedError
|
216
|
+
|
217
|
+
|
218
|
+
class Logger(AnyLogger[None], Abstract):
|
219
|
+
@abc.abstractmethod
|
220
|
+
def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
221
|
+
raise NotImplementedError
|
222
|
+
|
223
|
+
|
224
|
+
class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
|
225
|
+
@abc.abstractmethod
|
226
|
+
def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> ta.Awaitable[None]:
|
227
|
+
raise NotImplementedError
|
228
|
+
|
229
|
+
|
230
|
+
##
|
231
|
+
|
232
|
+
|
233
|
+
class AnyNopLogger(AnyLogger[T], Abstract):
|
234
|
+
@ta.final
|
235
|
+
def get_effective_level(self) -> LogLevel:
|
236
|
+
return 999
|
237
|
+
|
238
|
+
|
239
|
+
@ta.final
|
240
|
+
class NopLogger(AnyNopLogger[None], Logger):
|
241
|
+
def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
242
|
+
pass
|
243
|
+
|
244
|
+
|
245
|
+
@ta.final
|
246
|
+
class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
|
247
|
+
async def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
248
|
+
pass
|
omlish/logs/callers.py
CHANGED
@@ -12,32 +12,36 @@ import typing as ta
|
|
12
12
|
|
13
13
|
|
14
14
|
class LoggingCaller(ta.NamedTuple):
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
file_path: str
|
16
|
+
line_no: int
|
17
|
+
name: str
|
18
|
+
stack_info: ta.Optional[str]
|
19
19
|
|
20
20
|
@classmethod
|
21
21
|
def is_internal_frame(cls, frame: types.FrameType) -> bool:
|
22
|
-
|
22
|
+
file_path = os.path.normcase(frame.f_code.co_filename)
|
23
23
|
|
24
|
+
# Yes, really.
|
25
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L204
|
24
26
|
# https://github.com/python/cpython/commit/5ca6d7469be53960843df39bb900e9c3359f127f
|
25
|
-
if 'importlib' in
|
27
|
+
if 'importlib' in file_path and '_bootstrap' in file_path:
|
26
28
|
return True
|
27
29
|
|
28
30
|
return False
|
29
31
|
|
30
32
|
@classmethod
|
31
|
-
def find_frame(cls, ofs: int = 0) -> types.FrameType:
|
33
|
+
def find_frame(cls, ofs: int = 0) -> ta.Optional[types.FrameType]:
|
32
34
|
f: ta.Optional[types.FrameType] = sys._getframe(2 + ofs) # noqa
|
33
35
|
|
34
|
-
while f is not None
|
35
|
-
|
36
|
+
while f is not None:
|
37
|
+
# NOTE: We don't check __file__ like stdlib since we may be running amalgamated - we rely on careful, manual
|
38
|
+
# stack_offset management.
|
39
|
+
if hasattr(f, 'f_code'):
|
36
40
|
return f
|
37
41
|
|
38
42
|
f = f.f_back
|
39
43
|
|
40
|
-
|
44
|
+
return None
|
41
45
|
|
42
46
|
@classmethod
|
43
47
|
def find(
|
@@ -45,14 +49,16 @@ class LoggingCaller(ta.NamedTuple):
|
|
45
49
|
ofs: int = 0,
|
46
50
|
*,
|
47
51
|
stack_info: bool = False,
|
48
|
-
) -> 'LoggingCaller':
|
49
|
-
f
|
50
|
-
|
52
|
+
) -> ta.Optional['LoggingCaller']:
|
53
|
+
if (f := cls.find_frame(ofs + 1)) is None:
|
54
|
+
return None
|
51
55
|
|
56
|
+
# https://github.com/python/cpython/blob/08e9794517063c8cd92c48714071b1d3c60b71bd/Lib/logging/__init__.py#L1616-L1623 # noqa
|
52
57
|
sinfo = None
|
53
58
|
if stack_info:
|
54
59
|
sio = io.StringIO()
|
55
|
-
|
60
|
+
# In stdlib, but done elsewhere here:
|
61
|
+
# sio.write('Stack (most recent call last):\n')
|
56
62
|
traceback.print_stack(f, file=sio)
|
57
63
|
sinfo = sio.getvalue()
|
58
64
|
sio.close()
|
@@ -61,7 +67,7 @@ class LoggingCaller(ta.NamedTuple):
|
|
61
67
|
|
62
68
|
return cls(
|
63
69
|
f.f_code.co_filename,
|
64
|
-
f.f_lineno,
|
70
|
+
f.f_lineno or 0,
|
65
71
|
f.f_code.co_name,
|
66
72
|
sinfo,
|
67
73
|
)
|
omlish/logs/infos.py
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# ruff: noqa: UP045
|
2
|
+
# @omlish-lite
|
3
|
+
import os.path
|
4
|
+
import sys
|
5
|
+
import threading
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
@ta.final
|
13
|
+
class LoggingSourceFileInfo(ta.NamedTuple):
|
14
|
+
file_name: str
|
15
|
+
module: str
|
16
|
+
|
17
|
+
@classmethod
|
18
|
+
def build(cls, file_path: ta.Optional[str]) -> ta.Optional['LoggingSourceFileInfo']:
|
19
|
+
if file_path is None:
|
20
|
+
return None
|
21
|
+
|
22
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L331-L336 # noqa
|
23
|
+
try:
|
24
|
+
file_name = os.path.basename(file_path)
|
25
|
+
module = os.path.splitext(file_name)[0]
|
26
|
+
except (TypeError, ValueError, AttributeError):
|
27
|
+
return None
|
28
|
+
|
29
|
+
return cls(
|
30
|
+
file_name,
|
31
|
+
module,
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
|
37
|
+
|
38
|
+
@ta.final
|
39
|
+
class LoggingThreadInfo(ta.NamedTuple):
|
40
|
+
ident: int
|
41
|
+
native_id: ta.Optional[int]
|
42
|
+
name: str
|
43
|
+
|
44
|
+
@classmethod
|
45
|
+
def build(cls) -> 'LoggingThreadInfo':
|
46
|
+
return cls(
|
47
|
+
threading.get_ident(),
|
48
|
+
threading.get_native_id() if hasattr(threading, 'get_native_id') else None,
|
49
|
+
threading.current_thread().name,
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
##
|
54
|
+
|
55
|
+
|
56
|
+
@ta.final
|
57
|
+
class LoggingProcessInfo(ta.NamedTuple):
|
58
|
+
pid: int
|
59
|
+
|
60
|
+
@classmethod
|
61
|
+
def build(cls) -> 'LoggingProcessInfo':
|
62
|
+
return cls(
|
63
|
+
os.getpid(),
|
64
|
+
)
|
65
|
+
|
66
|
+
|
67
|
+
##
|
68
|
+
|
69
|
+
|
70
|
+
@ta.final
|
71
|
+
class LoggingMultiprocessingInfo(ta.NamedTuple):
|
72
|
+
process_name: str
|
73
|
+
|
74
|
+
@classmethod
|
75
|
+
def build(cls) -> ta.Optional['LoggingMultiprocessingInfo']:
|
76
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L355-L364 # noqa
|
77
|
+
if (mp := sys.modules.get('multiprocessing')) is None:
|
78
|
+
return None
|
79
|
+
|
80
|
+
return cls(
|
81
|
+
mp.current_process().name,
|
82
|
+
)
|
83
|
+
|
84
|
+
|
85
|
+
##
|
86
|
+
|
87
|
+
|
88
|
+
@ta.final
|
89
|
+
class LoggingAsyncioTaskInfo(ta.NamedTuple):
|
90
|
+
name: str
|
91
|
+
|
92
|
+
@classmethod
|
93
|
+
def build(cls) -> ta.Optional['LoggingAsyncioTaskInfo']:
|
94
|
+
# https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L372-L377 # noqa
|
95
|
+
if (asyncio := sys.modules.get('asyncio')) is None:
|
96
|
+
return None
|
97
|
+
|
98
|
+
try:
|
99
|
+
task = asyncio.current_task()
|
100
|
+
except Exception: # noqa
|
101
|
+
return None
|
102
|
+
|
103
|
+
return cls(
|
104
|
+
task.get_name(), # Always non-None
|
105
|
+
)
|
omlish/logs/levels.py
CHANGED
@@ -1,7 +1,71 @@
|
|
1
|
+
# ruff: noqa: UP006 UP045
|
1
2
|
# @omlish-lite
|
3
|
+
import logging
|
4
|
+
import typing as ta
|
2
5
|
|
3
6
|
|
4
7
|
LogLevel = int # ta.TypeAlias
|
5
8
|
|
6
9
|
|
7
10
|
##
|
11
|
+
|
12
|
+
|
13
|
+
@ta.final
|
14
|
+
class NamedLogLevel(int):
|
15
|
+
# logging.getLevelNamesMapping (or, as that is unavailable <3.11, logging._nameToLevel) includes the deprecated
|
16
|
+
# aliases.
|
17
|
+
_NAMES_BY_INT: ta.ClassVar[ta.Mapping[LogLevel, str]] = dict(sorted(logging._levelToName.items(), key=lambda t: -t[0])) # noqa
|
18
|
+
|
19
|
+
_INTS_BY_NAME: ta.ClassVar[ta.Mapping[str, LogLevel]] = {v: k for k, v in _NAMES_BY_INT.items()}
|
20
|
+
|
21
|
+
_NAME_INT_PAIRS: ta.ClassVar[ta.Sequence[ta.Tuple[str, LogLevel]]] = list(_INTS_BY_NAME.items())
|
22
|
+
|
23
|
+
#
|
24
|
+
|
25
|
+
@property
|
26
|
+
def exact_name(self) -> ta.Optional[str]:
|
27
|
+
return self._NAMES_BY_INT.get(self)
|
28
|
+
|
29
|
+
_effective_name: ta.Optional[str]
|
30
|
+
|
31
|
+
@property
|
32
|
+
def effective_name(self) -> ta.Optional[str]:
|
33
|
+
try:
|
34
|
+
return self._effective_name
|
35
|
+
except AttributeError:
|
36
|
+
pass
|
37
|
+
|
38
|
+
if (n := self.exact_name) is None:
|
39
|
+
for n, i in self._NAME_INT_PAIRS: # noqa
|
40
|
+
if self >= i:
|
41
|
+
break
|
42
|
+
else:
|
43
|
+
n = None
|
44
|
+
|
45
|
+
self._effective_name = n
|
46
|
+
return n
|
47
|
+
|
48
|
+
#
|
49
|
+
|
50
|
+
def __repr__(self) -> str:
|
51
|
+
return f'{self.__class__.__name__}({int(self)})'
|
52
|
+
|
53
|
+
def __str__(self) -> str:
|
54
|
+
return self.exact_name or f'{self.effective_name or "INVALID"}:{int(self)}'
|
55
|
+
|
56
|
+
#
|
57
|
+
|
58
|
+
CRITICAL: ta.ClassVar['NamedLogLevel']
|
59
|
+
ERROR: ta.ClassVar['NamedLogLevel']
|
60
|
+
WARNING: ta.ClassVar['NamedLogLevel']
|
61
|
+
INFO: ta.ClassVar['NamedLogLevel']
|
62
|
+
DEBUG: ta.ClassVar['NamedLogLevel']
|
63
|
+
NOTSET: ta.ClassVar['NamedLogLevel']
|
64
|
+
|
65
|
+
|
66
|
+
NamedLogLevel.CRITICAL = NamedLogLevel(logging.CRITICAL)
|
67
|
+
NamedLogLevel.ERROR = NamedLogLevel(logging.ERROR)
|
68
|
+
NamedLogLevel.WARNING = NamedLogLevel(logging.WARNING)
|
69
|
+
NamedLogLevel.INFO = NamedLogLevel(logging.INFO)
|
70
|
+
NamedLogLevel.DEBUG = NamedLogLevel(logging.DEBUG)
|
71
|
+
NamedLogLevel.NOTSET = NamedLogLevel(logging.NOTSET)
|
omlish/logs/modules.py
ADDED
omlish/logs/protocols.py
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# @omlish-lite
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .levels import LogLevel
|
5
|
+
|
6
|
+
|
7
|
+
##
|
8
|
+
|
9
|
+
|
10
|
+
class LoggerLike(ta.Protocol):
|
11
|
+
"""Satisfied by both our Logger and stdlib logging.Logger."""
|
12
|
+
|
13
|
+
def isEnabledFor(self, level: LogLevel) -> bool: ... # noqa
|
14
|
+
|
15
|
+
def getEffectiveLevel(self) -> LogLevel: ... # noqa
|
16
|
+
|
17
|
+
#
|
18
|
+
|
19
|
+
def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
20
|
+
|
21
|
+
def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
22
|
+
|
23
|
+
def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
24
|
+
|
25
|
+
def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
26
|
+
|
27
|
+
def exception(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
28
|
+
|
29
|
+
def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|
30
|
+
|
31
|
+
def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
|