omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev484__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 (226) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/__about__.py +15 -15
  3. omlish/argparse/all.py +17 -9
  4. omlish/argparse/cli.py +16 -3
  5. omlish/argparse/utils.py +21 -0
  6. omlish/asyncs/asyncio/rlock.py +110 -0
  7. omlish/asyncs/asyncio/sync.py +43 -0
  8. omlish/asyncs/asyncio/utils.py +2 -0
  9. omlish/asyncs/sync.py +25 -0
  10. omlish/bootstrap/_marshal.py +1 -1
  11. omlish/bootstrap/diag.py +12 -21
  12. omlish/bootstrap/main.py +2 -5
  13. omlish/bootstrap/sys.py +27 -28
  14. omlish/cexts/__init__.py +0 -0
  15. omlish/cexts/include/omlish/omlish.hh +1 -0
  16. omlish/collections/__init__.py +13 -1
  17. omlish/collections/attrregistry.py +210 -0
  18. omlish/collections/cache/impl.py +1 -0
  19. omlish/collections/identity.py +1 -0
  20. omlish/collections/mappings.py +28 -0
  21. omlish/collections/trie.py +5 -1
  22. omlish/collections/utils.py +77 -0
  23. omlish/concurrent/all.py +2 -1
  24. omlish/concurrent/futures.py +25 -0
  25. omlish/concurrent/threadlets.py +1 -1
  26. omlish/daemons/reparent.py +2 -3
  27. omlish/daemons/spawning.py +2 -3
  28. omlish/dataclasses/__init__.py +2 -0
  29. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  30. omlish/dataclasses/impl/api/classes/make.py +3 -0
  31. omlish/dataclasses/impl/concerns/repr.py +15 -2
  32. omlish/dataclasses/impl/configs.py +97 -37
  33. omlish/dataclasses/impl/generation/compilation.py +21 -19
  34. omlish/dataclasses/impl/generation/globals.py +1 -0
  35. omlish/dataclasses/impl/generation/ops.py +1 -0
  36. omlish/dataclasses/impl/generation/processor.py +105 -24
  37. omlish/dataclasses/impl/processing/base.py +8 -0
  38. omlish/dataclasses/impl/processing/driving.py +8 -8
  39. omlish/dataclasses/specs.py +1 -0
  40. omlish/dataclasses/tools/modifiers.py +5 -0
  41. omlish/diag/cmds/__init__.py +0 -0
  42. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  43. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  44. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  45. omlish/diag/pycharm.py +16 -2
  46. omlish/diag/pydevd.py +58 -40
  47. omlish/diag/replserver/console.py +1 -1
  48. omlish/dispatch/__init__.py +18 -12
  49. omlish/dispatch/methods.py +50 -140
  50. omlish/dom/rendering.py +1 -1
  51. omlish/formats/dotenv.py +1 -1
  52. omlish/formats/json/stream/__init__.py +13 -0
  53. omlish/funcs/guard.py +225 -0
  54. omlish/graphs/dot/rendering.py +1 -1
  55. omlish/http/all.py +44 -4
  56. omlish/http/clients/asyncs.py +33 -27
  57. omlish/http/clients/base.py +17 -1
  58. omlish/http/clients/coro/__init__.py +0 -0
  59. omlish/http/clients/coro/sync.py +171 -0
  60. omlish/http/clients/default.py +208 -29
  61. omlish/http/clients/executor.py +56 -0
  62. omlish/http/clients/httpx.py +72 -11
  63. omlish/http/clients/middleware.py +181 -0
  64. omlish/http/clients/sync.py +33 -27
  65. omlish/http/clients/syncasync.py +49 -0
  66. omlish/http/clients/urllib.py +6 -3
  67. omlish/http/coro/client/connection.py +15 -6
  68. omlish/http/coro/io.py +2 -0
  69. omlish/http/flasky/__init__.py +40 -0
  70. omlish/http/flasky/_compat.py +2 -0
  71. omlish/http/flasky/api.py +82 -0
  72. omlish/http/flasky/app.py +203 -0
  73. omlish/http/flasky/cvs.py +59 -0
  74. omlish/http/flasky/requests.py +20 -0
  75. omlish/http/flasky/responses.py +23 -0
  76. omlish/http/flasky/routes.py +23 -0
  77. omlish/http/flasky/types.py +15 -0
  78. omlish/http/urls.py +67 -0
  79. omlish/inject/__init__.py +38 -18
  80. omlish/inject/_dataclasses.py +4986 -0
  81. omlish/inject/binder.py +4 -48
  82. omlish/inject/elements.py +27 -0
  83. omlish/inject/helpers/__init__.py +0 -0
  84. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  85. omlish/inject/{tags.py → helpers/id.py} +2 -2
  86. omlish/inject/helpers/multis.py +143 -0
  87. omlish/inject/helpers/wrappers.py +54 -0
  88. omlish/inject/impl/elements.py +47 -17
  89. omlish/inject/impl/injector.py +20 -19
  90. omlish/inject/impl/inspect.py +10 -1
  91. omlish/inject/impl/maysync.py +3 -4
  92. omlish/inject/impl/multis.py +3 -0
  93. omlish/inject/impl/sync.py +3 -4
  94. omlish/inject/injector.py +31 -2
  95. omlish/inject/inspect.py +35 -0
  96. omlish/inject/maysync.py +2 -4
  97. omlish/inject/multis.py +8 -0
  98. omlish/inject/overrides.py +3 -3
  99. omlish/inject/privates.py +6 -0
  100. omlish/inject/providers.py +3 -2
  101. omlish/inject/sync.py +5 -4
  102. omlish/io/buffers.py +118 -0
  103. omlish/io/readers.py +29 -0
  104. omlish/iterators/transforms.py +2 -2
  105. omlish/lang/__init__.py +178 -97
  106. omlish/lang/_asyncs.cc +186 -0
  107. omlish/lang/asyncs.py +17 -0
  108. omlish/lang/casing.py +11 -0
  109. omlish/lang/contextmanagers.py +28 -4
  110. omlish/lang/functions.py +31 -22
  111. omlish/lang/imports/_capture.cc +11 -9
  112. omlish/lang/imports/capture.py +363 -170
  113. omlish/lang/imports/proxy.py +455 -152
  114. omlish/lang/lazyglobals.py +22 -9
  115. omlish/lang/params.py +17 -0
  116. omlish/lang/recursion.py +0 -1
  117. omlish/lang/sequences.py +124 -0
  118. omlish/lite/abstract.py +54 -24
  119. omlish/lite/asyncs.py +2 -2
  120. omlish/lite/attrops.py +2 -0
  121. omlish/lite/contextmanagers.py +4 -4
  122. omlish/lite/dataclasses.py +44 -0
  123. omlish/lite/maybes.py +8 -0
  124. omlish/lite/maysync.py +1 -5
  125. omlish/lite/pycharm.py +1 -1
  126. omlish/lite/typing.py +6 -0
  127. omlish/logs/all.py +1 -1
  128. omlish/logs/utils.py +1 -1
  129. omlish/manifests/loading.py +2 -1
  130. omlish/marshal/__init__.py +33 -13
  131. omlish/marshal/_dataclasses.py +2774 -0
  132. omlish/marshal/base/configs.py +12 -0
  133. omlish/marshal/base/contexts.py +36 -21
  134. omlish/marshal/base/funcs.py +8 -11
  135. omlish/marshal/base/options.py +8 -0
  136. omlish/marshal/base/registries.py +146 -44
  137. omlish/marshal/base/types.py +40 -16
  138. omlish/marshal/composite/iterables.py +33 -20
  139. omlish/marshal/composite/literals.py +20 -18
  140. omlish/marshal/composite/mappings.py +36 -23
  141. omlish/marshal/composite/maybes.py +29 -19
  142. omlish/marshal/composite/newtypes.py +16 -16
  143. omlish/marshal/composite/optionals.py +14 -14
  144. omlish/marshal/composite/special.py +15 -15
  145. omlish/marshal/composite/unions/__init__.py +0 -0
  146. omlish/marshal/composite/unions/literals.py +93 -0
  147. omlish/marshal/composite/unions/primitives.py +103 -0
  148. omlish/marshal/factories/invalidate.py +18 -68
  149. omlish/marshal/factories/method.py +26 -0
  150. omlish/marshal/factories/moduleimport/factories.py +22 -65
  151. omlish/marshal/factories/multi.py +13 -25
  152. omlish/marshal/factories/recursive.py +42 -56
  153. omlish/marshal/factories/typecache.py +29 -74
  154. omlish/marshal/factories/typemap.py +42 -43
  155. omlish/marshal/objects/dataclasses.py +129 -106
  156. omlish/marshal/objects/marshal.py +18 -14
  157. omlish/marshal/objects/namedtuples.py +48 -42
  158. omlish/marshal/objects/unmarshal.py +19 -15
  159. omlish/marshal/polymorphism/marshal.py +9 -11
  160. omlish/marshal/polymorphism/metadata.py +16 -5
  161. omlish/marshal/polymorphism/standard.py +13 -1
  162. omlish/marshal/polymorphism/unions.py +15 -105
  163. omlish/marshal/polymorphism/unmarshal.py +9 -10
  164. omlish/marshal/singular/enums.py +14 -18
  165. omlish/marshal/standard.py +10 -6
  166. omlish/marshal/trivial/any.py +1 -1
  167. omlish/marshal/trivial/forbidden.py +21 -26
  168. omlish/metadata.py +23 -1
  169. omlish/os/forkhooks.py +4 -0
  170. omlish/os/pidfiles/pinning.py +2 -2
  171. omlish/reflect/types.py +1 -0
  172. omlish/secrets/marshal.py +1 -1
  173. omlish/specs/jmespath/__init__.py +12 -3
  174. omlish/specs/jmespath/_dataclasses.py +2893 -0
  175. omlish/specs/jmespath/ast.py +1 -1
  176. omlish/specs/jsonrpc/__init__.py +13 -0
  177. omlish/specs/jsonrpc/_marshal.py +32 -23
  178. omlish/specs/jsonrpc/conns.py +10 -7
  179. omlish/specs/jsonschema/_marshal.py +1 -1
  180. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  181. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  182. omlish/specs/openapi/_marshal.py +31 -22
  183. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  184. omlish/sql/queries/_marshal.py +2 -2
  185. omlish/sql/queries/rendering.py +1 -1
  186. omlish/sql/tabledefs/_marshal.py +1 -1
  187. omlish/subprocesses/base.py +4 -0
  188. omlish/subprocesses/editor.py +1 -1
  189. omlish/sync.py +155 -21
  190. omlish/term/alt.py +60 -0
  191. omlish/term/confirm.py +8 -8
  192. omlish/term/pager.py +235 -0
  193. omlish/term/terminfo.py +935 -0
  194. omlish/term/termstate.py +67 -0
  195. omlish/term/vt100/terminal.py +0 -3
  196. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  197. omlish/testing/pytest/plugins/skips.py +2 -1
  198. omlish/testing/unittest/main.py +3 -3
  199. omlish/text/docwrap/__init__.py +3 -0
  200. omlish/text/docwrap/__main__.py +11 -0
  201. omlish/text/docwrap/api.py +83 -0
  202. omlish/text/docwrap/cli.py +86 -0
  203. omlish/text/docwrap/groups.py +84 -0
  204. omlish/text/docwrap/lists.py +167 -0
  205. omlish/text/docwrap/parts.py +146 -0
  206. omlish/text/docwrap/reflowing.py +106 -0
  207. omlish/text/docwrap/rendering.py +151 -0
  208. omlish/text/docwrap/utils.py +11 -0
  209. omlish/text/docwrap/wrapping.py +59 -0
  210. omlish/text/filecache.py +2 -2
  211. omlish/text/lorem.py +6 -0
  212. omlish/text/parts.py +2 -2
  213. omlish/text/textwrap.py +51 -0
  214. omlish/typedvalues/marshal.py +85 -59
  215. omlish/typedvalues/values.py +2 -1
  216. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
  217. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
  218. omlish/dataclasses/impl/generation/mangling.py +0 -18
  219. omlish/funcs/match.py +0 -227
  220. omlish/marshal/factories/match.py +0 -34
  221. omlish/marshal/factories/simple.py +0 -28
  222. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  223. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
  224. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
  225. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
  226. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.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,
@@ -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
@@ -140,6 +136,8 @@ def bind(
140
136
  ##
141
137
 
142
138
  providers: list[Provider] = []
139
+ if to_provider is not None:
140
+ providers.append(to_provider)
143
141
  if to_async_fn is not None:
144
142
  providers.append(AsyncFnProvider(to_async_fn))
145
143
  if to_fn is not None:
@@ -191,45 +189,3 @@ def bind(
191
189
  return elements[0]
192
190
  else:
193
191
  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/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,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
 
@@ -193,6 +209,10 @@ class ElementCollection(lang.Final):
193
209
 
194
210
  ##
195
211
 
212
+ @lang.cached_function
213
+ def scope_binding_scopes(self) -> ta.Sequence[Scope]:
214
+ return [sb.scope for sb in self.elements_of_type(ScopeBinding)]
215
+
196
216
  @lang.cached_function
197
217
  def eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
198
218
  bim = self.binding_impl_map()
@@ -201,3 +221,13 @@ class ElementCollection(lang.Final):
201
221
  bi = bim[e.key]
202
222
  ret.setdefault(bi.scope, []).append(e.key)
203
223
  return ret
224
+
225
+
226
+ ##
227
+
228
+
229
+ def collect_elements(es: Elements | CollectedElements) -> ElementCollection:
230
+ if isinstance(es, CollectedElements):
231
+ return check.isinstance(es, ElementCollection)
232
+ else:
233
+ return ElementCollection(es)
@@ -2,16 +2,16 @@
2
2
  TODO:
3
3
  - ** can currently bind in a child/private scope shadowing an external parent binding **
4
4
  - better source tracking
5
- - cache/export ElementCollections lol
6
5
  - scope bindings, auto in root
7
6
  - injector-internal / blacklisted bindings (Injector itself, default scopes) without rebuilding ElementCollection
8
7
  - config - proxies, impl select, etc
9
8
  - config is probably shared with ElementCollection... but not 'bound', must be shared everywhere
10
9
  - InjectorRoot object?
11
10
  - ** eagers in any scope, on scope init/open
12
- - injection listeners
13
11
  - unions - raise on ambiguous - usecase: sql.AsyncEngineLike
14
12
  - multiple live request scopes on single injector - use private injectors?
13
+ - more listeners - UnboundKeyListener
14
+ - lazy parent listener chain cache thing
15
15
  """
16
16
  import contextlib
17
17
  import functools
@@ -22,7 +22,7 @@ import weakref
22
22
  from ... import check
23
23
  from ... import lang
24
24
  from ...logs import all as logs
25
- from ..elements import Elements
25
+ from ..elements import CollectedElements
26
26
  from ..errors import CyclicDependencyError
27
27
  from ..errors import UnboundKeyError
28
28
  from ..injector import AsyncInjector
@@ -31,7 +31,6 @@ from ..keys import Key
31
31
  from ..keys import as_key
32
32
  from ..listeners import ProvisionListener
33
33
  from ..listeners import ProvisionListenerBinding
34
- from ..scopes import ScopeBinding
35
34
  from ..scopes import Singleton
36
35
  from ..scopes import ThreadScope
37
36
  from ..types import Scope
@@ -55,17 +54,16 @@ DEFAULT_SCOPES: list[Scope] = [
55
54
  ]
56
55
 
57
56
 
57
+ @ta.final
58
58
  class AsyncInjectorImpl(AsyncInjector, lang.Final):
59
59
  def __init__(
60
60
  self,
61
- ec: ElementCollection,
61
+ ec: CollectedElements,
62
62
  p: ta.Optional['AsyncInjectorImpl'] = None,
63
63
  *,
64
64
  internal_consts: dict[Key, ta.Any] | None = None,
65
65
  ) -> None:
66
- super().__init__()
67
-
68
- self._ec = check.isinstance(ec, ElementCollection)
66
+ self._ec = (ec := check.isinstance(ec, ElementCollection))
69
67
  self._p: AsyncInjectorImpl | None = check.isinstance(p, (AsyncInjectorImpl, None))
70
68
 
71
69
  self._internal_consts: dict[Key, ta.Any] = {
@@ -74,28 +72,31 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
74
72
  }
75
73
 
76
74
  self._bim = ec.binding_impl_map()
75
+
77
76
  self._ekbs = ec.eager_keys_by_scope()
77
+
78
78
  self._pls: tuple[ProvisionListener, ...] = tuple(
79
79
  b.listener # type: ignore[attr-defined]
80
80
  for b in itertools.chain(
81
81
  ec.elements_of_type(ProvisionListenerBinding),
82
- (p._pls if p is not None else ()), # noqa
82
+ p._pls if p is not None else (), # noqa
83
83
  )
84
84
  )
85
85
 
86
- self._cs: weakref.WeakSet[AsyncInjectorImpl] | None = None
87
86
  self._root: AsyncInjectorImpl = p._root if p is not None else self # noqa
88
87
 
89
- self.__cur_req: AsyncInjectorImpl._Request | None = None
90
-
91
- ss = [
92
- *DEFAULT_SCOPES,
93
- *[sb.scope for sb in ec.elements_of_type(ScopeBinding)],
94
- ]
95
88
  self._scopes: dict[Scope, ScopeImpl] = {
96
- s: make_scope_impl(s) for s in ss
89
+ s: make_scope_impl(s)
90
+ for s in itertools.chain(
91
+ DEFAULT_SCOPES,
92
+ ec.scope_binding_scopes(),
93
+ )
97
94
  }
98
95
 
96
+ _cs: weakref.WeakSet['AsyncInjectorImpl'] | None = None # noqa
97
+
98
+ __cur_req: ta.Optional['AsyncInjectorImpl._Request'] = None
99
+
99
100
  #
100
101
 
101
102
  _has_run_init: bool = False
@@ -256,7 +257,7 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
256
257
  return obj(**kws)
257
258
 
258
259
 
259
- async def create_async_injector(es: Elements) -> AsyncInjector:
260
- i = AsyncInjectorImpl(ElementCollection(es))
260
+ async def create_async_injector(ce: CollectedElements) -> AsyncInjector:
261
+ i = AsyncInjectorImpl(ce)
261
262
  await i._init() # noqa
262
263
  return i
@@ -75,7 +75,11 @@ def build_kwargs_target(
75
75
  skip_args: int = 0,
76
76
  skip_kwargs: ta.Iterable[str] | None = None,
77
77
  raw_optional: bool = False,
78
+ non_strict: bool = False,
78
79
  ) -> KwargsTarget:
80
+ if isinstance(obj, KwargsTarget):
81
+ return obj
82
+
79
83
  sig = signature(obj)
80
84
  tags = _TAGS.get(obj)
81
85
 
@@ -90,11 +94,15 @@ def build_kwargs_target(
90
94
  continue
91
95
 
92
96
  if p.annotation is inspect.Signature.empty:
97
+ if non_strict:
98
+ continue
93
99
  if p.default is not inspect.Parameter.empty:
94
100
  raise KeyError(f'{obj}, {p.name}')
95
101
  continue
96
102
 
97
103
  if p.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY):
104
+ if non_strict:
105
+ continue
98
106
  raise TypeError(sig)
99
107
 
100
108
  ann = p.annotation
@@ -119,7 +127,8 @@ def build_kwargs_target(
119
127
  k = dc.replace(k, tag=pt)
120
128
 
121
129
  if k in seen:
122
- raise ConflictingKeyError(k)
130
+ if not non_strict:
131
+ raise ConflictingKeyError(k)
123
132
  seen.add(k)
124
133
 
125
134
  kws.append(Kwarg(
@@ -1,13 +1,12 @@
1
1
  import typing as ta
2
2
 
3
3
  from ... import lang
4
- from ..elements import Elements
4
+ from ..elements import CollectedElements
5
5
  from ..injector import AsyncInjector
6
6
  from ..inspect import KwargsTarget
7
7
  from ..keys import Key
8
8
  from ..maysync import MaysyncInjector
9
9
  from ..sync import Injector
10
- from .elements import ElementCollection
11
10
  from .injector import AsyncInjectorImpl
12
11
 
13
12
 
@@ -30,10 +29,10 @@ class MaysyncInjectorImpl(MaysyncInjector, lang.Final):
30
29
  return lang.run_maysync(self._ai.inject(obj))
31
30
 
32
31
 
33
- def create_maysync_injector(es: Elements) -> MaysyncInjector:
32
+ def create_maysync_injector(ce: CollectedElements) -> MaysyncInjector:
34
33
  si = MaysyncInjectorImpl()
35
34
  ai = AsyncInjectorImpl(
36
- ElementCollection(es),
35
+ ce,
37
36
  internal_consts={
38
37
  Key(MaysyncInjector): si,
39
38
  Key(Injector): si,
@@ -1,5 +1,6 @@
1
1
  import typing as ta
2
2
 
3
+ from ... import check
3
4
  from ... import dataclasses as dc
4
5
  from ... import lang
5
6
  from ..elements import Element
@@ -58,6 +59,7 @@ class MapProviderImpl(ProviderImpl, lang.Final):
58
59
  def make_multi_provider_impl(p: Provider, es_by_ty: ta.MutableMapping[type, list[Element]]) -> ProviderImpl:
59
60
  if isinstance(p, SetProvider):
60
61
  sbs: ta.Iterable[SetBinding] = es_by_ty.pop(SetBinding, ()) # type: ignore
62
+ check.empty(es_by_ty)
61
63
  return SetProviderImpl([
62
64
  LinkProviderImpl(LinkProvider(sb.dst))
63
65
  for sb in sbs
@@ -65,6 +67,7 @@ def make_multi_provider_impl(p: Provider, es_by_ty: ta.MutableMapping[type, list
65
67
 
66
68
  elif isinstance(p, MapProvider):
67
69
  mbs: ta.Iterable[MapBinding] = es_by_ty.pop(MapBinding, ()) # type: ignore
70
+ check.empty(es_by_ty)
68
71
  return MapProviderImpl([
69
72
  MapProviderImpl.Entry(
70
73
  mb.map_key,
@@ -1,12 +1,11 @@
1
1
  import typing as ta
2
2
 
3
3
  from ... import lang
4
- from ..elements import Elements
4
+ from ..elements import CollectedElements
5
5
  from ..injector import AsyncInjector
6
6
  from ..inspect import KwargsTarget
7
7
  from ..keys import Key
8
8
  from ..sync import Injector
9
- from .elements import ElementCollection
10
9
  from .injector import AsyncInjectorImpl
11
10
 
12
11
 
@@ -29,10 +28,10 @@ class InjectorImpl(Injector, lang.Final):
29
28
  return lang.sync_await(self._ai.inject(obj))
30
29
 
31
30
 
32
- def create_injector(es: Elements) -> Injector:
31
+ def create_injector(ce: CollectedElements) -> Injector:
33
32
  si = InjectorImpl()
34
33
  ai = AsyncInjectorImpl(
35
- ElementCollection(es),
34
+ ce,
36
35
  internal_consts={
37
36
  Key(Injector): si,
38
37
  },