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.

Files changed (187) hide show
  1. omlish/__about__.py +7 -0
  2. omlish/__init__.py +0 -0
  3. omlish/argparse.py +223 -0
  4. omlish/asyncs/__init__.py +17 -0
  5. omlish/asyncs/anyio.py +23 -0
  6. omlish/asyncs/asyncio.py +19 -0
  7. omlish/asyncs/asyncs.py +76 -0
  8. omlish/asyncs/futures.py +179 -0
  9. omlish/asyncs/trio.py +11 -0
  10. omlish/c3.py +173 -0
  11. omlish/cached.py +9 -0
  12. omlish/check.py +231 -0
  13. omlish/collections/__init__.py +63 -0
  14. omlish/collections/_abc.py +156 -0
  15. omlish/collections/_io_abc.py +78 -0
  16. omlish/collections/cache/__init__.py +11 -0
  17. omlish/collections/cache/descriptor.py +188 -0
  18. omlish/collections/cache/impl.py +485 -0
  19. omlish/collections/cache/types.py +37 -0
  20. omlish/collections/coerce.py +337 -0
  21. omlish/collections/frozen.py +148 -0
  22. omlish/collections/identity.py +106 -0
  23. omlish/collections/indexed.py +75 -0
  24. omlish/collections/mappings.py +127 -0
  25. omlish/collections/ordered.py +81 -0
  26. omlish/collections/persistent.py +36 -0
  27. omlish/collections/skiplist.py +193 -0
  28. omlish/collections/sorted.py +126 -0
  29. omlish/collections/treap.py +228 -0
  30. omlish/collections/treapmap.py +144 -0
  31. omlish/collections/unmodifiable.py +174 -0
  32. omlish/collections/utils.py +110 -0
  33. omlish/configs/__init__.py +0 -0
  34. omlish/configs/flattening.py +147 -0
  35. omlish/configs/props.py +64 -0
  36. omlish/dataclasses/__init__.py +83 -0
  37. omlish/dataclasses/impl/__init__.py +6 -0
  38. omlish/dataclasses/impl/api.py +260 -0
  39. omlish/dataclasses/impl/as_.py +76 -0
  40. omlish/dataclasses/impl/exceptions.py +2 -0
  41. omlish/dataclasses/impl/fields.py +148 -0
  42. omlish/dataclasses/impl/frozen.py +55 -0
  43. omlish/dataclasses/impl/hashing.py +85 -0
  44. omlish/dataclasses/impl/init.py +173 -0
  45. omlish/dataclasses/impl/internals.py +118 -0
  46. omlish/dataclasses/impl/main.py +150 -0
  47. omlish/dataclasses/impl/metaclass.py +126 -0
  48. omlish/dataclasses/impl/metadata.py +74 -0
  49. omlish/dataclasses/impl/order.py +47 -0
  50. omlish/dataclasses/impl/params.py +150 -0
  51. omlish/dataclasses/impl/processing.py +16 -0
  52. omlish/dataclasses/impl/reflect.py +173 -0
  53. omlish/dataclasses/impl/replace.py +40 -0
  54. omlish/dataclasses/impl/repr.py +34 -0
  55. omlish/dataclasses/impl/simple.py +92 -0
  56. omlish/dataclasses/impl/slots.py +80 -0
  57. omlish/dataclasses/impl/utils.py +167 -0
  58. omlish/defs.py +193 -0
  59. omlish/dispatch/__init__.py +3 -0
  60. omlish/dispatch/dispatch.py +137 -0
  61. omlish/dispatch/functions.py +52 -0
  62. omlish/dispatch/methods.py +162 -0
  63. omlish/docker.py +149 -0
  64. omlish/dynamic.py +220 -0
  65. omlish/graphs/__init__.py +0 -0
  66. omlish/graphs/dot/__init__.py +19 -0
  67. omlish/graphs/dot/items.py +162 -0
  68. omlish/graphs/dot/rendering.py +147 -0
  69. omlish/graphs/dot/utils.py +30 -0
  70. omlish/graphs/trees.py +249 -0
  71. omlish/http/__init__.py +0 -0
  72. omlish/http/consts.py +20 -0
  73. omlish/http/wsgi.py +34 -0
  74. omlish/inject/__init__.py +85 -0
  75. omlish/inject/binder.py +12 -0
  76. omlish/inject/bindings.py +49 -0
  77. omlish/inject/eagers.py +21 -0
  78. omlish/inject/elements.py +43 -0
  79. omlish/inject/exceptions.py +49 -0
  80. omlish/inject/impl/__init__.py +0 -0
  81. omlish/inject/impl/bindings.py +19 -0
  82. omlish/inject/impl/elements.py +154 -0
  83. omlish/inject/impl/injector.py +182 -0
  84. omlish/inject/impl/inspect.py +98 -0
  85. omlish/inject/impl/private.py +109 -0
  86. omlish/inject/impl/providers.py +132 -0
  87. omlish/inject/impl/scopes.py +198 -0
  88. omlish/inject/injector.py +40 -0
  89. omlish/inject/inspect.py +14 -0
  90. omlish/inject/keys.py +43 -0
  91. omlish/inject/managed.py +24 -0
  92. omlish/inject/overrides.py +18 -0
  93. omlish/inject/private.py +29 -0
  94. omlish/inject/providers.py +111 -0
  95. omlish/inject/proxy.py +48 -0
  96. omlish/inject/scopes.py +84 -0
  97. omlish/inject/types.py +21 -0
  98. omlish/iterators.py +184 -0
  99. omlish/json.py +194 -0
  100. omlish/lang/__init__.py +112 -0
  101. omlish/lang/cached.py +267 -0
  102. omlish/lang/classes/__init__.py +24 -0
  103. omlish/lang/classes/abstract.py +74 -0
  104. omlish/lang/classes/restrict.py +137 -0
  105. omlish/lang/classes/simple.py +120 -0
  106. omlish/lang/classes/test/__init__.py +0 -0
  107. omlish/lang/classes/test/test_abstract.py +89 -0
  108. omlish/lang/classes/test/test_restrict.py +71 -0
  109. omlish/lang/classes/test/test_simple.py +58 -0
  110. omlish/lang/classes/test/test_virtual.py +72 -0
  111. omlish/lang/classes/virtual.py +130 -0
  112. omlish/lang/clsdct.py +67 -0
  113. omlish/lang/cmp.py +63 -0
  114. omlish/lang/contextmanagers.py +249 -0
  115. omlish/lang/datetimes.py +67 -0
  116. omlish/lang/descriptors.py +52 -0
  117. omlish/lang/functions.py +126 -0
  118. omlish/lang/imports.py +153 -0
  119. omlish/lang/iterables.py +54 -0
  120. omlish/lang/maybes.py +136 -0
  121. omlish/lang/objects.py +103 -0
  122. omlish/lang/resolving.py +50 -0
  123. omlish/lang/strings.py +128 -0
  124. omlish/lang/typing.py +92 -0
  125. omlish/libc.py +532 -0
  126. omlish/logs/__init__.py +9 -0
  127. omlish/logs/_abc.py +247 -0
  128. omlish/logs/configs.py +62 -0
  129. omlish/logs/filters.py +9 -0
  130. omlish/logs/formatters.py +67 -0
  131. omlish/logs/utils.py +20 -0
  132. omlish/marshal/__init__.py +52 -0
  133. omlish/marshal/any.py +25 -0
  134. omlish/marshal/base.py +201 -0
  135. omlish/marshal/base64.py +25 -0
  136. omlish/marshal/dataclasses.py +115 -0
  137. omlish/marshal/datetimes.py +90 -0
  138. omlish/marshal/enums.py +43 -0
  139. omlish/marshal/exceptions.py +7 -0
  140. omlish/marshal/factories.py +129 -0
  141. omlish/marshal/global_.py +33 -0
  142. omlish/marshal/iterables.py +57 -0
  143. omlish/marshal/mappings.py +66 -0
  144. omlish/marshal/naming.py +17 -0
  145. omlish/marshal/objects.py +106 -0
  146. omlish/marshal/optionals.py +49 -0
  147. omlish/marshal/polymorphism.py +147 -0
  148. omlish/marshal/primitives.py +43 -0
  149. omlish/marshal/registries.py +57 -0
  150. omlish/marshal/standard.py +80 -0
  151. omlish/marshal/utils.py +23 -0
  152. omlish/marshal/uuids.py +29 -0
  153. omlish/marshal/values.py +30 -0
  154. omlish/math.py +184 -0
  155. omlish/os.py +32 -0
  156. omlish/reflect.py +359 -0
  157. omlish/replserver/__init__.py +5 -0
  158. omlish/replserver/__main__.py +4 -0
  159. omlish/replserver/console.py +247 -0
  160. omlish/replserver/server.py +146 -0
  161. omlish/runmodule.py +28 -0
  162. omlish/stats.py +342 -0
  163. omlish/term.py +222 -0
  164. omlish/testing/__init__.py +7 -0
  165. omlish/testing/pydevd.py +225 -0
  166. omlish/testing/pytest/__init__.py +8 -0
  167. omlish/testing/pytest/helpers.py +35 -0
  168. omlish/testing/pytest/inject/__init__.py +1 -0
  169. omlish/testing/pytest/inject/harness.py +159 -0
  170. omlish/testing/pytest/plugins/__init__.py +20 -0
  171. omlish/testing/pytest/plugins/_registry.py +6 -0
  172. omlish/testing/pytest/plugins/logging.py +13 -0
  173. omlish/testing/pytest/plugins/pycharm.py +54 -0
  174. omlish/testing/pytest/plugins/repeat.py +19 -0
  175. omlish/testing/pytest/plugins/skips.py +32 -0
  176. omlish/testing/pytest/plugins/spacing.py +19 -0
  177. omlish/testing/pytest/plugins/switches.py +70 -0
  178. omlish/testing/testing.py +102 -0
  179. omlish/text/__init__.py +0 -0
  180. omlish/text/delimit.py +171 -0
  181. omlish/text/indent.py +50 -0
  182. omlish/text/parts.py +265 -0
  183. omlish-0.0.0.dev1.dist-info/LICENSE +21 -0
  184. omlish-0.0.0.dev1.dist-info/METADATA +17 -0
  185. omlish-0.0.0.dev1.dist-info/RECORD +187 -0
  186. omlish-0.0.0.dev1.dist-info/WHEEL +5 -0
  187. 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)
@@ -0,0 +1,14 @@
1
+ import typing as ta
2
+
3
+ from .keys import Key
4
+
5
+
6
+ class Kwarg(ta.NamedTuple):
7
+ name: str
8
+ key: Key
9
+ has_default: bool
10
+
11
+
12
+ class KwargsTarget(ta.NamedTuple):
13
+ obj: ta.Any
14
+ kwargs: ta.Sequence[Kwarg]
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)
@@ -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))
@@ -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
@@ -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