omlish 0.0.0.dev1__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 +7 -0
- omlish/__init__.py +0 -0
- omlish/argparse.py +223 -0
- omlish/asyncs/__init__.py +17 -0
- omlish/asyncs/anyio.py +23 -0
- omlish/asyncs/asyncio.py +19 -0
- omlish/asyncs/asyncs.py +76 -0
- omlish/asyncs/futures.py +179 -0
- omlish/asyncs/trio.py +11 -0
- omlish/c3.py +173 -0
- omlish/cached.py +9 -0
- omlish/check.py +231 -0
- omlish/collections/__init__.py +63 -0
- omlish/collections/_abc.py +156 -0
- omlish/collections/_io_abc.py +78 -0
- omlish/collections/cache/__init__.py +11 -0
- omlish/collections/cache/descriptor.py +188 -0
- omlish/collections/cache/impl.py +485 -0
- omlish/collections/cache/types.py +37 -0
- omlish/collections/coerce.py +337 -0
- omlish/collections/frozen.py +148 -0
- omlish/collections/identity.py +106 -0
- omlish/collections/indexed.py +75 -0
- omlish/collections/mappings.py +127 -0
- omlish/collections/ordered.py +81 -0
- omlish/collections/persistent.py +36 -0
- omlish/collections/skiplist.py +193 -0
- omlish/collections/sorted.py +126 -0
- omlish/collections/treap.py +228 -0
- omlish/collections/treapmap.py +144 -0
- omlish/collections/unmodifiable.py +174 -0
- omlish/collections/utils.py +110 -0
- omlish/configs/__init__.py +0 -0
- omlish/configs/flattening.py +147 -0
- omlish/configs/props.py +64 -0
- omlish/dataclasses/__init__.py +83 -0
- omlish/dataclasses/impl/__init__.py +6 -0
- omlish/dataclasses/impl/api.py +260 -0
- omlish/dataclasses/impl/as_.py +76 -0
- omlish/dataclasses/impl/exceptions.py +2 -0
- omlish/dataclasses/impl/fields.py +148 -0
- omlish/dataclasses/impl/frozen.py +55 -0
- omlish/dataclasses/impl/hashing.py +85 -0
- omlish/dataclasses/impl/init.py +173 -0
- omlish/dataclasses/impl/internals.py +118 -0
- omlish/dataclasses/impl/main.py +150 -0
- omlish/dataclasses/impl/metaclass.py +126 -0
- omlish/dataclasses/impl/metadata.py +74 -0
- omlish/dataclasses/impl/order.py +47 -0
- omlish/dataclasses/impl/params.py +150 -0
- omlish/dataclasses/impl/processing.py +16 -0
- omlish/dataclasses/impl/reflect.py +173 -0
- omlish/dataclasses/impl/replace.py +40 -0
- omlish/dataclasses/impl/repr.py +34 -0
- omlish/dataclasses/impl/simple.py +92 -0
- omlish/dataclasses/impl/slots.py +80 -0
- omlish/dataclasses/impl/utils.py +167 -0
- omlish/defs.py +193 -0
- omlish/dispatch/__init__.py +3 -0
- omlish/dispatch/dispatch.py +137 -0
- omlish/dispatch/functions.py +52 -0
- omlish/dispatch/methods.py +162 -0
- omlish/docker.py +149 -0
- omlish/dynamic.py +220 -0
- omlish/graphs/__init__.py +0 -0
- omlish/graphs/dot/__init__.py +19 -0
- omlish/graphs/dot/items.py +162 -0
- omlish/graphs/dot/rendering.py +147 -0
- omlish/graphs/dot/utils.py +30 -0
- omlish/graphs/trees.py +249 -0
- omlish/http/__init__.py +0 -0
- omlish/http/consts.py +20 -0
- omlish/http/wsgi.py +34 -0
- omlish/inject/__init__.py +85 -0
- omlish/inject/binder.py +12 -0
- omlish/inject/bindings.py +49 -0
- omlish/inject/eagers.py +21 -0
- omlish/inject/elements.py +43 -0
- omlish/inject/exceptions.py +49 -0
- omlish/inject/impl/__init__.py +0 -0
- omlish/inject/impl/bindings.py +19 -0
- omlish/inject/impl/elements.py +154 -0
- omlish/inject/impl/injector.py +182 -0
- omlish/inject/impl/inspect.py +98 -0
- omlish/inject/impl/private.py +109 -0
- omlish/inject/impl/providers.py +132 -0
- omlish/inject/impl/scopes.py +198 -0
- omlish/inject/injector.py +40 -0
- omlish/inject/inspect.py +14 -0
- omlish/inject/keys.py +43 -0
- omlish/inject/managed.py +24 -0
- omlish/inject/overrides.py +18 -0
- omlish/inject/private.py +29 -0
- omlish/inject/providers.py +111 -0
- omlish/inject/proxy.py +48 -0
- omlish/inject/scopes.py +84 -0
- omlish/inject/types.py +21 -0
- omlish/iterators.py +184 -0
- omlish/json.py +194 -0
- omlish/lang/__init__.py +112 -0
- omlish/lang/cached.py +267 -0
- omlish/lang/classes/__init__.py +24 -0
- omlish/lang/classes/abstract.py +74 -0
- omlish/lang/classes/restrict.py +137 -0
- omlish/lang/classes/simple.py +120 -0
- omlish/lang/classes/test/__init__.py +0 -0
- omlish/lang/classes/test/test_abstract.py +89 -0
- omlish/lang/classes/test/test_restrict.py +71 -0
- omlish/lang/classes/test/test_simple.py +58 -0
- omlish/lang/classes/test/test_virtual.py +72 -0
- omlish/lang/classes/virtual.py +130 -0
- omlish/lang/clsdct.py +67 -0
- omlish/lang/cmp.py +63 -0
- omlish/lang/contextmanagers.py +249 -0
- omlish/lang/datetimes.py +67 -0
- omlish/lang/descriptors.py +52 -0
- omlish/lang/functions.py +126 -0
- omlish/lang/imports.py +153 -0
- omlish/lang/iterables.py +54 -0
- omlish/lang/maybes.py +136 -0
- omlish/lang/objects.py +103 -0
- omlish/lang/resolving.py +50 -0
- omlish/lang/strings.py +128 -0
- omlish/lang/typing.py +92 -0
- omlish/libc.py +532 -0
- omlish/logs/__init__.py +9 -0
- omlish/logs/_abc.py +247 -0
- omlish/logs/configs.py +62 -0
- omlish/logs/filters.py +9 -0
- omlish/logs/formatters.py +67 -0
- omlish/logs/utils.py +20 -0
- omlish/marshal/__init__.py +52 -0
- omlish/marshal/any.py +25 -0
- omlish/marshal/base.py +201 -0
- omlish/marshal/base64.py +25 -0
- omlish/marshal/dataclasses.py +115 -0
- omlish/marshal/datetimes.py +90 -0
- omlish/marshal/enums.py +43 -0
- omlish/marshal/exceptions.py +7 -0
- omlish/marshal/factories.py +129 -0
- omlish/marshal/global_.py +33 -0
- omlish/marshal/iterables.py +57 -0
- omlish/marshal/mappings.py +66 -0
- omlish/marshal/naming.py +17 -0
- omlish/marshal/objects.py +106 -0
- omlish/marshal/optionals.py +49 -0
- omlish/marshal/polymorphism.py +147 -0
- omlish/marshal/primitives.py +43 -0
- omlish/marshal/registries.py +57 -0
- omlish/marshal/standard.py +80 -0
- omlish/marshal/utils.py +23 -0
- omlish/marshal/uuids.py +29 -0
- omlish/marshal/values.py +30 -0
- omlish/math.py +184 -0
- omlish/os.py +32 -0
- omlish/reflect.py +359 -0
- omlish/replserver/__init__.py +5 -0
- omlish/replserver/__main__.py +4 -0
- omlish/replserver/console.py +247 -0
- omlish/replserver/server.py +146 -0
- omlish/runmodule.py +28 -0
- omlish/stats.py +342 -0
- omlish/term.py +222 -0
- omlish/testing/__init__.py +7 -0
- omlish/testing/pydevd.py +225 -0
- omlish/testing/pytest/__init__.py +8 -0
- omlish/testing/pytest/helpers.py +35 -0
- omlish/testing/pytest/inject/__init__.py +1 -0
- omlish/testing/pytest/inject/harness.py +159 -0
- omlish/testing/pytest/plugins/__init__.py +20 -0
- omlish/testing/pytest/plugins/_registry.py +6 -0
- omlish/testing/pytest/plugins/logging.py +13 -0
- omlish/testing/pytest/plugins/pycharm.py +54 -0
- omlish/testing/pytest/plugins/repeat.py +19 -0
- omlish/testing/pytest/plugins/skips.py +32 -0
- omlish/testing/pytest/plugins/spacing.py +19 -0
- omlish/testing/pytest/plugins/switches.py +70 -0
- omlish/testing/testing.py +102 -0
- omlish/text/__init__.py +0 -0
- omlish/text/delimit.py +171 -0
- omlish/text/indent.py +50 -0
- omlish/text/parts.py +265 -0
- omlish-0.0.0.dev1.dist-info/LICENSE +21 -0
- omlish-0.0.0.dev1.dist-info/METADATA +17 -0
- omlish-0.0.0.dev1.dist-info/RECORD +187 -0
- omlish-0.0.0.dev1.dist-info/WHEEL +5 -0
- omlish-0.0.0.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import contextlib
|
|
3
|
+
import threading
|
|
4
|
+
import typing as ta
|
|
5
|
+
|
|
6
|
+
from ... import check
|
|
7
|
+
from ... import dataclasses as dc
|
|
8
|
+
from ... import lang
|
|
9
|
+
from ..bindings import Binding
|
|
10
|
+
from ..elements import Elements
|
|
11
|
+
from ..elements import as_elements
|
|
12
|
+
from ..exceptions import ScopeAlreadyOpenException
|
|
13
|
+
from ..exceptions import ScopeNotOpenException
|
|
14
|
+
from ..injector import Injector
|
|
15
|
+
from ..keys import Key
|
|
16
|
+
from ..providers import Provider
|
|
17
|
+
from ..providers import fn
|
|
18
|
+
from ..scopes import ScopeSeededProvider
|
|
19
|
+
from ..scopes import SeededScope
|
|
20
|
+
from ..scopes import Singleton
|
|
21
|
+
from ..scopes import Thread
|
|
22
|
+
from ..types import Scope
|
|
23
|
+
from ..types import Unscoped
|
|
24
|
+
from .bindings import BindingImpl
|
|
25
|
+
from .providers import PROVIDER_IMPLS_BY_PROVIDER
|
|
26
|
+
from .providers import ProviderImpl
|
|
27
|
+
|
|
28
|
+
if ta.TYPE_CHECKING:
|
|
29
|
+
from . import injector as injector_
|
|
30
|
+
else:
|
|
31
|
+
injector_ = lang.proxy_import('.injector', __package__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ScopeImpl(lang.Abstract):
|
|
35
|
+
@property
|
|
36
|
+
@abc.abstractmethod
|
|
37
|
+
def scope(self) -> Scope:
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
def auto_elements(self) -> Elements | None:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
@abc.abstractmethod
|
|
44
|
+
def provide(self, binding: BindingImpl, injector: Injector) -> ta.Any:
|
|
45
|
+
raise NotImplementedError
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class UnscopedScopeImpl(ScopeImpl, lang.Final):
|
|
49
|
+
@property
|
|
50
|
+
def scope(self) -> Unscoped:
|
|
51
|
+
return Unscoped()
|
|
52
|
+
|
|
53
|
+
def provide(self, binding: BindingImpl, injector: Injector) -> ta.Any:
|
|
54
|
+
return binding.provider.provide(injector)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SingletonScopeImpl(ScopeImpl, lang.Final):
|
|
58
|
+
def __init__(self) -> None:
|
|
59
|
+
super().__init__()
|
|
60
|
+
self._dct: dict[BindingImpl, ta.Any] = {}
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def scope(self) -> Singleton:
|
|
64
|
+
return Singleton()
|
|
65
|
+
|
|
66
|
+
def provide(self, binding: BindingImpl, injector: Injector) -> ta.Any:
|
|
67
|
+
try:
|
|
68
|
+
return self._dct[binding]
|
|
69
|
+
except KeyError:
|
|
70
|
+
pass
|
|
71
|
+
v = binding.provider.provide(injector)
|
|
72
|
+
self._dct[binding] = v
|
|
73
|
+
return v
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ThreadScopeImpl(ScopeImpl, lang.Final):
|
|
77
|
+
def __init__(self) -> None:
|
|
78
|
+
super().__init__()
|
|
79
|
+
self._local = threading.local()
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def scope(self) -> Thread:
|
|
83
|
+
return Thread()
|
|
84
|
+
|
|
85
|
+
def provide(self, binding: BindingImpl, injector: Injector) -> ta.Any:
|
|
86
|
+
dct: dict[BindingImpl, ta.Any]
|
|
87
|
+
try:
|
|
88
|
+
dct = self._local.dct
|
|
89
|
+
except AttributeError:
|
|
90
|
+
dct = self._local.dct = {}
|
|
91
|
+
try:
|
|
92
|
+
return dct[binding]
|
|
93
|
+
except KeyError:
|
|
94
|
+
pass
|
|
95
|
+
v = binding.provider.provide(injector)
|
|
96
|
+
dct[binding] = v
|
|
97
|
+
return v
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
##
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
104
|
+
class ScopeSeededProviderImpl(ProviderImpl):
|
|
105
|
+
p: ScopeSeededProvider
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def providers(self) -> ta.Iterable[Provider]:
|
|
109
|
+
return (self.p,)
|
|
110
|
+
|
|
111
|
+
def provide(self, injector: Injector) -> ta.Any:
|
|
112
|
+
ii = check.isinstance(injector, injector_.InjectorImpl)
|
|
113
|
+
ssi = check.isinstance(ii.get_scope_impl(self.p.ss), SeededScopeImpl)
|
|
114
|
+
return ssi.must_state().seeds[self.p.key]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
PROVIDER_IMPLS_BY_PROVIDER[ScopeSeededProvider] = ScopeSeededProviderImpl
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class SeededScopeImpl(ScopeImpl):
|
|
121
|
+
@dc.dataclass(frozen=True)
|
|
122
|
+
class State:
|
|
123
|
+
seeds: dict[Key, ta.Any]
|
|
124
|
+
prvs: dict[BindingImpl, ta.Any] = dc.field(default_factory=dict)
|
|
125
|
+
|
|
126
|
+
def __init__(self, ss: SeededScope) -> None:
|
|
127
|
+
super().__init__()
|
|
128
|
+
self._ss = check.isinstance(ss, SeededScope)
|
|
129
|
+
self._st: SeededScopeImpl.State | None = None
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def scope(self) -> SeededScope:
|
|
133
|
+
return self._ss
|
|
134
|
+
|
|
135
|
+
def must_state(self) -> 'SeededScopeImpl.State':
|
|
136
|
+
if (st := self._st) is None:
|
|
137
|
+
raise ScopeNotOpenException(self._ss)
|
|
138
|
+
return st
|
|
139
|
+
|
|
140
|
+
class Manager(SeededScope.Manager, lang.Final):
|
|
141
|
+
def __init__(self, ss: SeededScope, i: Injector) -> None:
|
|
142
|
+
super().__init__()
|
|
143
|
+
self._ss = check.isinstance(ss, SeededScope)
|
|
144
|
+
ii = check.isinstance(i, injector_.InjectorImpl)
|
|
145
|
+
self._ssi = check.isinstance(ii.get_scope_impl(self._ss), SeededScopeImpl)
|
|
146
|
+
|
|
147
|
+
@contextlib.contextmanager
|
|
148
|
+
def __call__(self, seeds: ta.Mapping[Key, ta.Any]) -> ta.Generator[None, None, None]:
|
|
149
|
+
try:
|
|
150
|
+
if self._ssi._st is not None:
|
|
151
|
+
raise ScopeAlreadyOpenException(self._ss)
|
|
152
|
+
self._ssi._st = SeededScopeImpl.State(dict(seeds))
|
|
153
|
+
yield
|
|
154
|
+
finally:
|
|
155
|
+
if self._ssi._st is None:
|
|
156
|
+
raise ScopeNotOpenException(self._ss)
|
|
157
|
+
self._ssi._st = None
|
|
158
|
+
|
|
159
|
+
def auto_elements(self) -> Elements:
|
|
160
|
+
return as_elements(
|
|
161
|
+
Binding(
|
|
162
|
+
Key(SeededScope.Manager, tag=self._ss),
|
|
163
|
+
fn(lang.typed_partial(SeededScopeImpl.Manager, ss=self._ss)),
|
|
164
|
+
scope=Singleton(),
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def provide(self, binding: BindingImpl, injector: Injector) -> ta.Any:
|
|
169
|
+
st = self.must_state()
|
|
170
|
+
try:
|
|
171
|
+
return st.prvs[binding]
|
|
172
|
+
except KeyError:
|
|
173
|
+
pass
|
|
174
|
+
v = binding.provider.provide(injector)
|
|
175
|
+
st.prvs[binding] = v
|
|
176
|
+
return v
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
##
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
SCOPE_IMPLS_BY_SCOPE: dict[type[Scope], ta.Callable[..., ScopeImpl]] = {
|
|
183
|
+
Unscoped: lambda _: UnscopedScopeImpl(),
|
|
184
|
+
Singleton: lambda _: SingletonScopeImpl(),
|
|
185
|
+
Thread: lambda _: ThreadScopeImpl(),
|
|
186
|
+
SeededScope: lambda s: SeededScopeImpl(s),
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def make_scope_impl(s: Scope) -> ScopeImpl:
|
|
191
|
+
try:
|
|
192
|
+
fac = SCOPE_IMPLS_BY_SCOPE[type(s)]
|
|
193
|
+
except KeyError:
|
|
194
|
+
pass
|
|
195
|
+
else:
|
|
196
|
+
return fac(s)
|
|
197
|
+
|
|
198
|
+
raise TypeError(s)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import lang
|
|
5
|
+
from .elements import Elements
|
|
6
|
+
from .inspect import KwargsTarget
|
|
7
|
+
from .keys import Key
|
|
8
|
+
|
|
9
|
+
_impl = lang.proxy_import('.impl.injector', __package__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
T = ta.TypeVar('T')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Injector(lang.Abstract):
|
|
16
|
+
@abc.abstractmethod
|
|
17
|
+
def try_provide(self, key: ta.Any) -> lang.Maybe[ta.Any]:
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
|
|
20
|
+
@abc.abstractmethod
|
|
21
|
+
def provide(self, key: ta.Any) -> ta.Any:
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
@abc.abstractmethod
|
|
25
|
+
def provide_kwargs(self, kt: KwargsTarget) -> ta.Mapping[str, ta.Any]:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def inject(self, obj: ta.Any) -> ta.Any:
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
def __getitem__(
|
|
33
|
+
self,
|
|
34
|
+
target: ta.Union[Key[T], type[T]],
|
|
35
|
+
) -> T:
|
|
36
|
+
return self.provide(target)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def create_injector(es: Elements) -> Injector:
|
|
40
|
+
return _impl.create_injector(es)
|
omlish/inject/inspect.py
ADDED
omlish/inject/keys.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import dataclasses as dc
|
|
5
|
+
from .. import lang
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = ta.TypeVar('T')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dc.dataclass(frozen=True)
|
|
15
|
+
@dc.extra_params(cache_hash=True)
|
|
16
|
+
class Key(lang.Final, ta.Generic[T]):
|
|
17
|
+
cls: type[T] | ta.NewType
|
|
18
|
+
tag: ta.Any = dc.field(default=None, kw_only=True)
|
|
19
|
+
multi: bool = dc.field(default=False, kw_only=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def as_key(o: ta.Any) -> Key:
|
|
26
|
+
if o is inspect.Parameter.empty:
|
|
27
|
+
raise TypeError(o)
|
|
28
|
+
if isinstance(o, Key):
|
|
29
|
+
return o
|
|
30
|
+
if isinstance(o, (type, ta.NewType)): # noqa
|
|
31
|
+
return Key(o)
|
|
32
|
+
raise TypeError(o)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def multi(o: ta.Any) -> Key:
|
|
39
|
+
return dc.replace(as_key(o), multi=True)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def tag(o: ta.Any, t: ta.Any) -> Key:
|
|
43
|
+
return dc.replace(as_key(o), tag=t)
|
omlish/inject/managed.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- proper ManagedProvider[Impl], DeferringProvider[Impl], closing (-> Deferring('close'))
|
|
4
|
+
"""
|
|
5
|
+
import contextlib
|
|
6
|
+
import typing as ta
|
|
7
|
+
|
|
8
|
+
from .eagers import eager
|
|
9
|
+
from .elements import Elements
|
|
10
|
+
from .elements import as_elements
|
|
11
|
+
from .injector import Injector
|
|
12
|
+
from .injector import create_injector
|
|
13
|
+
from .scopes import singleton
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@contextlib.contextmanager
|
|
17
|
+
def create_managed_injector(es: Elements) -> ta.Generator[Injector, None, None]:
|
|
18
|
+
i = create_injector(as_elements(
|
|
19
|
+
es,
|
|
20
|
+
singleton(contextlib.ExitStack),
|
|
21
|
+
eager(contextlib.ExitStack),
|
|
22
|
+
))
|
|
23
|
+
with i[contextlib.ExitStack]:
|
|
24
|
+
yield i
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import dataclasses as dc
|
|
4
|
+
from .. import lang
|
|
5
|
+
from .binder import bind
|
|
6
|
+
from .elements import Element
|
|
7
|
+
from .elements import Elements
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dc.dataclass(frozen=True)
|
|
11
|
+
@dc.extra_params(cache_hash=True)
|
|
12
|
+
class Overrides(Element, lang.Final):
|
|
13
|
+
ovr: Elements
|
|
14
|
+
src: Elements
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def override(ovr: ta.Any, *a: ta.Any) -> Element:
|
|
18
|
+
return Overrides(bind(ovr), bind(*a))
|
omlish/inject/private.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import check
|
|
4
|
+
from .. import dataclasses as dc
|
|
5
|
+
from .. import lang
|
|
6
|
+
from .elements import Element
|
|
7
|
+
from .elements import Elements
|
|
8
|
+
from .keys import Key
|
|
9
|
+
from .keys import as_key
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dc.dataclass(frozen=True)
|
|
13
|
+
@dc.extra_params(cache_hash=True)
|
|
14
|
+
class Expose(Element, lang.Final):
|
|
15
|
+
key: Key
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def expose(k: ta.Any) -> Element:
|
|
19
|
+
return Expose(as_key(k))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dc.dataclass(frozen=True)
|
|
23
|
+
@dc.extra_params(cache_hash=True)
|
|
24
|
+
class Private(Element, lang.Final):
|
|
25
|
+
elements: Elements
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def private(es: Elements) -> Element:
|
|
29
|
+
return Private(check.isinstance(es, Elements))
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import check
|
|
5
|
+
from .. import dataclasses as dc
|
|
6
|
+
from .. import lang
|
|
7
|
+
from .elements import Element
|
|
8
|
+
from .elements import Elements
|
|
9
|
+
from .impl.inspect import signature
|
|
10
|
+
from .keys import Key
|
|
11
|
+
from .keys import as_key
|
|
12
|
+
from .types import Cls
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _Missing(lang.NotInstantiable):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Provider(lang.Abstract):
|
|
23
|
+
@abc.abstractmethod
|
|
24
|
+
def provided_cls(self) -> Cls | None:
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def as_provider(o: ta.Any) -> Provider:
|
|
32
|
+
check.not_isinstance(o, (Element, Elements))
|
|
33
|
+
if isinstance(o, Provider):
|
|
34
|
+
return o
|
|
35
|
+
if isinstance(o, Key):
|
|
36
|
+
return link(o)
|
|
37
|
+
if isinstance(o, type):
|
|
38
|
+
return ctor(o)
|
|
39
|
+
if callable(o):
|
|
40
|
+
return fn(o)
|
|
41
|
+
return const(o)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
48
|
+
class FnProvider(Provider):
|
|
49
|
+
fn: ta.Any
|
|
50
|
+
cls: Cls | None = None
|
|
51
|
+
|
|
52
|
+
def provided_cls(self) -> Cls | None:
|
|
53
|
+
return self.cls
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def fn(fn: ta.Any, cls: ta.Optional[Cls] = _Missing) -> Provider:
|
|
57
|
+
check.not_isinstance(fn, type)
|
|
58
|
+
check.callable(fn)
|
|
59
|
+
if cls is _Missing:
|
|
60
|
+
sig = signature(fn)
|
|
61
|
+
cls = check.isinstance(sig.return_annotation, type)
|
|
62
|
+
return FnProvider(fn, cls)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
69
|
+
class CtorProvider(Provider):
|
|
70
|
+
cls: type
|
|
71
|
+
|
|
72
|
+
def provided_cls(self) -> Cls:
|
|
73
|
+
return self.cls
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def ctor(cls: type) -> Provider:
|
|
77
|
+
check.isinstance(cls, type)
|
|
78
|
+
return CtorProvider(cls)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
85
|
+
class ConstProvider(Provider):
|
|
86
|
+
v: ta.Any
|
|
87
|
+
cls: Cls | None = None
|
|
88
|
+
|
|
89
|
+
def provided_cls(self) -> Cls | None:
|
|
90
|
+
return self.cls
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def const(v: ta.Any, cls: ta.Optional[Cls] = _Missing) -> Provider:
|
|
94
|
+
if cls is _Missing:
|
|
95
|
+
cls = type(v)
|
|
96
|
+
return ConstProvider(v, cls)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
103
|
+
class LinkProvider(Provider):
|
|
104
|
+
k: Key
|
|
105
|
+
|
|
106
|
+
def provided_cls(self) -> Cls | None:
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def link(k: ta.Any) -> Provider:
|
|
111
|
+
return LinkProvider(as_key(k))
|
omlish/inject/proxy.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import lang
|
|
4
|
+
from .. import check
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@lang.cached_function
|
|
8
|
+
def _cyclic_dependency_proxy() -> tuple[type, ta.Callable[[ta.Any, ta.Any], None]]:
|
|
9
|
+
import wrapt # noqa
|
|
10
|
+
|
|
11
|
+
class _CyclicDependencyPlaceholder(lang.Final):
|
|
12
|
+
|
|
13
|
+
def __init__(self, cls: ta.Any) -> None:
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.__cls = cls
|
|
16
|
+
|
|
17
|
+
def __repr__(self) -> str:
|
|
18
|
+
return f'{type(self).__name__}({self.__cls!r})'
|
|
19
|
+
|
|
20
|
+
class _CyclicDependencyProxy(wrapt.ObjectProxy, lang.Final): # noqa
|
|
21
|
+
|
|
22
|
+
def __init__(self, cls):
|
|
23
|
+
super().__init__(_CyclicDependencyPlaceholder(cls))
|
|
24
|
+
if isinstance(cls, type):
|
|
25
|
+
self._self__class__ = cls
|
|
26
|
+
|
|
27
|
+
def __repr__(self) -> str:
|
|
28
|
+
return f'_CyclicDependencyProxy({self.__wrapped__!r})'
|
|
29
|
+
|
|
30
|
+
def __getattribute__(self, att):
|
|
31
|
+
if att == '__class__':
|
|
32
|
+
try:
|
|
33
|
+
return object.__getattribute__(self, '_self__class__') # noqa
|
|
34
|
+
except AttributeError:
|
|
35
|
+
pass
|
|
36
|
+
return object.__getattribute__(self, att) # noqa
|
|
37
|
+
|
|
38
|
+
def set(prox, obj):
|
|
39
|
+
check.state(type(prox) is _CyclicDependencyProxy)
|
|
40
|
+
check.not_isinstance(obj, _CyclicDependencyPlaceholder)
|
|
41
|
+
check.isinstance(prox.__wrapped__, _CyclicDependencyPlaceholder)
|
|
42
|
+
if hasattr(prox, '_self__class__'):
|
|
43
|
+
check.issubclass(type(obj), prox._self__class__)
|
|
44
|
+
prox.__wrapped__ = obj
|
|
45
|
+
if hasattr(prox, '_self__class__'):
|
|
46
|
+
del prox._self__class__
|
|
47
|
+
|
|
48
|
+
return (_CyclicDependencyProxy, set) # noqa
|
omlish/inject/scopes.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import contextlib
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from .. import check
|
|
6
|
+
from .. import dataclasses as dc
|
|
7
|
+
from .. import lang
|
|
8
|
+
from .bindings import Binding
|
|
9
|
+
from .bindings import as_binding
|
|
10
|
+
from .elements import Element
|
|
11
|
+
from .keys import Key
|
|
12
|
+
from .keys import as_key
|
|
13
|
+
from .providers import Provider
|
|
14
|
+
from .types import Cls
|
|
15
|
+
from .types import Scope
|
|
16
|
+
|
|
17
|
+
if ta.TYPE_CHECKING:
|
|
18
|
+
from . import injector as injector_
|
|
19
|
+
else:
|
|
20
|
+
injector_ = lang.proxy_import('.injector', __package__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dc.dataclass(frozen=True)
|
|
24
|
+
@dc.extra_params(cache_hash=True)
|
|
25
|
+
class ScopeBinding(Element, lang.Final):
|
|
26
|
+
scope: Scope = dc.xfield(coerce=check.of_isinstance(Scope))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def bind_scope(sc: Scope) -> Element:
|
|
30
|
+
return ScopeBinding(sc)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def in_(b: ta.Any, sc: Scope) -> 'Binding':
|
|
34
|
+
return dc.replace(as_binding(b), scope=check.isinstance(sc, Scope))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Singleton(Scope, lang.Singleton, lang.Final):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def singleton(b: ta.Any) -> 'Binding':
|
|
42
|
+
return in_(b, Singleton())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Thread(Scope, lang.Singleton, lang.Final):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dc.dataclass(frozen=True)
|
|
53
|
+
@dc.extra_params(cache_hash=True)
|
|
54
|
+
class SeededScope(Scope, lang.Final):
|
|
55
|
+
tag: ta.Any = dc.xfield(coerce=check.not_none)
|
|
56
|
+
|
|
57
|
+
class Manager(lang.Abstract):
|
|
58
|
+
@abc.abstractmethod
|
|
59
|
+
def __call__(self, seeds: ta.Mapping[Key, ta.Any]) -> ta.ContextManager[None]:
|
|
60
|
+
raise NotImplementedError
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
64
|
+
class ScopeSeededProvider(Provider):
|
|
65
|
+
ss: SeededScope = dc.xfield(coerce=check.of_isinstance(SeededScope))
|
|
66
|
+
key: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
|
67
|
+
|
|
68
|
+
def provided_cls(self) -> Cls | None:
|
|
69
|
+
return self.key.cls
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def bind_scope_seed(ss: SeededScope, k: ta.Any) -> Element:
|
|
73
|
+
k = as_key(k)
|
|
74
|
+
return Binding(k, ScopeSeededProvider(ss, k))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@contextlib.contextmanager
|
|
78
|
+
def enter_seeded_scope(
|
|
79
|
+
i: injector_.Injector,
|
|
80
|
+
ss: SeededScope,
|
|
81
|
+
keys: ta.Mapping[Key, ta.Any],
|
|
82
|
+
) -> ta.Generator[None, None, None]:
|
|
83
|
+
with i.provide(Key(SeededScope.Manager, tag=ss))(keys):
|
|
84
|
+
yield
|
omlish/inject/types.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import lang
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Cls = type | ta.NewType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Scope(lang.Abstract):
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return type(self).__name__
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Unscoped(Scope, lang.Singleton, lang.Final):
|
|
21
|
+
pass
|