omlish 0.0.0.dev425__py3-none-any.whl → 0.0.0.dev427__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.
Files changed (55) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/c3.py +4 -1
  3. omlish/configs/processing/flattening.py +1 -1
  4. omlish/configs/processing/merging.py +8 -6
  5. omlish/dataclasses/impl/concerns/doc.py +1 -1
  6. omlish/diag/_pycharm/runhack.py +1 -1
  7. omlish/diag/procfs.py +2 -2
  8. omlish/formats/json/stream/lexing.py +63 -16
  9. omlish/formats/json/stream/parsing.py +1 -1
  10. omlish/formats/json/stream/utils.py +2 -2
  11. omlish/formats/logfmt.py +8 -2
  12. omlish/funcs/genmachine.py +1 -1
  13. omlish/http/sse.py +1 -1
  14. omlish/inject/impl/injector.py +1 -1
  15. omlish/inject/impl/multis.py +2 -2
  16. omlish/inject/impl/providers.py +0 -4
  17. omlish/inject/impl/proxy.py +0 -2
  18. omlish/inject/scopes.py +0 -4
  19. omlish/io/buffers.py +1 -1
  20. omlish/lang/__init__.py +23 -13
  21. omlish/lang/{attrs.py → attrstorage.py} +15 -15
  22. omlish/lang/cached/property.py +2 -2
  23. omlish/lang/classes/simple.py +26 -4
  24. omlish/lang/collections.py +1 -1
  25. omlish/lang/iterables.py +2 -2
  26. omlish/lifecycles/contextmanagers.py +1 -1
  27. omlish/lifecycles/controller.py +1 -1
  28. omlish/lite/asyncs.py +5 -0
  29. omlish/lite/attrops.py +332 -0
  30. omlish/lite/cached.py +1 -1
  31. omlish/lite/maybes.py +2 -0
  32. omlish/lite/strings.py +0 -7
  33. omlish/lite/timing.py +4 -1
  34. omlish/logs/all.py +4 -0
  35. omlish/logs/base.py +138 -152
  36. omlish/logs/callers.py +3 -3
  37. omlish/logs/contexts.py +250 -0
  38. omlish/logs/infos.py +16 -5
  39. omlish/logs/modules.py +10 -0
  40. omlish/logs/protocols.py +7 -7
  41. omlish/logs/std/adapters.py +9 -5
  42. omlish/logs/std/records.py +26 -11
  43. omlish/logs/times.py +4 -6
  44. omlish/manifests/loading.py +6 -0
  45. omlish/os/atomics.py +1 -1
  46. omlish/reflect/types.py +22 -0
  47. omlish/sockets/server/server.py +1 -1
  48. {omlish-0.0.0.dev425.dist-info → omlish-0.0.0.dev427.dist-info}/METADATA +2 -2
  49. {omlish-0.0.0.dev425.dist-info → omlish-0.0.0.dev427.dist-info}/RECORD +53 -52
  50. omlish/lite/logs.py +0 -4
  51. omlish/lite/reprs.py +0 -85
  52. {omlish-0.0.0.dev425.dist-info → omlish-0.0.0.dev427.dist-info}/WHEEL +0 -0
  53. {omlish-0.0.0.dev425.dist-info → omlish-0.0.0.dev427.dist-info}/entry_points.txt +0 -0
  54. {omlish-0.0.0.dev425.dist-info → omlish-0.0.0.dev427.dist-info}/licenses/LICENSE +0 -0
  55. {omlish-0.0.0.dev425.dist-info → omlish-0.0.0.dev427.dist-info}/top_level.txt +0 -0
omlish/logs/base.py CHANGED
@@ -1,229 +1,215 @@
1
1
  # ruff: noqa: UP006 UP007 UP045 UP046
2
2
  # @omlish-lite
3
3
  import abc
4
- import sys
5
- import time
6
- import types
7
4
  import typing as ta
8
5
 
9
6
  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
7
+ from .contexts import CaptureLoggingContext
8
+ from .contexts import CaptureLoggingContextImpl
9
+ from .contexts import LoggingExcInfoArg
10
+ from .levels import LogLevel
16
11
  from .levels import NamedLogLevel
17
- from .times import LoggingTimeFields
18
12
 
19
13
 
20
14
  T = ta.TypeVar('T')
21
15
 
22
16
 
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
17
+ LoggingMsgFn = ta.Callable[[], ta.Union[str, tuple]] # ta.TypeAlias
28
18
 
29
19
 
30
20
  ##
31
21
 
32
22
 
33
- @ta.final
34
- class LoggingContext:
35
- level: NamedLogLevel
23
+ class AnyLogger(Abstract, ta.Generic[T]):
24
+ def is_enabled_for(self, level: LogLevel) -> bool:
25
+ return self.get_effective_level() >= level
36
26
 
37
- time_ns: int
27
+ @abc.abstractmethod
28
+ def get_effective_level(self) -> LogLevel:
29
+ raise NotImplementedError
38
30
 
39
- exc_info: ta.Optional[LoggingExcInfo] = None
40
- exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = None
31
+ #
41
32
 
42
33
  @ta.final
43
- class NOT_SET: # noqa
44
- def __new__(cls, *args, **kwargs): # noqa
45
- raise TypeError
46
-
47
- #
34
+ def isEnabledFor(self, level: LogLevel) -> bool: # noqa
35
+ return self.is_enabled_for(level)
48
36
 
49
- def __init__(
50
- self,
51
- level: LogLevel,
52
- *,
53
- time_ns: ta.Optional[int] = None,
37
+ @ta.final
38
+ def getEffectiveLevel(self) -> LogLevel: # noqa
39
+ return self.get_effective_level()
54
40
 
55
- exc_info: LoggingExcInfoArg = False,
41
+ ##
56
42
 
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]
43
+ @ta.overload
44
+ def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
45
+ ...
62
46
 
63
- #
47
+ @ta.overload
48
+ def log(self, level: LogLevel, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
49
+ ...
64
50
 
65
- if time_ns is None:
66
- time_ns = time.time_ns()
67
- self.time_ns: int = time_ns
51
+ @ta.overload
52
+ def log(self, level: LogLevel, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
53
+ ...
68
54
 
69
- #
55
+ @ta.final
56
+ def log(self, level: LogLevel, *args, **kwargs):
57
+ return self._log(CaptureLoggingContextImpl(level, stack_offset=1), *args, **kwargs)
70
58
 
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
59
+ #
86
60
 
87
- #
61
+ @ta.overload
62
+ def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
63
+ ...
88
64
 
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
65
+ @ta.overload
66
+ def debug(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
67
+ ...
94
68
 
95
- #
69
+ @ta.overload
70
+ def debug(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
71
+ ...
96
72
 
97
- self.thread = LoggingThreadInfo.build()
98
- self.process = LoggingProcessInfo.build()
99
- self.multiprocessing = LoggingMultiprocessingInfo.build()
100
- self.asyncio_task = LoggingAsyncioTaskInfo.build()
73
+ @ta.final
74
+ def debug(self, *args, **kwargs):
75
+ return self._log(CaptureLoggingContextImpl(NamedLogLevel.DEBUG, stack_offset=1), *args, **kwargs)
101
76
 
102
77
  #
103
78
 
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
- #
79
+ @ta.overload
80
+ def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
81
+ ...
117
82
 
118
- def inc_stack_offset(self, ofs: int = 1) -> 'LoggingContext':
119
- if hasattr(self, '_stack_offset'):
120
- self._stack_offset += ofs
121
- return self
83
+ @ta.overload
84
+ def info(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
85
+ ...
122
86
 
123
- _caller: ta.Optional[LoggingCaller]
87
+ @ta.overload
88
+ def info(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
89
+ ...
124
90
 
125
- def build_caller(self) -> ta.Optional[LoggingCaller]:
126
- """Must be cooperatively called only from the exact configured _stack_offset."""
91
+ @ta.final
92
+ def info(self, *args, **kwargs):
93
+ return self._log(CaptureLoggingContextImpl(NamedLogLevel.INFO, stack_offset=1), *args, **kwargs)
127
94
 
128
- try:
129
- return self._caller
130
- except AttributeError:
131
- pass
95
+ #
132
96
 
133
- caller = self._caller = LoggingCaller.find(
134
- self._stack_offset + 1,
135
- stack_info=self._stack_info,
136
- )
137
- return caller
97
+ @ta.overload
98
+ def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
99
+ ...
138
100
 
139
- def caller(self) -> ta.Optional[LoggingCaller]:
140
- try:
141
- return self._caller
142
- except AttributeError:
143
- return None
101
+ @ta.overload
102
+ def warning(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
103
+ ...
144
104
 
145
- _source_file: ta.Optional[LoggingSourceFileInfo]
105
+ @ta.overload
106
+ def warning(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
107
+ ...
146
108
 
147
- def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
148
- try:
149
- return self._source_file
150
- except AttributeError:
151
- pass
109
+ @ta.final
110
+ def warning(self, *args, **kwargs):
111
+ return self._log(CaptureLoggingContextImpl(NamedLogLevel.WARNING, stack_offset=1), *args, **kwargs)
152
112
 
153
- if (caller := self.caller()) is None:
154
- return None
113
+ #
155
114
 
156
- src_file = self._source_file = LoggingSourceFileInfo.build(caller.file_path)
157
- return src_file
115
+ @ta.overload
116
+ def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
117
+ ...
158
118
 
119
+ @ta.overload
120
+ def error(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
121
+ ...
159
122
 
160
- ##
123
+ @ta.overload
124
+ def error(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
125
+ ...
161
126
 
127
+ @ta.final
128
+ def error(self, *args, **kwargs):
129
+ return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, stack_offset=1), *args, **kwargs)
162
130
 
163
- class AnyLogger(Abstract, ta.Generic[T]):
164
- def is_enabled_for(self, level: LogLevel) -> bool:
165
- return self.get_effective_level() >= level
131
+ #
166
132
 
167
- @abc.abstractmethod
168
- def get_effective_level(self) -> LogLevel:
169
- raise NotImplementedError
133
+ @ta.overload
134
+ def exception(self, msg: str, *args: ta.Any, exc_info: LoggingExcInfoArg = True, **kwargs: ta.Any) -> T:
135
+ ...
170
136
 
171
- #
137
+ @ta.overload
138
+ def exception(self, msg: ta.Tuple[ta.Any, ...], *, exc_info: LoggingExcInfoArg = True, **kwargs: ta.Any) -> T:
139
+ ...
172
140
 
173
- @ta.final
174
- def isEnabledFor(self, level: LogLevel) -> bool: # noqa
175
- return self.is_enabled_for(level)
141
+ @ta.overload
142
+ def exception(self, msg_fn: LoggingMsgFn, *, exc_info: LoggingExcInfoArg = True, **kwargs: ta.Any) -> T:
143
+ ...
176
144
 
177
145
  @ta.final
178
- def getEffectiveLevel(self) -> LogLevel: # noqa
179
- return self.get_effective_level()
146
+ def exception(self, *args, exc_info: LoggingExcInfoArg = True, **kwargs):
147
+ return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, exc_info=exc_info, stack_offset=1), *args, **kwargs) # noqa
180
148
 
181
149
  #
182
150
 
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)
151
+ @ta.overload
152
+ def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
153
+ ...
186
154
 
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)
155
+ @ta.overload
156
+ def critical(self, msg: ta.Tuple[ta.Any, ...], **kwargs: ta.Any) -> T:
157
+ ...
190
158
 
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)
159
+ @ta.overload
160
+ def critical(self, msg_fn: LoggingMsgFn, **kwargs: ta.Any) -> T:
161
+ ...
194
162
 
195
163
  @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)
164
+ def critical(self, *args, **kwargs):
165
+ return self._log(CaptureLoggingContextImpl(NamedLogLevel.CRITICAL, stack_offset=1), *args, **kwargs)
166
+
167
+ ##
168
+
169
+ @classmethod
170
+ def _prepare_msg_args(cls, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> ta.Tuple[str, tuple]:
171
+ if callable(msg):
172
+ if args:
173
+ raise TypeError(f'Must not provide both a message function and args: {msg=} {args=}')
174
+ x = msg()
175
+ if isinstance(x, str):
176
+ return x, ()
177
+ elif isinstance(x, tuple):
178
+ if x:
179
+ return x[0], x[1:]
180
+ else:
181
+ return '', ()
182
+ else:
183
+ raise TypeError(x)
202
184
 
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)
185
+ elif isinstance(msg, tuple):
186
+ if args:
187
+ raise TypeError(f'Must not provide both a tuple message and args: {msg=} {args=}')
188
+ if msg:
189
+ return msg[0], msg[1:]
190
+ else:
191
+ return '', ()
206
192
 
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)
193
+ elif isinstance(msg, str):
194
+ return msg, args
210
195
 
211
- #
196
+ else:
197
+ raise TypeError(msg)
212
198
 
213
199
  @abc.abstractmethod
214
- def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
200
+ def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
215
201
  raise NotImplementedError
216
202
 
217
203
 
218
204
  class Logger(AnyLogger[None], Abstract):
219
205
  @abc.abstractmethod
220
- def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
206
+ def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
221
207
  raise NotImplementedError
222
208
 
223
209
 
224
210
  class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
225
211
  @abc.abstractmethod
226
- def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> ta.Awaitable[None]:
212
+ def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> ta.Awaitable[None]: # noqa
227
213
  raise NotImplementedError
228
214
 
229
215
 
@@ -238,11 +224,11 @@ class AnyNopLogger(AnyLogger[T], Abstract):
238
224
 
239
225
  @ta.final
240
226
  class NopLogger(AnyNopLogger[None], Logger):
241
- def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
227
+ def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
242
228
  pass
243
229
 
244
230
 
245
231
  @ta.final
246
232
  class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
247
- async def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
233
+ async def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
248
234
  pass
omlish/logs/callers.py CHANGED
@@ -7,11 +7,13 @@ import traceback
7
7
  import types
8
8
  import typing as ta
9
9
 
10
+ from .infos import LoggingContextInfo
11
+
10
12
 
11
13
  ##
12
14
 
13
15
 
14
- class LoggingCaller(ta.NamedTuple):
16
+ class LoggingCaller(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
15
17
  file_path: str
16
18
  line_no: int
17
19
  name: str
@@ -57,8 +59,6 @@ class LoggingCaller(ta.NamedTuple):
57
59
  sinfo = None
58
60
  if stack_info:
59
61
  sio = io.StringIO()
60
- # In stdlib, but done elsewhere here:
61
- # sio.write('Stack (most recent call last):\n')
62
62
  traceback.print_stack(f, file=sio)
63
63
  sinfo = sio.getvalue()
64
64
  sio.close()
@@ -0,0 +1,250 @@
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 LogLevel
17
+ from .levels import NamedLogLevel
18
+ from .times import LoggingTimeFields
19
+
20
+
21
+ LoggingExcInfoTuple = ta.Tuple[ta.Type[BaseException], BaseException, ta.Optional[types.TracebackType]] # ta.TypeAlias
22
+ LoggingExcInfo = ta.Union[BaseException, LoggingExcInfoTuple] # ta.TypeAlias
23
+ LoggingExcInfoArg = ta.Union[LoggingExcInfo, bool, None] # ta.TypeAlias
24
+
25
+
26
+ ##
27
+
28
+
29
+ class LoggingContext(Abstract):
30
+ @property
31
+ @abc.abstractmethod
32
+ def level(self) -> NamedLogLevel:
33
+ raise NotImplementedError
34
+
35
+ #
36
+
37
+ @property
38
+ @abc.abstractmethod
39
+ def time_ns(self) -> int:
40
+ raise NotImplementedError
41
+
42
+ @property
43
+ @abc.abstractmethod
44
+ def times(self) -> LoggingTimeFields:
45
+ raise NotImplementedError
46
+
47
+ #
48
+
49
+ @property
50
+ @abc.abstractmethod
51
+ def exc_info(self) -> ta.Optional[LoggingExcInfo]:
52
+ raise NotImplementedError
53
+
54
+ @property
55
+ @abc.abstractmethod
56
+ def exc_info_tuple(self) -> ta.Optional[LoggingExcInfoTuple]:
57
+ raise NotImplementedError
58
+
59
+ #
60
+
61
+ @abc.abstractmethod
62
+ def caller(self) -> ta.Optional[LoggingCaller]:
63
+ raise NotImplementedError
64
+
65
+ @abc.abstractmethod
66
+ def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
67
+ raise NotImplementedError
68
+
69
+ #
70
+
71
+ @abc.abstractmethod
72
+ def thread(self) -> ta.Optional[LoggingThreadInfo]:
73
+ raise NotImplementedError
74
+
75
+ @abc.abstractmethod
76
+ def process(self) -> ta.Optional[LoggingProcessInfo]:
77
+ raise NotImplementedError
78
+
79
+ @abc.abstractmethod
80
+ def multiprocessing(self) -> ta.Optional[LoggingMultiprocessingInfo]:
81
+ raise NotImplementedError
82
+
83
+ @abc.abstractmethod
84
+ def asyncio_task(self) -> ta.Optional[LoggingAsyncioTaskInfo]:
85
+ raise NotImplementedError
86
+
87
+
88
+ ##
89
+
90
+
91
+ class CaptureLoggingContext(LoggingContext, Abstract):
92
+ @abc.abstractmethod
93
+ def capture(self) -> None:
94
+ """Must be cooperatively called only from the expected locations."""
95
+
96
+ raise NotImplementedError
97
+
98
+
99
+ @ta.final
100
+ class CaptureLoggingContextImpl(CaptureLoggingContext):
101
+ @ta.final
102
+ class NOT_SET: # noqa
103
+ def __new__(cls, *args, **kwargs): # noqa
104
+ raise TypeError
105
+
106
+ #
107
+
108
+ def __init__(
109
+ self,
110
+ level: LogLevel,
111
+ *,
112
+ time_ns: ta.Optional[int] = None,
113
+
114
+ exc_info: LoggingExcInfoArg = False,
115
+
116
+ caller: ta.Union[LoggingCaller, ta.Type[NOT_SET], None] = NOT_SET,
117
+ stack_offset: int = 0,
118
+ stack_info: bool = False,
119
+ ) -> None:
120
+ self._level: NamedLogLevel = level if level.__class__ is NamedLogLevel else NamedLogLevel(level) # type: ignore[assignment] # noqa
121
+
122
+ #
123
+
124
+ if time_ns is None:
125
+ time_ns = time.time_ns()
126
+ self._time_ns: int = time_ns
127
+
128
+ #
129
+
130
+ if exc_info is True:
131
+ sys_exc_info = sys.exc_info()
132
+ if sys_exc_info[0] is not None:
133
+ exc_info = sys_exc_info
134
+ else:
135
+ exc_info = None
136
+ elif exc_info is False:
137
+ exc_info = None
138
+
139
+ if exc_info is not None:
140
+ self._exc_info: ta.Optional[LoggingExcInfo] = exc_info
141
+ if isinstance(exc_info, BaseException):
142
+ self._exc_info_tuple: ta.Optional[LoggingExcInfoTuple] = (type(exc_info), exc_info, exc_info.__traceback__) # noqa
143
+ else:
144
+ self._exc_info_tuple = exc_info
145
+ else:
146
+ self._exc_info = None
147
+ self._exc_info_tuple = None
148
+
149
+ #
150
+
151
+ if caller is not CaptureLoggingContextImpl.NOT_SET:
152
+ self._caller = caller # type: ignore[assignment]
153
+ else:
154
+ self._stack_offset = stack_offset
155
+ self._stack_info = stack_info
156
+
157
+ #
158
+
159
+ self._thread = LoggingThreadInfo.build()
160
+ self._process = LoggingProcessInfo.build()
161
+ self._multiprocessing = LoggingMultiprocessingInfo.build()
162
+ self._asyncio_task = LoggingAsyncioTaskInfo.build()
163
+
164
+ #
165
+
166
+ @property
167
+ def level(self) -> NamedLogLevel:
168
+ return self._level
169
+
170
+ #
171
+
172
+ @property
173
+ def time_ns(self) -> int:
174
+ return self._time_ns
175
+
176
+ _times: LoggingTimeFields
177
+
178
+ @property
179
+ def times(self) -> LoggingTimeFields:
180
+ try:
181
+ return self._times
182
+ except AttributeError:
183
+ pass
184
+
185
+ times = self._times = LoggingTimeFields.build(self.time_ns)
186
+ return times
187
+
188
+ @property
189
+ def exc_info(self) -> ta.Optional[LoggingExcInfo]:
190
+ return self._exc_info
191
+
192
+ @property
193
+ def exc_info_tuple(self) -> ta.Optional[LoggingExcInfoTuple]:
194
+ return self._exc_info_tuple
195
+
196
+ #
197
+
198
+ def inc_stack_offset(self, ofs: int = 1) -> 'LoggingContext':
199
+ if hasattr(self, '_stack_offset'):
200
+ self._stack_offset += ofs
201
+ return self
202
+
203
+ _caller: ta.Optional[LoggingCaller]
204
+
205
+ def capture(self) -> None:
206
+ """Must be cooperatively called only from the exact configured _stack_offset."""
207
+
208
+ try:
209
+ self._caller # noqa
210
+ except AttributeError:
211
+ pass
212
+
213
+ self._caller = LoggingCaller.find(
214
+ self._stack_offset + 1,
215
+ stack_info=self._stack_info,
216
+ )
217
+
218
+ def caller(self) -> ta.Optional[LoggingCaller]:
219
+ try:
220
+ return self._caller
221
+ except AttributeError:
222
+ return None
223
+
224
+ _source_file: ta.Optional[LoggingSourceFileInfo]
225
+
226
+ def source_file(self) -> ta.Optional[LoggingSourceFileInfo]:
227
+ try:
228
+ return self._source_file
229
+ except AttributeError:
230
+ pass
231
+
232
+ if (caller := self.caller()) is None:
233
+ return None
234
+
235
+ src_file = self._source_file = LoggingSourceFileInfo.build(caller.file_path)
236
+ return src_file
237
+
238
+ #
239
+
240
+ def thread(self) -> ta.Optional[LoggingThreadInfo]:
241
+ return self._thread
242
+
243
+ def process(self) -> ta.Optional[LoggingProcessInfo]:
244
+ return self._process
245
+
246
+ def multiprocessing(self) -> ta.Optional[LoggingMultiprocessingInfo]:
247
+ return self._multiprocessing
248
+
249
+ def asyncio_task(self) -> ta.Optional[LoggingAsyncioTaskInfo]:
250
+ return self._asyncio_task