omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev493__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 (265) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/README.md +199 -0
  3. omlish/__about__.py +21 -16
  4. omlish/argparse/all.py +17 -9
  5. omlish/argparse/cli.py +16 -3
  6. omlish/argparse/utils.py +21 -0
  7. omlish/asyncs/asyncio/rlock.py +110 -0
  8. omlish/asyncs/asyncio/sync.py +43 -0
  9. omlish/asyncs/asyncio/utils.py +2 -0
  10. omlish/asyncs/sync.py +25 -0
  11. omlish/bootstrap/_marshal.py +1 -1
  12. omlish/bootstrap/diag.py +12 -21
  13. omlish/bootstrap/main.py +2 -5
  14. omlish/bootstrap/sys.py +27 -28
  15. omlish/cexts/__init__.py +0 -0
  16. omlish/cexts/include/omlish/omlish.hh +1 -0
  17. omlish/collections/__init__.py +13 -1
  18. omlish/collections/attrregistry.py +210 -0
  19. omlish/collections/cache/impl.py +1 -0
  20. omlish/collections/identity.py +1 -0
  21. omlish/collections/mappings.py +28 -0
  22. omlish/collections/trie.py +5 -1
  23. omlish/collections/utils.py +77 -0
  24. omlish/concurrent/all.py +2 -1
  25. omlish/concurrent/futures.py +25 -0
  26. omlish/concurrent/threadlets.py +1 -1
  27. omlish/daemons/reparent.py +2 -3
  28. omlish/daemons/spawning.py +2 -3
  29. omlish/dataclasses/__init__.py +2 -0
  30. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  31. omlish/dataclasses/impl/api/classes/make.py +3 -0
  32. omlish/dataclasses/impl/concerns/repr.py +15 -2
  33. omlish/dataclasses/impl/configs.py +97 -37
  34. omlish/dataclasses/impl/generation/compilation.py +21 -19
  35. omlish/dataclasses/impl/generation/globals.py +1 -0
  36. omlish/dataclasses/impl/generation/ops.py +1 -0
  37. omlish/dataclasses/impl/generation/plans.py +2 -17
  38. omlish/dataclasses/impl/generation/processor.py +106 -25
  39. omlish/dataclasses/impl/processing/base.py +8 -0
  40. omlish/dataclasses/impl/processing/driving.py +19 -7
  41. omlish/dataclasses/specs.py +1 -0
  42. omlish/dataclasses/tools/modifiers.py +5 -0
  43. omlish/diag/_pycharm/runhack.py +1 -1
  44. omlish/diag/cmds/__init__.py +0 -0
  45. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  46. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  47. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  48. omlish/diag/pycharm.py +16 -2
  49. omlish/diag/pydevd.py +58 -40
  50. omlish/diag/replserver/console.py +1 -1
  51. omlish/dispatch/__init__.py +18 -12
  52. omlish/dispatch/methods.py +50 -140
  53. omlish/dom/rendering.py +1 -1
  54. omlish/formats/dotenv.py +1 -1
  55. omlish/formats/json/stream/__init__.py +13 -0
  56. omlish/funcs/guard.py +225 -0
  57. omlish/graphs/dot/rendering.py +1 -1
  58. omlish/http/all.py +44 -4
  59. omlish/http/clients/asyncs.py +33 -27
  60. omlish/http/clients/base.py +17 -1
  61. omlish/http/clients/coro/__init__.py +0 -0
  62. omlish/http/clients/coro/sync.py +171 -0
  63. omlish/http/clients/default.py +208 -29
  64. omlish/http/clients/executor.py +56 -0
  65. omlish/http/clients/httpx.py +72 -11
  66. omlish/http/clients/middleware.py +181 -0
  67. omlish/http/clients/sync.py +33 -27
  68. omlish/http/clients/syncasync.py +49 -0
  69. omlish/http/clients/urllib.py +6 -3
  70. omlish/http/coro/client/connection.py +15 -6
  71. omlish/http/coro/io.py +2 -0
  72. omlish/http/flasky/__init__.py +40 -0
  73. omlish/http/flasky/_compat.py +2 -0
  74. omlish/http/flasky/api.py +82 -0
  75. omlish/http/flasky/app.py +203 -0
  76. omlish/http/flasky/cvs.py +59 -0
  77. omlish/http/flasky/requests.py +20 -0
  78. omlish/http/flasky/responses.py +23 -0
  79. omlish/http/flasky/routes.py +23 -0
  80. omlish/http/flasky/types.py +15 -0
  81. omlish/http/urls.py +67 -0
  82. omlish/inject/__init__.py +57 -29
  83. omlish/inject/_dataclasses.py +5148 -0
  84. omlish/inject/binder.py +11 -52
  85. omlish/inject/eagers.py +2 -0
  86. omlish/inject/elements.py +27 -0
  87. omlish/inject/helpers/__init__.py +0 -0
  88. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  89. omlish/inject/{tags.py → helpers/id.py} +2 -2
  90. omlish/inject/helpers/late.py +76 -0
  91. omlish/inject/{managed.py → helpers/managed.py} +10 -10
  92. omlish/inject/helpers/multis.py +143 -0
  93. omlish/inject/helpers/wrappers.py +54 -0
  94. omlish/inject/impl/elements.py +54 -21
  95. omlish/inject/impl/injector.py +29 -25
  96. omlish/inject/impl/inspect.py +10 -1
  97. omlish/inject/impl/maysync.py +3 -4
  98. omlish/inject/impl/multis.py +3 -0
  99. omlish/inject/impl/sync.py +3 -4
  100. omlish/inject/injector.py +31 -2
  101. omlish/inject/inspect.py +35 -0
  102. omlish/inject/maysync.py +2 -4
  103. omlish/inject/multis.py +8 -0
  104. omlish/inject/overrides.py +3 -3
  105. omlish/inject/privates.py +6 -0
  106. omlish/inject/providers.py +3 -2
  107. omlish/inject/sync.py +5 -4
  108. omlish/io/buffers.py +118 -0
  109. omlish/io/readers.py +29 -0
  110. omlish/iterators/transforms.py +2 -2
  111. omlish/lang/__init__.py +180 -97
  112. omlish/lang/_asyncs.cc +186 -0
  113. omlish/lang/asyncs.py +17 -0
  114. omlish/lang/casing.py +11 -0
  115. omlish/lang/contextmanagers.py +28 -4
  116. omlish/lang/functions.py +31 -22
  117. omlish/lang/imports/_capture.cc +11 -9
  118. omlish/lang/imports/capture.py +363 -170
  119. omlish/lang/imports/proxy.py +455 -152
  120. omlish/lang/lazyglobals.py +22 -9
  121. omlish/lang/params.py +17 -0
  122. omlish/lang/recursion.py +0 -1
  123. omlish/lang/sequences.py +124 -0
  124. omlish/lifecycles/README.md +30 -0
  125. omlish/lifecycles/__init__.py +87 -13
  126. omlish/lifecycles/_dataclasses.py +1388 -0
  127. omlish/lifecycles/base.py +178 -64
  128. omlish/lifecycles/contextmanagers.py +113 -4
  129. omlish/lifecycles/controller.py +150 -87
  130. omlish/lifecycles/injection.py +143 -0
  131. omlish/lifecycles/listeners.py +56 -0
  132. omlish/lifecycles/managed.py +142 -0
  133. omlish/lifecycles/manager.py +218 -93
  134. omlish/lifecycles/states.py +2 -0
  135. omlish/lifecycles/transitions.py +3 -0
  136. omlish/lifecycles/unwrap.py +57 -0
  137. omlish/lite/abstract.py +54 -24
  138. omlish/lite/asyncs.py +2 -2
  139. omlish/lite/attrops.py +2 -0
  140. omlish/lite/contextmanagers.py +4 -4
  141. omlish/lite/dataclasses.py +44 -0
  142. omlish/lite/maybes.py +8 -0
  143. omlish/lite/maysync.py +1 -5
  144. omlish/lite/pycharm.py +1 -1
  145. omlish/lite/typing.py +24 -0
  146. omlish/logs/_amalg.py +1 -1
  147. omlish/logs/all.py +25 -11
  148. omlish/logs/asyncs.py +73 -0
  149. omlish/logs/base.py +101 -12
  150. omlish/logs/contexts.py +4 -1
  151. omlish/logs/lists.py +125 -0
  152. omlish/logs/modules.py +19 -1
  153. omlish/logs/std/loggers.py +6 -1
  154. omlish/logs/std/noisy.py +11 -9
  155. omlish/logs/{standard.py → std/standard.py} +3 -4
  156. omlish/logs/utils.py +17 -2
  157. omlish/manifests/loading.py +2 -1
  158. omlish/marshal/__init__.py +33 -13
  159. omlish/marshal/_dataclasses.py +2774 -0
  160. omlish/marshal/base/configs.py +12 -0
  161. omlish/marshal/base/contexts.py +36 -21
  162. omlish/marshal/base/funcs.py +8 -11
  163. omlish/marshal/base/options.py +8 -0
  164. omlish/marshal/base/registries.py +146 -44
  165. omlish/marshal/base/types.py +40 -16
  166. omlish/marshal/composite/iterables.py +33 -20
  167. omlish/marshal/composite/literals.py +20 -18
  168. omlish/marshal/composite/mappings.py +36 -23
  169. omlish/marshal/composite/maybes.py +29 -19
  170. omlish/marshal/composite/newtypes.py +16 -16
  171. omlish/marshal/composite/optionals.py +14 -14
  172. omlish/marshal/composite/special.py +15 -15
  173. omlish/marshal/composite/unions/__init__.py +0 -0
  174. omlish/marshal/composite/unions/literals.py +93 -0
  175. omlish/marshal/composite/unions/primitives.py +103 -0
  176. omlish/marshal/factories/invalidate.py +18 -68
  177. omlish/marshal/factories/method.py +26 -0
  178. omlish/marshal/factories/moduleimport/factories.py +22 -65
  179. omlish/marshal/factories/multi.py +13 -25
  180. omlish/marshal/factories/recursive.py +42 -56
  181. omlish/marshal/factories/typecache.py +29 -74
  182. omlish/marshal/factories/typemap.py +42 -43
  183. omlish/marshal/objects/dataclasses.py +129 -106
  184. omlish/marshal/objects/marshal.py +18 -14
  185. omlish/marshal/objects/namedtuples.py +48 -42
  186. omlish/marshal/objects/unmarshal.py +19 -15
  187. omlish/marshal/polymorphism/marshal.py +9 -11
  188. omlish/marshal/polymorphism/metadata.py +16 -5
  189. omlish/marshal/polymorphism/standard.py +13 -1
  190. omlish/marshal/polymorphism/unions.py +15 -105
  191. omlish/marshal/polymorphism/unmarshal.py +9 -10
  192. omlish/marshal/singular/enums.py +14 -18
  193. omlish/marshal/standard.py +10 -6
  194. omlish/marshal/trivial/any.py +1 -1
  195. omlish/marshal/trivial/forbidden.py +21 -26
  196. omlish/metadata.py +23 -1
  197. omlish/os/forkhooks.py +4 -0
  198. omlish/os/pidfiles/pinning.py +2 -2
  199. omlish/reflect/__init__.py +43 -26
  200. omlish/reflect/ops.py +10 -1
  201. omlish/reflect/types.py +1 -0
  202. omlish/secrets/marshal.py +1 -1
  203. omlish/specs/jmespath/__init__.py +12 -3
  204. omlish/specs/jmespath/_dataclasses.py +2893 -0
  205. omlish/specs/jmespath/ast.py +1 -1
  206. omlish/specs/jsonrpc/__init__.py +13 -0
  207. omlish/specs/jsonrpc/_marshal.py +32 -23
  208. omlish/specs/jsonrpc/conns.py +10 -7
  209. omlish/specs/jsonschema/_marshal.py +1 -1
  210. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  211. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  212. omlish/specs/openapi/_marshal.py +31 -22
  213. omlish/sql/__init__.py +24 -5
  214. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  215. omlish/sql/api/dbapi.py +1 -1
  216. omlish/sql/dbapi/__init__.py +15 -0
  217. omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
  218. omlish/sql/queries/__init__.py +3 -0
  219. omlish/sql/queries/_marshal.py +2 -2
  220. omlish/sql/queries/rendering.py +1 -1
  221. omlish/sql/tabledefs/_marshal.py +1 -1
  222. omlish/subprocesses/base.py +4 -0
  223. omlish/subprocesses/editor.py +1 -1
  224. omlish/sync.py +155 -21
  225. omlish/term/alt.py +60 -0
  226. omlish/term/confirm.py +8 -8
  227. omlish/term/pager.py +235 -0
  228. omlish/term/terminfo.py +935 -0
  229. omlish/term/termstate.py +67 -0
  230. omlish/term/vt100/terminal.py +0 -3
  231. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  232. omlish/testing/pytest/plugins/asyncs/plugin.py +2 -0
  233. omlish/testing/pytest/plugins/skips.py +2 -1
  234. omlish/testing/unittest/main.py +3 -3
  235. omlish/text/docwrap/__init__.py +3 -0
  236. omlish/text/docwrap/__main__.py +11 -0
  237. omlish/text/docwrap/api.py +83 -0
  238. omlish/text/docwrap/cli.py +91 -0
  239. omlish/text/docwrap/groups.py +84 -0
  240. omlish/text/docwrap/lists.py +167 -0
  241. omlish/text/docwrap/parts.py +146 -0
  242. omlish/text/docwrap/reflowing.py +106 -0
  243. omlish/text/docwrap/rendering.py +151 -0
  244. omlish/text/docwrap/utils.py +11 -0
  245. omlish/text/docwrap/wrapping.py +59 -0
  246. omlish/text/filecache.py +2 -2
  247. omlish/text/lorem.py +6 -0
  248. omlish/text/parts.py +2 -2
  249. omlish/text/textwrap.py +51 -0
  250. omlish/typedvalues/marshal.py +85 -59
  251. omlish/typedvalues/values.py +2 -1
  252. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/METADATA +36 -32
  253. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +260 -199
  254. omlish/dataclasses/impl/generation/mangling.py +0 -18
  255. omlish/funcs/match.py +0 -227
  256. omlish/lifecycles/abstract.py +0 -86
  257. omlish/marshal/factories/match.py +0 -34
  258. omlish/marshal/factories/simple.py +0 -28
  259. /omlish/inject/{impl → helpers}/proxy.py +0 -0
  260. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  261. /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
  262. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
  263. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
  264. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
  265. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/top_level.txt +0 -0
omlish/inject/binder.py CHANGED
@@ -11,13 +11,8 @@ from .bindings import Binding
11
11
  from .eagers import Eager
12
12
  from .elements import Element
13
13
  from .elements import Elements
14
- from .elements import as_elements
15
14
  from .keys import Key
16
15
  from .keys import as_key
17
- from .multis import MapBinding
18
- from .multis import SetBinding
19
- from .multis import is_map_multi_key
20
- from .multis import is_set_multi_key
21
16
  from .privates import Expose
22
17
  from .providers import AsyncFnProvider
23
18
  from .providers import ConstProvider
@@ -27,7 +22,6 @@ from .providers import LinkProvider
27
22
  from .providers import Provider
28
23
  from .scopes import SCOPE_ALIASES
29
24
  from .scopes import Singleton
30
- from .tags import Id
31
25
  from .types import Scope
32
26
  from .types import Unscoped
33
27
 
@@ -84,6 +78,7 @@ def bind(
84
78
  *,
85
79
  tag: ta.Any = None,
86
80
 
81
+ to_provider: Provider | None = None,
87
82
  to_async_fn: ta.Any = None,
88
83
  to_fn: ta.Any = None,
89
84
  to_ctor: ta.Any = None,
@@ -93,7 +88,7 @@ def bind(
93
88
  in_: Scope | None = None,
94
89
  singleton: bool = False,
95
90
 
96
- eager: bool = False,
91
+ eager: bool | int = False,
97
92
  expose: bool = False,
98
93
  ) -> Element | Elements:
99
94
  if obj is None or obj is inspect.Parameter.empty:
@@ -104,6 +99,7 @@ def bind(
104
99
  ##
105
100
 
106
101
  has_to = (
102
+ to_provider is not None or
107
103
  to_async_fn is not None or
108
104
  to_fn is not None or
109
105
  to_ctor is not None or
@@ -121,7 +117,10 @@ def bind(
121
117
  elif _is_fn(obj) and not has_to:
122
118
  sig = _inspect.signature(obj)
123
119
  ty = rfl.type_(sig.return_annotation)
124
- to_fn = obj
120
+ if inspect.iscoroutinefunction(obj):
121
+ to_async_fn = obj
122
+ else:
123
+ to_fn = obj
125
124
  key = Key(ty)
126
125
  else:
127
126
  if to_const is not None:
@@ -140,6 +139,8 @@ def bind(
140
139
  ##
141
140
 
142
141
  providers: list[Provider] = []
142
+ if to_provider is not None:
143
+ providers.append(to_provider)
143
144
  if to_async_fn is not None:
144
145
  providers.append(AsyncFnProvider(to_async_fn))
145
146
  if to_fn is not None:
@@ -182,8 +183,8 @@ def bind(
182
183
 
183
184
  elements: list[Element] = [binding]
184
185
 
185
- if eager:
186
- elements.append(Eager(key))
186
+ if eager is not False:
187
+ elements.append(Eager(key, priority=eager if isinstance(eager, int) else 0))
187
188
  if expose:
188
189
  elements.append(Expose(key))
189
190
 
@@ -191,45 +192,3 @@ def bind(
191
192
  return elements[0]
192
193
  else:
193
194
  return Elements(frozenset(elements))
194
-
195
-
196
- ##
197
-
198
-
199
- def bind_set_entry_const(
200
- multi_key: ta.Any,
201
- obj: ta.Any,
202
- *,
203
- tag: ta.Any | None = None,
204
- ) -> Elements:
205
- multi_key = as_key(multi_key)
206
- check.arg(is_set_multi_key(multi_key))
207
-
208
- if tag is None:
209
- tag = Id(id(obj), tag=multi_key.tag)
210
- obj_key: Key = Key(type(obj), tag=tag)
211
-
212
- return as_elements(
213
- bind(obj_key, to_const=obj),
214
- SetBinding(multi_key, obj_key),
215
- )
216
-
217
-
218
- def bind_map_entry_const(
219
- multi_key: ta.Any,
220
- map_key: ta.Any,
221
- obj: ta.Any,
222
- *,
223
- tag: ta.Any | None = None,
224
- ) -> Elements:
225
- multi_key = as_key(multi_key)
226
- check.arg(is_map_multi_key(multi_key))
227
-
228
- if tag is None:
229
- tag = Id(id(obj), tag=multi_key.tag)
230
- obj_key: Key = Key(type(obj), tag=tag)
231
-
232
- return as_elements(
233
- bind(obj_key, to_const=obj),
234
- MapBinding(multi_key, map_key, obj_key),
235
- )
omlish/inject/eagers.py CHANGED
@@ -16,3 +16,5 @@ from .keys import Key
16
16
  @dc.extra_class_params(cache_hash=True)
17
17
  class Eager(Element, lang.Final):
18
18
  key: Key = dc.xfield(coerce=check.of_isinstance(Key))
19
+
20
+ priority: int = dc.xfield(0, kw_only=True)
omlish/inject/elements.py CHANGED
@@ -6,6 +6,12 @@ from .. import lang
6
6
  from .impl.origins import HasOriginsImpl
7
7
 
8
8
 
9
+ if ta.TYPE_CHECKING:
10
+ from .impl import elements as _elements
11
+ else:
12
+ _elements = lang.proxy_import('.impl.elements', __package__)
13
+
14
+
9
15
  ##
10
16
 
11
17
 
@@ -64,3 +70,24 @@ def as_elements(*args: Elemental) -> Elements:
64
70
  es if es else None,
65
71
  cs if cs else None,
66
72
  )
73
+
74
+
75
+ def iter_elements(*args: Elemental) -> ta.Iterator[Element]:
76
+ for a in args:
77
+ if isinstance(a, Element):
78
+ yield a
79
+ elif isinstance(a, (Elements, ElementGenerator)):
80
+ yield from a
81
+ else:
82
+ raise TypeError(a)
83
+
84
+
85
+ ##
86
+
87
+
88
+ class CollectedElements(lang.PackageSealed, lang.Abstract):
89
+ pass
90
+
91
+
92
+ def collect_elements(es: Elements | CollectedElements) -> CollectedElements:
93
+ return _elements.collect_elements(es)
File without changes
@@ -1,8 +1,8 @@
1
1
  import typing as ta
2
2
 
3
- from .. import dataclasses as dc
4
- from .. import lang
5
- from .impl.origins import HasOriginsImpl
3
+ from ... import dataclasses as dc
4
+ from ... import lang
5
+ from ..impl.origins import HasOriginsImpl
6
6
 
7
7
 
8
8
  T = ta.TypeVar('T')
@@ -1,7 +1,7 @@
1
1
  import typing as ta
2
2
 
3
- from .. import dataclasses as dc
4
- from .types import Tag
3
+ from ... import dataclasses as dc
4
+ from ..types import Tag
5
5
 
6
6
 
7
7
  ##
@@ -0,0 +1,76 @@
1
+ """
2
+ A minor tidiness for hiding refs to [Async]Injector in circ-dep workarounds. Unfortunately unsuitable outside of
3
+ injection modules due to it itself being in here (as user code shouldn't reference injector guts), but at least it's
4
+ something.
5
+ """
6
+ import abc
7
+ import functools
8
+ import typing as ta
9
+
10
+ from ... import lang
11
+ from ... import reflect as rfl
12
+ from ..binder import bind
13
+ from ..elements import Elements
14
+ from ..elements import as_elements
15
+ from ..injector import AsyncInjector
16
+ from ..inspect import KwargsTarget
17
+ from ..keys import Key
18
+ from ..keys import as_key
19
+ from ..sync import Injector
20
+
21
+
22
+ T = ta.TypeVar('T')
23
+
24
+
25
+ ##
26
+
27
+
28
+ class Late(lang.Abstract, ta.Generic[T]):
29
+ @abc.abstractmethod
30
+ def __call__(self) -> T:
31
+ raise NotImplementedError
32
+
33
+
34
+ class AsyncLate(lang.Abstract, ta.Generic[T]):
35
+ @abc.abstractmethod
36
+ def __call__(self) -> ta.Awaitable[T]:
37
+ raise NotImplementedError
38
+
39
+
40
+ #
41
+
42
+
43
+ def _bind_late(
44
+ injector_cls: type,
45
+ late_cls: ta.Any,
46
+ inner: ta.Any,
47
+ outer: ta.Any | None = None,
48
+ ) -> Elements:
49
+ inner_key = as_key(inner)
50
+
51
+ outer_key: Key
52
+ outer_fac: ta.Callable[[ta.Any], ta.Any]
53
+ if outer is None:
54
+ inner_ann = rfl.to_annotation(inner_key.ty)
55
+ outer_ann = late_cls[inner_ann]
56
+ outer_key = Key(rfl.type_(outer_ann))
57
+ outer_fac = lang.identity
58
+ else:
59
+ outer_key = as_key(outer)
60
+ outer_cls = rfl.get_concrete_type(outer_key.ty)
61
+ outer_fac = outer_cls # type: ignore[assignment]
62
+
63
+ return as_elements(
64
+ bind(outer_key, to_fn=KwargsTarget.of( # noqa
65
+ lambda i: outer_fac(functools.partial(i.provide, inner_key)),
66
+ i=injector_cls,
67
+ )),
68
+ )
69
+
70
+
71
+ def bind_late(inner: ta.Any, outer: ta.Any | None = None) -> Elements:
72
+ return _bind_late(Injector, Late, inner, outer)
73
+
74
+
75
+ def bind_async_late(inner: ta.Any, outer: ta.Any | None = None) -> Elements:
76
+ return _bind_late(AsyncInjector, AsyncLate, inner, outer)
@@ -5,20 +5,20 @@ TODO:
5
5
  import contextlib
6
6
  import typing as ta
7
7
 
8
- from .. import lang
9
- from .binder import bind
10
- from .elements import Elemental
11
- from .impl.inspect import build_kwargs_target
8
+ from ... import lang
9
+ from ..binder import bind
10
+ from ..elements import Elemental
11
+ from ..impl.inspect import build_kwargs_target
12
12
 
13
13
 
14
14
  if ta.TYPE_CHECKING:
15
- from . import injector as _injector
16
- from . import maysync as _maysync
17
- from . import sync as _sync
15
+ from .. import injector as _injector
16
+ from .. import maysync as _maysync
17
+ from .. import sync as _sync
18
18
  else:
19
- _injector = lang.proxy_import('.injector', __package__)
20
- _maysync = lang.proxy_import('.maysync', __package__)
21
- _sync = lang.proxy_import('.sync', __package__)
19
+ _injector = lang.proxy_import('..injector', __package__)
20
+ _maysync = lang.proxy_import('..maysync', __package__)
21
+ _sync = lang.proxy_import('..sync', __package__)
22
22
 
23
23
 
24
24
  T = ta.TypeVar('T')
@@ -0,0 +1,143 @@
1
+ import typing as ta
2
+
3
+ from ... import cached
4
+ from ... import check
5
+ from ... import dataclasses as dc
6
+ from ... import lang
7
+ from ... import reflect as rfl
8
+ from ..binder import bind
9
+ from ..elements import Elements
10
+ from ..elements import as_elements
11
+ from ..inspect import KwargsTarget
12
+ from ..keys import Key
13
+ from ..keys import as_key
14
+ from ..multis import MapBinding
15
+ from ..multis import SetBinder
16
+ from ..multis import SetBinding
17
+ from ..multis import is_map_multi_key
18
+ from ..multis import is_set_multi_key
19
+ from ..providers import FnProvider
20
+ from .id import Id
21
+
22
+
23
+ ItemT = ta.TypeVar('ItemT')
24
+
25
+
26
+ ##
27
+
28
+
29
+ def bind_set_entry_const(
30
+ multi_key: ta.Any,
31
+ obj: ta.Any,
32
+ *,
33
+ tag: ta.Any | None = None,
34
+ ) -> Elements:
35
+ multi_key = as_key(multi_key)
36
+ check.arg(is_set_multi_key(multi_key))
37
+
38
+ if tag is None:
39
+ tag = Id(id(obj), tag=multi_key.tag)
40
+ obj_key: Key = Key(type(obj), tag=tag)
41
+
42
+ return as_elements(
43
+ bind(obj_key, to_const=obj),
44
+ SetBinding(multi_key, obj_key),
45
+ )
46
+
47
+
48
+ def bind_map_entry_const(
49
+ multi_key: ta.Any,
50
+ map_key: ta.Any,
51
+ obj: ta.Any,
52
+ *,
53
+ tag: ta.Any | None = None,
54
+ ) -> Elements:
55
+ multi_key = as_key(multi_key)
56
+ check.arg(is_map_multi_key(multi_key))
57
+
58
+ if tag is None:
59
+ tag = Id(id(obj), tag=multi_key.tag)
60
+ obj_key: Key = Key(type(obj), tag=tag)
61
+
62
+ return as_elements(
63
+ bind(obj_key, to_const=obj),
64
+ MapBinding(multi_key, map_key, obj_key),
65
+ )
66
+
67
+
68
+ ##
69
+
70
+
71
+ @ta.final
72
+ class ItemsBinderHelper(ta.Generic[ItemT]):
73
+ def __init__(self, items_cls: ta.Any) -> None:
74
+ self._items_cls = items_cls
75
+
76
+ @cached.property
77
+ def _item_rty(self) -> rfl.Type:
78
+ rty = check.isinstance(rfl.type_(rfl.get_orig_class(self)), rfl.Generic)
79
+ check.is_(rty.cls, self.__class__)
80
+ return check.single(rty.args)
81
+
82
+ @dc.dataclass(frozen=True, eq=False)
83
+ class _ItemsBox:
84
+ vs: ta.Sequence
85
+
86
+ @cached.property
87
+ def _items_box(self) -> type[_ItemsBox]:
88
+ if isinstance(item_rty := self._item_rty, type):
89
+ sfx = item_rty.__qualname__
90
+ else:
91
+ sfx = str(item_rty).replace("'", '')
92
+
93
+ return lang.new_type( # noqa
94
+ f'{ItemsBinderHelper._ItemsBox.__qualname__}${sfx}@{id(self):x}',
95
+ (ItemsBinderHelper._ItemsBox,),
96
+ {},
97
+ )
98
+
99
+ @cached.property
100
+ def _set_key(self) -> Key:
101
+ return as_key(ta.AbstractSet[self._item_rty]) # type: ignore
102
+
103
+ def bind_item_consts(self, *items: ItemT) -> Elements:
104
+ return as_elements(
105
+ bind_set_entry_const(self._set_key, self._items_box(items)),
106
+ )
107
+
108
+ @dc.dataclass(frozen=True, eq=False)
109
+ @dc.extra_class_params(repr_id=True)
110
+ class _ItemTag:
111
+ pass
112
+
113
+ def bind_item(self, **kwargs: ta.Any) -> Elements:
114
+ tag = ItemsBinderHelper._ItemTag()
115
+ item_key: Key = Key(ta.Any, tag=tag)
116
+ items_box_key: Key = Key(self._items_box, tag=tag)
117
+ return as_elements(
118
+ bind(item_key, **kwargs),
119
+ bind(
120
+ items_box_key,
121
+ to_provider=FnProvider(KwargsTarget.of(
122
+ lambda v: self._items_box([v]),
123
+ v=item_key,
124
+ )),
125
+ ),
126
+ SetBinding(self._set_key, items_box_key),
127
+ )
128
+
129
+ def bind_items_provider(self, **kwargs: ta.Any) -> Elements:
130
+ return as_elements(
131
+ SetBinder[self._item_rty](), # type: ignore
132
+ bind(
133
+ self._items_cls,
134
+ to_provider=FnProvider(KwargsTarget.of(
135
+ lambda s: self._items_cls([v for i in s for v in i.vs]),
136
+ s=self._set_key,
137
+ )),
138
+ **kwargs,
139
+ ),
140
+ )
141
+
142
+
143
+ items_binder_helper = ItemsBinderHelper
@@ -0,0 +1,54 @@
1
+ import typing as ta
2
+
3
+ from ... import cached
4
+ from ... import dataclasses as dc
5
+ from ..binder import bind
6
+ from ..elements import Elemental
7
+ from ..keys import Key
8
+ from ..keys import as_key
9
+ from ..privates import private
10
+
11
+
12
+ ##
13
+
14
+
15
+ @ta.final
16
+ class WrapperBinderHelper:
17
+ def __init__(self, key: ta.Any) -> None:
18
+ self._key = as_key(key)
19
+ self._root = WrapperBinderHelper._Root()
20
+ self._top = WrapperBinderHelper._Level(self._root, 0)
21
+
22
+ @dc.dataclass(frozen=True, eq=False)
23
+ @dc.extra_class_params(repr_id=True)
24
+ class _Root:
25
+ pass
26
+
27
+ @dc.dataclass(frozen=True)
28
+ class _Level:
29
+ root: 'WrapperBinderHelper._Root'
30
+ level: int
31
+
32
+ def next(self) -> 'WrapperBinderHelper._Level':
33
+ return WrapperBinderHelper._Level(self.root, self.level + 1)
34
+
35
+ @cached.property
36
+ def key(self) -> Key:
37
+ return Key(ta.Any, tag=self)
38
+
39
+ @property
40
+ def top(self) -> Key:
41
+ return self._top.key
42
+
43
+ def push_bind(self, **kwargs: ta.Any) -> Elemental:
44
+ prv = self._top
45
+ nxt = prv.next()
46
+ out = private(
47
+ *([bind(self._key, to_key=prv.key)] if prv.level else []),
48
+ bind(nxt.key, **kwargs, expose=True),
49
+ )
50
+ self._top = nxt
51
+ return out
52
+
53
+
54
+ wrapper_binder_helper = WrapperBinderHelper
@@ -27,6 +27,7 @@ from ... import collections as col
27
27
  from ... import lang
28
28
  from ..bindings import Binding
29
29
  from ..eagers import Eager
30
+ from ..elements import CollectedElements
30
31
  from ..elements import Element
31
32
  from ..elements import Elements
32
33
  from ..errors import ConflictingKeyError
@@ -47,7 +48,7 @@ from .multis import make_multi_provider_impl
47
48
  from .origins import Origins
48
49
  from .origins import set_origins
49
50
  from .providers import ProviderImpl
50
- from .providers2 import make_provider_impl
51
+ from .providersmap import make_provider_impl
51
52
  from .scopes import make_scope_impl
52
53
 
53
54
 
@@ -63,7 +64,24 @@ ElementT = ta.TypeVar('ElementT', bound=Element)
63
64
  ##
64
65
 
65
66
 
66
- class ElementCollection(lang.Final):
67
+ _SIMPLE_KEYED_ELEMENT_TYPES: tuple[type[Element], ...] = (
68
+ Binding,
69
+ Eager,
70
+ Expose,
71
+ )
72
+
73
+ _SIMPLE_NON_KEYED_ELEMENT_TYPES: tuple[type[Element], ...] = (
74
+ ProvisionListenerBinding,
75
+ )
76
+
77
+ _NON_BINDING_ELEMENT_TYPES: tuple[type[Element], ...] = (
78
+ Eager,
79
+ Expose,
80
+ ProvisionListenerBinding,
81
+ )
82
+
83
+
84
+ class ElementCollection(CollectedElements, lang.Final):
67
85
  def __init__(self, es: Elements) -> None:
68
86
  super().__init__()
69
87
 
@@ -96,28 +114,28 @@ class ElementCollection(lang.Final):
96
114
  out.setdefault(k, []).extend(e)
97
115
 
98
116
  for e in es:
99
- if isinstance(e, ScopeBinding):
117
+ if isinstance(e, _SIMPLE_KEYED_ELEMENT_TYPES):
118
+ add(e.key, e) # type: ignore[attr-defined] # noqa
119
+
120
+ elif isinstance(e, _SIMPLE_NON_KEYED_ELEMENT_TYPES):
121
+ add(None, e)
122
+
123
+ elif isinstance(e, (SetBinding, MapBinding)):
124
+ add(e.multi_key, e)
125
+
126
+ elif isinstance(e, ScopeBinding):
100
127
  add(None, e)
101
128
  sci = make_scope_impl(e.scope)
102
129
  if (sae := sci.auto_elements()) is not None:
103
130
  self._build_raw_element_multimap(sae, out)
104
131
 
105
- elif isinstance(e, (Binding, Eager, Expose)):
106
- add(e.key, e)
107
-
108
132
  elif isinstance(e, Private):
109
133
  pi = self._get_private_info(e)
110
134
  self._build_raw_element_multimap(pi.owner_elements(), out)
111
135
 
112
- elif isinstance(e, (SetBinding, MapBinding)):
113
- add(e.multi_key, e)
114
-
115
- elif isinstance(e, ProvisionListenerBinding):
116
- add(None, e)
117
-
118
136
  elif isinstance(e, Overrides):
119
- ovr = self._build_raw_element_multimap(e.ovr)
120
137
  src = self._build_raw_element_multimap(e.src)
138
+ ovr = self._build_raw_element_multimap(e.ovr)
121
139
  for k, b in src.items(): # FIXME: merge None keys?
122
140
  try:
123
141
  bs = ovr[k]
@@ -166,9 +184,8 @@ class ElementCollection(lang.Final):
166
184
 
167
185
  es_by_ty = col.multi_map_by(type, es)
168
186
 
169
- es_by_ty.pop(Eager, None)
170
- es_by_ty.pop(Expose, None)
171
- es_by_ty.pop(ProvisionListenerBinding, None)
187
+ for nb_ty in _NON_BINDING_ELEMENT_TYPES:
188
+ es_by_ty.pop(nb_ty, None)
172
189
 
173
190
  if (bs := es_by_ty.pop(Binding, None)):
174
191
  b = self._get_single_binding(k, bs) # type: ignore
@@ -176,7 +193,6 @@ class ElementCollection(lang.Final):
176
193
  p: ProviderImpl
177
194
  if isinstance(b.provider, (SetProvider, MapProvider)):
178
195
  p = make_multi_provider_impl(b.provider, es_by_ty)
179
-
180
196
  else:
181
197
  p = make_provider_impl(b.provider)
182
198
 
@@ -194,10 +210,27 @@ class ElementCollection(lang.Final):
194
210
  ##
195
211
 
196
212
  @lang.cached_function
197
- def eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
213
+ def scope_binding_scopes(self) -> ta.Sequence[Scope]:
214
+ return [sb.scope for sb in self.elements_of_type(ScopeBinding)]
215
+
216
+ @lang.cached_function
217
+ def sorted_eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
198
218
  bim = self.binding_impl_map()
199
- ret: dict[Scope, list[Key]] = {}
219
+ dct: dict[Scope, list[Eager]] = {}
200
220
  for e in self.elements_of_type(Eager):
201
221
  bi = bim[e.key]
202
- ret.setdefault(bi.scope, []).append(e.key)
203
- return ret
222
+ dct.setdefault(bi.scope, []).append(e)
223
+ return {
224
+ sc: tuple(eg.key for eg in sorted(egs, key=lambda eg: eg.priority))
225
+ for sc, egs in dct.items()
226
+ }
227
+
228
+
229
+ ##
230
+
231
+
232
+ def collect_elements(es: Elements | CollectedElements) -> ElementCollection:
233
+ if isinstance(es, CollectedElements):
234
+ return check.isinstance(es, ElementCollection)
235
+ else:
236
+ return ElementCollection(es)