omlish 0.0.0.dev5__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/asyncs/__init__.py +9 -0
- omlish/asyncs/anyio.py +83 -19
- omlish/asyncs/asyncio.py +23 -0
- omlish/asyncs/asyncs.py +9 -6
- omlish/asyncs/bridge.py +316 -0
- omlish/asyncs/trio_asyncio.py +7 -3
- omlish/collections/__init__.py +1 -0
- omlish/collections/identity.py +7 -0
- omlish/configs/strings.py +94 -0
- omlish/dataclasses/__init__.py +9 -0
- omlish/dataclasses/impl/copy.py +30 -0
- omlish/dataclasses/impl/exceptions.py +6 -0
- omlish/dataclasses/impl/fields.py +24 -25
- omlish/dataclasses/impl/init.py +4 -2
- omlish/dataclasses/impl/main.py +2 -0
- omlish/dataclasses/utils.py +44 -0
- omlish/diag/__init__.py +4 -0
- omlish/diag/procfs.py +2 -2
- omlish/{testing → diag}/pydevd.py +35 -0
- omlish/dispatch/_dispatch2.py +65 -0
- omlish/dispatch/_dispatch3.py +104 -0
- omlish/docker.py +1 -1
- omlish/fnpairs.py +11 -0
- omlish/http/asgi.py +2 -1
- omlish/http/collections.py +15 -0
- omlish/http/consts.py +16 -1
- omlish/http/sessions.py +10 -3
- omlish/inject/__init__.py +45 -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 +1 -1
- omlish/inject/impl/elements.py +37 -12
- omlish/inject/impl/injector.py +19 -2
- omlish/inject/impl/inspect.py +33 -5
- omlish/inject/impl/origins.py +75 -0
- omlish/inject/impl/{private.py → privates.py} +2 -2
- omlish/inject/impl/scopes.py +6 -2
- omlish/inject/injector.py +8 -4
- omlish/inject/inspect.py +18 -0
- omlish/inject/keys.py +8 -14
- omlish/inject/listeners.py +26 -0
- omlish/inject/managed.py +76 -10
- omlish/inject/multis.py +68 -18
- 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 +13 -6
- omlish/inject/types.py +3 -1
- omlish/lang/__init__.py +8 -2
- omlish/lang/cached.py +2 -2
- omlish/lang/classes/restrict.py +2 -1
- omlish/lang/classes/simple.py +18 -8
- omlish/lang/contextmanagers.py +12 -3
- omlish/lang/descriptors.py +131 -0
- omlish/lang/functions.py +8 -28
- omlish/lang/iterables.py +20 -1
- omlish/lang/typing.py +5 -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/marshal/__init__.py +4 -0
- omlish/marshal/naming.py +4 -0
- omlish/marshal/objects.py +1 -0
- omlish/marshal/polymorphism.py +4 -4
- omlish/reflect.py +134 -19
- 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/{configs → serde}/dotenv.py +12 -24
- omlish/{json.py → serde/json.py} +2 -1
- 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/term.py +1 -1
- omlish/testing/pytest/__init__.py +3 -2
- omlish/testing/pytest/inject/harness.py +3 -3
- omlish/testing/pytest/marks.py +4 -7
- omlish/testing/pytest/plugins/__init__.py +1 -0
- omlish/testing/pytest/plugins/asyncs.py +136 -0
- omlish/testing/pytest/plugins/pydevd.py +1 -1
- omlish/text/glyphsplit.py +92 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/RECORD +100 -72
- /omlish/{configs → serde}/props.py +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/top_level.txt +0 -0
omlish/inject/__init__.py
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
"""
|
|
2
2
|
~> https://github.com/google/guice/commit/70248eafa90cd70a68b293763e53f6aec656e73c
|
|
3
3
|
"""
|
|
4
|
+
from .binder import ( # noqa
|
|
5
|
+
bind,
|
|
6
|
+
bind_as_fn,
|
|
7
|
+
)
|
|
8
|
+
|
|
4
9
|
from .bindings import ( # noqa
|
|
5
10
|
Binding,
|
|
6
|
-
as_,
|
|
7
|
-
as_binding,
|
|
8
11
|
)
|
|
9
12
|
|
|
10
13
|
from .eagers import ( # noqa
|
|
11
|
-
|
|
14
|
+
Eager,
|
|
12
15
|
)
|
|
13
16
|
|
|
14
17
|
from .elements import ( # noqa
|
|
15
|
-
as_elements,
|
|
16
18
|
Element,
|
|
19
|
+
Elemental,
|
|
17
20
|
Elements,
|
|
21
|
+
as_elements,
|
|
18
22
|
)
|
|
19
23
|
|
|
20
24
|
from .exceptions import ( # noqa
|
|
21
25
|
BaseKeyError,
|
|
26
|
+
ConflictingKeyError,
|
|
22
27
|
CyclicDependencyError,
|
|
23
|
-
DuplicateKeyError,
|
|
24
28
|
ScopeAlreadyOpenError,
|
|
25
29
|
ScopeError,
|
|
26
30
|
ScopeNotOpenError,
|
|
@@ -35,57 +39,81 @@ from .injector import ( # noqa
|
|
|
35
39
|
from .inspect import ( # noqa
|
|
36
40
|
Kwarg,
|
|
37
41
|
KwargsTarget,
|
|
42
|
+
build_kwargs_target,
|
|
43
|
+
tag,
|
|
38
44
|
)
|
|
39
45
|
|
|
40
46
|
from .keys import ( # noqa
|
|
41
47
|
Key,
|
|
42
48
|
as_key,
|
|
43
|
-
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
from .listeners import ( # noqa
|
|
52
|
+
ProvisionListener,
|
|
53
|
+
ProvisionListenerBinding,
|
|
54
|
+
bind_provision_listener,
|
|
44
55
|
)
|
|
45
56
|
|
|
46
57
|
from .managed import ( # noqa
|
|
58
|
+
create_async_managed_injector,
|
|
47
59
|
create_managed_injector,
|
|
60
|
+
make_async_managed_provider,
|
|
61
|
+
make_managed_provider,
|
|
48
62
|
)
|
|
49
63
|
|
|
50
64
|
from .multis import ( # noqa
|
|
51
65
|
MapBinding,
|
|
66
|
+
MapProvider,
|
|
52
67
|
SetBinding,
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
SetProvider,
|
|
69
|
+
MapBinder,
|
|
70
|
+
SetBinder,
|
|
71
|
+
|
|
72
|
+
MapBinder as map_binder, # noqa
|
|
73
|
+
SetBinder as set_binder, # noqa
|
|
55
74
|
)
|
|
56
75
|
|
|
57
76
|
|
|
58
77
|
from .overrides import ( # noqa
|
|
78
|
+
Overrides,
|
|
59
79
|
override,
|
|
60
80
|
)
|
|
61
81
|
|
|
62
|
-
from .
|
|
63
|
-
|
|
82
|
+
from .origins import ( # noqa
|
|
83
|
+
HasOrigins,
|
|
84
|
+
Origin,
|
|
85
|
+
Origins,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
from .privates import ( # noqa
|
|
89
|
+
Expose,
|
|
90
|
+
Private,
|
|
64
91
|
private,
|
|
92
|
+
|
|
93
|
+
Expose as expose, # noqa
|
|
65
94
|
)
|
|
66
95
|
|
|
67
96
|
from .providers import ( # noqa
|
|
97
|
+
ConstProvider,
|
|
98
|
+
CtorProvider,
|
|
99
|
+
FnProvider,
|
|
100
|
+
LinkProvider,
|
|
68
101
|
Provider,
|
|
69
|
-
as_provider,
|
|
70
|
-
const,
|
|
71
|
-
ctor,
|
|
72
|
-
fn,
|
|
73
|
-
link,
|
|
74
102
|
)
|
|
75
103
|
|
|
76
104
|
from .scopes import ( # noqa
|
|
77
105
|
ScopeBinding,
|
|
106
|
+
ScopeSeededProvider,
|
|
78
107
|
SeededScope,
|
|
79
108
|
Singleton,
|
|
80
109
|
Thread,
|
|
81
110
|
bind_scope,
|
|
82
111
|
bind_scope_seed,
|
|
83
112
|
enter_seeded_scope,
|
|
84
|
-
in_,
|
|
85
|
-
singleton,
|
|
86
113
|
)
|
|
87
114
|
|
|
88
115
|
from .types import ( # noqa
|
|
89
116
|
Scope,
|
|
117
|
+
Tag,
|
|
90
118
|
Unscoped,
|
|
91
119
|
)
|
omlish/inject/binder.py
CHANGED
|
@@ -1,12 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- multis?
|
|
4
|
+
|
|
5
|
+
class SetBinding(Element, lang.Final):
|
|
6
|
+
class SetProvider(Provider):
|
|
7
|
+
|
|
8
|
+
class MapBinding(Element, lang.Final):
|
|
9
|
+
class MapProvider(Provider):
|
|
10
|
+
"""
|
|
11
|
+
import functools
|
|
12
|
+
import inspect
|
|
13
|
+
import types
|
|
1
14
|
import typing as ta
|
|
2
15
|
|
|
16
|
+
from .. import check
|
|
17
|
+
from .. import dataclasses as dc
|
|
18
|
+
from .. import lang
|
|
19
|
+
from .. import reflect as rfl
|
|
20
|
+
from .bindings import Binding
|
|
21
|
+
from .eagers import Eager
|
|
3
22
|
from .elements import Element
|
|
4
23
|
from .elements import Elements
|
|
5
|
-
from .
|
|
24
|
+
from .keys import Key
|
|
25
|
+
from .keys import as_key
|
|
26
|
+
from .privates import Expose
|
|
27
|
+
from .providers import ConstProvider
|
|
28
|
+
from .providers import CtorProvider
|
|
29
|
+
from .providers import FnProvider
|
|
30
|
+
from .providers import LinkProvider
|
|
31
|
+
from .providers import Provider
|
|
32
|
+
from .scopes import SCOPE_ALIASES
|
|
33
|
+
from .scopes import Singleton
|
|
34
|
+
from .types import Scope
|
|
35
|
+
from .types import Unscoped
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if ta.TYPE_CHECKING:
|
|
39
|
+
from .impl.inspect import inspect as _inspect
|
|
40
|
+
else:
|
|
41
|
+
_inspect = lang.proxy_import('.impl.inspect', __package__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
T = ta.TypeVar('T')
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
_FN_TYPES: tuple[type, ...] = (
|
|
51
|
+
types.FunctionType,
|
|
52
|
+
types.MethodType,
|
|
53
|
+
|
|
54
|
+
classmethod,
|
|
55
|
+
staticmethod,
|
|
56
|
+
|
|
57
|
+
functools.partial,
|
|
58
|
+
functools.partialmethod,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _is_fn(obj: ta.Any) -> bool:
|
|
63
|
+
return isinstance(obj, _FN_TYPES)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def bind_as_fn(cls: type[T]) -> type[T]:
|
|
67
|
+
check.isinstance(cls, type)
|
|
68
|
+
global _FN_TYPES
|
|
69
|
+
if cls not in _FN_TYPES:
|
|
70
|
+
_FN_TYPES = (*_FN_TYPES, cls)
|
|
71
|
+
return cls
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
_BANNED_BIND_TYPES = (
|
|
78
|
+
Element,
|
|
79
|
+
Provider,
|
|
80
|
+
Elements,
|
|
81
|
+
Scope,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def bind(
|
|
86
|
+
obj: ta.Any,
|
|
87
|
+
*,
|
|
88
|
+
tag: ta.Any = None,
|
|
89
|
+
|
|
90
|
+
in_: Scope | None = None,
|
|
91
|
+
singleton: bool = False,
|
|
92
|
+
|
|
93
|
+
to_fn: ta.Any = None,
|
|
94
|
+
to_ctor: ta.Any = None,
|
|
95
|
+
to_const: ta.Any = None,
|
|
96
|
+
to_key: ta.Any = None,
|
|
97
|
+
|
|
98
|
+
eager: bool = False,
|
|
99
|
+
expose: bool = False,
|
|
100
|
+
) -> Element | Elements:
|
|
101
|
+
if obj is None or obj is inspect.Parameter.empty:
|
|
102
|
+
raise TypeError(obj)
|
|
103
|
+
if isinstance(obj, _BANNED_BIND_TYPES):
|
|
104
|
+
raise TypeError(obj)
|
|
105
|
+
|
|
106
|
+
##
|
|
107
|
+
|
|
108
|
+
has_to = (
|
|
109
|
+
to_fn is not None or
|
|
110
|
+
to_ctor is not None or
|
|
111
|
+
to_const is not None or
|
|
112
|
+
to_key is not None
|
|
113
|
+
)
|
|
114
|
+
if isinstance(obj, Key):
|
|
115
|
+
key = obj
|
|
116
|
+
elif isinstance(obj, type):
|
|
117
|
+
if not has_to:
|
|
118
|
+
to_ctor = obj
|
|
119
|
+
key = Key(obj)
|
|
120
|
+
elif isinstance(obj, rfl.TYPES) or rfl.is_type(obj):
|
|
121
|
+
key = Key(obj)
|
|
122
|
+
elif _is_fn(obj) and not has_to:
|
|
123
|
+
sig = _inspect.signature(obj)
|
|
124
|
+
ty = rfl.type_(sig.return_annotation)
|
|
125
|
+
to_fn = obj
|
|
126
|
+
key = Key(ty)
|
|
127
|
+
else:
|
|
128
|
+
if to_const is not None:
|
|
129
|
+
raise TypeError('Cannot bind instance with to_const')
|
|
130
|
+
to_const = obj
|
|
131
|
+
key = Key(type(obj))
|
|
132
|
+
del has_to
|
|
133
|
+
|
|
134
|
+
##
|
|
135
|
+
|
|
136
|
+
if tag is not None:
|
|
137
|
+
if key.tag is not None:
|
|
138
|
+
raise TypeError('Tag already set')
|
|
139
|
+
key = dc.replace(key, tag=tag)
|
|
140
|
+
|
|
141
|
+
##
|
|
142
|
+
|
|
143
|
+
providers: list[Provider] = []
|
|
144
|
+
if to_fn is not None:
|
|
145
|
+
providers.append(FnProvider(to_fn))
|
|
146
|
+
if to_ctor is not None:
|
|
147
|
+
providers.append(CtorProvider(to_ctor))
|
|
148
|
+
if to_const is not None:
|
|
149
|
+
providers.append(ConstProvider(to_const))
|
|
150
|
+
if to_key is not None:
|
|
151
|
+
providers.append(LinkProvider(as_key(to_key)))
|
|
152
|
+
if not providers:
|
|
153
|
+
raise TypeError('Must specify provider')
|
|
154
|
+
if len(providers) > 1:
|
|
155
|
+
raise TypeError('May not specify multiple providers')
|
|
156
|
+
provider, = providers
|
|
157
|
+
|
|
158
|
+
##
|
|
159
|
+
|
|
160
|
+
scopes: list[Scope] = []
|
|
161
|
+
if in_ is not None:
|
|
162
|
+
if isinstance(in_, str):
|
|
163
|
+
scopes.append(SCOPE_ALIASES[in_])
|
|
164
|
+
else:
|
|
165
|
+
scopes.append(check.isinstance(in_, Scope))
|
|
166
|
+
if singleton:
|
|
167
|
+
scopes.append(Singleton())
|
|
168
|
+
if len(scopes) > 1:
|
|
169
|
+
raise TypeError('May not specify multiple scopes')
|
|
170
|
+
scope: Scope
|
|
171
|
+
if not scopes:
|
|
172
|
+
scope = Unscoped()
|
|
173
|
+
else:
|
|
174
|
+
scope, = scopes
|
|
175
|
+
|
|
176
|
+
##
|
|
177
|
+
|
|
178
|
+
binding = Binding(key, provider, scope)
|
|
179
|
+
|
|
180
|
+
##
|
|
6
181
|
|
|
182
|
+
elements: list[Element] = [binding]
|
|
7
183
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
184
|
+
if eager:
|
|
185
|
+
elements.append(Eager(key))
|
|
186
|
+
if expose:
|
|
187
|
+
elements.append(Expose(key))
|
|
11
188
|
|
|
12
|
-
|
|
189
|
+
if len(elements) == 1:
|
|
190
|
+
return elements[0]
|
|
191
|
+
else:
|
|
192
|
+
return Elements(frozenset(elements))
|
omlish/inject/bindings.py
CHANGED
|
@@ -1,49 +1,16 @@
|
|
|
1
|
-
import typing as ta
|
|
2
|
-
|
|
3
1
|
from .. import check
|
|
4
2
|
from .. import dataclasses as dc
|
|
5
3
|
from .. import lang
|
|
6
4
|
from .elements import Element
|
|
7
|
-
from .elements import Elements
|
|
8
5
|
from .keys import Key
|
|
9
|
-
from .keys import as_key
|
|
10
6
|
from .providers import Provider
|
|
11
|
-
from .providers import as_provider
|
|
12
|
-
from .providers import const
|
|
13
|
-
from .providers import ctor
|
|
14
|
-
from .providers import fn
|
|
15
7
|
from .types import Scope
|
|
16
8
|
from .types import Unscoped
|
|
17
9
|
|
|
18
10
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
|
|
22
11
|
@dc.dataclass(frozen=True)
|
|
23
12
|
@dc.extra_params(cache_hash=True)
|
|
24
13
|
class Binding(Element, lang.Final):
|
|
25
|
-
key: Key
|
|
26
|
-
provider: Provider
|
|
27
|
-
scope: Scope = Unscoped()
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def as_binding(o: ta.Any) -> Binding:
|
|
34
|
-
check.not_none(o)
|
|
35
|
-
if isinstance(o, Binding):
|
|
36
|
-
return o
|
|
37
|
-
check.not_isinstance(o, (Element, Elements))
|
|
38
|
-
if isinstance(o, Provider):
|
|
39
|
-
return Binding(Key(check.not_none(o.provided_ty())), o)
|
|
40
|
-
if isinstance(o, type):
|
|
41
|
-
return as_binding(ctor(o))
|
|
42
|
-
if callable(o):
|
|
43
|
-
return as_binding(fn(o))
|
|
44
|
-
ty = type(o)
|
|
45
|
-
return Binding(Key(ty), const(o, ty))
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def as_(k: ta.Any, p: ta.Any) -> Binding:
|
|
49
|
-
return Binding(as_key(k), as_provider(p))
|
|
14
|
+
key: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
|
15
|
+
provider: Provider = dc.xfield(coerce=check.of_isinstance(Provider))
|
|
16
|
+
scope: Scope = dc.xfield(default=Unscoped(), coerce=check.of_isinstance(Scope))
|
omlish/inject/eagers.py
CHANGED
|
@@ -2,20 +2,14 @@
|
|
|
2
2
|
TODO:
|
|
3
3
|
- SCOPED - eagers for EACH SCOPE
|
|
4
4
|
"""
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
from .. import check
|
|
7
6
|
from .. import dataclasses as dc
|
|
8
7
|
from .. import lang
|
|
9
8
|
from .elements import Element
|
|
10
9
|
from .keys import Key
|
|
11
|
-
from .keys import as_key
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
@dc.dataclass(frozen=True)
|
|
15
13
|
@dc.extra_params(cache_hash=True)
|
|
16
14
|
class Eager(Element, lang.Final):
|
|
17
|
-
key: Key
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def eager(k: ta.Any) -> Element:
|
|
21
|
-
return Eager(as_key(k))
|
|
15
|
+
key: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
omlish/inject/elements.py
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
TODO:
|
|
3
|
-
- as_element[s] - universal
|
|
4
|
-
"""
|
|
1
|
+
import abc
|
|
5
2
|
import typing as ta
|
|
6
3
|
|
|
4
|
+
from .. import check
|
|
7
5
|
from .. import dataclasses as dc
|
|
8
6
|
from .. import lang
|
|
7
|
+
from .impl.origins import HasOriginsImpl
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
class Element(lang.Abstract):
|
|
10
|
+
class Element(HasOriginsImpl, lang.Abstract, lang.PackageSealed):
|
|
12
11
|
pass
|
|
13
12
|
|
|
14
13
|
|
|
14
|
+
class ElementGenerator(lang.Abstract, lang.PackageSealed):
|
|
15
|
+
@abc.abstractmethod
|
|
16
|
+
def __iter__(self) -> ta.Iterator[Element]:
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
|
|
19
|
+
|
|
15
20
|
@dc.dataclass(frozen=True)
|
|
16
21
|
class Elements(lang.Final):
|
|
17
|
-
es: frozenset[Element] | None = None
|
|
18
|
-
cs: frozenset['Elements'] | None = None
|
|
22
|
+
es: frozenset[Element] | None = dc.xfield(None, coerce=check.of_isinstance((frozenset, None)))
|
|
23
|
+
cs: frozenset['Elements'] | None = dc.xfield(None, coerce=check.of_isinstance((frozenset, None)))
|
|
19
24
|
|
|
20
25
|
def __iter__(self) -> ta.Generator[Element, None, None]:
|
|
21
26
|
if self.es:
|
|
@@ -25,18 +30,34 @@ class Elements(lang.Final):
|
|
|
25
30
|
yield from c
|
|
26
31
|
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
Elemental = ta.Union[ # noqa
|
|
34
|
+
Element,
|
|
35
|
+
Elements,
|
|
36
|
+
ElementGenerator,
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def as_elements(*args: Elemental) -> Elements:
|
|
29
41
|
es: set[Element] = set()
|
|
30
42
|
cs: set[Elements] = set()
|
|
31
|
-
|
|
43
|
+
|
|
44
|
+
def rec(a):
|
|
32
45
|
if isinstance(a, Element):
|
|
33
46
|
es.add(a)
|
|
34
47
|
elif isinstance(a, Elements):
|
|
35
48
|
cs.add(a)
|
|
49
|
+
elif isinstance(a, ElementGenerator):
|
|
50
|
+
for n in a:
|
|
51
|
+
rec(n)
|
|
36
52
|
else:
|
|
37
53
|
raise TypeError(a)
|
|
54
|
+
|
|
55
|
+
for a in args:
|
|
56
|
+
rec(a)
|
|
57
|
+
|
|
38
58
|
if not es and len(cs) == 1:
|
|
39
59
|
return next(iter(cs))
|
|
60
|
+
|
|
40
61
|
return Elements(
|
|
41
62
|
frozenset(es) if es else None,
|
|
42
63
|
frozenset(cs) if cs else None,
|
omlish/inject/exceptions.py
CHANGED
omlish/inject/impl/elements.py
CHANGED
|
@@ -10,6 +10,7 @@ Multi's + Scopes:
|
|
|
10
10
|
|
|
11
11
|
Element Types:
|
|
12
12
|
- Binding
|
|
13
|
+
- ProvisionListenerBinding
|
|
13
14
|
- SetBinding
|
|
14
15
|
- MapBinding
|
|
15
16
|
- Eager
|
|
@@ -18,6 +19,7 @@ Element Types:
|
|
|
18
19
|
- Private
|
|
19
20
|
- ScopeBinding
|
|
20
21
|
"""
|
|
22
|
+
import copy
|
|
21
23
|
import typing as ta
|
|
22
24
|
|
|
23
25
|
from ... import check
|
|
@@ -27,28 +29,32 @@ from ..bindings import Binding
|
|
|
27
29
|
from ..eagers import Eager
|
|
28
30
|
from ..elements import Element
|
|
29
31
|
from ..elements import Elements
|
|
30
|
-
from ..exceptions import
|
|
32
|
+
from ..exceptions import ConflictingKeyError
|
|
33
|
+
from ..exceptions import UnboundKeyError
|
|
31
34
|
from ..keys import Key
|
|
35
|
+
from ..listeners import ProvisionListenerBinding
|
|
32
36
|
from ..multis import MapBinding
|
|
33
37
|
from ..multis import MapProvider
|
|
34
38
|
from ..multis import SetBinding
|
|
35
39
|
from ..multis import SetProvider
|
|
36
40
|
from ..overrides import Overrides
|
|
37
|
-
from ..
|
|
38
|
-
from ..
|
|
41
|
+
from ..privates import Expose
|
|
42
|
+
from ..privates import Private
|
|
39
43
|
from ..scopes import ScopeBinding
|
|
40
44
|
from ..types import Scope
|
|
41
45
|
from .bindings import BindingImpl
|
|
42
46
|
from .multis import make_multi_provider_impl
|
|
47
|
+
from .origins import Origins
|
|
48
|
+
from .origins import set_origins
|
|
43
49
|
from .providers import ProviderImpl
|
|
44
50
|
from .providers import make_provider_impl
|
|
45
51
|
from .scopes import make_scope_impl
|
|
46
52
|
|
|
47
53
|
|
|
48
54
|
if ta.TYPE_CHECKING:
|
|
49
|
-
from . import
|
|
55
|
+
from . import privates as privates_
|
|
50
56
|
else:
|
|
51
|
-
|
|
57
|
+
privates_ = lang.proxy_import('.privates', __package__)
|
|
52
58
|
|
|
53
59
|
|
|
54
60
|
ElementT = ta.TypeVar('ElementT', bound=Element)
|
|
@@ -60,17 +66,17 @@ class ElementCollection(lang.Final):
|
|
|
60
66
|
|
|
61
67
|
self._es = check.isinstance(es, Elements)
|
|
62
68
|
|
|
63
|
-
self._private_infos: ta.MutableMapping[Private,
|
|
69
|
+
self._private_infos: ta.MutableMapping[Private, privates_.PrivateInfo] | None = None
|
|
64
70
|
|
|
65
71
|
##
|
|
66
72
|
|
|
67
|
-
def _get_private_info(self, p: Private) -> '
|
|
73
|
+
def _get_private_info(self, p: Private) -> 'privates_.PrivateInfo':
|
|
68
74
|
if (pis := self._private_infos) is None:
|
|
69
75
|
self._private_infos = pis = col.IdentityKeyDict()
|
|
70
76
|
try:
|
|
71
77
|
return pis[p]
|
|
72
78
|
except KeyError:
|
|
73
|
-
pis[p] = ec =
|
|
79
|
+
pis[p] = ec = privates_.PrivateInfo(self, p)
|
|
74
80
|
return ec
|
|
75
81
|
|
|
76
82
|
##
|
|
@@ -103,6 +109,9 @@ class ElementCollection(lang.Final):
|
|
|
103
109
|
elif isinstance(e, (SetBinding, MapBinding)):
|
|
104
110
|
add(e.multi_key, e)
|
|
105
111
|
|
|
112
|
+
elif isinstance(e, ProvisionListenerBinding):
|
|
113
|
+
add(None, e)
|
|
114
|
+
|
|
106
115
|
elif isinstance(e, Overrides):
|
|
107
116
|
ovr = self._build_raw_element_multimap(e.ovr)
|
|
108
117
|
src = self._build_raw_element_multimap(e.src)
|
|
@@ -128,6 +137,24 @@ class ElementCollection(lang.Final):
|
|
|
128
137
|
|
|
129
138
|
##
|
|
130
139
|
|
|
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
|
|
154
|
+
|
|
155
|
+
else:
|
|
156
|
+
return check.isinstance(check.single(bs), Binding)
|
|
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
160
|
for k, es in em.items():
|
|
@@ -138,14 +165,12 @@ class ElementCollection(lang.Final):
|
|
|
138
165
|
|
|
139
166
|
es_by_ty.pop(Eager, None)
|
|
140
167
|
es_by_ty.pop(Expose, None)
|
|
168
|
+
es_by_ty.pop(ProvisionListenerBinding, None)
|
|
141
169
|
|
|
142
170
|
if (bs := es_by_ty.pop(Binding, None)):
|
|
143
|
-
|
|
144
|
-
raise DuplicateKeyError(k)
|
|
171
|
+
b = self._get_single_binding(k, bs) # type: ignore
|
|
145
172
|
|
|
146
|
-
b: Binding = check.isinstance(check.single(bs), Binding)
|
|
147
173
|
p: ProviderImpl
|
|
148
|
-
|
|
149
174
|
if isinstance(b.provider, (SetProvider, MapProvider)):
|
|
150
175
|
p = make_multi_provider_impl(b.provider, es_by_ty)
|
|
151
176
|
|
omlish/inject/impl/injector.py
CHANGED
|
@@ -12,6 +12,8 @@ TODO:
|
|
|
12
12
|
- multiple live request scopes on single injector - use private injectors?
|
|
13
13
|
"""
|
|
14
14
|
import contextlib
|
|
15
|
+
import functools
|
|
16
|
+
import itertools
|
|
15
17
|
import typing as ta
|
|
16
18
|
import weakref
|
|
17
19
|
|
|
@@ -24,6 +26,8 @@ from ..injector import Injector
|
|
|
24
26
|
from ..inspect import KwargsTarget
|
|
25
27
|
from ..keys import Key
|
|
26
28
|
from ..keys import as_key
|
|
29
|
+
from ..listeners import ProvisionListener
|
|
30
|
+
from ..listeners import ProvisionListenerBinding
|
|
27
31
|
from ..scopes import ScopeBinding
|
|
28
32
|
from ..scopes import Singleton
|
|
29
33
|
from ..scopes import Thread
|
|
@@ -55,6 +59,13 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
55
59
|
|
|
56
60
|
self._bim = ec.binding_impl_map()
|
|
57
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
|
+
)
|
|
58
69
|
|
|
59
70
|
self._cs: weakref.WeakSet[InjectorImpl] | None = None
|
|
60
71
|
self._root: InjectorImpl = p._root if p is not None else self # noqa
|
|
@@ -70,6 +81,7 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
self._instantiate_eagers(Unscoped())
|
|
84
|
+
self._instantiate_eagers(Singleton())
|
|
73
85
|
|
|
74
86
|
_root: 'InjectorImpl'
|
|
75
87
|
|
|
@@ -109,7 +121,7 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
109
121
|
check.not_in(key, self._provisions)
|
|
110
122
|
self._provisions[key] = v
|
|
111
123
|
|
|
112
|
-
def __enter__(self
|
|
124
|
+
def __enter__(self) -> ta.Self:
|
|
113
125
|
return self
|
|
114
126
|
|
|
115
127
|
def __exit__(self, *exc) -> None:
|
|
@@ -142,7 +154,12 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
142
154
|
bi = self._bim.get(key)
|
|
143
155
|
if bi is not None:
|
|
144
156
|
sc = self._scopes[bi.scope]
|
|
145
|
-
|
|
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
|
+
|
|
146
163
|
cr.handle_provision(key, v)
|
|
147
164
|
return lang.just(v)
|
|
148
165
|
|