omlish 0.0.0.dev421__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/logs/callers.py +23 -4
- omlish/logs/levels.py +3 -0
- omlish/logs/protocol.py +21 -17
- 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-0.0.0.dev421.dist-info → omlish-0.0.0.dev422.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev421.dist-info → omlish-0.0.0.dev422.dist-info}/RECORD +15 -10
- {omlish-0.0.0.dev421.dist-info → omlish-0.0.0.dev422.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev421.dist-info → omlish-0.0.0.dev422.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev421.dist-info → omlish-0.0.0.dev422.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev421.dist-info → omlish-0.0.0.dev422.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007 UP045
|
2
|
+
# @omlish-lite
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
from .types import ABSENT_TYPED_LOGGER_VALUE
|
6
|
+
from .types import AbsentTypedLoggerValue
|
7
|
+
from .types import ResolvedTypedLoggerFieldValue
|
8
|
+
from .types import TypedLoggerFieldValue
|
9
|
+
from .types import TypedLoggerValue
|
10
|
+
from .types import TypedLoggerValueOrAbsent
|
11
|
+
from .types import UnwrappedTypedLoggerFieldValue
|
12
|
+
from .types import unwrap_typed_logger_field_value
|
13
|
+
|
14
|
+
|
15
|
+
if ta.TYPE_CHECKING:
|
16
|
+
from .bindings import TypedLoggerBindings
|
17
|
+
|
18
|
+
|
19
|
+
TypedLoggerValueT = ta.TypeVar('TypedLoggerValueT', bound='TypedLoggerValue')
|
20
|
+
|
21
|
+
|
22
|
+
##
|
23
|
+
|
24
|
+
|
25
|
+
class RecursiveTypedLoggerValueError(Exception):
|
26
|
+
def __init__(self, cls: ta.Type[TypedLoggerValue], rec: ta.Sequence[ta.Type[TypedLoggerValue]]) -> None:
|
27
|
+
super().__init__()
|
28
|
+
|
29
|
+
self.cls = cls
|
30
|
+
self.rec = rec
|
31
|
+
|
32
|
+
def __repr__(self) -> str:
|
33
|
+
return (
|
34
|
+
f'{self.__class__.__name__}('
|
35
|
+
f'cls={self.cls!r}, '
|
36
|
+
f'rec={self.rec!r}'
|
37
|
+
f')'
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
class UnboundTypedLoggerValueError(Exception):
|
42
|
+
def __init__(self, cls: ta.Type[TypedLoggerValue]) -> None:
|
43
|
+
super().__init__()
|
44
|
+
|
45
|
+
self.cls = cls
|
46
|
+
|
47
|
+
def __repr__(self) -> str:
|
48
|
+
return f'{self.__class__.__name__}(cls={self.cls!r})'
|
49
|
+
|
50
|
+
|
51
|
+
#
|
52
|
+
|
53
|
+
|
54
|
+
@ta.final
|
55
|
+
class TypedLoggerContext:
|
56
|
+
def __init__(
|
57
|
+
self,
|
58
|
+
bindings: 'TypedLoggerBindings',
|
59
|
+
*,
|
60
|
+
no_auto_default_values: bool = False,
|
61
|
+
default_absent: bool = False,
|
62
|
+
) -> None:
|
63
|
+
super().__init__()
|
64
|
+
|
65
|
+
self._bindings = bindings
|
66
|
+
self._no_auto_default_values = no_auto_default_values
|
67
|
+
self._default_absent = default_absent
|
68
|
+
|
69
|
+
self._values: ta.Dict[ta.Type[TypedLoggerValue], TypedLoggerValueOrAbsent] = dict(bindings.const_value_map) # noqa
|
70
|
+
self._rec: ta.Dict[ta.Type[TypedLoggerValue], None] = {}
|
71
|
+
|
72
|
+
@property
|
73
|
+
def bindings(self) -> 'TypedLoggerBindings':
|
74
|
+
return self._bindings
|
75
|
+
|
76
|
+
@property
|
77
|
+
def no_auto_default_values(self) -> bool:
|
78
|
+
return self._no_auto_default_values
|
79
|
+
|
80
|
+
@property
|
81
|
+
def default_absent(self) -> bool:
|
82
|
+
return self._default_absent
|
83
|
+
|
84
|
+
#
|
85
|
+
|
86
|
+
def __getitem__(self, cls: ta.Type[TypedLoggerValueT]) -> ta.Union[TypedLoggerValueT, AbsentTypedLoggerValue]:
|
87
|
+
try:
|
88
|
+
return self._values[cls] # type: ignore[return-value]
|
89
|
+
except KeyError:
|
90
|
+
pass
|
91
|
+
|
92
|
+
if not issubclass(cls, TypedLoggerValue):
|
93
|
+
raise TypeError(cls)
|
94
|
+
|
95
|
+
if cls in self._rec:
|
96
|
+
raise RecursiveTypedLoggerValueError(cls, list(self._rec))
|
97
|
+
|
98
|
+
self._rec[cls] = None
|
99
|
+
try:
|
100
|
+
v: ta.Union[TypedLoggerValueOrAbsent]
|
101
|
+
|
102
|
+
if (bv := self._bindings.lookup_value(cls)) is not None:
|
103
|
+
if bv is ABSENT_TYPED_LOGGER_VALUE: # noqa
|
104
|
+
v = ABSENT_TYPED_LOGGER_VALUE
|
105
|
+
|
106
|
+
else:
|
107
|
+
v = bv._typed_logger_provide_value(self) # noqa
|
108
|
+
|
109
|
+
else:
|
110
|
+
if not self._no_auto_default_values and (dt := cls._typed_logger_maybe_provide_default_value(self)):
|
111
|
+
[v] = dt
|
112
|
+
|
113
|
+
elif self._default_absent:
|
114
|
+
v = ABSENT_TYPED_LOGGER_VALUE
|
115
|
+
|
116
|
+
else:
|
117
|
+
raise UnboundTypedLoggerValueError(cls) from None
|
118
|
+
|
119
|
+
self._values[cls] = v
|
120
|
+
return v # type: ignore[return-value]
|
121
|
+
|
122
|
+
finally:
|
123
|
+
self._rec.pop(cls)
|
124
|
+
|
125
|
+
#
|
126
|
+
|
127
|
+
def resolve_field_value(self, fv: TypedLoggerFieldValue) -> ResolvedTypedLoggerFieldValue:
|
128
|
+
if fv is ABSENT_TYPED_LOGGER_VALUE:
|
129
|
+
return fv # type: ignore[return-value]
|
130
|
+
|
131
|
+
elif isinstance(fv, type):
|
132
|
+
return self[fv]._typed_logger_resolve_field_value(self) # type: ignore[type-var] # noqa
|
133
|
+
|
134
|
+
else:
|
135
|
+
return fv._typed_logger_resolve_field_value(self) # noqa
|
136
|
+
|
137
|
+
def unwrap_field_value(self, fv: TypedLoggerFieldValue) -> UnwrappedTypedLoggerFieldValue:
|
138
|
+
return unwrap_typed_logger_field_value(self.resolve_field_value(fv))
|
@@ -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
|