omlish 0.0.0.dev426__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.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev426'
2
- __revision__ = '94f5fd2fe7124da5d9b5af104cba12b1bbdb3286'
1
+ __version__ = '0.0.0.dev427'
2
+ __revision__ = '13723ced41ac43696831e7a640c46f91663c9a46'
3
3
 
4
4
 
5
5
  #
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
omlish/logs/infos.py CHANGED
@@ -9,8 +9,19 @@ import typing as ta
9
9
  ##
10
10
 
11
11
 
12
+ class _LoggingContextInfo:
13
+ def __mro_entries__(self, bases):
14
+ return ()
15
+
16
+
17
+ LoggingContextInfo: type = ta.cast(ta.Any, _LoggingContextInfo())
18
+
19
+
20
+ ##
21
+
22
+
12
23
  @ta.final
13
- class LoggingSourceFileInfo(ta.NamedTuple):
24
+ class LoggingSourceFileInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
14
25
  file_name: str
15
26
  module: str
16
27
 
@@ -36,7 +47,7 @@ class LoggingSourceFileInfo(ta.NamedTuple):
36
47
 
37
48
 
38
49
  @ta.final
39
- class LoggingThreadInfo(ta.NamedTuple):
50
+ class LoggingThreadInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
40
51
  ident: int
41
52
  native_id: ta.Optional[int]
42
53
  name: str
@@ -54,7 +65,7 @@ class LoggingThreadInfo(ta.NamedTuple):
54
65
 
55
66
 
56
67
  @ta.final
57
- class LoggingProcessInfo(ta.NamedTuple):
68
+ class LoggingProcessInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
58
69
  pid: int
59
70
 
60
71
  @classmethod
@@ -68,7 +79,7 @@ class LoggingProcessInfo(ta.NamedTuple):
68
79
 
69
80
 
70
81
  @ta.final
71
- class LoggingMultiprocessingInfo(ta.NamedTuple):
82
+ class LoggingMultiprocessingInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
72
83
  process_name: str
73
84
 
74
85
  @classmethod
@@ -86,7 +97,7 @@ class LoggingMultiprocessingInfo(ta.NamedTuple):
86
97
 
87
98
 
88
99
  @ta.final
89
- class LoggingAsyncioTaskInfo(ta.NamedTuple):
100
+ class LoggingAsyncioTaskInfo(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
90
101
  name: str
91
102
 
92
103
  @classmethod
omlish/logs/protocols.py CHANGED
@@ -16,16 +16,16 @@ class LoggerLike(ta.Protocol):
16
16
 
17
17
  #
18
18
 
19
- def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
19
+ def log(self, level: LogLevel, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
20
20
 
21
- def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
21
+ def debug(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
22
22
 
23
- def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
23
+ def info(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
24
24
 
25
- def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
25
+ def warning(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
26
26
 
27
- def exception(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
27
+ def error(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
28
28
 
29
- def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
29
+ def exception(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
30
30
 
31
- def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
31
+ def critical(self, msg: str, /, *args: ta.Any, **kwargs: ta.Any) -> None: ... # noqa
@@ -1,10 +1,12 @@
1
+ # ruff: noqa: UP007
1
2
  # @omlish-lite
2
3
  import logging
3
4
  import typing as ta
4
5
 
5
6
  from ..base import Logger
6
- from ..base import LoggingContext
7
- from ..base import LogLevel
7
+ from ..base import LoggingMsgFn
8
+ from ..contexts import CaptureLoggingContext
9
+ from ..levels import LogLevel
8
10
  from .records import LoggingContextLogRecord
9
11
 
10
12
 
@@ -24,15 +26,17 @@ class StdLogger(Logger):
24
26
  def get_effective_level(self) -> LogLevel:
25
27
  return self._std.getEffectiveLevel()
26
28
 
27
- def _log(self, ctx: LoggingContext, msg: str, *args: ta.Any) -> None:
29
+ def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
28
30
  if not self.is_enabled_for(ctx.level):
29
31
  return
30
32
 
31
- ctx.build_caller()
33
+ ctx.capture()
34
+
35
+ ms, args = self._prepare_msg_args(msg, *args)
32
36
 
33
37
  rec = LoggingContextLogRecord(
34
38
  name=self._std.name,
35
- msg=msg,
39
+ msg=ms,
36
40
  args=args,
37
41
 
38
42
  _logging_context=ctx,
@@ -5,8 +5,9 @@ import logging
5
5
  import sys
6
6
  import typing as ta
7
7
 
8
- from ..base import LoggingContext
9
- from ..base import LoggingExcInfoTuple
8
+ from ...lite.check import check
9
+ from ..contexts import LoggingContext
10
+ from ..contexts import LoggingExcInfoTuple
10
11
  from ..warnings import LoggingSetupWarning
11
12
 
12
13
 
@@ -268,19 +269,33 @@ class LoggingContextLogRecord(logging.LogRecord):
268
269
  self.msecs: float = times.msecs
269
270
  self.relativeCreated: float = times.relative_created
270
271
 
271
- thread = ctx.thread
272
- self.thread: ta.Optional[int] = thread.ident
273
- self.threadName: ta.Optional[str] = thread.name
272
+ if logging.logThreads:
273
+ thread = check.not_none(ctx.thread())
274
+ self.thread: ta.Optional[int] = thread.ident
275
+ self.threadName: ta.Optional[str] = thread.name
276
+ else:
277
+ self.thread = None
278
+ self.threadName = None
274
279
 
275
- process = ctx.process
276
- self.process: ta.Optional[int] = process.pid
280
+ if logging.logProcesses:
281
+ process = check.not_none(ctx.process())
282
+ self.process: ta.Optional[int] = process.pid
283
+ else:
284
+ self.process = None
277
285
 
278
- if (mp := ctx.multiprocessing) is not None:
279
- self.processName: ta.Optional[str] = mp.process_name
286
+ if logging.logMultiprocessing:
287
+ if (mp := ctx.multiprocessing()) is not None:
288
+ self.processName: ta.Optional[str] = mp.process_name
289
+ else:
290
+ self.processName = None
280
291
  else:
281
292
  self.processName = None
282
293
 
283
- if (at := ctx.asyncio_task) is not None:
284
- self.taskName: ta.Optional[str] = at.name
294
+ # Absent <3.12
295
+ if getattr(logging, 'logAsyncioTasks', None):
296
+ if (at := ctx.asyncio_task()) is not None:
297
+ self.taskName: ta.Optional[str] = at.name
298
+ else:
299
+ self.taskName = None
285
300
  else:
286
301
  self.taskName = None
omlish/logs/times.py CHANGED
@@ -4,13 +4,14 @@ import logging
4
4
  import time
5
5
  import typing as ta
6
6
 
7
+ from .infos import LoggingContextInfo
7
8
  from .warnings import LoggingSetupWarning
8
9
 
9
10
 
10
11
  ##
11
12
 
12
13
 
13
- class LoggingTimeFields(ta.NamedTuple):
14
+ class LoggingTimeFields(LoggingContextInfo, ta.NamedTuple): # type: ignore[misc]
14
15
  """Maps directly to stdlib `logging.LogRecord` fields, and must be kept in sync with it."""
15
16
 
16
17
  created: float
@@ -8,6 +8,12 @@ TODO:
8
8
  - TypeMap style weak cache of issubclass queries
9
9
  - wait.. lazily load the class for virtual subclass queries? xor support virtual bases?
10
10
  - weakref class dict keys?
11
+ - cheap_discover_package_root_dirs: Seq[str] | None = None or smth
12
+ - maaaybe.. add an EnvVar? OMLISH_MANIFEST_ROOT_DIRS? if set to : delimited, turns off package disco and overrides
13
+ scan_root_dirs
14
+ - currently the cli cant subprocess itself and keep manifests working
15
+ - EnvVar cls is already lite
16
+
11
17
  """
12
18
  import dataclasses as dc
13
19
  import importlib.machinery
@@ -20,7 +20,7 @@ from .handlers import SocketServerHandler
20
20
  ##
21
21
 
22
22
 
23
- class SocketServer(Abstract):
23
+ class SocketServer:
24
24
  _DEFAULT_LOGGER = logging.getLogger('.'.join([__name__, 'SocketServer']))
25
25
 
26
26
  def __init__(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev426
3
+ Version: 0.0.0.dev427
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.omlish-manifests.json,sha256=FLw7xkPiSXuImZgqSP8BwrEib2R1doSzUPLUkc-QUIA,8410
2
- omlish/__about__.py,sha256=MYyAs26jJT38QRig9P3Am4tubSo27oP0MXHBDXiAQ_g,3575
2
+ omlish/__about__.py,sha256=QkXaBAuv7ZX9gYuayTSwd9nc5AxEetkjHMvq5rsSLtM,3575
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ZNIMl1kwg3qdei4DiUrJPQe5M81S1e76N-GuNSwLBAE,8683
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -498,25 +498,26 @@ omlish/lite/typing.py,sha256=m2CyJTz2OVOCPRvp-0UuEx7oleZgXqs3rYXijE0bTsA,1280
498
498
  omlish/lite/wrappers.py,sha256=d00Ls2kFHuogKd5wEBaU65VNCN10YXIZtiwu1mbMpmA,530
499
499
  omlish/logs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
500
500
  omlish/logs/all.py,sha256=9VOobL0p3E_6QWsnBmlCPhxz5zqs-AiH4jvZR3--ZZA,1085
501
- omlish/logs/base.py,sha256=BOKgiimvHboKE9UTMKpRSeSZoZhhyf0TQ5u-mRozLWU,7071
502
- omlish/logs/callers.py,sha256=j6zq_cKdCrlG4O1ODgZPqUKmQ2ebmCnJrdzHmtffCtQ,2094
503
- omlish/logs/infos.py,sha256=-FRTuawNwvK2SBjNKby5IUsS0Q2o9H1q8tFTqDNeWIE,2348
501
+ omlish/logs/base.py,sha256=LxDnvCDzZr9AfjO_5NmoE9z1Mqw_7meQ7WGgGYissXk,6649
502
+ omlish/logs/callers.py,sha256=tRj24LxlEimDGCplDVVJDvRSt_u72cgjnnvb0mdZgvU,2065
503
+ omlish/logs/contexts.py,sha256=apXaLOk7LlXNdz_gpF5zHhTLpFFBmB2_SHX8UVxopks,6537
504
+ omlish/logs/infos.py,sha256=VPx9VrcN1fCPnXjc8QCsMlww0fgEG3qdkzo2KhSxMwY,2716
504
505
  omlish/logs/levels.py,sha256=b3i-70OcvNMcOQBzg1IUHBPESfHz0uu5mi5t_jbwv1Y,1966
505
506
  omlish/logs/modules.py,sha256=EFOqubbU5P0GW576OL6j9fmizNN2oyfpsp5YnKCoUgk,194
506
- omlish/logs/protocols.py,sha256=19Hre-98MWnN9oGz4O4RBczpwA5Slg15l9Y-ilbixgs,918
507
+ omlish/logs/protocols.py,sha256=8l5TgNUNgfrqYPA6dVijPFGLxzWhvgS4Cca64XHT0jo,939
507
508
  omlish/logs/standard.py,sha256=ppho-wDecGxUiRXOdCIlblmrQhqXZ0oQOGayy8N53XY,3237
508
- omlish/logs/times.py,sha256=KFe7FuBjkFxVz6Kl9TlTD2J1FLLeHUs8OI8tKw8StKQ,2491
509
+ omlish/logs/times.py,sha256=ro2TSD_Xwq_cNN8f_FrEGfdhD4PTbh0PLSG13PGx39E,2571
509
510
  omlish/logs/utils.py,sha256=fKvP342qBmE8wwTgUQ8Tf0-ATVhCm90UYBQt2pk0044,1883
510
511
  omlish/logs/warnings.py,sha256=xyhDgiPy1p8Kp5D9sb_NZiBnQ26SUppaHqC27dtQzok,67
511
512
  omlish/logs/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
512
- omlish/logs/std/adapters.py,sha256=8Z3e9hZRz-8HpW3PnlTURAQLzGG7gb327P4tQ9AZSV8,853
513
+ omlish/logs/std/adapters.py,sha256=qRXusW8iGYF29C3cy1srb4pgJ0Fv5li8bUvnegxo9o8,1005
513
514
  omlish/logs/std/configs.py,sha256=aDQahqBJXJpmQBaxXVK5S2xi_I_nrLLcCLTq6Q2K6EQ,1037
514
515
  omlish/logs/std/filters.py,sha256=Z8tHOvBRiP-OYm2z5cwY-lYXRimNgferEARMwvwx5Pw,380
515
516
  omlish/logs/std/handlers.py,sha256=nYPKV6Kzz8Oeyw4-3ZdJKKEUfgubfmwzqr3RaCNcmS4,338
516
517
  omlish/logs/std/json.py,sha256=QJ5lywLsRsPyVno2nk2kOw-Z1z3bfrDiZzqcRUdWUMY,1382
517
518
  omlish/logs/std/noisy.py,sha256=hWpbseerZqlHdEPEajDTSmcRhx8LmmNAxz_7GBZAO9s,353
518
519
  omlish/logs/std/proxy.py,sha256=9MVV5kbj9nwl3KZGtrYCIb5XTpv33f33zZ7P_B58fX0,2394
519
- omlish/logs/std/records.py,sha256=TCpslKiIBYI2ZIvJWyABpa7Ovt-f4jpQYsYuHE2oYTA,10183
520
+ omlish/logs/std/records.py,sha256=muJA3KKjGm5mgsHXtOyrpaEZu553M-UviNgNvXdISXY,10718
520
521
  omlish/logs/typed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
521
522
  omlish/logs/typed/bindings.py,sha256=H_B-3LTGFlBIWYDJ8rei1VMfInE8KrBwRo3bJ_BgFj8,18184
522
523
  omlish/logs/typed/contexts.py,sha256=MU1RqHtmYbnxoLfLhMZbTbRd-WfYEyeVo7_3VnG3rY0,4027
@@ -525,7 +526,7 @@ omlish/logs/typed/values.py,sha256=w8ukpJZ3lx_st7Mj7riP2squzGmMaz7OuUxw_NZCZsY,3
525
526
  omlish/manifests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
526
527
  omlish/manifests/base.py,sha256=wW-De-pU3cef-0vGsgo-ypftDwc3tCBxhRAzPtrdark,918
527
528
  omlish/manifests/globals.py,sha256=kVqQ-fT4kc7xWzLHoI731GviitFPv2v2yqw-p7t7Exs,2628
528
- omlish/manifests/loading.py,sha256=RUFRZ8ILU_aG11BYDpWAYlc--58HmlSYSLHkHqLUwd8,17208
529
+ omlish/manifests/loading.py,sha256=s6KnhdFQCsI2i0Rus1sMU0so2v8dUBnk59BJkSnxGt8,17514
529
530
  omlish/manifests/static.py,sha256=9BaPBLkuzxHmg5A-5k9BjjBFINCdmFOIu06dMFgCfz4,497
530
531
  omlish/manifests/types.py,sha256=NeOGuIVrcbqjCDbQ3MnCxxHAgHnw0CkWJsBzo230PWE,453
531
532
  omlish/marshal/__init__.py,sha256=cdRll_tPZaTBIY0yLejCXZdZB3fUVkIoDMb8aESLkl8,5981
@@ -646,7 +647,7 @@ omlish/sockets/ports.py,sha256=WofuoMTQNUhbEreL0lrMQyVEZm6awuLLpFvn6jnFKrw,1778
646
647
  omlish/sockets/wait.py,sha256=GQIRABh_NWHePuNVz5bBeg7iVln-YHc3945ZA0qr-mA,1565
647
648
  omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
648
649
  omlish/sockets/server/handlers.py,sha256=T--CSns9810f25--0Blv5bspAXhMjULtKEcXSGUpxOo,3912
649
- omlish/sockets/server/server.py,sha256=Z3kJ5LbO-sdJICxouv85KtDEmRb5_0kGNyHgdVrVzlM,6299
650
+ omlish/sockets/server/server.py,sha256=Il1y0LHRa_eB6FLLcuVXbfu9tLOpOttLBCffh3wWSBA,6289
650
651
  omlish/sockets/server/ssl.py,sha256=QLiPcng1f1RFDhw4aS-Fy57fm0R7CXm76STpHmT55z0,1067
651
652
  omlish/sockets/server/threading.py,sha256=CPr_290vpN3o4p2ehQBIwk-d8Ciasylp9v2zUQSB5xk,2862
652
653
  omlish/specs/__init__.py,sha256=fxpcfXEWESH2kiPStKhl2dAdfLkZVTt_cssZKyqBLiQ,87
@@ -891,9 +892,9 @@ omlish/typedvalues/marshal.py,sha256=AtBz7Jq-BfW8vwM7HSxSpR85JAXmxK2T0xDblmm1HI0
891
892
  omlish/typedvalues/of_.py,sha256=UXkxSj504WI2UrFlqdZJbu2hyDwBhL7XVrc2qdR02GQ,1309
892
893
  omlish/typedvalues/reflect.py,sha256=PAvKW6T4cW7u--iX80w3HWwZUS3SmIZ2_lQjT65uAyk,1026
893
894
  omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
894
- omlish-0.0.0.dev426.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
895
- omlish-0.0.0.dev426.dist-info/METADATA,sha256=3sUi7uVlvrzlrg2y4hzuNQqNdMcPQ7K_V1wfLElyjrM,19243
896
- omlish-0.0.0.dev426.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
897
- omlish-0.0.0.dev426.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
898
- omlish-0.0.0.dev426.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
899
- omlish-0.0.0.dev426.dist-info/RECORD,,
895
+ omlish-0.0.0.dev427.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
896
+ omlish-0.0.0.dev427.dist-info/METADATA,sha256=XcyPFrnqymcaHcY3ycLj3wJn1_l-E-qdd3MilxfuI8o,19243
897
+ omlish-0.0.0.dev427.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
898
+ omlish-0.0.0.dev427.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
899
+ omlish-0.0.0.dev427.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
900
+ omlish-0.0.0.dev427.dist-info/RECORD,,