omlish 0.0.0.dev120__py3-none-any.whl → 0.0.0.dev121__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|