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
@@ -2,16 +2,19 @@
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
+ - https://github.com/7mind/izumi-chibi-ts
16
+ - Axis tagging for conditional bindings (e.g., dev vs prod implementations)
17
+ - Fail-fast validation with circular and missing dependency detection
15
18
  """
16
19
  import contextlib
17
20
  import functools
@@ -22,7 +25,7 @@ import weakref
22
25
  from ... import check
23
26
  from ... import lang
24
27
  from ...logs import all as logs
25
- from ..elements import Elements
28
+ from ..elements import CollectedElements
26
29
  from ..errors import CyclicDependencyError
27
30
  from ..errors import UnboundKeyError
28
31
  from ..injector import AsyncInjector
@@ -31,7 +34,6 @@ from ..keys import Key
31
34
  from ..keys import as_key
32
35
  from ..listeners import ProvisionListener
33
36
  from ..listeners import ProvisionListenerBinding
34
- from ..scopes import ScopeBinding
35
37
  from ..scopes import Singleton
36
38
  from ..scopes import ThreadScope
37
39
  from ..types import Scope
@@ -55,17 +57,16 @@ DEFAULT_SCOPES: list[Scope] = [
55
57
  ]
56
58
 
57
59
 
60
+ @ta.final
58
61
  class AsyncInjectorImpl(AsyncInjector, lang.Final):
59
62
  def __init__(
60
63
  self,
61
- ec: ElementCollection,
64
+ ec: CollectedElements,
62
65
  p: ta.Optional['AsyncInjectorImpl'] = None,
63
66
  *,
64
67
  internal_consts: dict[Key, ta.Any] | None = None,
65
68
  ) -> None:
66
- super().__init__()
67
-
68
- self._ec = check.isinstance(ec, ElementCollection)
69
+ self._ec = (ec := check.isinstance(ec, ElementCollection))
69
70
  self._p: AsyncInjectorImpl | None = check.isinstance(p, (AsyncInjectorImpl, None))
70
71
 
71
72
  self._internal_consts: dict[Key, ta.Any] = {
@@ -74,28 +75,31 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
74
75
  }
75
76
 
76
77
  self._bim = ec.binding_impl_map()
77
- self._ekbs = ec.eager_keys_by_scope()
78
- self._pls: tuple[ProvisionListener, ...] = tuple(
79
- b.listener # type: ignore[attr-defined]
80
- for b in itertools.chain(
81
- ec.elements_of_type(ProvisionListenerBinding),
82
- (p._pls if p is not None else ()), # noqa
83
- )
78
+
79
+ self._ekbs = ec.sorted_eager_keys_by_scope()
80
+
81
+ self._pls: tuple[ProvisionListener, ...] = (
82
+ *(
83
+ b.listener
84
+ for b in ec.elements_of_type(ProvisionListenerBinding)
85
+ ),
86
+ *(p._pls if p is not None else []), # noqa
84
87
  )
85
88
 
86
- self._cs: weakref.WeakSet[AsyncInjectorImpl] | None = None
87
89
  self._root: AsyncInjectorImpl = p._root if p is not None else self # noqa
88
90
 
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
91
  self._scopes: dict[Scope, ScopeImpl] = {
96
- s: make_scope_impl(s) for s in ss
92
+ s: make_scope_impl(s)
93
+ for s in itertools.chain(
94
+ DEFAULT_SCOPES,
95
+ ec.scope_binding_scopes(),
96
+ )
97
97
  }
98
98
 
99
+ _cs: weakref.WeakSet['AsyncInjectorImpl'] | None = None # noqa
100
+
101
+ __cur_req: ta.Optional['AsyncInjectorImpl._Request'] = None
102
+
99
103
  #
100
104
 
101
105
  _has_run_init: bool = False
@@ -256,7 +260,7 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
256
260
  return obj(**kws)
257
261
 
258
262
 
259
- async def create_async_injector(es: Elements) -> AsyncInjector:
260
- i = AsyncInjectorImpl(ElementCollection(es))
263
+ async def create_async_injector(ce: CollectedElements) -> AsyncInjector:
264
+ i = AsyncInjectorImpl(ce)
261
265
  await i._init() # noqa
262
266
  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
  },
omlish/inject/injector.py CHANGED
@@ -1,9 +1,12 @@
1
1
  import abc
2
2
  import typing as ta
3
3
 
4
+ from .. import check
4
5
  from .. import lang
6
+ from .elements import CollectedElements
5
7
  from .elements import Elemental
6
8
  from .elements import as_elements
9
+ from .elements import collect_elements
7
10
  from .inspect import KwargsTarget
8
11
  from .keys import Key
9
12
 
@@ -44,5 +47,31 @@ class AsyncInjector(lang.Abstract):
44
47
  return self.provide(target)
45
48
 
46
49
 
47
- def create_async_injector(*args: Elemental) -> ta.Awaitable[AsyncInjector]:
48
- return _injector.create_async_injector(as_elements(*args))
50
+ ##
51
+
52
+
53
+ @ta.final
54
+ class _InjectorCreator(ta.Generic[T]):
55
+ def __init__(self, fac: ta.Callable[[CollectedElements], T]) -> None:
56
+ self._fac = fac
57
+
58
+ @ta.overload
59
+ def __call__(self, es: CollectedElements, /) -> T: ...
60
+
61
+ @ta.overload
62
+ def __call__(self, *es: Elemental) -> T: ...
63
+
64
+ def __call__(self, arg0, *argv):
65
+ ce: CollectedElements
66
+ if isinstance(arg0, CollectedElements):
67
+ check.arg(not argv)
68
+ ce = arg0
69
+ else:
70
+ ce = collect_elements(as_elements(arg0, *argv))
71
+ return self._fac(ce)
72
+
73
+
74
+ ##
75
+
76
+
77
+ create_async_injector = _InjectorCreator[ta.Awaitable[AsyncInjector]](lambda ce: _injector.create_async_injector(ce))
omlish/inject/inspect.py CHANGED
@@ -2,6 +2,7 @@ import typing as ta
2
2
 
3
3
  from .. import lang
4
4
  from .keys import Key
5
+ from .keys import as_key
5
6
 
6
7
 
7
8
  if ta.TYPE_CHECKING:
@@ -21,11 +22,45 @@ class Kwarg(ta.NamedTuple):
21
22
  key: Key
22
23
  has_default: bool
23
24
 
25
+ @classmethod
26
+ def of(
27
+ cls,
28
+ name: str,
29
+ key: Key,
30
+ *,
31
+ has_default: bool = False,
32
+ ) -> 'Kwarg':
33
+ return cls(
34
+ name,
35
+ key,
36
+ has_default,
37
+ )
38
+
24
39
 
25
40
  class KwargsTarget(ta.NamedTuple):
26
41
  obj: ta.Any
27
42
  kwargs: ta.Sequence[Kwarg]
28
43
 
44
+ @classmethod
45
+ def of(
46
+ cls,
47
+ obj: ta.Any,
48
+ *kws: Kwarg,
49
+ **kwargs: tuple[Key, bool] | Key | ta.Any,
50
+ ) -> 'KwargsTarget':
51
+ kw_kwargs: list[Kwarg] = []
52
+ for n, v in kwargs.items():
53
+ if isinstance(v, tuple):
54
+ kw_k, kw_hd = v
55
+ kw_kwargs.append(Kwarg.of(n, kw_k, has_default=kw_hd))
56
+ else:
57
+ kw_kwargs.append(Kwarg.of(n, as_key(v)))
58
+
59
+ return cls(
60
+ obj,
61
+ (*kws, *kw_kwargs),
62
+ )
63
+
29
64
 
30
65
  def tag(obj: T, **kwargs: ta.Any) -> T:
31
66
  return _inspect.tag(obj, **kwargs)
omlish/inject/maysync.py CHANGED
@@ -1,8 +1,7 @@
1
1
  import typing as ta
2
2
 
3
3
  from .. import lang
4
- from .elements import Elemental
5
- from .elements import as_elements
4
+ from .injector import _InjectorCreator
6
5
  from .sync import Injector
7
6
 
8
7
 
@@ -25,5 +24,4 @@ class MaysyncInjector(Injector, lang.Abstract):
25
24
  ##
26
25
 
27
26
 
28
- def create_maysync_injector(*args: Elemental) -> MaysyncInjector:
29
- return _maysync.create_maysync_injector(as_elements(*args))
27
+ create_maysync_injector = _InjectorCreator[MaysyncInjector](lambda ce: _maysync.create_maysync_injector(ce))
omlish/inject/multis.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """
2
2
  TODO:
3
+ - DynamicSetBinding / DynamicMapBinding ? provider of set[T] / map[K, V] ?
4
+ - doable not guicey - too much dynamism
3
5
  - scopes
4
6
  """
5
7
  import collections.abc
@@ -94,6 +96,9 @@ class SetBinder(ElementGenerator, ta.Generic[T]):
94
96
  yield from self._sbs
95
97
 
96
98
 
99
+ set_binder = SetBinder
100
+
101
+
97
102
  #
98
103
 
99
104
 
@@ -123,3 +128,6 @@ class MapBinder(ElementGenerator, ta.Generic[K, V]):
123
128
  def __iter__(self) -> ta.Iterator[Element]:
124
129
  yield self._map_provider_binding
125
130
  yield from self._mbs
131
+
132
+
133
+ map_binder = MapBinder
@@ -14,9 +14,9 @@ from .elements import as_elements
14
14
  @dc.dataclass(frozen=True)
15
15
  @dc.extra_class_params(cache_hash=True)
16
16
  class Overrides(Element, lang.Final):
17
- ovr: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
18
17
  src: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
18
+ ovr: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
19
19
 
20
20
 
21
- def override(ovr: ta.Any, *a: ta.Any) -> Element:
22
- return Overrides(as_elements(ovr), as_elements(*a))
21
+ def override(src: ta.Any, *ovr: ta.Any) -> Element:
22
+ return Overrides(as_elements(src), as_elements(*ovr))
omlish/inject/privates.py CHANGED
@@ -18,6 +18,12 @@ class Expose(Element, lang.Final):
18
18
  key: Key = dc.xfield(coerce=as_key)
19
19
 
20
20
 
21
+ expose = Expose
22
+
23
+
24
+ #
25
+
26
+
21
27
  @dc.dataclass(frozen=True)
22
28
  @dc.extra_class_params(cache_hash=True)
23
29
  class Private(Element, lang.Final):
@@ -3,6 +3,7 @@ import typing as ta
3
3
  from .. import check
4
4
  from .. import dataclasses as dc
5
5
  from .. import lang
6
+ from .inspect import KwargsTarget
6
7
  from .keys import Key
7
8
 
8
9
 
@@ -20,13 +21,13 @@ class Provider(lang.Abstract):
20
21
  @dc.dataclass(frozen=True)
21
22
  @dc.extra_class_params(cache_hash=True)
22
23
  class AsyncFnProvider(Provider):
23
- fn: ta.Any = dc.xfield(validate=callable)
24
+ fn: ta.Any = dc.xfield(validate=lambda v: callable(v) or isinstance(v, KwargsTarget))
24
25
 
25
26
 
26
27
  @dc.dataclass(frozen=True)
27
28
  @dc.extra_class_params(cache_hash=True)
28
29
  class FnProvider(Provider):
29
- fn: ta.Any = dc.xfield(validate=callable)
30
+ fn: ta.Any = dc.xfield(validate=lambda v: callable(v) or isinstance(v, KwargsTarget))
30
31
 
31
32
 
32
33
  @dc.dataclass(frozen=True)
omlish/inject/sync.py CHANGED
@@ -2,8 +2,7 @@ import abc
2
2
  import typing as ta
3
3
 
4
4
  from .. import lang
5
- from .elements import Elemental
6
- from .elements import as_elements
5
+ from .injector import _InjectorCreator
7
6
  from .inspect import KwargsTarget
8
7
  from .keys import Key
9
8
 
@@ -44,5 +43,7 @@ class Injector(lang.Abstract):
44
43
  return self.provide(target)
45
44
 
46
45
 
47
- def create_injector(*args: Elemental) -> Injector:
48
- return _sync.create_injector(as_elements(*args))
46
+ ##
47
+
48
+
49
+ create_injector = _InjectorCreator[Injector](lambda ce: _sync.create_injector(ce))
omlish/io/buffers.py CHANGED
@@ -1,10 +1,18 @@
1
1
  # ruff: noqa: UP006 UP007 UP043 UP045
2
2
  # @omlish-lite
3
+ """
4
+ TODO:
5
+ - overhaul and just coro-ify pyio?
6
+ """
3
7
  import io
4
8
  import typing as ta
5
9
 
6
10
  from ..lite.attrops import attr_repr
7
11
  from ..lite.check import check
12
+ from .readers import AsyncBufferedBytesReader
13
+ from .readers import AsyncRawBytesReader
14
+ from .readers import BufferedBytesReader
15
+ from .readers import RawBytesReader
8
16
 
9
17
 
10
18
  ##
@@ -183,6 +191,9 @@ class ReadableListBuffer:
183
191
 
184
192
  self._lst: list[bytes] = []
185
193
 
194
+ def __bool__(self) -> ta.NoReturn:
195
+ raise TypeError("Use 'buf is not None' or 'len(buf)'.")
196
+
186
197
  def __len__(self) -> int:
187
198
  return sum(map(len, self._lst))
188
199
 
@@ -208,6 +219,9 @@ class ReadableListBuffer:
208
219
 
209
220
  def read(self, n: ta.Optional[int] = None) -> ta.Optional[bytes]:
210
221
  if n is None:
222
+ if not self._lst:
223
+ return b''
224
+
211
225
  o = b''.join(self._lst)
212
226
  self._lst = []
213
227
  return o
@@ -246,6 +260,110 @@ class ReadableListBuffer:
246
260
  r = self.read_until_(delim)
247
261
  return r if isinstance(r, bytes) else None
248
262
 
263
+ #
264
+
265
+ DEFAULT_BUFFERED_READER_CHUNK_SIZE: ta.ClassVar[int] = -1
266
+
267
+ @ta.final
268
+ class _BufferedBytesReader(BufferedBytesReader):
269
+ def __init__(
270
+ self,
271
+ raw: RawBytesReader,
272
+ buf: 'ReadableListBuffer',
273
+ *,
274
+ chunk_size: ta.Optional[int] = None,
275
+ ) -> None:
276
+ self._raw = raw
277
+ self._buf = buf
278
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
279
+
280
+ def read1(self, n: int = -1, /) -> bytes:
281
+ if n < 0:
282
+ n = self._chunk_size
283
+ if not n:
284
+ return b''
285
+ if 0 < n <= len(self._buf):
286
+ return self._buf.read(n) or b''
287
+ return self._raw.read1(n)
288
+
289
+ def read(self, /, n: int = -1) -> bytes:
290
+ if n < 0:
291
+ return self.readall()
292
+ while len(self._buf) < n:
293
+ if not (b := self._raw.read1(n)):
294
+ break
295
+ self._buf.feed(b)
296
+ return self._buf.read(n) or b''
297
+
298
+ def readall(self) -> bytes:
299
+ buf = io.BytesIO()
300
+ buf.write(self._buf.read() or b'')
301
+ while (b := self._raw.read1(self._chunk_size)):
302
+ buf.write(b)
303
+ return buf.getvalue()
304
+
305
+ def new_buffered_reader(
306
+ self,
307
+ raw: RawBytesReader,
308
+ *,
309
+ chunk_size: ta.Optional[int] = None,
310
+ ) -> BufferedBytesReader:
311
+ return self._BufferedBytesReader(
312
+ raw,
313
+ self,
314
+ chunk_size=chunk_size,
315
+ )
316
+
317
+ @ta.final
318
+ class _AsyncBufferedBytesReader(AsyncBufferedBytesReader):
319
+ def __init__(
320
+ self,
321
+ raw: AsyncRawBytesReader,
322
+ buf: 'ReadableListBuffer',
323
+ *,
324
+ chunk_size: ta.Optional[int] = None,
325
+ ) -> None:
326
+ self._raw = raw
327
+ self._buf = buf
328
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
329
+
330
+ async def read1(self, n: int = -1, /) -> bytes:
331
+ if n < 0:
332
+ n = self._chunk_size
333
+ if not n:
334
+ return b''
335
+ if 0 < n <= len(self._buf):
336
+ return self._buf.read(n) or b''
337
+ return await self._raw.read1(n)
338
+
339
+ async def read(self, /, n: int = -1) -> bytes:
340
+ if n < 0:
341
+ return await self.readall()
342
+ while len(self._buf) < n:
343
+ if not (b := await self._raw.read1(n)):
344
+ break
345
+ self._buf.feed(b)
346
+ return self._buf.read(n) or b''
347
+
348
+ async def readall(self) -> bytes:
349
+ buf = io.BytesIO()
350
+ buf.write(self._buf.read() or b'')
351
+ while b := await self._raw.read1(self._chunk_size):
352
+ buf.write(b)
353
+ return buf.getvalue()
354
+
355
+ def new_async_buffered_reader(
356
+ self,
357
+ raw: AsyncRawBytesReader,
358
+ *,
359
+ chunk_size: ta.Optional[int] = None,
360
+ ) -> AsyncBufferedBytesReader:
361
+ return self._AsyncBufferedBytesReader(
362
+ raw,
363
+ self,
364
+ chunk_size=chunk_size,
365
+ )
366
+
249
367
 
250
368
  ##
251
369
 
omlish/io/readers.py ADDED
@@ -0,0 +1,29 @@
1
+ # ruff: noqa: UP045
2
+ # @omlish-lite
3
+ import typing as ta
4
+
5
+
6
+ ##
7
+
8
+
9
+ class RawBytesReader(ta.Protocol):
10
+ def read1(self, n: int = -1, /) -> bytes: ...
11
+
12
+
13
+ class BufferedBytesReader(RawBytesReader, ta.Protocol):
14
+ def read(self, n: int = -1, /) -> bytes: ...
15
+
16
+ def readall(self) -> bytes: ...
17
+
18
+
19
+ #
20
+
21
+
22
+ class AsyncRawBytesReader(ta.Protocol):
23
+ def read1(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
24
+
25
+
26
+ class AsyncBufferedBytesReader(AsyncRawBytesReader, ta.Protocol):
27
+ def read(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
28
+
29
+ def readall(self) -> ta.Awaitable[bytes]: ...
@@ -83,8 +83,8 @@ apply = Apply
83
83
 
84
84
  @dc.dataclass(frozen=True)
85
85
  class Flatten(Transform_[ta.Iterable[T], T]):
86
- def __call__(self, it: ta.Iterable[ta.Iterable[T]]) -> ta.Iterable[U]:
87
- return itertools.chain.from_iterable(it) # type: ignore[arg-type]
86
+ def __call__(self, it: ta.Iterable[ta.Iterable[T]]) -> ta.Iterable[T]:
87
+ return itertools.chain.from_iterable(it)
88
88
 
89
89
 
90
90
  flatten = Flatten