omlish 0.0.0.dev4__py3-none-any.whl → 0.0.0.dev6__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.
Potentially problematic release.
This version of omlish might be problematic. Click here for more details.
- omlish/__about__.py +1 -1
- omlish/__init__.py +1 -1
- omlish/asyncs/__init__.py +10 -4
- omlish/asyncs/anyio.py +142 -12
- omlish/asyncs/asyncio.py +23 -0
- omlish/asyncs/asyncs.py +9 -6
- omlish/asyncs/bridge.py +316 -0
- omlish/asyncs/flavors.py +27 -1
- omlish/asyncs/trio_asyncio.py +28 -18
- omlish/c3.py +1 -1
- omlish/cached.py +1 -2
- omlish/collections/__init__.py +5 -1
- omlish/collections/cache/impl.py +1 -1
- omlish/collections/identity.py +7 -0
- omlish/collections/indexed.py +1 -1
- omlish/collections/utils.py +38 -6
- omlish/configs/__init__.py +5 -0
- omlish/configs/classes.py +53 -0
- omlish/configs/strings.py +94 -0
- omlish/dataclasses/__init__.py +9 -0
- omlish/dataclasses/impl/api.py +1 -1
- omlish/dataclasses/impl/as_.py +1 -1
- omlish/dataclasses/impl/copy.py +30 -0
- omlish/dataclasses/impl/exceptions.py +6 -0
- omlish/dataclasses/impl/fields.py +25 -25
- omlish/dataclasses/impl/init.py +5 -3
- omlish/dataclasses/impl/main.py +3 -0
- omlish/dataclasses/impl/metaclass.py +6 -1
- omlish/dataclasses/impl/order.py +1 -1
- omlish/dataclasses/impl/reflect.py +15 -2
- omlish/dataclasses/utils.py +44 -0
- omlish/defs.py +1 -1
- omlish/diag/__init__.py +4 -0
- omlish/diag/procfs.py +31 -3
- omlish/diag/procstats.py +32 -0
- omlish/{testing → diag}/pydevd.py +35 -0
- omlish/diag/replserver/console.py +3 -3
- omlish/diag/replserver/server.py +6 -5
- omlish/diag/threads.py +86 -0
- omlish/dispatch/_dispatch2.py +65 -0
- omlish/dispatch/_dispatch3.py +104 -0
- omlish/docker.py +20 -1
- omlish/fnpairs.py +37 -18
- omlish/graphs/dags.py +113 -0
- omlish/graphs/domination.py +268 -0
- omlish/graphs/trees.py +2 -2
- omlish/http/__init__.py +25 -0
- omlish/http/asgi.py +132 -0
- omlish/http/collections.py +15 -0
- omlish/http/consts.py +47 -5
- omlish/http/cookies.py +194 -0
- omlish/http/dates.py +70 -0
- omlish/http/encodings.py +6 -0
- omlish/http/json.py +273 -0
- omlish/http/sessions.py +204 -0
- omlish/inject/__init__.py +51 -17
- omlish/inject/binder.py +185 -5
- omlish/inject/bindings.py +3 -36
- omlish/inject/eagers.py +2 -8
- omlish/inject/elements.py +30 -9
- omlish/inject/exceptions.py +3 -3
- omlish/inject/impl/elements.py +65 -31
- omlish/inject/impl/injector.py +20 -2
- omlish/inject/impl/inspect.py +33 -5
- omlish/inject/impl/multis.py +74 -0
- omlish/inject/impl/origins.py +75 -0
- omlish/inject/impl/{private.py → privates.py} +2 -2
- omlish/inject/impl/providers.py +19 -39
- omlish/inject/{proxy.py → impl/proxy.py} +2 -2
- omlish/inject/impl/scopes.py +7 -2
- omlish/inject/injector.py +9 -4
- omlish/inject/inspect.py +18 -0
- omlish/inject/keys.py +11 -23
- omlish/inject/listeners.py +26 -0
- omlish/inject/managed.py +76 -10
- omlish/inject/multis.py +120 -0
- omlish/inject/origins.py +27 -0
- omlish/inject/overrides.py +5 -4
- omlish/inject/{private.py → privates.py} +6 -10
- omlish/inject/providers.py +12 -85
- omlish/inject/scopes.py +20 -9
- omlish/inject/types.py +2 -8
- omlish/iterators.py +13 -0
- omlish/lang/__init__.py +12 -2
- omlish/lang/cached.py +2 -2
- omlish/lang/classes/restrict.py +3 -2
- omlish/lang/classes/simple.py +18 -8
- omlish/lang/classes/virtual.py +2 -2
- omlish/lang/contextmanagers.py +75 -2
- omlish/lang/datetimes.py +6 -5
- omlish/lang/descriptors.py +131 -0
- omlish/lang/functions.py +18 -28
- omlish/lang/imports.py +11 -2
- omlish/lang/iterables.py +20 -1
- omlish/lang/typing.py +6 -0
- omlish/lifecycles/__init__.py +34 -0
- omlish/lifecycles/abstract.py +43 -0
- omlish/lifecycles/base.py +51 -0
- omlish/lifecycles/contextmanagers.py +74 -0
- omlish/lifecycles/controller.py +116 -0
- omlish/lifecycles/manager.py +161 -0
- omlish/lifecycles/states.py +43 -0
- omlish/lifecycles/transitions.py +64 -0
- omlish/logs/formatters.py +1 -1
- omlish/logs/utils.py +1 -1
- omlish/marshal/__init__.py +4 -0
- omlish/marshal/datetimes.py +1 -1
- omlish/marshal/naming.py +4 -0
- omlish/marshal/objects.py +1 -0
- omlish/marshal/polymorphism.py +4 -4
- omlish/reflect.py +139 -18
- omlish/secrets/__init__.py +7 -0
- omlish/secrets/marshal.py +41 -0
- omlish/secrets/passwords.py +120 -0
- omlish/secrets/secrets.py +47 -0
- omlish/serde/__init__.py +0 -0
- omlish/serde/dotenv.py +574 -0
- omlish/{json.py → serde/json.py} +4 -2
- omlish/serde/props.py +604 -0
- omlish/serde/yaml.py +223 -0
- omlish/sql/dbs.py +1 -1
- omlish/sql/duckdb.py +136 -0
- omlish/sql/sqlean.py +17 -0
- omlish/sync.py +70 -0
- omlish/term.py +7 -2
- omlish/testing/pytest/__init__.py +8 -2
- omlish/testing/pytest/helpers.py +0 -24
- omlish/testing/pytest/inject/harness.py +4 -4
- omlish/testing/pytest/marks.py +45 -0
- omlish/testing/pytest/plugins/__init__.py +3 -0
- omlish/testing/pytest/plugins/asyncs.py +136 -0
- omlish/testing/pytest/plugins/managermarks.py +60 -0
- omlish/testing/pytest/plugins/pydevd.py +1 -1
- omlish/testing/testing.py +10 -0
- omlish/text/delimit.py +4 -0
- omlish/text/glyphsplit.py +92 -0
- {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/METADATA +1 -1
- omlish-0.0.0.dev6.dist-info/RECORD +240 -0
- {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/WHEEL +1 -1
- omlish/configs/props.py +0 -64
- omlish-0.0.0.dev4.dist-info/RECORD +0 -195
- {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/top_level.txt +0 -0
omlish/inject/impl/elements.py
CHANGED
|
@@ -10,12 +10,16 @@ Multi's + Scopes:
|
|
|
10
10
|
|
|
11
11
|
Element Types:
|
|
12
12
|
- Binding
|
|
13
|
+
- ProvisionListenerBinding
|
|
14
|
+
- SetBinding
|
|
15
|
+
- MapBinding
|
|
13
16
|
- Eager
|
|
14
17
|
- Overrides
|
|
15
18
|
- Expose
|
|
16
19
|
- Private
|
|
17
20
|
- ScopeBinding
|
|
18
21
|
"""
|
|
22
|
+
import copy
|
|
19
23
|
import typing as ta
|
|
20
24
|
|
|
21
25
|
from ... import check
|
|
@@ -25,22 +29,32 @@ from ..bindings import Binding
|
|
|
25
29
|
from ..eagers import Eager
|
|
26
30
|
from ..elements import Element
|
|
27
31
|
from ..elements import Elements
|
|
28
|
-
from ..exceptions import
|
|
32
|
+
from ..exceptions import ConflictingKeyError
|
|
33
|
+
from ..exceptions import UnboundKeyError
|
|
29
34
|
from ..keys import Key
|
|
35
|
+
from ..listeners import ProvisionListenerBinding
|
|
36
|
+
from ..multis import MapBinding
|
|
37
|
+
from ..multis import MapProvider
|
|
38
|
+
from ..multis import SetBinding
|
|
39
|
+
from ..multis import SetProvider
|
|
30
40
|
from ..overrides import Overrides
|
|
31
|
-
from ..
|
|
32
|
-
from ..
|
|
41
|
+
from ..privates import Expose
|
|
42
|
+
from ..privates import Private
|
|
33
43
|
from ..scopes import ScopeBinding
|
|
34
44
|
from ..types import Scope
|
|
35
45
|
from .bindings import BindingImpl
|
|
36
|
-
from .
|
|
46
|
+
from .multis import make_multi_provider_impl
|
|
47
|
+
from .origins import Origins
|
|
48
|
+
from .origins import set_origins
|
|
49
|
+
from .providers import ProviderImpl
|
|
37
50
|
from .providers import make_provider_impl
|
|
38
51
|
from .scopes import make_scope_impl
|
|
39
52
|
|
|
53
|
+
|
|
40
54
|
if ta.TYPE_CHECKING:
|
|
41
|
-
from . import
|
|
55
|
+
from . import privates as privates_
|
|
42
56
|
else:
|
|
43
|
-
|
|
57
|
+
privates_ = lang.proxy_import('.privates', __package__)
|
|
44
58
|
|
|
45
59
|
|
|
46
60
|
ElementT = ta.TypeVar('ElementT', bound=Element)
|
|
@@ -52,17 +66,17 @@ class ElementCollection(lang.Final):
|
|
|
52
66
|
|
|
53
67
|
self._es = check.isinstance(es, Elements)
|
|
54
68
|
|
|
55
|
-
self._private_infos: ta.MutableMapping[Private,
|
|
69
|
+
self._private_infos: ta.MutableMapping[Private, privates_.PrivateInfo] | None = None
|
|
56
70
|
|
|
57
71
|
##
|
|
58
72
|
|
|
59
|
-
def _get_private_info(self, p: Private) -> '
|
|
73
|
+
def _get_private_info(self, p: Private) -> 'privates_.PrivateInfo':
|
|
60
74
|
if (pis := self._private_infos) is None:
|
|
61
75
|
self._private_infos = pis = col.IdentityKeyDict()
|
|
62
76
|
try:
|
|
63
77
|
return pis[p]
|
|
64
78
|
except KeyError:
|
|
65
|
-
pis[p] = ec =
|
|
79
|
+
pis[p] = ec = privates_.PrivateInfo(self, p)
|
|
66
80
|
return ec
|
|
67
81
|
|
|
68
82
|
##
|
|
@@ -92,10 +106,16 @@ class ElementCollection(lang.Final):
|
|
|
92
106
|
pi = self._get_private_info(e)
|
|
93
107
|
self._build_raw_element_multimap(pi.owner_elements(), out)
|
|
94
108
|
|
|
109
|
+
elif isinstance(e, (SetBinding, MapBinding)):
|
|
110
|
+
add(e.multi_key, e)
|
|
111
|
+
|
|
112
|
+
elif isinstance(e, ProvisionListenerBinding):
|
|
113
|
+
add(None, e)
|
|
114
|
+
|
|
95
115
|
elif isinstance(e, Overrides):
|
|
96
116
|
ovr = self._build_raw_element_multimap(e.ovr)
|
|
97
117
|
src = self._build_raw_element_multimap(e.src)
|
|
98
|
-
for k, b in src.items():
|
|
118
|
+
for k, b in src.items(): # FIXME: merge None keys?
|
|
99
119
|
try:
|
|
100
120
|
bs = ovr[k]
|
|
101
121
|
except KeyError:
|
|
@@ -117,36 +137,50 @@ class ElementCollection(lang.Final):
|
|
|
117
137
|
|
|
118
138
|
##
|
|
119
139
|
|
|
120
|
-
def
|
|
121
|
-
if
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
140
|
+
def _get_single_binding(self, k: Key, bs: ta.Sequence[Binding]) -> Binding:
|
|
141
|
+
if not bs:
|
|
142
|
+
raise UnboundKeyError(k)
|
|
143
|
+
|
|
144
|
+
elif len(bs) > 1:
|
|
145
|
+
d: dict = {}
|
|
146
|
+
for b in bs:
|
|
147
|
+
d.setdefault(b, []).append(b)
|
|
148
|
+
if len(d) > 1:
|
|
149
|
+
raise ConflictingKeyError(k)
|
|
150
|
+
l = check.single(d.values())
|
|
151
|
+
b = copy.copy(l[0])
|
|
152
|
+
set_origins(b, Origins(tuple(o for c in l for o in c.origins)))
|
|
153
|
+
return b
|
|
127
154
|
|
|
128
155
|
else:
|
|
129
|
-
|
|
156
|
+
return check.isinstance(check.single(bs), Binding)
|
|
130
157
|
|
|
131
158
|
def _build_binding_impl_map(self, em: ta.Mapping[Key | None, ta.Sequence[Element]]) -> dict[Key, BindingImpl]:
|
|
132
159
|
pm: dict[Key, BindingImpl] = {}
|
|
133
|
-
mm: dict[Key, list[BindingImpl]] = {}
|
|
134
160
|
for k, es in em.items():
|
|
135
161
|
if k is None:
|
|
136
162
|
continue
|
|
137
163
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
164
|
+
es_by_ty = col.multi_map_by(type, es)
|
|
165
|
+
|
|
166
|
+
es_by_ty.pop(Eager, None)
|
|
167
|
+
es_by_ty.pop(Expose, None)
|
|
168
|
+
es_by_ty.pop(ProvisionListenerBinding, None)
|
|
169
|
+
|
|
170
|
+
if (bs := es_by_ty.pop(Binding, None)):
|
|
171
|
+
b = self._get_single_binding(k, bs) # type: ignore
|
|
172
|
+
|
|
173
|
+
p: ProviderImpl
|
|
174
|
+
if isinstance(b.provider, (SetProvider, MapProvider)):
|
|
175
|
+
p = make_multi_provider_impl(b.provider, es_by_ty)
|
|
176
|
+
|
|
177
|
+
else:
|
|
178
|
+
p = make_provider_impl(b.provider)
|
|
179
|
+
|
|
180
|
+
pm[k] = BindingImpl(b.key, p, b.scope, b)
|
|
181
|
+
|
|
182
|
+
if es_by_ty:
|
|
183
|
+
raise TypeError(set(es_by_ty))
|
|
150
184
|
|
|
151
185
|
return pm
|
|
152
186
|
|
omlish/inject/impl/injector.py
CHANGED
|
@@ -9,8 +9,11 @@ TODO:
|
|
|
9
9
|
- ** eagers in any scope, on scope init/open
|
|
10
10
|
- injection listeners
|
|
11
11
|
- unions - raise on ambiguous - usecase: sql.AsyncEngineLike
|
|
12
|
+
- multiple live request scopes on single injector - use private injectors?
|
|
12
13
|
"""
|
|
13
14
|
import contextlib
|
|
15
|
+
import functools
|
|
16
|
+
import itertools
|
|
14
17
|
import typing as ta
|
|
15
18
|
import weakref
|
|
16
19
|
|
|
@@ -23,6 +26,8 @@ from ..injector import Injector
|
|
|
23
26
|
from ..inspect import KwargsTarget
|
|
24
27
|
from ..keys import Key
|
|
25
28
|
from ..keys import as_key
|
|
29
|
+
from ..listeners import ProvisionListener
|
|
30
|
+
from ..listeners import ProvisionListenerBinding
|
|
26
31
|
from ..scopes import ScopeBinding
|
|
27
32
|
from ..scopes import Singleton
|
|
28
33
|
from ..scopes import Thread
|
|
@@ -54,6 +59,13 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
54
59
|
|
|
55
60
|
self._bim = ec.binding_impl_map()
|
|
56
61
|
self._ekbs = ec.eager_keys_by_scope()
|
|
62
|
+
self._pls: tuple[ProvisionListener, ...] = tuple(
|
|
63
|
+
b.listener
|
|
64
|
+
for b in itertools.chain(
|
|
65
|
+
ec.elements_of_type(ProvisionListenerBinding),
|
|
66
|
+
(p._pls if p is not None else ()), # noqa
|
|
67
|
+
)
|
|
68
|
+
)
|
|
57
69
|
|
|
58
70
|
self._cs: weakref.WeakSet[InjectorImpl] | None = None
|
|
59
71
|
self._root: InjectorImpl = p._root if p is not None else self # noqa
|
|
@@ -69,6 +81,7 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
self._instantiate_eagers(Unscoped())
|
|
84
|
+
self._instantiate_eagers(Singleton())
|
|
72
85
|
|
|
73
86
|
_root: 'InjectorImpl'
|
|
74
87
|
|
|
@@ -108,7 +121,7 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
108
121
|
check.not_in(key, self._provisions)
|
|
109
122
|
self._provisions[key] = v
|
|
110
123
|
|
|
111
|
-
def __enter__(self
|
|
124
|
+
def __enter__(self) -> ta.Self:
|
|
112
125
|
return self
|
|
113
126
|
|
|
114
127
|
def __exit__(self, *exc) -> None:
|
|
@@ -141,7 +154,12 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
141
154
|
bi = self._bim.get(key)
|
|
142
155
|
if bi is not None:
|
|
143
156
|
sc = self._scopes[bi.scope]
|
|
144
|
-
|
|
157
|
+
|
|
158
|
+
fn = lambda: sc.provide(bi, self) # noqa
|
|
159
|
+
for pl in self._pls:
|
|
160
|
+
fn = functools.partial(pl, self, key, bi.binding, fn)
|
|
161
|
+
v = fn()
|
|
162
|
+
|
|
145
163
|
cr.handle_provision(key, v)
|
|
146
164
|
return lang.just(v)
|
|
147
165
|
|
omlish/inject/impl/inspect.py
CHANGED
|
@@ -5,24 +5,29 @@ TODO:
|
|
|
5
5
|
- tag decorator - @inj.tag(x='foo')
|
|
6
6
|
- *unpack optional here*
|
|
7
7
|
"""
|
|
8
|
+
import dataclasses as dc
|
|
8
9
|
import inspect
|
|
9
10
|
import types
|
|
10
11
|
import typing as ta
|
|
11
12
|
import weakref
|
|
12
13
|
|
|
13
14
|
from ... import reflect as rfl
|
|
14
|
-
from ..exceptions import
|
|
15
|
+
from ..exceptions import ConflictingKeyError
|
|
15
16
|
from ..inspect import Kwarg
|
|
16
17
|
from ..inspect import KwargsTarget
|
|
17
18
|
from ..keys import Key
|
|
18
19
|
from ..keys import as_key
|
|
19
|
-
from ..
|
|
20
|
+
from ..types import Tag
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
T = ta.TypeVar('T')
|
|
22
24
|
P = ta.ParamSpec('P')
|
|
23
25
|
R = ta.TypeVar('R')
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
##
|
|
29
|
+
|
|
30
|
+
|
|
26
31
|
_signature_cache: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
|
|
27
32
|
|
|
28
33
|
|
|
@@ -38,9 +43,20 @@ def signature(obj: ta.Any) -> inspect.Signature:
|
|
|
38
43
|
return sig
|
|
39
44
|
|
|
40
45
|
|
|
46
|
+
##
|
|
47
|
+
|
|
48
|
+
|
|
41
49
|
_tags: ta.MutableMapping[ta.Any, dict[str, ta.Any]] = weakref.WeakKeyDictionary()
|
|
42
50
|
|
|
43
51
|
|
|
52
|
+
def tag(obj: T, **kwargs: ta.Any) -> T:
|
|
53
|
+
for v in kwargs.values():
|
|
54
|
+
if isinstance(v, Tag):
|
|
55
|
+
raise TypeError(v)
|
|
56
|
+
_tags.setdefault(obj, {}).update(**kwargs)
|
|
57
|
+
return obj
|
|
58
|
+
|
|
59
|
+
|
|
44
60
|
def tags(**kwargs: ta.Any) -> ta.Callable[[ta.Callable[P, R]], ta.Callable[P, R]]:
|
|
45
61
|
def inner(obj):
|
|
46
62
|
_tags[obj] = kwargs
|
|
@@ -48,6 +64,9 @@ def tags(**kwargs: ta.Any) -> ta.Callable[[ta.Callable[P, R]], ta.Callable[P, R]
|
|
|
48
64
|
return inner
|
|
49
65
|
|
|
50
66
|
|
|
67
|
+
##
|
|
68
|
+
|
|
69
|
+
|
|
51
70
|
def build_kwargs_target(
|
|
52
71
|
obj: ta.Any,
|
|
53
72
|
*,
|
|
@@ -78,12 +97,21 @@ def build_kwargs_target(
|
|
|
78
97
|
):
|
|
79
98
|
[ann] = [a for a in rf.args if a is not types.NoneType]
|
|
80
99
|
|
|
81
|
-
|
|
100
|
+
rty = rfl.type_(ann)
|
|
101
|
+
|
|
102
|
+
tag = None
|
|
103
|
+
if isinstance(rty, rfl.Annotated):
|
|
104
|
+
for e in rty.md:
|
|
105
|
+
if isinstance(e, Tag):
|
|
106
|
+
tag = e.tag
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
k: Key = Key(rfl.strip_annotations(rty), tag=tag)
|
|
82
110
|
if tags is not None and (pt := tags.get(p.name)) is not None:
|
|
83
|
-
k =
|
|
111
|
+
k = dc.replace(k, tag=pt)
|
|
84
112
|
|
|
85
113
|
if k in seen:
|
|
86
|
-
raise
|
|
114
|
+
raise ConflictingKeyError(k)
|
|
87
115
|
seen.add(k)
|
|
88
116
|
|
|
89
117
|
kws.append(Kwarg(
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from ... import dataclasses as dc
|
|
4
|
+
from ... import lang
|
|
5
|
+
from ..elements import Element
|
|
6
|
+
from ..injector import Injector
|
|
7
|
+
from ..multis import MapBinding
|
|
8
|
+
from ..multis import MapProvider
|
|
9
|
+
from ..multis import SetBinding
|
|
10
|
+
from ..multis import SetProvider
|
|
11
|
+
from ..providers import LinkProvider
|
|
12
|
+
from ..providers import Provider
|
|
13
|
+
from .providers import LinkProviderImpl
|
|
14
|
+
from .providers import ProviderImpl
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
18
|
+
class SetProviderImpl(ProviderImpl, lang.Final):
|
|
19
|
+
ps: ta.Sequence[ProviderImpl]
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def providers(self) -> ta.Iterable[Provider]:
|
|
23
|
+
for p in self.ps:
|
|
24
|
+
yield from p.providers
|
|
25
|
+
|
|
26
|
+
def provide(self, injector: Injector) -> ta.Any:
|
|
27
|
+
rv = set()
|
|
28
|
+
for ep in self.ps:
|
|
29
|
+
o = ep.provide(injector)
|
|
30
|
+
rv.add(o)
|
|
31
|
+
return rv
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
35
|
+
class MapProviderImpl(ProviderImpl, lang.Final):
|
|
36
|
+
class Entry(ta.NamedTuple):
|
|
37
|
+
k: ta.Any
|
|
38
|
+
v: ProviderImpl
|
|
39
|
+
|
|
40
|
+
es: ta.Sequence[Entry]
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def providers(self) -> ta.Iterable[Provider]:
|
|
44
|
+
for e in self.es:
|
|
45
|
+
yield from e.v.providers
|
|
46
|
+
|
|
47
|
+
def provide(self, injector: Injector) -> ta.Any:
|
|
48
|
+
rv = {}
|
|
49
|
+
for e in self.es:
|
|
50
|
+
o = e.v.provide(injector)
|
|
51
|
+
rv[e.k] = o
|
|
52
|
+
return rv
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def make_multi_provider_impl(p: Provider, es_by_ty: ta.MutableMapping[type, list[Element]]) -> ProviderImpl:
|
|
56
|
+
if isinstance(p, SetProvider):
|
|
57
|
+
sbs: ta.Iterable[SetBinding] = es_by_ty.pop(SetBinding, ()) # type: ignore
|
|
58
|
+
return SetProviderImpl([
|
|
59
|
+
LinkProviderImpl(LinkProvider(sb.dst))
|
|
60
|
+
for sb in sbs
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
elif isinstance(p, MapProvider):
|
|
64
|
+
mbs: ta.Iterable[MapBinding] = es_by_ty.pop(MapBinding, ()) # type: ignore
|
|
65
|
+
return MapProviderImpl([
|
|
66
|
+
MapProviderImpl.Entry(
|
|
67
|
+
mb.map_key,
|
|
68
|
+
LinkProviderImpl(LinkProvider(mb.dst)),
|
|
69
|
+
)
|
|
70
|
+
for mb in mbs
|
|
71
|
+
])
|
|
72
|
+
|
|
73
|
+
else:
|
|
74
|
+
raise TypeError(p)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import types
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from ... import dataclasses as dc
|
|
6
|
+
from ... import lang
|
|
7
|
+
from ..origins import HasOrigins
|
|
8
|
+
from ..origins import Origin
|
|
9
|
+
from ..origins import Origins
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
HasOriginsT = ta.TypeVar('HasOriginsT', bound=HasOrigins)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ORIGIN_NUM_FRAMES = 8
|
|
19
|
+
ORIGIN_BASE_OFS = 2
|
|
20
|
+
|
|
21
|
+
ORIGIN_IGNORED_PACKAGES = frozenset([
|
|
22
|
+
__package__,
|
|
23
|
+
__package__.rpartition('.')[0],
|
|
24
|
+
|
|
25
|
+
lang.__name__,
|
|
26
|
+
lang.functions.__name__,
|
|
27
|
+
|
|
28
|
+
dc.__name__,
|
|
29
|
+
dc.impl.__name__, # noqa
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_origin_frame(f: types.FrameType) -> bool:
|
|
34
|
+
gl = f.f_globals
|
|
35
|
+
try:
|
|
36
|
+
pkg = gl['__package__']
|
|
37
|
+
except KeyError:
|
|
38
|
+
pass
|
|
39
|
+
else:
|
|
40
|
+
if pkg in ORIGIN_IGNORED_PACKAGES:
|
|
41
|
+
return False
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def build_origin(ofs: int = 0) -> Origin:
|
|
46
|
+
lst = [] # type: ignore
|
|
47
|
+
cur = sys._getframe(ORIGIN_BASE_OFS + ofs) # noqa
|
|
48
|
+
while len(lst) < ORIGIN_NUM_FRAMES and cur is not None:
|
|
49
|
+
if is_origin_frame(cur):
|
|
50
|
+
lst.append(cur)
|
|
51
|
+
cur = cur.f_back # type: ignore
|
|
52
|
+
return Origin(tuple(str(f) for f in lst))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
ORIGINS_ATTR = '__inject_origins__'
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def set_origins(obj: HasOriginsT, origins: Origins) -> HasOriginsT:
|
|
62
|
+
obj.__dict__[ORIGINS_ATTR] = origins
|
|
63
|
+
return obj
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class HasOriginsImpl(HasOrigins):
|
|
67
|
+
@property
|
|
68
|
+
def origins(self) -> Origins:
|
|
69
|
+
return self.__dict__[ORIGINS_ATTR]
|
|
70
|
+
|
|
71
|
+
def __post_init__(self) -> None:
|
|
72
|
+
dc.maybe_post_init(super())
|
|
73
|
+
if ORIGINS_ATTR in self.__dict__:
|
|
74
|
+
raise AttributeError('Origin already set')
|
|
75
|
+
set_origins(self, Origins((build_origin(),)))
|
|
@@ -14,8 +14,8 @@ from ..eagers import Eager
|
|
|
14
14
|
from ..elements import Element
|
|
15
15
|
from ..injector import Injector
|
|
16
16
|
from ..keys import Key
|
|
17
|
-
from ..
|
|
18
|
-
from ..
|
|
17
|
+
from ..privates import Expose
|
|
18
|
+
from ..privates import Private
|
|
19
19
|
from ..providers import Provider
|
|
20
20
|
from ..scopes import Singleton
|
|
21
21
|
from .elements import ElementCollection
|
omlish/inject/impl/providers.py
CHANGED
|
@@ -5,10 +5,9 @@ TODO:
|
|
|
5
5
|
import abc
|
|
6
6
|
import typing as ta
|
|
7
7
|
|
|
8
|
-
from .. import Cls
|
|
9
|
-
from ... import check
|
|
10
8
|
from ... import dataclasses as dc
|
|
11
9
|
from ... import lang
|
|
10
|
+
from ... import reflect as rfl
|
|
12
11
|
from ..injector import Injector
|
|
13
12
|
from ..inspect import KwargsTarget
|
|
14
13
|
from ..providers import ConstProvider
|
|
@@ -19,6 +18,9 @@ from ..providers import Provider
|
|
|
19
18
|
from .inspect import build_kwargs_target
|
|
20
19
|
|
|
21
20
|
|
|
21
|
+
##
|
|
22
|
+
|
|
23
|
+
|
|
22
24
|
class ProviderImpl(lang.Abstract):
|
|
23
25
|
@property
|
|
24
26
|
@abc.abstractmethod
|
|
@@ -30,14 +32,20 @@ class ProviderImpl(lang.Abstract):
|
|
|
30
32
|
raise NotImplementedError
|
|
31
33
|
|
|
32
34
|
|
|
35
|
+
##
|
|
36
|
+
|
|
37
|
+
|
|
33
38
|
@dc.dataclass(frozen=True, eq=False)
|
|
34
39
|
class InternalProvider(Provider):
|
|
35
40
|
impl: ProviderImpl
|
|
36
41
|
|
|
37
|
-
def
|
|
42
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
38
43
|
raise TypeError
|
|
39
44
|
|
|
40
45
|
|
|
46
|
+
##
|
|
47
|
+
|
|
48
|
+
|
|
41
49
|
@dc.dataclass(frozen=True, eq=False)
|
|
42
50
|
class CallableProviderImpl(ProviderImpl, lang.Final):
|
|
43
51
|
p: FnProvider | CtorProvider
|
|
@@ -51,6 +59,9 @@ class CallableProviderImpl(ProviderImpl, lang.Final):
|
|
|
51
59
|
return injector.inject(self.kt)
|
|
52
60
|
|
|
53
61
|
|
|
62
|
+
##
|
|
63
|
+
|
|
64
|
+
|
|
54
65
|
@dc.dataclass(frozen=True, eq=False)
|
|
55
66
|
class ConstProviderImpl(ProviderImpl, lang.Final):
|
|
56
67
|
p: ConstProvider
|
|
@@ -63,6 +74,9 @@ class ConstProviderImpl(ProviderImpl, lang.Final):
|
|
|
63
74
|
return self.p.v
|
|
64
75
|
|
|
65
76
|
|
|
77
|
+
##
|
|
78
|
+
|
|
79
|
+
|
|
66
80
|
@dc.dataclass(frozen=True, eq=False)
|
|
67
81
|
class LinkProviderImpl(ProviderImpl, lang.Final):
|
|
68
82
|
p: LinkProvider
|
|
@@ -75,46 +89,12 @@ class LinkProviderImpl(ProviderImpl, lang.Final):
|
|
|
75
89
|
return injector.provide(self.p.k)
|
|
76
90
|
|
|
77
91
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def _unnest_multi_providers(ps: ta.Iterable[ProviderImpl]) -> ta.Sequence[ProviderImpl]:
|
|
82
|
-
lst = []
|
|
83
|
-
|
|
84
|
-
def rec(o):
|
|
85
|
-
if isinstance(o, MultiProviderImpl):
|
|
86
|
-
for c in o.ps:
|
|
87
|
-
rec(c)
|
|
88
|
-
else:
|
|
89
|
-
lst.append(check.isinstance(o, ProviderImpl))
|
|
90
|
-
|
|
91
|
-
for o in ps:
|
|
92
|
-
rec(o)
|
|
93
|
-
return tuple(lst)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@dc.dataclass(frozen=True, eq=False)
|
|
97
|
-
class MultiProviderImpl(ProviderImpl, lang.Final):
|
|
98
|
-
ps: ta.Sequence[ProviderImpl] = dc.xfield(coerce=_unnest_multi_providers)
|
|
99
|
-
|
|
100
|
-
@property
|
|
101
|
-
def providers(self) -> ta.Iterable[Provider]:
|
|
102
|
-
for p in self.ps:
|
|
103
|
-
yield from p.providers
|
|
104
|
-
|
|
105
|
-
def provide(self, injector: Injector) -> ta.Any:
|
|
106
|
-
rv = []
|
|
107
|
-
for ep in self.ps:
|
|
108
|
-
o = ep.provide(injector)
|
|
109
|
-
if isinstance(o, _ILLEGAL_MULTI_TYPES):
|
|
110
|
-
raise TypeError(o)
|
|
111
|
-
rv.extend(o)
|
|
112
|
-
return rv
|
|
92
|
+
##
|
|
113
93
|
|
|
114
94
|
|
|
115
95
|
PROVIDER_IMPLS_BY_PROVIDER: dict[type[Provider], ta.Callable[..., ProviderImpl]] = {
|
|
116
96
|
FnProvider: lambda p: CallableProviderImpl(p, build_kwargs_target(p.fn)),
|
|
117
|
-
CtorProvider: lambda p: CallableProviderImpl(p, build_kwargs_target(p.
|
|
97
|
+
CtorProvider: lambda p: CallableProviderImpl(p, build_kwargs_target(p.ty)),
|
|
118
98
|
ConstProvider: ConstProviderImpl,
|
|
119
99
|
LinkProvider: LinkProviderImpl,
|
|
120
100
|
InternalProvider: lambda p: p.impl,
|
omlish/inject/impl/scopes.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- ContextVar ('context')
|
|
4
|
+
"""
|
|
1
5
|
import abc
|
|
2
6
|
import contextlib
|
|
3
7
|
import threading
|
|
@@ -13,8 +17,8 @@ from ..exceptions import ScopeAlreadyOpenError
|
|
|
13
17
|
from ..exceptions import ScopeNotOpenError
|
|
14
18
|
from ..injector import Injector
|
|
15
19
|
from ..keys import Key
|
|
20
|
+
from ..providers import FnProvider
|
|
16
21
|
from ..providers import Provider
|
|
17
|
-
from ..providers import fn
|
|
18
22
|
from ..scopes import ScopeSeededProvider
|
|
19
23
|
from ..scopes import SeededScope
|
|
20
24
|
from ..scopes import Singleton
|
|
@@ -25,6 +29,7 @@ from .bindings import BindingImpl
|
|
|
25
29
|
from .providers import PROVIDER_IMPLS_BY_PROVIDER
|
|
26
30
|
from .providers import ProviderImpl
|
|
27
31
|
|
|
32
|
+
|
|
28
33
|
if ta.TYPE_CHECKING:
|
|
29
34
|
from . import injector as injector_
|
|
30
35
|
else:
|
|
@@ -161,7 +166,7 @@ class SeededScopeImpl(ScopeImpl):
|
|
|
161
166
|
return as_elements(
|
|
162
167
|
Binding(
|
|
163
168
|
Key(SeededScope.Manager, tag=self._ss),
|
|
164
|
-
|
|
169
|
+
FnProvider(lang.typed_partial(SeededScopeImpl.Manager, ss=self._ss)),
|
|
165
170
|
scope=Singleton(),
|
|
166
171
|
),
|
|
167
172
|
)
|
omlish/inject/injector.py
CHANGED
|
@@ -2,11 +2,16 @@ import abc
|
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
4
|
from .. import lang
|
|
5
|
-
from .elements import
|
|
5
|
+
from .elements import Elemental
|
|
6
|
+
from .elements import as_elements
|
|
6
7
|
from .inspect import KwargsTarget
|
|
7
8
|
from .keys import Key
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
if ta.TYPE_CHECKING:
|
|
12
|
+
from .impl import injector as _injector
|
|
13
|
+
else:
|
|
14
|
+
_injector = lang.proxy_import('.impl.injector', __package__)
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
T = ta.TypeVar('T')
|
|
@@ -36,5 +41,5 @@ class Injector(lang.Abstract):
|
|
|
36
41
|
return self.provide(target)
|
|
37
42
|
|
|
38
43
|
|
|
39
|
-
def create_injector(
|
|
40
|
-
return
|
|
44
|
+
def create_injector(*args: Elemental) -> Injector:
|
|
45
|
+
return _injector.create_injector(as_elements(*args))
|
omlish/inject/inspect.py
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
|
+
from .. import lang
|
|
3
4
|
from .keys import Key
|
|
4
5
|
|
|
5
6
|
|
|
7
|
+
if ta.TYPE_CHECKING:
|
|
8
|
+
from .impl import inspect as _inspect
|
|
9
|
+
else:
|
|
10
|
+
_inspect = lang.proxy_import('.impl.inspect', __package__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
T = ta.TypeVar('T')
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
class Kwarg(ta.NamedTuple):
|
|
7
17
|
name: str
|
|
8
18
|
key: Key
|
|
@@ -12,3 +22,11 @@ class Kwarg(ta.NamedTuple):
|
|
|
12
22
|
class KwargsTarget(ta.NamedTuple):
|
|
13
23
|
obj: ta.Any
|
|
14
24
|
kwargs: ta.Sequence[Kwarg]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def tag(obj: T, **kwargs: ta.Any) -> T:
|
|
28
|
+
return _inspect.tag(obj, **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def build_kwargs_target(obj: ta.Any, **kwargs: ta.Any) -> KwargsTarget:
|
|
32
|
+
return _inspect.build_kwargs_target(obj, **kwargs)
|