omlish 0.0.0.dev420__py3-none-any.whl → 0.0.0.dev422__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 +2 -2
- omlish/asyncs/bluelet/core.py +2 -2
- omlish/asyncs/bluelet/events.py +4 -3
- omlish/asyncs/bluelet/files.py +2 -2
- omlish/asyncs/bluelet/sockets.py +2 -2
- omlish/collections/frozen.py +1 -1
- omlish/collections/kv/base.py +1 -1
- omlish/collections/multimaps.py +1 -1
- omlish/collections/persistent/treapmap.py +2 -1
- omlish/concurrent/threadlets.py +2 -2
- omlish/configs/formats.py +5 -4
- omlish/configs/processing/flattening.py +2 -1
- omlish/configs/processing/rewriting.py +2 -2
- omlish/configs/shadow.py +3 -2
- omlish/daemons/spawning.py +2 -2
- omlish/daemons/targets.py +1 -1
- omlish/daemons/waiting.py +2 -1
- omlish/dataclasses/impl/generation/base.py +3 -2
- omlish/dataclasses/impl/generation/compilation.py +2 -1
- omlish/dataclasses/impl/generation/ops.py +2 -2
- omlish/dataclasses/impl/generation/processor.py +1 -1
- omlish/dataclasses/metaclass/meta.py +1 -0
- omlish/dataclasses/tools/static.py +5 -1
- omlish/formats/dotenv.py +3 -1
- omlish/formats/logfmt.py +111 -0
- omlish/formats/yaml.py +1 -1
- omlish/funcs/builders.py +2 -1
- omlish/funcs/match.py +1 -1
- omlish/funcs/pairs.py +41 -23
- omlish/funcs/pipes.py +3 -1
- omlish/http/asgi.py +2 -1
- omlish/http/coro/client/io.py +3 -2
- omlish/http/coro/server/server.py +2 -2
- omlish/http/handlers.py +2 -1
- omlish/http/jwt.py +1 -1
- omlish/http/parsing.py +2 -2
- omlish/io/compress/base.py +3 -2
- omlish/io/coro/readers.py +4 -3
- omlish/io/fdio/handlers.py +3 -2
- omlish/io/fdio/pollers.py +3 -1
- omlish/lang/__init__.py +20 -8
- omlish/lang/attrs.py +3 -1
- omlish/lang/casing.py +3 -1
- omlish/lang/classes/abstract.py +35 -96
- omlish/lang/classes/virtual.py +2 -2
- omlish/lang/contextmanagers.py +6 -4
- omlish/lang/generators.py +2 -2
- omlish/lang/iterables.py +6 -6
- omlish/lang/objects.py +0 -58
- omlish/lang/outcomes.py +3 -1
- omlish/lang/typing.py +5 -24
- omlish/lite/abstract.py +119 -0
- omlish/lite/contextmanagers.py +4 -1
- omlish/lite/inject.py +9 -9
- omlish/lite/marshal.py +230 -114
- omlish/lite/maybes.py +3 -1
- omlish/lite/maysync.py +4 -2
- omlish/lite/objects.py +81 -0
- omlish/lite/reflect.py +0 -15
- omlish/lite/timeouts.py +3 -1
- omlish/lite/wrappers.py +23 -0
- omlish/logs/abc.py +21 -5
- omlish/logs/all.py +14 -3
- omlish/logs/callers.py +23 -4
- omlish/logs/configs.py +13 -10
- omlish/logs/levels.py +7 -0
- omlish/logs/protocol.py +84 -42
- omlish/logs/typed/__init__.py +0 -0
- omlish/logs/typed/bindings.py +537 -0
- omlish/logs/typed/contexts.py +138 -0
- omlish/logs/typed/types.py +484 -0
- omlish/logs/typed/values.py +114 -0
- omlish/marshal/__init__.py +1 -0
- omlish/os/atomics.py +3 -2
- omlish/os/deathpacts/base.py +4 -2
- omlish/os/forkhooks.py +2 -2
- omlish/os/pidfiles/pinning.py +2 -1
- omlish/reflect/types.py +4 -3
- omlish/secrets/crypto.py +1 -1
- omlish/sockets/bind.py +2 -1
- omlish/sockets/handlers.py +3 -2
- omlish/sockets/server/handlers.py +2 -1
- omlish/sockets/server/server.py +4 -3
- omlish/sql/api/base.py +2 -2
- omlish/subprocesses/asyncs.py +2 -1
- omlish/subprocesses/base.py +2 -2
- omlish/subprocesses/maysync.py +1 -2
- omlish/subprocesses/run.py +2 -1
- omlish/subprocesses/sync.py +2 -1
- omlish/term/coloring.py +3 -1
- omlish/testing/pytest/plugins/asyncs/backends/base.py +3 -1
- omlish/testing/unittest/loading.py +2 -2
- omlish/text/asdl.py +4 -3
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/RECORD +99 -89
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,484 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007 UP045 UP046
|
2
|
+
# @omlish-lite
|
3
|
+
import abc
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from ...lite.abstract import Abstract
|
7
|
+
from ...lite.reflect import is_union_alias
|
8
|
+
from ...lite.strings import snake_case
|
9
|
+
from ...lite.wrappers import update_wrapper_no_annotations
|
10
|
+
|
11
|
+
|
12
|
+
if ta.TYPE_CHECKING:
|
13
|
+
from .bindings import TypedLoggerBindings
|
14
|
+
from .contexts import TypedLoggerContext
|
15
|
+
|
16
|
+
|
17
|
+
T = ta.TypeVar('T')
|
18
|
+
U = ta.TypeVar('U')
|
19
|
+
|
20
|
+
TypedLoggerValueT = ta.TypeVar('TypedLoggerValueT', bound='TypedLoggerValue')
|
21
|
+
|
22
|
+
TypedLoggerValueOrProvider = ta.Union['TypedLoggerValue', 'TypedLoggerValueProvider'] # ta.TypeAlias
|
23
|
+
|
24
|
+
AbsentTypedLoggerValue = ta.Type['ABSENT_TYPED_LOGGER_VALUE'] # ta.TypeAlias
|
25
|
+
|
26
|
+
TypedLoggerValueOrAbsent = ta.Union['TypedLoggerValue', AbsentTypedLoggerValue]
|
27
|
+
TypedLoggerValueOrProviderOrAbsent = ta.Union[TypedLoggerValueOrProvider, AbsentTypedLoggerValue] # ta.TypeAlias
|
28
|
+
|
29
|
+
TypedLoggerFieldValue = ta.Union[TypedLoggerValueOrProviderOrAbsent, ta.Type['TypedLoggerValue'], 'TypedLoggerConstFieldValue'] # ta.TypeAlias # noqa
|
30
|
+
ResolvedTypedLoggerFieldValue = ta.Union[TypedLoggerValueOrAbsent, 'TypedLoggerConstFieldValue'] # ta.TypeAlias
|
31
|
+
UnwrappedTypedLoggerFieldValue = ta.Union[TypedLoggerValueOrAbsent, ta.Any] # ta.TypeAlias
|
32
|
+
|
33
|
+
|
34
|
+
##
|
35
|
+
|
36
|
+
|
37
|
+
class _TypedLoggerInternalMethods(ta.Protocol): # noqa
|
38
|
+
def _typed_logger_provide_value(self, ctx: 'TypedLoggerContext') -> TypedLoggerValueOrAbsent: ... # noqa
|
39
|
+
|
40
|
+
def _typed_logger_maybe_provide_default_value(self, ctx: 'TypedLoggerContext') -> ta.Tuple[TypedLoggerValueOrAbsent, ...]: ... # noqa
|
41
|
+
|
42
|
+
def _typed_logger_resolve_field_value(self, ctx: 'TypedLoggerContext') -> ResolvedTypedLoggerFieldValue: ... # noqa
|
43
|
+
|
44
|
+
def _typed_logger_unwrap_field_value(self, ctx: 'TypedLoggerContext') -> UnwrappedTypedLoggerFieldValue: ... # noqa
|
45
|
+
|
46
|
+
def _typed_logger_visit_bindings(self, vst: 'TypedLoggerBindings._Visitor') -> None: ... # noqa
|
47
|
+
|
48
|
+
|
49
|
+
##
|
50
|
+
|
51
|
+
|
52
|
+
@ta.final
|
53
|
+
class ABSENT_TYPED_LOGGER_VALUE: # noqa
|
54
|
+
def __new__(cls, *args, **kwargs): # noqa
|
55
|
+
raise TypeError
|
56
|
+
|
57
|
+
#
|
58
|
+
|
59
|
+
@classmethod
|
60
|
+
def map_absent(cls, fn: ta.Callable[[TypedLoggerValueT], U]) -> AbsentTypedLoggerValue: # noqa
|
61
|
+
return cls
|
62
|
+
|
63
|
+
#
|
64
|
+
|
65
|
+
@classmethod
|
66
|
+
def _typed_logger_provide_value(cls, ctx: 'TypedLoggerContext') -> TypedLoggerValueOrAbsent: # noqa
|
67
|
+
return cls
|
68
|
+
|
69
|
+
@classmethod
|
70
|
+
def _typed_logger_resolve_field_value(cls, ctx: 'TypedLoggerContext') -> ResolvedTypedLoggerFieldValue: # noqa
|
71
|
+
return cls
|
72
|
+
|
73
|
+
@classmethod
|
74
|
+
def _typed_logger_unwrap_field_value(cls, ctx: 'TypedLoggerContext') -> UnwrappedTypedLoggerFieldValue: # noqa
|
75
|
+
return cls
|
76
|
+
|
77
|
+
|
78
|
+
##
|
79
|
+
|
80
|
+
|
81
|
+
class TypedLoggerValue(Abstract, ta.Generic[T]):
|
82
|
+
def __init__(self, v: T) -> None:
|
83
|
+
self._v = v
|
84
|
+
|
85
|
+
@classmethod
|
86
|
+
def of(cls: ta.Type[TypedLoggerValueT], v: ta.Any) -> ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]:
|
87
|
+
return cls(v) if v is not ABSENT_TYPED_LOGGER_VALUE else ABSENT_TYPED_LOGGER_VALUE
|
88
|
+
|
89
|
+
@property
|
90
|
+
def v(self) -> T:
|
91
|
+
return self._v
|
92
|
+
|
93
|
+
def __repr__(self) -> str:
|
94
|
+
return f'{self.__class__.__module__}.{self.__class__.__qualname__}({self._v!r})'
|
95
|
+
|
96
|
+
#
|
97
|
+
|
98
|
+
def map_absent( # noqa
|
99
|
+
self: TypedLoggerValueT,
|
100
|
+
fn: ta.Callable[[TypedLoggerValueT], U],
|
101
|
+
) -> ta.Union[U, AbsentTypedLoggerValue]:
|
102
|
+
return fn(self)
|
103
|
+
|
104
|
+
#
|
105
|
+
|
106
|
+
_hash: int
|
107
|
+
|
108
|
+
def __hash__(self) -> int:
|
109
|
+
try:
|
110
|
+
return self._hash
|
111
|
+
except AttributeError:
|
112
|
+
pass
|
113
|
+
h = self._hash = hash((self.__class__, self.v))
|
114
|
+
return h
|
115
|
+
|
116
|
+
def __eq__(self, o):
|
117
|
+
if not isinstance(o, TypedLoggerValue):
|
118
|
+
return NotImplemented
|
119
|
+
return self.__class__ is o.__class__ and self.v == o.v
|
120
|
+
|
121
|
+
def __ne__(self, o):
|
122
|
+
return not (self == o)
|
123
|
+
|
124
|
+
#
|
125
|
+
|
126
|
+
_default_key: ta.ClassVar[ta.Union[str, bool]] = False
|
127
|
+
|
128
|
+
@ta.final
|
129
|
+
@classmethod
|
130
|
+
def default_key(cls) -> ta.Optional[str]:
|
131
|
+
return cls.__default_key # type: ignore[attr-defined]
|
132
|
+
|
133
|
+
#
|
134
|
+
|
135
|
+
class ContextLambda(ta.NamedTuple):
|
136
|
+
fn: ta.Callable[['TypedLoggerContext'], ta.Any]
|
137
|
+
|
138
|
+
#
|
139
|
+
|
140
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
141
|
+
super().__init_subclass__(**kwargs)
|
142
|
+
|
143
|
+
if '__call__' in cls.__dict__:
|
144
|
+
raise TypeError(f'{cls} should not be callable')
|
145
|
+
|
146
|
+
try:
|
147
|
+
dtb = DefaultTypedLoggerValue
|
148
|
+
except NameError:
|
149
|
+
pass
|
150
|
+
else:
|
151
|
+
if hasattr(cls, '_default_value') and not issubclass(cls, dtb):
|
152
|
+
raise TypeError(f'{cls} should be a subclass of DefaultTypedLoggerValue if it has a _default_value')
|
153
|
+
|
154
|
+
dks: ta.Optional[str]
|
155
|
+
if isinstance((dk := getattr(cls, '_default_key')), str):
|
156
|
+
dks = dk
|
157
|
+
elif dk is True:
|
158
|
+
dks = snake_case(cls.__name__)
|
159
|
+
elif dk is False:
|
160
|
+
dks = None
|
161
|
+
else:
|
162
|
+
raise TypeError(dk)
|
163
|
+
cls.__default_key = dks # type: ignore[attr-defined]
|
164
|
+
|
165
|
+
#
|
166
|
+
|
167
|
+
@ta.final
|
168
|
+
def _typed_logger_provide_value(self, ctx: 'TypedLoggerContext') -> TypedLoggerValueOrAbsent: # noqa
|
169
|
+
return self
|
170
|
+
|
171
|
+
@classmethod
|
172
|
+
def _typed_logger_maybe_provide_default_value(cls, ctx: 'TypedLoggerContext') -> ta.Tuple[TypedLoggerValueOrAbsent, ...]: # noqa
|
173
|
+
return ()
|
174
|
+
|
175
|
+
@ta.final
|
176
|
+
def _typed_logger_resolve_field_value(self, ctx: 'TypedLoggerContext') -> ResolvedTypedLoggerFieldValue: # noqa
|
177
|
+
return self
|
178
|
+
|
179
|
+
@ta.final
|
180
|
+
def _typed_logger_unwrap_field_value(self, ctx: 'TypedLoggerContext') -> UnwrappedTypedLoggerFieldValue: # noqa
|
181
|
+
return self._v
|
182
|
+
|
183
|
+
@ta.final
|
184
|
+
def _typed_logger_visit_bindings(self, vst: 'TypedLoggerBindings._Visitor') -> None: # noqa
|
185
|
+
vst.accept_values(((type(self), self),))
|
186
|
+
vst.accept_const_values(((type(self), self),))
|
187
|
+
|
188
|
+
|
189
|
+
class DefaultTypedLoggerValue(TypedLoggerValue[T], Abstract):
|
190
|
+
_default_value: ta.ClassVar[ta.Union[
|
191
|
+
AbsentTypedLoggerValue,
|
192
|
+
classmethod,
|
193
|
+
TypedLoggerValue.ContextLambda,
|
194
|
+
ta.Any,
|
195
|
+
]] = ABSENT_TYPED_LOGGER_VALUE
|
196
|
+
|
197
|
+
@ta.final
|
198
|
+
@classmethod
|
199
|
+
def default_provider(cls) -> 'TypedLoggerValueProvider':
|
200
|
+
try:
|
201
|
+
return cls.__default_provider # type: ignore[attr-defined]
|
202
|
+
except AttributeError:
|
203
|
+
pass
|
204
|
+
|
205
|
+
# Must be done late to support typing forwardrefs.
|
206
|
+
dp: TypedLoggerValueProvider
|
207
|
+
dv = next(mc.__dict__['_default_value'] for mc in cls.__mro__ if '_default_value' in mc.__dict__)
|
208
|
+
|
209
|
+
if dv is ABSENT_TYPED_LOGGER_VALUE:
|
210
|
+
dp = ConstTypedLoggerValueProvider(cls, ABSENT_TYPED_LOGGER_VALUE)
|
211
|
+
|
212
|
+
elif isinstance(dv, classmethod):
|
213
|
+
fn = dv.__get__(None, cls)
|
214
|
+
fl: ta.Any = lambda **kw: cls(v) if (v := fn(**kw)) is not ABSENT_TYPED_LOGGER_VALUE else v
|
215
|
+
dp = FnTypedLoggerValueProvider(cls, update_wrapper_no_annotations(fl, fn))
|
216
|
+
|
217
|
+
elif isinstance(dv, TypedLoggerValue.ContextLambda):
|
218
|
+
fl = lambda ctx: cls(dv.fn(ctx))
|
219
|
+
dp = ContextFnTypedLoggerValueProvider(cls, update_wrapper_no_annotations(fl, dv.fn))
|
220
|
+
|
221
|
+
else:
|
222
|
+
dp = ConstTypedLoggerValueProvider(cls, cls(dv))
|
223
|
+
|
224
|
+
cls.__default_provider = dp # type: ignore[attr-defined]
|
225
|
+
return dp
|
226
|
+
|
227
|
+
#
|
228
|
+
|
229
|
+
@ta.final
|
230
|
+
@classmethod
|
231
|
+
def _typed_logger_maybe_provide_default_value(cls, ctx: 'TypedLoggerContext') -> ta.Tuple[TypedLoggerValueOrAbsent, ...]: # noqa
|
232
|
+
return (cls.default_provider().provide_value(ctx),)
|
233
|
+
|
234
|
+
|
235
|
+
##
|
236
|
+
|
237
|
+
|
238
|
+
class TypedLoggerValueProvider(Abstract, ta.Generic[TypedLoggerValueT]):
|
239
|
+
@property
|
240
|
+
@abc.abstractmethod
|
241
|
+
def cls(self) -> ta.Type[TypedLoggerValueT]:
|
242
|
+
raise NotImplementedError
|
243
|
+
|
244
|
+
@abc.abstractmethod
|
245
|
+
def provide_value(self, ctx: 'TypedLoggerContext') -> ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]:
|
246
|
+
raise NotImplementedError
|
247
|
+
|
248
|
+
#
|
249
|
+
|
250
|
+
@ta.final
|
251
|
+
def _typed_logger_provide_value(self, ctx: 'TypedLoggerContext') -> TypedLoggerValueOrAbsent: # noqa
|
252
|
+
return self.provide_value(ctx)
|
253
|
+
|
254
|
+
@ta.final
|
255
|
+
def _typed_logger_resolve_field_value(self, ctx: 'TypedLoggerContext') -> ResolvedTypedLoggerFieldValue: # noqa
|
256
|
+
return self.provide_value(ctx)
|
257
|
+
|
258
|
+
def _typed_logger_visit_bindings(self, vst: 'TypedLoggerBindings._Visitor') -> None: # noqa
|
259
|
+
vst.accept_values(((self.cls, self),))
|
260
|
+
|
261
|
+
|
262
|
+
#
|
263
|
+
|
264
|
+
|
265
|
+
class ConstTypedLoggerValueProvider(TypedLoggerValueProvider[TypedLoggerValueT]):
|
266
|
+
def __init__(
|
267
|
+
self,
|
268
|
+
cls: ta.Type[TypedLoggerValueT],
|
269
|
+
v: ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue],
|
270
|
+
) -> None:
|
271
|
+
super().__init__()
|
272
|
+
|
273
|
+
self._cls = cls
|
274
|
+
self._v = v
|
275
|
+
|
276
|
+
@property
|
277
|
+
def cls(self) -> ta.Type[TypedLoggerValueT]:
|
278
|
+
return self._cls
|
279
|
+
|
280
|
+
@property
|
281
|
+
def v(self) -> ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]:
|
282
|
+
return self._v
|
283
|
+
|
284
|
+
def provide_value(self, ctx: 'TypedLoggerContext') -> ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]:
|
285
|
+
return self._v
|
286
|
+
|
287
|
+
#
|
288
|
+
|
289
|
+
@ta.final
|
290
|
+
def _typed_logger_visit_bindings(self, vst: 'TypedLoggerBindings._Visitor') -> None: # noqa
|
291
|
+
vst.accept_values(((self.cls, self),))
|
292
|
+
vst.accept_const_values(((self.cls, self._v),))
|
293
|
+
|
294
|
+
|
295
|
+
#
|
296
|
+
|
297
|
+
|
298
|
+
@ta.final
|
299
|
+
class ContextFnTypedLoggerValueProvider(TypedLoggerValueProvider[TypedLoggerValueT]):
|
300
|
+
def __init__(
|
301
|
+
self,
|
302
|
+
cls: ta.Type[TypedLoggerValueT],
|
303
|
+
fn: ta.Callable[['TypedLoggerContext'], ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]],
|
304
|
+
) -> None:
|
305
|
+
super().__init__()
|
306
|
+
|
307
|
+
self._cls = cls
|
308
|
+
self._fn = fn
|
309
|
+
|
310
|
+
@property
|
311
|
+
def cls(self) -> ta.Type[TypedLoggerValueT]:
|
312
|
+
return self._cls
|
313
|
+
|
314
|
+
@property
|
315
|
+
def fn(self) -> ta.Callable[['TypedLoggerContext'], ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]]:
|
316
|
+
return self._fn
|
317
|
+
|
318
|
+
def __repr__(self) -> str:
|
319
|
+
return f'{self.__class__.__name__}({self._cls!r}, {self._fn!r})'
|
320
|
+
|
321
|
+
def provide_value(self, ctx: 'TypedLoggerContext') -> ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]:
|
322
|
+
return self._fn(ctx)
|
323
|
+
|
324
|
+
|
325
|
+
#
|
326
|
+
|
327
|
+
|
328
|
+
class FnTypedLoggerValueProviderAnnotationError(TypeError):
|
329
|
+
pass
|
330
|
+
|
331
|
+
|
332
|
+
@ta.final
|
333
|
+
class FnTypedLoggerValueProvider(TypedLoggerValueProvider[TypedLoggerValueT]):
|
334
|
+
def __init__(
|
335
|
+
self,
|
336
|
+
cls: ta.Type[TypedLoggerValueT],
|
337
|
+
fn: ta.Callable[[], ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]],
|
338
|
+
) -> None:
|
339
|
+
super().__init__()
|
340
|
+
|
341
|
+
self._cls = cls
|
342
|
+
self._fn = fn
|
343
|
+
|
344
|
+
self._kw = self._get_fn_kwargs(fn)
|
345
|
+
|
346
|
+
@classmethod
|
347
|
+
def _get_fn_kwargs(cls, fn: ta.Any) -> ta.Mapping[str, ta.Type[TypedLoggerValue]]:
|
348
|
+
o = fn
|
349
|
+
s = {o}
|
350
|
+
while hasattr(o, '__wrapped__'):
|
351
|
+
o = o.__wrapped__
|
352
|
+
if o in s:
|
353
|
+
raise RuntimeError(f'Recursive unwrap: {o!r} in {s!r}')
|
354
|
+
s.add(o)
|
355
|
+
|
356
|
+
def get_kw(anns: ta.Dict[str, ta.Any]) -> ta.Dict[str, ta.Type[TypedLoggerValue]]:
|
357
|
+
anns = dict(anns)
|
358
|
+
anns.pop('return', None)
|
359
|
+
if not anns:
|
360
|
+
return {}
|
361
|
+
|
362
|
+
kw: ta.Dict[str, ta.Type[TypedLoggerValue]] = {}
|
363
|
+
for k, v in anns.items():
|
364
|
+
def bad() -> ta.NoReturn:
|
365
|
+
raise FnTypedLoggerValueProviderAnnotationError(
|
366
|
+
f'{fn} has invalid annotation {k} of {v} - must be subtype of TypedLoggerValue or' # noqa
|
367
|
+
f'Union[TypedLoggerValue, AbsentTypedLoggerValue]',
|
368
|
+
)
|
369
|
+
|
370
|
+
a = v
|
371
|
+
if is_union_alias(a):
|
372
|
+
if len(us := set(a)) != 2 or AbsentTypedLoggerValue not in us:
|
373
|
+
bad()
|
374
|
+
[a] = us - {AbsentTypedLoggerValue}
|
375
|
+
|
376
|
+
if not (isinstance(a, type) and issubclass(a, TypedLoggerValue)):
|
377
|
+
bad()
|
378
|
+
|
379
|
+
kw[k] = a
|
380
|
+
|
381
|
+
return kw
|
382
|
+
|
383
|
+
try:
|
384
|
+
# Note: transparently falls back to magic property on 3.14+
|
385
|
+
return get_kw(getattr(o, '__annotations__', {}))
|
386
|
+
except FnTypedLoggerValueProviderAnnotationError:
|
387
|
+
pass
|
388
|
+
|
389
|
+
# This is much slower, so only do it if necessary.
|
390
|
+
return get_kw(ta.get_type_hints(o))
|
391
|
+
|
392
|
+
@property
|
393
|
+
def cls(self) -> ta.Type[TypedLoggerValueT]:
|
394
|
+
return self._cls
|
395
|
+
|
396
|
+
@property
|
397
|
+
def fn(self) -> ta.Callable[[], ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]]:
|
398
|
+
return self._fn
|
399
|
+
|
400
|
+
def __repr__(self) -> str:
|
401
|
+
return f'{self.__class__.__name__}({self._cls!r}, {self._fn!r})'
|
402
|
+
|
403
|
+
def provide_value(self, ctx: 'TypedLoggerContext') -> ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]:
|
404
|
+
return self._fn(**{
|
405
|
+
k: v
|
406
|
+
for k, c in self._kw.items()
|
407
|
+
if (v := ctx[c]) is not ABSENT_TYPED_LOGGER_VALUE
|
408
|
+
})
|
409
|
+
|
410
|
+
|
411
|
+
##
|
412
|
+
|
413
|
+
|
414
|
+
@ta.final
|
415
|
+
class TypedLoggerConstFieldValue(ta.NamedTuple):
|
416
|
+
v: ta.Any
|
417
|
+
|
418
|
+
#
|
419
|
+
|
420
|
+
@ta.final
|
421
|
+
def _typed_logger_resolve_field_value(self, ctx: 'TypedLoggerContext') -> ResolvedTypedLoggerFieldValue: # noqa
|
422
|
+
return self
|
423
|
+
|
424
|
+
@ta.final
|
425
|
+
def _typed_logger_unwrap_field_value(self, ctx: 'TypedLoggerContext') -> UnwrappedTypedLoggerFieldValue: # noqa
|
426
|
+
return self.v
|
427
|
+
|
428
|
+
|
429
|
+
@ta.final
|
430
|
+
class TypedLoggerField(ta.NamedTuple):
|
431
|
+
k: str
|
432
|
+
v: TypedLoggerFieldValue
|
433
|
+
|
434
|
+
#
|
435
|
+
|
436
|
+
def _typed_logger_visit_bindings(self, vst: 'TypedLoggerBindings._Visitor') -> None: # noqa
|
437
|
+
vst.accept_keys(((self.k, self.v),))
|
438
|
+
|
439
|
+
|
440
|
+
##
|
441
|
+
|
442
|
+
|
443
|
+
TYPED_LOGGER_VALUE_OR_PROVIDER_TYPES: ta.Tuple[ta.Type[TypedLoggerValueOrProvider], ...] = (
|
444
|
+
TypedLoggerValue,
|
445
|
+
TypedLoggerValueProvider,
|
446
|
+
)
|
447
|
+
|
448
|
+
TYPED_LOGGER_VALUE_OR_ABSENT_TYPES: ta.Tuple[ta.Union[
|
449
|
+
ta.Type[TypedLoggerValue],
|
450
|
+
AbsentTypedLoggerValue,
|
451
|
+
], ...] = (
|
452
|
+
TypedLoggerValue,
|
453
|
+
ABSENT_TYPED_LOGGER_VALUE,
|
454
|
+
)
|
455
|
+
|
456
|
+
TYPED_LOGGER_VALUE_OR_PROVIDER_OR_ABSENT_TYPES: ta.Tuple[ta.Union[
|
457
|
+
ta.Type[TypedLoggerValueOrProvider],
|
458
|
+
AbsentTypedLoggerValue,
|
459
|
+
], ...] = (
|
460
|
+
*TYPED_LOGGER_VALUE_OR_PROVIDER_TYPES,
|
461
|
+
ABSENT_TYPED_LOGGER_VALUE,
|
462
|
+
)
|
463
|
+
|
464
|
+
|
465
|
+
##
|
466
|
+
|
467
|
+
|
468
|
+
@ta.overload
|
469
|
+
def unwrap_typed_logger_field_value(rfv: TypedLoggerValue[T]) -> TypedLoggerValue[T]:
|
470
|
+
...
|
471
|
+
|
472
|
+
|
473
|
+
@ta.overload
|
474
|
+
def unwrap_typed_logger_field_value(rfv: AbsentTypedLoggerValue) -> AbsentTypedLoggerValue:
|
475
|
+
...
|
476
|
+
|
477
|
+
|
478
|
+
@ta.overload
|
479
|
+
def unwrap_typed_logger_field_value(rfv: TypedLoggerConstFieldValue) -> ta.Any:
|
480
|
+
...
|
481
|
+
|
482
|
+
|
483
|
+
def unwrap_typed_logger_field_value(rfv):
|
484
|
+
return rfv._typed_logger_unwrap_field_value(rfv) # noqa
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# ruff: noqa: UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import logging
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
import threading
|
7
|
+
import time
|
8
|
+
import typing as ta
|
9
|
+
|
10
|
+
from ...logs.callers import LoggingCaller
|
11
|
+
from ...logs.levels import LogLevel
|
12
|
+
from .types import ABSENT_TYPED_LOGGER_VALUE
|
13
|
+
from .types import AbsentTypedLoggerValue
|
14
|
+
from .types import DefaultTypedLoggerValue
|
15
|
+
from .types import TypedLoggerValue
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
class StandardTypedLoggerValues:
|
22
|
+
def __new__(cls, *args, **kwargs): # noqa
|
23
|
+
raise TypeError
|
24
|
+
|
25
|
+
#
|
26
|
+
|
27
|
+
class TimeNs(DefaultTypedLoggerValue[int]):
|
28
|
+
@classmethod
|
29
|
+
def _default_value(cls) -> float:
|
30
|
+
return time.time()
|
31
|
+
|
32
|
+
class Time(DefaultTypedLoggerValue[float]):
|
33
|
+
_default_key = True
|
34
|
+
_default_value = TypedLoggerValue.ContextLambda(
|
35
|
+
lambda ctx: ctx[StandardTypedLoggerValues.TimeNs].map_absent(
|
36
|
+
lambda tv: tv.v / 1e9,
|
37
|
+
),
|
38
|
+
)
|
39
|
+
|
40
|
+
#
|
41
|
+
|
42
|
+
class Level(TypedLoggerValue[LogLevel]):
|
43
|
+
pass
|
44
|
+
|
45
|
+
class LevelName(DefaultTypedLoggerValue[str]):
|
46
|
+
_default_key = 'level'
|
47
|
+
_default_value = TypedLoggerValue.ContextLambda(
|
48
|
+
lambda ctx: ctx[StandardTypedLoggerValues.Level].map_absent(
|
49
|
+
lambda lvl: logging.getLevelName(lvl.v),
|
50
|
+
),
|
51
|
+
)
|
52
|
+
|
53
|
+
#
|
54
|
+
|
55
|
+
class Msg(TypedLoggerValue[str]):
|
56
|
+
_default_key = True
|
57
|
+
|
58
|
+
#
|
59
|
+
|
60
|
+
class Caller(TypedLoggerValue[LoggingCaller]):
|
61
|
+
pass
|
62
|
+
|
63
|
+
#
|
64
|
+
|
65
|
+
class Tid(DefaultTypedLoggerValue[int]):
|
66
|
+
@classmethod
|
67
|
+
def _default_value(cls) -> ta.Union[int, AbsentTypedLoggerValue]:
|
68
|
+
if hasattr(threading, 'get_native_id'):
|
69
|
+
return threading.get_native_id()
|
70
|
+
else:
|
71
|
+
return ABSENT_TYPED_LOGGER_VALUE
|
72
|
+
|
73
|
+
class ThreadIdent(DefaultTypedLoggerValue[int]):
|
74
|
+
@classmethod
|
75
|
+
def _default_value(cls) -> int:
|
76
|
+
return threading.get_ident()
|
77
|
+
|
78
|
+
class ThreadName(DefaultTypedLoggerValue[int]):
|
79
|
+
@classmethod
|
80
|
+
def _default_value(cls) -> str:
|
81
|
+
return threading.current_thread().name
|
82
|
+
|
83
|
+
#
|
84
|
+
|
85
|
+
class Pid(DefaultTypedLoggerValue[int]):
|
86
|
+
@classmethod
|
87
|
+
def _default_value(cls) -> int:
|
88
|
+
return os.getpid()
|
89
|
+
|
90
|
+
class ProcessName(DefaultTypedLoggerValue[str]):
|
91
|
+
@classmethod
|
92
|
+
def _default_value(cls) -> ta.Union[str, AbsentTypedLoggerValue]:
|
93
|
+
if (mp := sys.modules.get('multiprocessing')) is None:
|
94
|
+
return ABSENT_TYPED_LOGGER_VALUE
|
95
|
+
|
96
|
+
# Errors may occur if multiprocessing has not finished loading yet - e.g. if a custom import hook causes
|
97
|
+
# third-party code to run when multiprocessing calls import. See issue 8200 for an example
|
98
|
+
try:
|
99
|
+
return mp.current_process().name
|
100
|
+
except Exception: # noqa
|
101
|
+
return ABSENT_TYPED_LOGGER_VALUE
|
102
|
+
|
103
|
+
#
|
104
|
+
|
105
|
+
class AsyncioTaskName(DefaultTypedLoggerValue[str]):
|
106
|
+
@classmethod
|
107
|
+
def _default_value(cls) -> ta.Union[str, AbsentTypedLoggerValue]:
|
108
|
+
if (asyncio := sys.modules.get('asyncio')) is None:
|
109
|
+
return ABSENT_TYPED_LOGGER_VALUE
|
110
|
+
|
111
|
+
try:
|
112
|
+
return asyncio.current_task().get_name()
|
113
|
+
except Exception: # noqa
|
114
|
+
return ABSENT_TYPED_LOGGER_VALUE
|
omlish/marshal/__init__.py
CHANGED
@@ -9,6 +9,7 @@ TODO:
|
|
9
9
|
- can't disambiguate from str - can't coexist in bare union
|
10
10
|
- factories being free MatchFns does more harm than good - in practice these are such big guns you want to write a
|
11
11
|
class body if only ceremonially
|
12
|
+
- simple lite interop like inj - alt ObjMarshalerManager impl for Context
|
12
13
|
|
13
14
|
See:
|
14
15
|
- https://github.com/python-attrs/cattrs
|
omlish/os/atomics.py
CHANGED
@@ -6,6 +6,7 @@ import shutil
|
|
6
6
|
import tempfile
|
7
7
|
import typing as ta
|
8
8
|
|
9
|
+
from ..lite.abstract import Abstract
|
9
10
|
from ..lite.check import check
|
10
11
|
from ..lite.strings import attr_repr
|
11
12
|
|
@@ -17,7 +18,7 @@ AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
|
17
18
|
##
|
18
19
|
|
19
20
|
|
20
|
-
class AtomicPathSwap(
|
21
|
+
class AtomicPathSwap(Abstract):
|
21
22
|
def __init__(
|
22
23
|
self,
|
23
24
|
kind: AtomicPathSwapKind,
|
@@ -105,7 +106,7 @@ class AtomicPathSwap(abc.ABC):
|
|
105
106
|
self.abort()
|
106
107
|
|
107
108
|
|
108
|
-
class AtomicPathSwapping(
|
109
|
+
class AtomicPathSwapping(Abstract):
|
109
110
|
@abc.abstractmethod
|
110
111
|
def begin_atomic_path_swap(
|
111
112
|
self,
|
omlish/os/deathpacts/base.py
CHANGED
@@ -5,11 +5,13 @@ import sys
|
|
5
5
|
import time
|
6
6
|
import typing as ta
|
7
7
|
|
8
|
+
from ... import lang
|
9
|
+
|
8
10
|
|
9
11
|
##
|
10
12
|
|
11
13
|
|
12
|
-
class Deathpact(
|
14
|
+
class Deathpact(lang.Abstract):
|
13
15
|
@abc.abstractmethod
|
14
16
|
def poll(self) -> None:
|
15
17
|
raise NotImplementedError
|
@@ -23,7 +25,7 @@ class NopDeathpact(Deathpact):
|
|
23
25
|
##
|
24
26
|
|
25
27
|
|
26
|
-
class BaseDeathpact(Deathpact,
|
28
|
+
class BaseDeathpact(Deathpact, lang.Abstract):
|
27
29
|
def __init__(
|
28
30
|
self,
|
29
31
|
*,
|
omlish/os/forkhooks.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007 UP045
|
2
2
|
# @omlish-lite
|
3
|
-
import abc
|
4
3
|
import os
|
5
4
|
import threading
|
6
5
|
import typing as ta
|
7
6
|
|
7
|
+
from ..lite.abstract import Abstract
|
8
8
|
from ..lite.check import check
|
9
9
|
|
10
10
|
|
@@ -145,7 +145,7 @@ class _ForkHookManager:
|
|
145
145
|
#
|
146
146
|
|
147
147
|
|
148
|
-
class ForkHook(
|
148
|
+
class ForkHook(Abstract):
|
149
149
|
@ta.final
|
150
150
|
def __new__(cls, *args, **kwargs): # noqa
|
151
151
|
raise TypeError
|
omlish/os/pidfiles/pinning.py
CHANGED
@@ -27,6 +27,7 @@ import typing as ta
|
|
27
27
|
|
28
28
|
from ...diag.lslocks import LslocksCommand
|
29
29
|
from ...diag.lsof import LsofCommand
|
30
|
+
from ...lite.abstract import Abstract
|
30
31
|
from ...lite.check import check
|
31
32
|
from ...lite.timeouts import Timeout
|
32
33
|
from ...lite.timeouts import TimeoutLike
|
@@ -37,7 +38,7 @@ from .pidfile import Pidfile
|
|
37
38
|
##
|
38
39
|
|
39
40
|
|
40
|
-
class PidfilePinner(
|
41
|
+
class PidfilePinner(Abstract):
|
41
42
|
def __init__(
|
42
43
|
self,
|
43
44
|
*,
|