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,154 @@
1
+ """
2
+ Scopes in general:
3
+ - clearly some notion of 'activeness for a given request'
4
+
5
+ Overrides + Scopes:
6
+ -
7
+
8
+ Multi's + Scopes:
9
+ - scope on a multi vs each element?
10
+
11
+ Element Types:
12
+ - Binding
13
+ - Eager
14
+ - Overrides
15
+ - Expose
16
+ - Private
17
+ - ScopeSeed
18
+ """
19
+ import typing as ta
20
+
21
+ from ... import check
22
+ from ... import collections as col
23
+ from ... import lang
24
+ from ..bindings import Binding
25
+ from ..eagers import Eager
26
+ from ..elements import Element
27
+ from ..elements import Elements
28
+ from ..exceptions import DuplicateKeyException
29
+ from ..keys import Key
30
+ from ..overrides import Overrides
31
+ from ..private import Expose
32
+ from ..private import Private
33
+ from ..scopes import ScopeBinding
34
+ from .bindings import BindingImpl
35
+ from .providers import MultiProviderImpl
36
+ from .providers import make_provider_impl
37
+ from .scopes import make_scope_impl
38
+
39
+ if ta.TYPE_CHECKING:
40
+ from . import private as private_
41
+ else:
42
+ private_ = lang.proxy_import('.private', __package__)
43
+
44
+
45
+ ElementT = ta.TypeVar('ElementT', bound=Element)
46
+
47
+
48
+ class ElementCollection(lang.Final):
49
+ def __init__(self, es: Elements) -> None:
50
+ super().__init__()
51
+
52
+ self._es = check.isinstance(es, Elements)
53
+
54
+ self._private_infos: ta.MutableMapping[Private, 'private_.PrivateInfo'] | None = None
55
+
56
+ ##
57
+
58
+ def _get_private_info(self, p: Private) -> 'private_.PrivateInfo':
59
+ if (pis := self._private_infos) is None:
60
+ self._private_infos = pis = col.IdentityKeyDict()
61
+ try:
62
+ return pis[p]
63
+ except KeyError:
64
+ pis[p] = ec = private_.PrivateInfo(self, p)
65
+ return ec
66
+
67
+ ##
68
+
69
+ def _build_raw_element_multimap(
70
+ self,
71
+ es: ta.Iterable[Element],
72
+ out: dict[Key | None, list[Element]] | None = None,
73
+ ) -> dict[Key | None, list[Element]]:
74
+ if out is None:
75
+ out = {}
76
+
77
+ def add(k: Key | None, *e: Element) -> None:
78
+ out.setdefault(k, []).extend(e)
79
+
80
+ for e in es:
81
+ if isinstance(e, ScopeBinding):
82
+ add(None, e)
83
+ sci = make_scope_impl(e.scope)
84
+ if (sae := sci.auto_elements()) is not None:
85
+ self._build_raw_element_multimap(sae, out)
86
+
87
+ elif isinstance(e, (Binding, Eager, Expose)):
88
+ add(e.key, e)
89
+
90
+ elif isinstance(e, Private):
91
+ pi = self._get_private_info(e)
92
+ self._build_raw_element_multimap(pi.owner_elements(), out)
93
+
94
+ elif isinstance(e, Overrides):
95
+ ovr = self._build_raw_element_multimap(e.ovr)
96
+ src = self._build_raw_element_multimap(e.src)
97
+ for k, b in src.items():
98
+ try:
99
+ bs = ovr[k]
100
+ except KeyError:
101
+ bs = src[k]
102
+ add(k, *bs)
103
+
104
+ else:
105
+ raise TypeError(e)
106
+
107
+ return out
108
+
109
+ @lang.cached_function
110
+ def element_multimap(self) -> ta.Mapping[Key | None, ta.Sequence[Element]]:
111
+ return self._build_raw_element_multimap(self._es)
112
+
113
+ @lang.cached_function
114
+ def elements_of_type(self, *tys: type[ElementT]) -> ta.Sequence[ElementT]:
115
+ return tuple(e for es in self.element_multimap().values() for e in es if isinstance(e, tys)) # noqa
116
+
117
+ ##
118
+
119
+ def _make_binding_impls(self, e: Element) -> ta.Iterable[BindingImpl]:
120
+ if isinstance(e, Binding):
121
+ p = make_provider_impl(e.provider)
122
+ return (BindingImpl(e.key, p, e.scope, e),)
123
+
124
+ elif isinstance(e, (Eager, Expose)):
125
+ return ()
126
+
127
+ else:
128
+ raise TypeError(e)
129
+
130
+ def _build_binding_impl_map(self, em: ta.Mapping[Key | None, ta.Sequence[Element]]) -> dict[Key, BindingImpl]:
131
+ pm: dict[Key, BindingImpl] = {}
132
+ mm: dict[Key, list[BindingImpl]] = {}
133
+ for k, es in em.items():
134
+ if k is None:
135
+ continue
136
+
137
+ bis = [bi for e in es for bi in self._make_binding_impls(e)]
138
+ if k.multi:
139
+ mm.setdefault(k, []).extend(bis)
140
+ else:
141
+ if len(bis) > 1:
142
+ raise DuplicateKeyException(k)
143
+ [pm[k]] = bis
144
+
145
+ if mm:
146
+ for k, aps in mm.items():
147
+ mp = MultiProviderImpl([ap.provider for ap in aps])
148
+ pm[k] = BindingImpl(k, mp) # FIXME: SCOPING
149
+
150
+ return pm
151
+
152
+ @lang.cached_function
153
+ def binding_impl_map(self) -> ta.Mapping[Key, BindingImpl]:
154
+ return self._build_binding_impl_map(self.element_multimap())
@@ -0,0 +1,182 @@
1
+ """
2
+ TODO:
3
+ - cache/export ElementCollections lol
4
+ - scope bindings, auto in root
5
+ - injector-internal / blacklisted bindings (Injector itself, default scopes) without rebuilding ElementCollection
6
+ - config - proxies, impl select, etc
7
+ - config is probably shared with ElementCollection... but not 'bound', must be shared everywhere
8
+ - InjectorRoot object?
9
+ - ** eagers in any scope, on scope init/open
10
+ """
11
+ import contextlib
12
+ import typing as ta
13
+ import weakref
14
+
15
+ from ... import check
16
+ from ... import lang
17
+ from ..eagers import Eager
18
+ from ..elements import Elements
19
+ from ..exceptions import CyclicDependencyException
20
+ from ..exceptions import UnboundKeyException
21
+ from ..injector import Injector
22
+ from ..inspect import KwargsTarget
23
+ from ..keys import Key
24
+ from ..keys import as_key
25
+ from ..scopes import ScopeBinding
26
+ from ..scopes import Singleton
27
+ from ..scopes import Thread
28
+ from ..types import Scope
29
+ from ..types import Unscoped
30
+ from .elements import ElementCollection
31
+ from .inspect import build_kwargs_target
32
+ from .scopes import ScopeImpl
33
+ from .scopes import make_scope_impl
34
+
35
+
36
+ DEFAULT_SCOPES: list[Scope] = [
37
+ Unscoped(),
38
+ Singleton(),
39
+ Thread(),
40
+ ]
41
+
42
+
43
+ class InjectorImpl(Injector, lang.Final):
44
+ def __init__(self, ec: ElementCollection, p: ta.Optional['InjectorImpl'] = None) -> None:
45
+ super().__init__()
46
+
47
+ self._ec = check.isinstance(ec, ElementCollection)
48
+ self._p: ta.Optional[InjectorImpl] = check.isinstance(p, (InjectorImpl, None))
49
+
50
+ self._internal_consts: dict[Key, ta.Any] = {
51
+ Key(Injector): self,
52
+ }
53
+
54
+ self._bim = ec.binding_impl_map()
55
+
56
+ self._cs: weakref.WeakSet[InjectorImpl] | None = None
57
+ self._root: InjectorImpl = p._root if p is not None else self
58
+
59
+ self.__cur_req: InjectorImpl._Request | None = None
60
+
61
+ ss = [
62
+ *DEFAULT_SCOPES,
63
+ *[sb.scope for sb in ec.elements_of_type(ScopeBinding)],
64
+ ]
65
+ self._scopes: dict[Scope, ScopeImpl] = {
66
+ s: make_scope_impl(s) for s in ss
67
+ }
68
+
69
+ self._instantiate_eagers()
70
+
71
+ _root: 'InjectorImpl'
72
+
73
+ def _instantiate_eagers(self) -> None:
74
+ for e in self._ec.elements_of_type(Eager):
75
+ self.provide(e.key)
76
+
77
+ def get_scope_impl(self, sc: Scope) -> ScopeImpl:
78
+ return self._scopes[sc]
79
+
80
+ def create_child(self, ec: ElementCollection) -> Injector:
81
+ c = InjectorImpl(ec, self)
82
+ if self._cs is None:
83
+ self._cs = weakref.WeakSet()
84
+ self._cs.add(c)
85
+ return c
86
+
87
+ class _Request:
88
+ def __init__(self, injector: 'InjectorImpl') -> None:
89
+ super().__init__()
90
+ self._injector = injector
91
+ self._provisions: dict[Key, ta.Any] = {}
92
+ self._seen_keys: set[Key] = set()
93
+
94
+ def handle_key(self, key: Key) -> lang.Maybe:
95
+ try:
96
+ return lang.just(self._provisions[key])
97
+ except KeyError:
98
+ pass
99
+ if key in self._seen_keys:
100
+ raise CyclicDependencyException(key)
101
+ self._seen_keys.add(key)
102
+ return lang.empty()
103
+
104
+ def handle_provision(self, key: Key, v: ta.Any) -> None:
105
+ check.in_(key, self._seen_keys)
106
+ check.not_in(key, self._provisions)
107
+ self._provisions[key] = v
108
+
109
+ def __enter__(self: ta.Self) -> ta.Self:
110
+ return self
111
+
112
+ def __exit__(self, *exc) -> None:
113
+ pass
114
+
115
+ @contextlib.contextmanager
116
+ def _current_request(self) -> ta.Generator[_Request, None, None]:
117
+ if (cr := self.__cur_req) is not None:
118
+ yield cr
119
+ return
120
+
121
+ with self._Request(self) as cr:
122
+ try:
123
+ self.__cur_req = cr
124
+ yield cr
125
+ finally:
126
+ self.__cur_req = None
127
+
128
+ def try_provide(self, key: ta.Any) -> lang.Maybe[ta.Any]:
129
+ key = as_key(key)
130
+
131
+ with self._current_request() as cr:
132
+ ic = self._internal_consts.get(key)
133
+ if ic is not None:
134
+ return lang.just(ic)
135
+
136
+ if (rv := cr.handle_key(key)).present:
137
+ return rv
138
+
139
+ bi = self._bim.get(key)
140
+ if bi is not None:
141
+ sc = self._scopes[bi.scope]
142
+ v = sc.provide(bi, self)
143
+ cr.handle_provision(key, v)
144
+ return lang.just(v)
145
+
146
+ if self._p is not None:
147
+ pv = self._p.try_provide(key)
148
+ if pv is not None:
149
+ return pv
150
+
151
+ return lang.empty()
152
+
153
+ def provide(self, key: ta.Any) -> ta.Any:
154
+ v = self.try_provide(key)
155
+ if v.present:
156
+ return v.must()
157
+ raise UnboundKeyException(key)
158
+
159
+ def provide_kwargs(self, kt: KwargsTarget) -> ta.Mapping[str, ta.Any]:
160
+ ret: dict[str, ta.Any] = {}
161
+ for kw in kt.kwargs:
162
+ if kw.has_default:
163
+ if not (mv := self.try_provide(kw.key)).present:
164
+ continue
165
+ v = mv.must()
166
+ else:
167
+ v = self.provide(kw.key)
168
+ ret[kw.name] = v
169
+ return ret
170
+
171
+ def inject(self, obj: ta.Any) -> ta.Any:
172
+ if isinstance(obj, KwargsTarget):
173
+ obj, kt = obj.obj, obj
174
+ else:
175
+ kt = build_kwargs_target(obj)
176
+ kws = self.provide_kwargs(kt)
177
+ # FIXME: still 'injecting' (as in has a req) if ctor needs and uses Injector
178
+ return obj(**kws)
179
+
180
+
181
+ def create_injector(es: Elements) -> Injector:
182
+ return InjectorImpl(ElementCollection(es))
@@ -0,0 +1,98 @@
1
+ """
2
+ TODO:
3
+ - cache kwarg_keys
4
+ - tag annotations? x: ta.Annotated[int, inj.Tag('foo')]
5
+ - tag decorator - @inj.tag(x='foo')
6
+ - *unpack optional here*
7
+ """
8
+ import inspect
9
+ import types
10
+ import typing as ta
11
+ import weakref
12
+
13
+ from ... import reflect as rfl
14
+ from ..exceptions import DuplicateKeyException
15
+ from ..inspect import Kwarg
16
+ from ..inspect import KwargsTarget
17
+ from ..keys import Key
18
+ from ..keys import as_key
19
+ from ..keys import tag
20
+
21
+
22
+ P = ta.ParamSpec('P')
23
+ R = ta.TypeVar('R')
24
+
25
+
26
+ _signature_cache: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
27
+
28
+
29
+ def signature(obj: ta.Any) -> inspect.Signature:
30
+ try:
31
+ return _signature_cache[obj]
32
+ except TypeError:
33
+ return inspect.signature(obj)
34
+ except KeyError:
35
+ pass
36
+ sig = inspect.signature(obj)
37
+ _signature_cache[obj] = sig
38
+ return sig
39
+
40
+
41
+ _tags: ta.MutableMapping[ta.Any, dict[str, ta.Any]] = weakref.WeakKeyDictionary()
42
+
43
+
44
+ def tags(**kwargs: ta.Any) -> ta.Callable[[ta.Callable[P, R]], ta.Callable[P, R]]:
45
+ def inner(obj):
46
+ _tags[obj] = kwargs
47
+ return obj
48
+ return inner
49
+
50
+
51
+ def build_kwargs_target(
52
+ obj: ta.Any,
53
+ *,
54
+ skip_args: int = 0,
55
+ skip_kwargs: ta.Optional[ta.Iterable[ta.Any]] = None,
56
+ raw_optional: bool = False,
57
+ ) -> KwargsTarget:
58
+ sig = signature(obj)
59
+ tags = _tags.get(obj)
60
+
61
+ seen: set[Key] = set(map(as_key, skip_kwargs)) if skip_kwargs is not None else set()
62
+ kws: list[Kwarg] = []
63
+ for p in list(sig.parameters.values())[skip_args:]:
64
+ if p.annotation is inspect.Signature.empty:
65
+ if p.default is not inspect.Parameter.empty:
66
+ raise KeyError(f'{obj}, {p.name}')
67
+ continue
68
+
69
+ if p.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY):
70
+ raise TypeError(sig)
71
+
72
+ ann = p.annotation
73
+ if (
74
+ not raw_optional and
75
+ isinstance(rf := rfl.type_(ann), rfl.Union) and
76
+ len(rf.args) == 2 # noqa
77
+ and types.NoneType in rf.args
78
+ ):
79
+ [ann] = [a for a in rf.args if a is not types.NoneType]
80
+
81
+ k = as_key(ann)
82
+ if tags is not None and (pt := tags.get(p.name)) is not None:
83
+ k = tag(k, pt)
84
+
85
+ if k in seen:
86
+ raise DuplicateKeyException(k)
87
+ seen.add(k)
88
+
89
+ kws.append(Kwarg(
90
+ p.name,
91
+ k,
92
+ p.default is not inspect.Parameter.empty,
93
+ ))
94
+
95
+ return KwargsTarget(
96
+ obj,
97
+ kws,
98
+ )
@@ -0,0 +1,109 @@
1
+ """
2
+ TODO:
3
+ - add origin to Id
4
+ """
5
+ import itertools
6
+ import typing as ta
7
+
8
+ from ... import cached
9
+ from ... import check
10
+ from ... import dataclasses as dc
11
+ from ... import lang
12
+ from ..bindings import Binding
13
+ from ..eagers import Eager
14
+ from ..elements import Element
15
+ from ..injector import Injector
16
+ from ..keys import Key
17
+ from ..private import Expose
18
+ from ..private import Private
19
+ from ..providers import Provider
20
+ from ..scopes import Singleton
21
+ from .elements import ElementCollection
22
+ from .injector import InjectorImpl
23
+ from .providers import InternalProvider
24
+ from .providers import ProviderImpl
25
+
26
+
27
+ ##
28
+
29
+
30
+ _PRIVATE_COUNT = itertools.count()
31
+
32
+
33
+ @dc.dataclass(frozen=True)
34
+ class PrivateInjectorId(lang.Final):
35
+ id: int
36
+
37
+
38
+ ##
39
+
40
+
41
+ @dc.dataclass(eq=False)
42
+ class PrivateInjectorProviderImpl(ProviderImpl, lang.Final):
43
+ id: PrivateInjectorId
44
+ ec: ElementCollection
45
+
46
+ @property
47
+ def providers(self) -> ta.Iterable[Provider]:
48
+ return ()
49
+
50
+ def provide(self, injector: Injector) -> ta.Any:
51
+ return check.isinstance(injector, InjectorImpl).create_child(self.ec)
52
+
53
+
54
+ ##
55
+
56
+
57
+ @dc.dataclass(eq=False)
58
+ class ExposedPrivateProviderImpl(ProviderImpl, lang.Final):
59
+ pik: Key
60
+ k: Key
61
+
62
+ @property
63
+ def providers(self) -> ta.Iterable[Provider]:
64
+ return ()
65
+
66
+ def provide(self, injector: Injector) -> ta.Any:
67
+ pi = injector.provide(self.pik)
68
+ return pi.provide(self.k)
69
+
70
+
71
+ ##
72
+
73
+
74
+ @dc.dataclass(frozen=True)
75
+ class PrivateInfo(lang.Final):
76
+ owner: ElementCollection
77
+ p: Private
78
+
79
+ @cached.property
80
+ def id(self) -> PrivateInjectorId:
81
+ return PrivateInjectorId(next(_PRIVATE_COUNT))
82
+
83
+ @cached.property
84
+ def pik(self) -> Key:
85
+ return Key(InjectorImpl, tag=self.id)
86
+
87
+ @cached.function
88
+ def element_collection(self) -> ElementCollection:
89
+ return ElementCollection(self.p.elements)
90
+
91
+ ##
92
+
93
+ @cached.function
94
+ def private_provider_impl(self) -> PrivateInjectorProviderImpl:
95
+ return PrivateInjectorProviderImpl(self.id, self.element_collection())
96
+
97
+ @cached.function
98
+ def exposed_provider_impls(self) -> ta.Sequence[ExposedPrivateProviderImpl]:
99
+ exs = self.element_collection().elements_of_type(Expose)
100
+ return [ExposedPrivateProviderImpl(self.pik, ex.key) for ex in exs]
101
+
102
+ @cached.function
103
+ def owner_elements(self) -> ta.Iterable[Element]:
104
+ lst: list[Element] = [
105
+ Binding(self.pik, InternalProvider(self.private_provider_impl()), Singleton()),
106
+ Eager(self.pik),
107
+ *(Binding(ep.k, InternalProvider(ep)) for ep in self.exposed_provider_impls()),
108
+ ]
109
+ return lst
@@ -0,0 +1,132 @@
1
+ """
2
+ TODO:
3
+ - required_keys
4
+ """
5
+ import abc
6
+ import typing as ta
7
+
8
+ from .. import Cls
9
+ from ... import check
10
+ from ... import dataclasses as dc
11
+ from ... import lang
12
+ from ..injector import Injector
13
+ from ..inspect import KwargsTarget
14
+ from ..providers import ConstProvider
15
+ from ..providers import CtorProvider
16
+ from ..providers import FnProvider
17
+ from ..providers import LinkProvider
18
+ from ..providers import Provider
19
+ from .inspect import build_kwargs_target
20
+
21
+
22
+ class ProviderImpl(lang.Abstract):
23
+ @property
24
+ @abc.abstractmethod
25
+ def providers(self) -> ta.Iterable[Provider]:
26
+ raise NotImplementedError
27
+
28
+ @abc.abstractmethod
29
+ def provide(self, injector: Injector) -> ta.Any:
30
+ raise NotImplementedError
31
+
32
+
33
+ @dc.dataclass(frozen=True, eq=False)
34
+ class InternalProvider(Provider):
35
+ impl: ProviderImpl
36
+
37
+ def provided_cls(self) -> Cls | None:
38
+ raise TypeError
39
+
40
+
41
+ @dc.dataclass(frozen=True, eq=False)
42
+ class CallableProviderImpl(ProviderImpl, lang.Final):
43
+ p: FnProvider | CtorProvider
44
+ kt: KwargsTarget
45
+
46
+ @property
47
+ def providers(self) -> ta.Iterable[Provider]:
48
+ return (self.p,)
49
+
50
+ def provide(self, injector: Injector) -> ta.Any:
51
+ return injector.inject(self.kt)
52
+
53
+
54
+ @dc.dataclass(frozen=True, eq=False)
55
+ class ConstProviderImpl(ProviderImpl, lang.Final):
56
+ p: ConstProvider
57
+
58
+ @property
59
+ def providers(self) -> ta.Iterable[Provider]:
60
+ return (self.p,)
61
+
62
+ def provide(self, injector: Injector) -> ta.Any:
63
+ return self.p.v
64
+
65
+
66
+ @dc.dataclass(frozen=True, eq=False)
67
+ class LinkProviderImpl(ProviderImpl, lang.Final):
68
+ p: LinkProvider
69
+
70
+ @property
71
+ def providers(self) -> ta.Iterable[Provider]:
72
+ return (self.p,)
73
+
74
+ def provide(self, injector: Injector) -> ta.Any:
75
+ return injector.provide(self.p.k)
76
+
77
+
78
+ _ILLEGAL_MULTI_TYPES = (str, bytes, bytearray)
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
113
+
114
+
115
+ PROVIDER_IMPLS_BY_PROVIDER: dict[type[Provider], ta.Callable[..., ProviderImpl]] = {
116
+ FnProvider: lambda p: CallableProviderImpl(p, build_kwargs_target(p.fn)),
117
+ CtorProvider: lambda p: CallableProviderImpl(p, build_kwargs_target(p.cls)),
118
+ ConstProvider: ConstProviderImpl,
119
+ LinkProvider: LinkProviderImpl,
120
+ InternalProvider: lambda p: p.impl,
121
+ }
122
+
123
+
124
+ def make_provider_impl(p: Provider) -> ProviderImpl:
125
+ try:
126
+ fac = PROVIDER_IMPLS_BY_PROVIDER[type(p)]
127
+ except KeyError:
128
+ pass
129
+ else:
130
+ return fac(p)
131
+
132
+ raise TypeError(p)