omlish 0.0.0.dev120__py3-none-any.whl → 0.0.0.dev121__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/inject/binder.py +1 -1
- omlish/inject/impl/inspect.py +9 -11
- omlish/lite/cached.py +1 -1
- omlish/lite/contextmanagers.py +4 -0
- omlish/lite/inject.py +624 -0
- omlish/lite/maybes.py +45 -0
- {omlish-0.0.0.dev120.dist-info → omlish-0.0.0.dev121.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev120.dist-info → omlish-0.0.0.dev121.dist-info}/RECORD +13 -11
- {omlish-0.0.0.dev120.dist-info → omlish-0.0.0.dev121.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev120.dist-info → omlish-0.0.0.dev121.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev120.dist-info → omlish-0.0.0.dev121.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev120.dist-info → omlish-0.0.0.dev121.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/inject/binder.py
CHANGED
omlish/inject/impl/inspect.py
CHANGED
@@ -7,7 +7,6 @@ TODO:
|
|
7
7
|
"""
|
8
8
|
import dataclasses as dc
|
9
9
|
import inspect
|
10
|
-
import types
|
11
10
|
import typing as ta
|
12
11
|
import weakref
|
13
12
|
|
@@ -28,38 +27,38 @@ R = ta.TypeVar('R')
|
|
28
27
|
##
|
29
28
|
|
30
29
|
|
31
|
-
|
30
|
+
_SIGNATURE_CACHE: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
|
32
31
|
|
33
32
|
|
34
33
|
def signature(obj: ta.Any) -> inspect.Signature:
|
35
34
|
try:
|
36
|
-
return
|
35
|
+
return _SIGNATURE_CACHE[obj]
|
37
36
|
except TypeError:
|
38
37
|
return inspect.signature(obj)
|
39
38
|
except KeyError:
|
40
39
|
pass
|
41
40
|
sig = inspect.signature(obj)
|
42
|
-
|
41
|
+
_SIGNATURE_CACHE[obj] = sig
|
43
42
|
return sig
|
44
43
|
|
45
44
|
|
46
45
|
##
|
47
46
|
|
48
47
|
|
49
|
-
|
48
|
+
_TAGS: ta.MutableMapping[ta.Any, dict[str, ta.Any]] = weakref.WeakKeyDictionary()
|
50
49
|
|
51
50
|
|
52
51
|
def tag(obj: T, **kwargs: ta.Any) -> T:
|
53
52
|
for v in kwargs.values():
|
54
53
|
if isinstance(v, Tag):
|
55
54
|
raise TypeError(v)
|
56
|
-
|
55
|
+
_TAGS.setdefault(obj, {}).update(**kwargs)
|
57
56
|
return obj
|
58
57
|
|
59
58
|
|
60
59
|
def tags(**kwargs: ta.Any) -> ta.Callable[[ta.Callable[P, R]], ta.Callable[P, R]]:
|
61
60
|
def inner(obj):
|
62
|
-
|
61
|
+
_TAGS[obj] = kwargs
|
63
62
|
return obj
|
64
63
|
return inner
|
65
64
|
|
@@ -75,7 +74,7 @@ def build_kwargs_target(
|
|
75
74
|
raw_optional: bool = False,
|
76
75
|
) -> KwargsTarget:
|
77
76
|
sig = signature(obj)
|
78
|
-
tags =
|
77
|
+
tags = _TAGS.get(obj)
|
79
78
|
|
80
79
|
seen: set[Key] = set(map(as_key, skip_kwargs)) if skip_kwargs is not None else set()
|
81
80
|
kws: list[Kwarg] = []
|
@@ -92,10 +91,9 @@ def build_kwargs_target(
|
|
92
91
|
if (
|
93
92
|
not raw_optional and
|
94
93
|
isinstance(rf := rfl.type_(ann), rfl.Union) and
|
95
|
-
|
96
|
-
and types.NoneType in rf.args
|
94
|
+
rf.is_optional
|
97
95
|
):
|
98
|
-
|
96
|
+
ann = rf.without_none()
|
99
97
|
|
100
98
|
rty = rfl.type_(ann)
|
101
99
|
|
omlish/lite/cached.py
CHANGED
omlish/lite/contextmanagers.py
CHANGED
@@ -25,8 +25,12 @@ class ExitStacked:
|
|
25
25
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
26
26
|
if (es := self._exit_stack) is None:
|
27
27
|
return None
|
28
|
+
self._exit_contexts()
|
28
29
|
return es.__exit__(exc_type, exc_val, exc_tb)
|
29
30
|
|
31
|
+
def _exit_contexts(self) -> None:
|
32
|
+
pass
|
33
|
+
|
30
34
|
def _enter_context(self, cm: ta.ContextManager[T]) -> T:
|
31
35
|
es = check_not_none(self._exit_stack)
|
32
36
|
return es.enter_context(cm)
|
omlish/lite/inject.py
ADDED
@@ -0,0 +1,624 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import abc
|
3
|
+
import dataclasses as dc
|
4
|
+
import functools
|
5
|
+
import inspect
|
6
|
+
import types
|
7
|
+
import typing as ta
|
8
|
+
import weakref
|
9
|
+
|
10
|
+
from .check import check_isinstance
|
11
|
+
from .check import check_not_isinstance
|
12
|
+
from .maybes import Maybe
|
13
|
+
from .reflect import get_optional_alias_arg
|
14
|
+
from .reflect import is_optional_alias
|
15
|
+
|
16
|
+
|
17
|
+
T = ta.TypeVar('T')
|
18
|
+
|
19
|
+
InjectorKeyCls = ta.Union[type, ta.NewType]
|
20
|
+
|
21
|
+
InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
|
22
|
+
InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
23
|
+
|
24
|
+
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
25
|
+
|
26
|
+
|
27
|
+
###
|
28
|
+
# types
|
29
|
+
|
30
|
+
|
31
|
+
@dc.dataclass(frozen=True)
|
32
|
+
class InjectorKey:
|
33
|
+
cls: InjectorKeyCls
|
34
|
+
tag: ta.Any = None
|
35
|
+
array: bool = False
|
36
|
+
|
37
|
+
|
38
|
+
##
|
39
|
+
|
40
|
+
|
41
|
+
class InjectorProvider(abc.ABC):
|
42
|
+
@abc.abstractmethod
|
43
|
+
def provider_fn(self) -> InjectorProviderFn:
|
44
|
+
raise NotImplementedError
|
45
|
+
|
46
|
+
|
47
|
+
##
|
48
|
+
|
49
|
+
|
50
|
+
@dc.dataclass(frozen=True)
|
51
|
+
class InjectorBinding:
|
52
|
+
key: InjectorKey
|
53
|
+
provider: InjectorProvider
|
54
|
+
|
55
|
+
|
56
|
+
class InjectorBindings(abc.ABC):
|
57
|
+
@abc.abstractmethod
|
58
|
+
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
59
|
+
raise NotImplementedError
|
60
|
+
|
61
|
+
##
|
62
|
+
|
63
|
+
|
64
|
+
class Injector(abc.ABC):
|
65
|
+
@abc.abstractmethod
|
66
|
+
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
67
|
+
raise NotImplementedError
|
68
|
+
|
69
|
+
@abc.abstractmethod
|
70
|
+
def provide(self, key: ta.Any) -> ta.Any:
|
71
|
+
raise NotImplementedError
|
72
|
+
|
73
|
+
@abc.abstractmethod
|
74
|
+
def provide_kwargs(self, obj: ta.Any) -> ta.Mapping[str, ta.Any]:
|
75
|
+
raise NotImplementedError
|
76
|
+
|
77
|
+
@abc.abstractmethod
|
78
|
+
def inject(self, obj: ta.Any) -> ta.Any:
|
79
|
+
raise NotImplementedError
|
80
|
+
|
81
|
+
|
82
|
+
###
|
83
|
+
# exceptions
|
84
|
+
|
85
|
+
|
86
|
+
@dc.dataclass(frozen=True)
|
87
|
+
class InjectorKeyError(Exception):
|
88
|
+
key: InjectorKey
|
89
|
+
|
90
|
+
source: ta.Any = None
|
91
|
+
name: ta.Optional[str] = None
|
92
|
+
|
93
|
+
|
94
|
+
@dc.dataclass(frozen=True)
|
95
|
+
class UnboundInjectorKeyError(InjectorKeyError):
|
96
|
+
pass
|
97
|
+
|
98
|
+
|
99
|
+
@dc.dataclass(frozen=True)
|
100
|
+
class DuplicateInjectorKeyError(InjectorKeyError):
|
101
|
+
pass
|
102
|
+
|
103
|
+
|
104
|
+
###
|
105
|
+
# keys
|
106
|
+
|
107
|
+
|
108
|
+
def as_injector_key(o: ta.Any) -> InjectorKey:
|
109
|
+
if o is inspect.Parameter.empty:
|
110
|
+
raise TypeError(o)
|
111
|
+
if isinstance(o, InjectorKey):
|
112
|
+
return o
|
113
|
+
if isinstance(o, (type, ta.NewType)):
|
114
|
+
return InjectorKey(o)
|
115
|
+
raise TypeError(o)
|
116
|
+
|
117
|
+
|
118
|
+
###
|
119
|
+
# providers
|
120
|
+
|
121
|
+
|
122
|
+
@dc.dataclass(frozen=True)
|
123
|
+
class FnInjectorProvider(InjectorProvider):
|
124
|
+
fn: ta.Any
|
125
|
+
|
126
|
+
def __post_init__(self) -> None:
|
127
|
+
check_not_isinstance(self.fn, type)
|
128
|
+
|
129
|
+
def provider_fn(self) -> InjectorProviderFn:
|
130
|
+
def pfn(i: Injector) -> ta.Any:
|
131
|
+
return i.inject(self.fn)
|
132
|
+
|
133
|
+
return pfn
|
134
|
+
|
135
|
+
|
136
|
+
@dc.dataclass(frozen=True)
|
137
|
+
class CtorInjectorProvider(InjectorProvider):
|
138
|
+
cls: type
|
139
|
+
|
140
|
+
def __post_init__(self) -> None:
|
141
|
+
check_isinstance(self.cls, type)
|
142
|
+
|
143
|
+
def provider_fn(self) -> InjectorProviderFn:
|
144
|
+
def pfn(i: Injector) -> ta.Any:
|
145
|
+
return i.inject(self.cls)
|
146
|
+
|
147
|
+
return pfn
|
148
|
+
|
149
|
+
|
150
|
+
@dc.dataclass(frozen=True)
|
151
|
+
class ConstInjectorProvider(InjectorProvider):
|
152
|
+
v: ta.Any
|
153
|
+
|
154
|
+
def provider_fn(self) -> InjectorProviderFn:
|
155
|
+
return lambda _: self.v
|
156
|
+
|
157
|
+
|
158
|
+
@dc.dataclass(frozen=True)
|
159
|
+
class SingletonInjectorProvider(InjectorProvider):
|
160
|
+
p: InjectorProvider
|
161
|
+
|
162
|
+
def __post_init__(self) -> None:
|
163
|
+
check_isinstance(self.p, InjectorProvider)
|
164
|
+
|
165
|
+
def provider_fn(self) -> InjectorProviderFn:
|
166
|
+
v = not_set = object()
|
167
|
+
|
168
|
+
def pfn(i: Injector) -> ta.Any:
|
169
|
+
nonlocal v
|
170
|
+
if v is not_set:
|
171
|
+
v = ufn(i)
|
172
|
+
return v
|
173
|
+
|
174
|
+
ufn = self.p.provider_fn()
|
175
|
+
return pfn
|
176
|
+
|
177
|
+
|
178
|
+
@dc.dataclass(frozen=True)
|
179
|
+
class LinkInjectorProvider(InjectorProvider):
|
180
|
+
k: InjectorKey
|
181
|
+
|
182
|
+
def __post_init__(self) -> None:
|
183
|
+
check_isinstance(self.k, InjectorKey)
|
184
|
+
|
185
|
+
def provider_fn(self) -> InjectorProviderFn:
|
186
|
+
def pfn(i: Injector) -> ta.Any:
|
187
|
+
return i.provide(self.k)
|
188
|
+
|
189
|
+
return pfn
|
190
|
+
|
191
|
+
|
192
|
+
@dc.dataclass(frozen=True)
|
193
|
+
class ArrayInjectorProvider(InjectorProvider):
|
194
|
+
ps: ta.Sequence[InjectorProvider]
|
195
|
+
|
196
|
+
def provider_fn(self) -> InjectorProviderFn:
|
197
|
+
ps = [p.provider_fn() for p in self.ps]
|
198
|
+
|
199
|
+
def pfn(i: Injector) -> ta.Any:
|
200
|
+
rv = []
|
201
|
+
for ep in ps:
|
202
|
+
o = ep(i)
|
203
|
+
rv.append(o)
|
204
|
+
return rv
|
205
|
+
|
206
|
+
return pfn
|
207
|
+
|
208
|
+
|
209
|
+
###
|
210
|
+
# bindings
|
211
|
+
|
212
|
+
|
213
|
+
@dc.dataclass(frozen=True)
|
214
|
+
class _InjectorBindings(InjectorBindings):
|
215
|
+
bs: ta.Optional[ta.Sequence[InjectorBinding]] = None
|
216
|
+
ps: ta.Optional[ta.Sequence[InjectorBindings]] = None
|
217
|
+
|
218
|
+
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
219
|
+
if self.bs is not None:
|
220
|
+
yield from self.bs
|
221
|
+
if self.ps is not None:
|
222
|
+
for p in self.ps:
|
223
|
+
yield from p.bindings()
|
224
|
+
|
225
|
+
|
226
|
+
def as_injector_bindings(*args: InjectorBindingOrBindings) -> InjectorBindings:
|
227
|
+
bs: ta.List[InjectorBinding] = []
|
228
|
+
ps: ta.List[InjectorBindings] = []
|
229
|
+
for a in args:
|
230
|
+
if isinstance(a, InjectorBindings):
|
231
|
+
ps.append(a)
|
232
|
+
elif isinstance(a, InjectorBinding):
|
233
|
+
bs.append(a)
|
234
|
+
else:
|
235
|
+
raise TypeError(a)
|
236
|
+
return _InjectorBindings(
|
237
|
+
bs or None,
|
238
|
+
ps or None,
|
239
|
+
)
|
240
|
+
|
241
|
+
|
242
|
+
##
|
243
|
+
|
244
|
+
|
245
|
+
@dc.dataclass(frozen=True)
|
246
|
+
class OverridesInjectorBindings(InjectorBindings):
|
247
|
+
p: InjectorBindings
|
248
|
+
m: ta.Mapping[InjectorKey, InjectorBinding]
|
249
|
+
|
250
|
+
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
251
|
+
for b in self.p.bindings():
|
252
|
+
yield self.m.get(b.key, b)
|
253
|
+
|
254
|
+
|
255
|
+
def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
256
|
+
m: ta.Dict[InjectorKey, InjectorBinding] = {}
|
257
|
+
for b in as_injector_bindings(*args).bindings():
|
258
|
+
if b.key in m:
|
259
|
+
raise DuplicateInjectorKeyError(b.key)
|
260
|
+
m[b.key] = b
|
261
|
+
return OverridesInjectorBindings(p, m)
|
262
|
+
|
263
|
+
|
264
|
+
##
|
265
|
+
|
266
|
+
|
267
|
+
def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey, InjectorProvider]:
|
268
|
+
pm: ta.Dict[InjectorKey, InjectorProvider] = {}
|
269
|
+
am: ta.Dict[InjectorKey, ta.List[InjectorProvider]] = {}
|
270
|
+
|
271
|
+
for b in bs.bindings():
|
272
|
+
if b.key.array:
|
273
|
+
am.setdefault(b.key, []).append(b.provider)
|
274
|
+
else:
|
275
|
+
if b.key in pm:
|
276
|
+
raise KeyError(b.key)
|
277
|
+
pm[b.key] = b.provider
|
278
|
+
|
279
|
+
if am:
|
280
|
+
for k, aps in am.items():
|
281
|
+
pm[k] = ArrayInjectorProvider(aps)
|
282
|
+
|
283
|
+
return pm
|
284
|
+
|
285
|
+
|
286
|
+
###
|
287
|
+
# inspection
|
288
|
+
|
289
|
+
|
290
|
+
_INJECTION_SIGNATURE_CACHE: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
|
291
|
+
|
292
|
+
|
293
|
+
def _injection_signature(obj: ta.Any) -> inspect.Signature:
|
294
|
+
try:
|
295
|
+
return _INJECTION_SIGNATURE_CACHE[obj]
|
296
|
+
except TypeError:
|
297
|
+
return inspect.signature(obj)
|
298
|
+
except KeyError:
|
299
|
+
pass
|
300
|
+
sig = inspect.signature(obj)
|
301
|
+
_INJECTION_SIGNATURE_CACHE[obj] = sig
|
302
|
+
return sig
|
303
|
+
|
304
|
+
|
305
|
+
class InjectionKwarg(ta.NamedTuple):
|
306
|
+
name: str
|
307
|
+
key: InjectorKey
|
308
|
+
has_default: bool
|
309
|
+
|
310
|
+
|
311
|
+
class InjectionKwargsTarget(ta.NamedTuple):
|
312
|
+
obj: ta.Any
|
313
|
+
kwargs: ta.Sequence[InjectionKwarg]
|
314
|
+
|
315
|
+
|
316
|
+
def build_injection_kwargs_target(
|
317
|
+
obj: ta.Any,
|
318
|
+
*,
|
319
|
+
skip_args: int = 0,
|
320
|
+
skip_kwargs: ta.Optional[ta.Iterable[ta.Any]] = None,
|
321
|
+
raw_optional: bool = False,
|
322
|
+
) -> InjectionKwargsTarget:
|
323
|
+
sig = _injection_signature(obj)
|
324
|
+
|
325
|
+
seen: ta.Set[InjectorKey] = set(map(as_injector_key, skip_kwargs)) if skip_kwargs is not None else set()
|
326
|
+
kws: ta.List[InjectionKwarg] = []
|
327
|
+
for p in list(sig.parameters.values())[skip_args:]:
|
328
|
+
if p.annotation is inspect.Signature.empty:
|
329
|
+
if p.default is not inspect.Parameter.empty:
|
330
|
+
raise KeyError(f'{obj}, {p.name}')
|
331
|
+
continue
|
332
|
+
|
333
|
+
if p.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY):
|
334
|
+
raise TypeError(sig)
|
335
|
+
|
336
|
+
ann = p.annotation
|
337
|
+
if (
|
338
|
+
not raw_optional and
|
339
|
+
is_optional_alias(ann)
|
340
|
+
):
|
341
|
+
ann = get_optional_alias_arg(ann)
|
342
|
+
|
343
|
+
k = as_injector_key(ann)
|
344
|
+
|
345
|
+
if k in seen:
|
346
|
+
raise DuplicateInjectorKeyError(k)
|
347
|
+
seen.add(k)
|
348
|
+
|
349
|
+
kws.append(InjectionKwarg(
|
350
|
+
p.name,
|
351
|
+
k,
|
352
|
+
p.default is not inspect.Parameter.empty,
|
353
|
+
))
|
354
|
+
|
355
|
+
return InjectionKwargsTarget(
|
356
|
+
obj,
|
357
|
+
kws,
|
358
|
+
)
|
359
|
+
|
360
|
+
|
361
|
+
###
|
362
|
+
# binder
|
363
|
+
|
364
|
+
|
365
|
+
class InjectorBinder:
|
366
|
+
def __new__(cls, *args, **kwargs): # noqa
|
367
|
+
raise TypeError
|
368
|
+
|
369
|
+
_FN_TYPES: ta.Tuple[type, ...] = (
|
370
|
+
types.FunctionType,
|
371
|
+
types.MethodType,
|
372
|
+
|
373
|
+
classmethod,
|
374
|
+
staticmethod,
|
375
|
+
|
376
|
+
functools.partial,
|
377
|
+
functools.partialmethod,
|
378
|
+
)
|
379
|
+
|
380
|
+
@classmethod
|
381
|
+
def _is_fn(cls, obj: ta.Any) -> bool:
|
382
|
+
return isinstance(obj, cls._FN_TYPES)
|
383
|
+
|
384
|
+
@classmethod
|
385
|
+
def bind_as_fn(cls, icls: ta.Type[T]) -> ta.Type[T]:
|
386
|
+
check_isinstance(icls, type)
|
387
|
+
if icls not in cls._FN_TYPES:
|
388
|
+
cls._FN_TYPES = (*cls._FN_TYPES, icls)
|
389
|
+
return icls
|
390
|
+
|
391
|
+
_BANNED_BIND_TYPES: ta.Tuple[type, ...] = (
|
392
|
+
InjectorProvider,
|
393
|
+
)
|
394
|
+
|
395
|
+
@classmethod
|
396
|
+
def bind(
|
397
|
+
cls,
|
398
|
+
obj: ta.Any,
|
399
|
+
*,
|
400
|
+
key: ta.Any = None,
|
401
|
+
tag: ta.Any = None,
|
402
|
+
array: ta.Optional[bool] = None, # noqa
|
403
|
+
|
404
|
+
to_fn: ta.Any = None,
|
405
|
+
to_ctor: ta.Any = None,
|
406
|
+
to_const: ta.Any = None,
|
407
|
+
to_key: ta.Any = None,
|
408
|
+
|
409
|
+
singleton: bool = False,
|
410
|
+
) -> InjectorBinding:
|
411
|
+
if obj is None or obj is inspect.Parameter.empty:
|
412
|
+
raise TypeError(obj)
|
413
|
+
if isinstance(obj, cls._BANNED_BIND_TYPES):
|
414
|
+
raise TypeError(obj)
|
415
|
+
|
416
|
+
##
|
417
|
+
|
418
|
+
if key is not None:
|
419
|
+
key = as_injector_key(key)
|
420
|
+
|
421
|
+
##
|
422
|
+
|
423
|
+
has_to = (
|
424
|
+
to_fn is not None or
|
425
|
+
to_ctor is not None or
|
426
|
+
to_const is not None or
|
427
|
+
to_key is not None
|
428
|
+
)
|
429
|
+
if isinstance(obj, InjectorKey):
|
430
|
+
if key is None:
|
431
|
+
key = obj
|
432
|
+
elif isinstance(obj, type):
|
433
|
+
if not has_to:
|
434
|
+
to_ctor = obj
|
435
|
+
if key is None:
|
436
|
+
key = InjectorKey(obj)
|
437
|
+
elif cls._is_fn(obj) and not has_to:
|
438
|
+
to_fn = obj
|
439
|
+
if key is None:
|
440
|
+
sig = _injection_signature(obj)
|
441
|
+
ty = check_isinstance(sig.return_annotation, type)
|
442
|
+
key = InjectorKey(ty)
|
443
|
+
else:
|
444
|
+
if to_const is not None:
|
445
|
+
raise TypeError('Cannot bind instance with to_const')
|
446
|
+
to_const = obj
|
447
|
+
if key is None:
|
448
|
+
key = InjectorKey(type(obj))
|
449
|
+
del has_to
|
450
|
+
|
451
|
+
##
|
452
|
+
|
453
|
+
if tag is not None:
|
454
|
+
if key.tag is not None:
|
455
|
+
raise TypeError('Tag already set')
|
456
|
+
key = dc.replace(key, tag=tag)
|
457
|
+
|
458
|
+
if array is not None:
|
459
|
+
key = dc.replace(key, array=array)
|
460
|
+
|
461
|
+
##
|
462
|
+
|
463
|
+
providers: ta.List[InjectorProvider] = []
|
464
|
+
if to_fn is not None:
|
465
|
+
providers.append(FnInjectorProvider(to_fn))
|
466
|
+
if to_ctor is not None:
|
467
|
+
providers.append(CtorInjectorProvider(to_ctor))
|
468
|
+
if to_const is not None:
|
469
|
+
providers.append(ConstInjectorProvider(to_const))
|
470
|
+
if to_key is not None:
|
471
|
+
providers.append(LinkInjectorProvider(as_injector_key(to_key)))
|
472
|
+
if not providers:
|
473
|
+
raise TypeError('Must specify provider')
|
474
|
+
if len(providers) > 1:
|
475
|
+
raise TypeError('May not specify multiple providers')
|
476
|
+
provider, = providers
|
477
|
+
|
478
|
+
##
|
479
|
+
|
480
|
+
if singleton:
|
481
|
+
provider = SingletonInjectorProvider(provider)
|
482
|
+
|
483
|
+
##
|
484
|
+
|
485
|
+
binding = InjectorBinding(key, provider)
|
486
|
+
|
487
|
+
##
|
488
|
+
|
489
|
+
return binding
|
490
|
+
|
491
|
+
|
492
|
+
###
|
493
|
+
# injector
|
494
|
+
|
495
|
+
|
496
|
+
_INJECTOR_INJECTOR_KEY = InjectorKey(Injector)
|
497
|
+
|
498
|
+
|
499
|
+
class _Injector(Injector):
|
500
|
+
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
501
|
+
super().__init__()
|
502
|
+
|
503
|
+
self._bs = check_isinstance(bs, InjectorBindings)
|
504
|
+
self._p: ta.Optional[Injector] = check_isinstance(p, (Injector, type(None)))
|
505
|
+
|
506
|
+
self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
|
507
|
+
|
508
|
+
if _INJECTOR_INJECTOR_KEY in self._pfm:
|
509
|
+
raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
|
510
|
+
|
511
|
+
def try_provide(self, key: ta.Any) -> Maybe[ta.Any]:
|
512
|
+
key = as_injector_key(key)
|
513
|
+
|
514
|
+
if key == _INJECTOR_INJECTOR_KEY:
|
515
|
+
return Maybe.just(self)
|
516
|
+
|
517
|
+
fn = self._pfm.get(key)
|
518
|
+
if fn is not None:
|
519
|
+
return Maybe.just(fn(self))
|
520
|
+
|
521
|
+
if self._p is not None:
|
522
|
+
pv = self._p.try_provide(key)
|
523
|
+
if pv is not None:
|
524
|
+
return Maybe.empty()
|
525
|
+
|
526
|
+
return Maybe.empty()
|
527
|
+
|
528
|
+
def provide(self, key: ta.Any) -> ta.Any:
|
529
|
+
v = self.try_provide(key)
|
530
|
+
if v.present:
|
531
|
+
return v.must()
|
532
|
+
raise UnboundInjectorKeyError(key)
|
533
|
+
|
534
|
+
def provide_kwargs(self, obj: ta.Any) -> ta.Mapping[str, ta.Any]:
|
535
|
+
kt = build_injection_kwargs_target(obj)
|
536
|
+
ret: ta.Dict[str, ta.Any] = {}
|
537
|
+
for kw in kt.kwargs:
|
538
|
+
if kw.has_default:
|
539
|
+
if not (mv := self.try_provide(kw.key)).present:
|
540
|
+
continue
|
541
|
+
v = mv.must()
|
542
|
+
else:
|
543
|
+
v = self.provide(kw.key)
|
544
|
+
ret[kw.name] = v
|
545
|
+
return ret
|
546
|
+
|
547
|
+
def inject(self, obj: ta.Any) -> ta.Any:
|
548
|
+
kws = self.provide_kwargs(obj)
|
549
|
+
return obj(**kws)
|
550
|
+
|
551
|
+
|
552
|
+
###
|
553
|
+
# injection helpers
|
554
|
+
|
555
|
+
|
556
|
+
class Injection:
|
557
|
+
def __new__(cls, *args, **kwargs): # noqa
|
558
|
+
raise TypeError
|
559
|
+
|
560
|
+
# keys
|
561
|
+
|
562
|
+
@classmethod
|
563
|
+
def as_key(cls, o: ta.Any) -> InjectorKey:
|
564
|
+
return as_injector_key(o)
|
565
|
+
|
566
|
+
@classmethod
|
567
|
+
def array(cls, o: ta.Any) -> InjectorKey:
|
568
|
+
return dc.replace(as_injector_key(o), array=True)
|
569
|
+
|
570
|
+
@classmethod
|
571
|
+
def tag(cls, o: ta.Any, t: ta.Any) -> InjectorKey:
|
572
|
+
return dc.replace(as_injector_key(o), tag=t)
|
573
|
+
|
574
|
+
# bindings
|
575
|
+
|
576
|
+
@classmethod
|
577
|
+
def as_bindings(cls, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
578
|
+
return as_injector_bindings(*args)
|
579
|
+
|
580
|
+
@classmethod
|
581
|
+
def override(cls, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
582
|
+
return injector_override(p, *args)
|
583
|
+
|
584
|
+
# binder
|
585
|
+
|
586
|
+
@classmethod
|
587
|
+
def bind(
|
588
|
+
cls,
|
589
|
+
obj: ta.Any,
|
590
|
+
*,
|
591
|
+
key: ta.Any = None,
|
592
|
+
tag: ta.Any = None,
|
593
|
+
array: ta.Optional[bool] = None, # noqa
|
594
|
+
|
595
|
+
to_fn: ta.Any = None,
|
596
|
+
to_ctor: ta.Any = None,
|
597
|
+
to_const: ta.Any = None,
|
598
|
+
to_key: ta.Any = None,
|
599
|
+
|
600
|
+
singleton: bool = False,
|
601
|
+
) -> InjectorBinding:
|
602
|
+
return InjectorBinder.bind(
|
603
|
+
obj,
|
604
|
+
|
605
|
+
key=key,
|
606
|
+
tag=tag,
|
607
|
+
array=array,
|
608
|
+
|
609
|
+
to_fn=to_fn,
|
610
|
+
to_ctor=to_ctor,
|
611
|
+
to_const=to_const,
|
612
|
+
to_key=to_key,
|
613
|
+
|
614
|
+
singleton=singleton,
|
615
|
+
)
|
616
|
+
|
617
|
+
# injector
|
618
|
+
|
619
|
+
@classmethod
|
620
|
+
def create_injector(cls, *args: InjectorBindingOrBindings, p: ta.Optional[Injector] = None) -> Injector:
|
621
|
+
return _Injector(as_injector_bindings(*args), p)
|
622
|
+
|
623
|
+
|
624
|
+
inj = Injection
|
omlish/lite/maybes.py
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
import abc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
|
5
|
+
T = ta.TypeVar('T')
|
6
|
+
|
7
|
+
|
8
|
+
class Maybe(ta.Generic[T]):
|
9
|
+
@property
|
10
|
+
@abc.abstractmethod
|
11
|
+
def present(self) -> bool:
|
12
|
+
raise NotImplementedError
|
13
|
+
|
14
|
+
@abc.abstractmethod
|
15
|
+
def must(self) -> T:
|
16
|
+
raise NotImplementedError
|
17
|
+
|
18
|
+
@classmethod
|
19
|
+
def just(cls, v: T) -> 'Maybe[T]':
|
20
|
+
return tuple.__new__(_Maybe, (v,)) # noqa
|
21
|
+
|
22
|
+
_empty: ta.ClassVar['Maybe']
|
23
|
+
|
24
|
+
@classmethod
|
25
|
+
def empty(cls) -> 'Maybe[T]':
|
26
|
+
return Maybe._empty
|
27
|
+
|
28
|
+
|
29
|
+
class _Maybe(Maybe[T], tuple):
|
30
|
+
__slots__ = ()
|
31
|
+
|
32
|
+
def __init_subclass__(cls, **kwargs):
|
33
|
+
raise TypeError
|
34
|
+
|
35
|
+
@property
|
36
|
+
def present(self) -> bool:
|
37
|
+
return bool(self)
|
38
|
+
|
39
|
+
def must(self) -> T:
|
40
|
+
if not self:
|
41
|
+
raise ValueError
|
42
|
+
return self[0]
|
43
|
+
|
44
|
+
|
45
|
+
Maybe._empty = tuple.__new__(_Maybe, ()) # noqa
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=CxGnj-UiRPlZgmgWoovDWrOnqpSEmBy_kqA7cdfSA3w,1431
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=aTRzdXeouYEQLsJZyhmeZxxg3aaQgoLMDv_T3jZ_HZE,3352
|
3
3
|
omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
omlish/argparse.py,sha256=cqKGAqcxuxv_s62z0gq29L9KAvg_3-_rFvXKjVpRJjo,8126
|
5
5
|
omlish/c3.py,sha256=4vogWgwPb8TbNS2KkZxpoWbwjj7MuHG2lQG-hdtkvjI,8062
|
@@ -232,7 +232,7 @@ omlish/http/sessions.py,sha256=VZ_WS5uiQG5y7i3u8oKuQMqf8dPKUOjFm_qk_0OvI8c,4793
|
|
232
232
|
omlish/http/sse.py,sha256=MDs9RvxQXoQliImcc6qK1ERajEYM7Q1l8xmr-9ceNBc,2315
|
233
233
|
omlish/http/wsgi.py,sha256=czZsVUX-l2YTlMrUjKN49wRoP4rVpS0qpeBn4O5BoMY,948
|
234
234
|
omlish/inject/__init__.py,sha256=JQ7x8l9MjU-kJ5ap7cPVq7SY7zbbCIrjyJAF0UeE5-s,1886
|
235
|
-
omlish/inject/binder.py,sha256=
|
235
|
+
omlish/inject/binder.py,sha256=DAbc8TZi5w8Mna0TUtq0mT4jeDVA7i7SlBtOFrh2swc,4185
|
236
236
|
omlish/inject/bindings.py,sha256=pLXn2U3kvmAS-68IOG-tr77DbiI-wp9hGyy4lhG6_H8,525
|
237
237
|
omlish/inject/eagers.py,sha256=5AkGYuwijG0ihsH9NSaZotggalJ5_xWXhHE9mkn6IBA,329
|
238
238
|
omlish/inject/elements.py,sha256=BzTnkNS-3iAMI47LMC2543u6A8Tfk3aJXn3CO191ez4,1547
|
@@ -254,7 +254,7 @@ omlish/inject/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
254
254
|
omlish/inject/impl/bindings.py,sha256=8H586RCgmvwq53XBL9WMbb-1-Tdw_hh9zxIDCwcHA1c,414
|
255
255
|
omlish/inject/impl/elements.py,sha256=bJBbHce_eZyIua2wbcejMwd9Uv-QeYcQ-c5N1qOXSmU,5950
|
256
256
|
omlish/inject/impl/injector.py,sha256=WxIVOF6zvipb44302_j-xQZt8vnNCY0gdLX6UWzslXI,7550
|
257
|
-
omlish/inject/impl/inspect.py,sha256=
|
257
|
+
omlish/inject/impl/inspect.py,sha256=qtHAOo7MvM7atWgxApYXZ0Dmw5zoP45ErPH3mnx9Kvk,2947
|
258
258
|
omlish/inject/impl/multis.py,sha256=rRIWNCiTGaSWQUz_jxEy8LUmzdJDAlG94sLHYDS-ncg,2048
|
259
259
|
omlish/inject/impl/origins.py,sha256=-cdcwz3BWb5LuC9Yn5ynYOwyPsKH06-kCc-3U0PxZ5w,1640
|
260
260
|
omlish/inject/impl/privates.py,sha256=alpCYyk5VJ9lJknbRH2nLVNFYVvFhkj-VC1Vco3zCFQ,2613
|
@@ -298,15 +298,17 @@ omlish/lifecycles/manager.py,sha256=Au66KaO-fI-SEJALaPUJsCHYW2GE20xextk1wKn2BEU,
|
|
298
298
|
omlish/lifecycles/states.py,sha256=zqMOU2ZU-MDNnWuwauM3_anIAiXM8LoBDElDEraptFg,1292
|
299
299
|
omlish/lifecycles/transitions.py,sha256=qQtFby-h4VzbvgaUqT2NnbNumlcOx9FVVADP9t83xj4,1939
|
300
300
|
omlish/lite/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
301
|
-
omlish/lite/cached.py,sha256=
|
301
|
+
omlish/lite/cached.py,sha256=Fs-ljXVJmHBjAaHc-JuJXMEV4MNSX5c_KHZIM3AEaIw,694
|
302
302
|
omlish/lite/check.py,sha256=ouJme9tkzWXKymm_xZDK4mngdYSkxDrok6CSegvf-1w,1015
|
303
|
-
omlish/lite/contextmanagers.py,sha256=
|
303
|
+
omlish/lite/contextmanagers.py,sha256=_jfNdpYvxkbKwyjQLbK-o69W89GoEuUfl_NrCosE9lU,1308
|
304
304
|
omlish/lite/docker.py,sha256=3IVZZtIm7-UdB2SwArmN_MosTva1_KifyYp3YWjODbE,337
|
305
|
+
omlish/lite/inject.py,sha256=O3k8EYRKRKTkqoamM7awbcBvnYwy_fKIUPVf4FcObyg,15060
|
305
306
|
omlish/lite/io.py,sha256=lcpI1cS_Kn90tvYMg8ZWkSlYloS4RFqXCk-rKyclhdg,3148
|
306
307
|
omlish/lite/journald.py,sha256=3nfahFbTrdrfp9txhtto6JYAyrus2kcAFtviqdm3qAo,3949
|
307
308
|
omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
|
308
309
|
omlish/lite/logs.py,sha256=tw7mbQslkyo9LopfgQnj0tYiqJ9TDNiw7D07aF7Dm2g,6176
|
309
310
|
omlish/lite/marshal.py,sha256=SyYMsJ-TaGO9FX7LykBB0WtqsqetX9eLBotPiz3M_xg,9478
|
311
|
+
omlish/lite/maybes.py,sha256=7OlHJ8Q2r4wQ-aRbZSlJY7x0e8gDvufFdlohGEIJ3P4,833
|
310
312
|
omlish/lite/pidfile.py,sha256=PRSDOAXmNkNwxh-Vwif0Nrs8RAmWroiNhLKIbdjwzBc,1723
|
311
313
|
omlish/lite/reflect.py,sha256=9QYJwdINraq1JNMEgvoqeSlVvRRgOXpxAkpgX8EgRXc,1307
|
312
314
|
omlish/lite/runtime.py,sha256=VUhmNQvwf8QzkWSKj4Q0ReieJA_PzHaJNRBivfTseow,452
|
@@ -471,9 +473,9 @@ omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,329
|
|
471
473
|
omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
|
472
474
|
omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
|
473
475
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
474
|
-
omlish-0.0.0.
|
475
|
-
omlish-0.0.0.
|
476
|
-
omlish-0.0.0.
|
477
|
-
omlish-0.0.0.
|
478
|
-
omlish-0.0.0.
|
479
|
-
omlish-0.0.0.
|
476
|
+
omlish-0.0.0.dev121.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
477
|
+
omlish-0.0.0.dev121.dist-info/METADATA,sha256=a5C2OhlCjHtn2VNKTUvzGAslZqvU4XAYaOvBfNd1884,4000
|
478
|
+
omlish-0.0.0.dev121.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
479
|
+
omlish-0.0.0.dev121.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
480
|
+
omlish-0.0.0.dev121.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
481
|
+
omlish-0.0.0.dev121.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|