omlish 0.0.0.dev469__py3-none-any.whl → 0.0.0.dev471__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.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev469'
2
- __revision__ = 'efea836fe350fa24e6b724d30d414b2d87ee1341'
1
+ __version__ = '0.0.0.dev471'
2
+ __revision__ = '2a1fe39e9894efdf0e28923ddd0067f441d9d964'
3
3
 
4
4
 
5
5
  #
omlish/http/all.py CHANGED
@@ -7,16 +7,15 @@ with _lang.auto_proxy_init(globals()):
7
7
  from .clients.asyncs import ( # noqa
8
8
  AsyncStreamHttpResponse,
9
9
 
10
- async_close_response,
11
- async_closing_response,
12
- async_read_response,
10
+ async_close_http_client_response,
11
+ async_closing_http_client_response,
12
+ async_read_http_client_response,
13
13
 
14
14
  AsyncHttpClient,
15
15
  )
16
16
 
17
17
  from .clients.base import ( # noqa
18
18
  DEFAULT_ENCODING,
19
-
20
19
  is_success_status,
21
20
 
22
21
  HttpRequest,
@@ -24,8 +23,12 @@ with _lang.auto_proxy_init(globals()):
24
23
  BaseHttpResponse,
25
24
  HttpResponse,
26
25
 
26
+ HttpClientContext,
27
+
27
28
  HttpClientError,
28
29
  HttpStatusError,
30
+
31
+ BaseHttpClient,
29
32
  )
30
33
 
31
34
  from .clients.default import ( # noqa
@@ -42,18 +45,35 @@ with _lang.auto_proxy_init(globals()):
42
45
 
43
46
  from .clients.httpx import ( # noqa
44
47
  HttpxHttpClient,
48
+
49
+ HttpxAsyncHttpClient,
50
+ )
51
+
52
+ from .clients.middleware import ( # noqa
53
+ HttpClientMiddleware,
54
+ AbstractMiddlewareHttpClient,
55
+
56
+ MiddlewareHttpClient,
57
+ MiddlewareAsyncHttpClient,
58
+
59
+ TooManyRedirectsHttpClientError,
60
+ RedirectHandlingHttpClientMiddleware,
45
61
  )
46
62
 
47
63
  from .clients.sync import ( # noqa
48
64
  StreamHttpResponse,
49
65
 
50
- close_response,
51
- closing_response,
52
- read_response,
66
+ close_http_client_response,
67
+ closing_http_client_response,
68
+ read_http_client_response,
53
69
 
54
70
  HttpClient,
55
71
  )
56
72
 
73
+ from .clients.syncasync import ( # noqa
74
+ SyncAsyncHttpClient,
75
+ )
76
+
57
77
  from .clients.urllib import ( # noqa
58
78
  UrllibHttpClient,
59
79
  )
@@ -57,13 +57,13 @@ class AsyncStreamHttpResponse(BaseHttpResponse):
57
57
 
58
58
  async def close(self) -> None:
59
59
  if (c := self._closer) is not None:
60
- await c()
60
+ await c() # noqa
61
61
 
62
62
 
63
63
  #
64
64
 
65
65
 
66
- async def async_close_response(resp: BaseHttpResponse) -> None:
66
+ async def async_close_http_client_response(resp: BaseHttpResponse) -> None:
67
67
  if isinstance(resp, HttpResponse):
68
68
  pass
69
69
 
@@ -75,7 +75,7 @@ async def async_close_response(resp: BaseHttpResponse) -> None:
75
75
 
76
76
 
77
77
  @contextlib.asynccontextmanager
78
- async def async_closing_response(resp: BaseHttpResponseT) -> ta.AsyncGenerator[BaseHttpResponseT, None]:
78
+ async def async_closing_http_client_response(resp: BaseHttpResponseT) -> ta.AsyncGenerator[BaseHttpResponseT, None]:
79
79
  if isinstance(resp, HttpResponse):
80
80
  yield resp
81
81
  return
@@ -90,7 +90,7 @@ async def async_closing_response(resp: BaseHttpResponseT) -> ta.AsyncGenerator[B
90
90
  raise TypeError(resp)
91
91
 
92
92
 
93
- async def async_read_response(resp: BaseHttpResponse) -> HttpResponse:
93
+ async def async_read_http_client_response(resp: BaseHttpResponse) -> HttpResponse:
94
94
  if isinstance(resp, HttpResponse):
95
95
  return resp
96
96
 
@@ -121,12 +121,12 @@ class AsyncHttpClient(BaseHttpClient, Abstract):
121
121
  context: ta.Optional[HttpClientContext] = None,
122
122
  check: bool = False,
123
123
  ) -> HttpResponse:
124
- async with async_closing_response(await self.stream_request(
124
+ async with async_closing_http_client_response(await self.stream_request(
125
125
  req,
126
126
  context=context,
127
127
  check=check,
128
128
  )) as resp:
129
- return await async_read_response(resp)
129
+ return await async_read_http_client_response(resp)
130
130
 
131
131
  async def stream_request(
132
132
  self,
@@ -146,10 +146,10 @@ class AsyncHttpClient(BaseHttpClient, Abstract):
146
146
  cause = resp.underlying
147
147
  else:
148
148
  cause = None
149
- raise HttpStatusError(await async_read_response(resp)) from cause # noqa
149
+ raise HttpStatusError(await async_read_http_client_response(resp)) from cause # noqa
150
150
 
151
151
  except Exception:
152
- await async_close_response(resp)
152
+ await async_close_http_client_response(resp)
153
153
  raise
154
154
 
155
155
  return resp
@@ -23,7 +23,7 @@ from .base import HttpClientError
23
23
  from .base import HttpRequest
24
24
  from .sync import HttpClient
25
25
  from .sync import StreamHttpResponse
26
- from .sync import close_response
26
+ from .sync import close_http_client_response
27
27
 
28
28
 
29
29
  BaseHttpClientT = ta.TypeVar('BaseHttpClientT', bound=BaseHttpClient)
@@ -84,6 +84,9 @@ class AbstractMiddlewareHttpClient(Abstract, ta.Generic[BaseHttpClientT]):
84
84
  return resp
85
85
 
86
86
 
87
+ #
88
+
89
+
87
90
  class MiddlewareHttpClient(AbstractMiddlewareHttpClient[HttpClient], HttpClient):
88
91
  def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
89
92
  while True:
@@ -95,7 +98,7 @@ class MiddlewareHttpClient(AbstractMiddlewareHttpClient[HttpClient], HttpClient)
95
98
  out = self._process_response(ctx, req, resp)
96
99
 
97
100
  if isinstance(out, HttpRequest):
98
- close_response(resp)
101
+ close_http_client_response(resp)
99
102
  req = out
100
103
  continue
101
104
 
@@ -106,7 +109,7 @@ class MiddlewareHttpClient(AbstractMiddlewareHttpClient[HttpClient], HttpClient)
106
109
  raise TypeError(out) # noqa
107
110
 
108
111
  except Exception:
109
- close_response(resp)
112
+ close_http_client_response(resp)
110
113
  raise
111
114
 
112
115
  raise RuntimeError
@@ -57,13 +57,13 @@ class StreamHttpResponse(BaseHttpResponse):
57
57
 
58
58
  def close(self) -> None:
59
59
  if (c := self._closer) is not None:
60
- c()
60
+ c() # noqa
61
61
 
62
62
 
63
63
  #
64
64
 
65
65
 
66
- def close_response(resp: BaseHttpResponse) -> None:
66
+ def close_http_client_response(resp: BaseHttpResponse) -> None:
67
67
  if isinstance(resp, HttpResponse):
68
68
  pass
69
69
 
@@ -75,7 +75,7 @@ def close_response(resp: BaseHttpResponse) -> None:
75
75
 
76
76
 
77
77
  @contextlib.contextmanager
78
- def closing_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
78
+ def closing_http_client_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
79
79
  if isinstance(resp, HttpResponse):
80
80
  yield resp
81
81
  return
@@ -88,7 +88,7 @@ def closing_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
88
88
  raise TypeError(resp)
89
89
 
90
90
 
91
- def read_response(resp: BaseHttpResponse) -> HttpResponse:
91
+ def read_http_client_response(resp: BaseHttpResponse) -> HttpResponse:
92
92
  if isinstance(resp, HttpResponse):
93
93
  return resp
94
94
 
@@ -119,12 +119,12 @@ class HttpClient(BaseHttpClient, Abstract):
119
119
  context: ta.Optional[HttpClientContext] = None,
120
120
  check: bool = False,
121
121
  ) -> HttpResponse:
122
- with closing_response(self.stream_request(
122
+ with closing_http_client_response(self.stream_request(
123
123
  req,
124
124
  context=context,
125
125
  check=check,
126
126
  )) as resp:
127
- return read_response(resp)
127
+ return read_http_client_response(resp)
128
128
 
129
129
  def stream_request(
130
130
  self,
@@ -144,10 +144,10 @@ class HttpClient(BaseHttpClient, Abstract):
144
144
  cause = resp.underlying
145
145
  else:
146
146
  cause = None
147
- raise HttpStatusError(read_response(resp)) from cause # noqa
147
+ raise HttpStatusError(read_http_client_response(resp)) from cause # noqa
148
148
 
149
149
  except Exception:
150
- close_response(resp)
150
+ close_http_client_response(resp)
151
151
  raise
152
152
 
153
153
  return resp
@@ -0,0 +1,43 @@
1
+ # ruff: noqa: UP043 UP045
2
+ # @omlish-lite
3
+ import dataclasses as dc
4
+
5
+ from .asyncs import AsyncHttpClient
6
+ from .asyncs import AsyncStreamHttpResponse
7
+ from .base import HttpClientContext
8
+ from .base import HttpRequest
9
+ from .sync import HttpClient
10
+ from .sync import StreamHttpResponse
11
+
12
+
13
+ ##
14
+
15
+
16
+ class SyncAsyncHttpClient(AsyncHttpClient):
17
+ def __init__(self, client: HttpClient) -> None:
18
+ super().__init__()
19
+
20
+ self._client = client
21
+
22
+ @dc.dataclass(frozen=True)
23
+ class _StreamAdapter:
24
+ ul: StreamHttpResponse
25
+
26
+ async def read1(self, /, n: int = -1) -> bytes:
27
+ return self.ul.stream.read1(n)
28
+
29
+ async def close(self) -> None:
30
+ self.ul.close()
31
+
32
+ async def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> AsyncStreamHttpResponse:
33
+ resp = self._client.stream_request(req, context=ctx)
34
+ return AsyncStreamHttpResponse(
35
+ status=resp.status,
36
+ headers=resp.headers,
37
+ request=req,
38
+ underlying=resp,
39
+ **(dict( # type: ignore
40
+ stream=(adapter := self._StreamAdapter(resp)),
41
+ _closer=adapter.close,
42
+ ) if resp.has_data else {}),
43
+ )
omlish/inject/__init__.py CHANGED
@@ -57,8 +57,12 @@ with _lang.auto_proxy_init(globals()):
57
57
  Element,
58
58
  Elements,
59
59
  Elemental,
60
+
60
61
  as_elements,
61
62
  iter_elements,
63
+
64
+ CollectedElements,
65
+ collect_elements,
62
66
  )
63
67
 
64
68
  from .errors import ( # noqa
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
 
@@ -74,3 +80,14 @@ def iter_elements(*args: Elemental) -> ta.Iterator[Element]:
74
80
  yield from a
75
81
  else:
76
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)
@@ -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
 
@@ -80,7 +81,7 @@ _NON_BINDING_ELEMENT_TYPES: tuple[type[Element], ...] = (
80
81
  )
81
82
 
82
83
 
83
- class ElementCollection(lang.Final):
84
+ class ElementCollection(CollectedElements, lang.Final):
84
85
  def __init__(self, es: Elements) -> None:
85
86
  super().__init__()
86
87
 
@@ -208,6 +209,10 @@ class ElementCollection(lang.Final):
208
209
 
209
210
  ##
210
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
+
211
216
  @lang.cached_function
212
217
  def eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
213
218
  bim = self.binding_impl_map()
@@ -216,3 +221,13 @@ class ElementCollection(lang.Final):
216
221
  bi = bim[e.key]
217
222
  ret.setdefault(bi.scope, []).append(e.key)
218
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,14 +2,12 @@
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?
15
13
  - more listeners - UnboundKeyListener
@@ -24,7 +22,7 @@ import weakref
24
22
  from ... import check
25
23
  from ... import lang
26
24
  from ...logs import all as logs
27
- from ..elements import Elements
25
+ from ..elements import CollectedElements
28
26
  from ..errors import CyclicDependencyError
29
27
  from ..errors import UnboundKeyError
30
28
  from ..injector import AsyncInjector
@@ -33,7 +31,6 @@ from ..keys import Key
33
31
  from ..keys import as_key
34
32
  from ..listeners import ProvisionListener
35
33
  from ..listeners import ProvisionListenerBinding
36
- from ..scopes import ScopeBinding
37
34
  from ..scopes import Singleton
38
35
  from ..scopes import ThreadScope
39
36
  from ..types import Scope
@@ -61,14 +58,12 @@ DEFAULT_SCOPES: list[Scope] = [
61
58
  class AsyncInjectorImpl(AsyncInjector, lang.Final):
62
59
  def __init__(
63
60
  self,
64
- ec: ElementCollection,
61
+ ec: CollectedElements,
65
62
  p: ta.Optional['AsyncInjectorImpl'] = None,
66
63
  *,
67
64
  internal_consts: dict[Key, ta.Any] | None = None,
68
65
  ) -> None:
69
- super().__init__()
70
-
71
- self._ec = check.isinstance(ec, ElementCollection)
66
+ self._ec = (ec := check.isinstance(ec, ElementCollection))
72
67
  self._p: AsyncInjectorImpl | None = check.isinstance(p, (AsyncInjectorImpl, None))
73
68
 
74
69
  self._internal_consts: dict[Key, ta.Any] = {
@@ -77,28 +72,31 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
77
72
  }
78
73
 
79
74
  self._bim = ec.binding_impl_map()
75
+
80
76
  self._ekbs = ec.eager_keys_by_scope()
77
+
81
78
  self._pls: tuple[ProvisionListener, ...] = tuple(
82
79
  b.listener # type: ignore[attr-defined]
83
80
  for b in itertools.chain(
84
81
  ec.elements_of_type(ProvisionListenerBinding),
85
- (p._pls if p is not None else ()), # noqa
82
+ p._pls if p is not None else (), # noqa
86
83
  )
87
84
  )
88
85
 
89
- self._cs: weakref.WeakSet[AsyncInjectorImpl] | None = None # noqa
90
86
  self._root: AsyncInjectorImpl = p._root if p is not None else self # noqa
91
87
 
92
- self.__cur_req: AsyncInjectorImpl._Request | None = None
93
-
94
- ss = [
95
- *DEFAULT_SCOPES,
96
- *[sb.scope for sb in ec.elements_of_type(ScopeBinding)],
97
- ]
98
88
  self._scopes: dict[Scope, ScopeImpl] = {
99
- 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
+ )
100
94
  }
101
95
 
96
+ _cs: weakref.WeakSet['AsyncInjectorImpl'] | None = None # noqa
97
+
98
+ __cur_req: ta.Optional['AsyncInjectorImpl._Request'] = None
99
+
102
100
  #
103
101
 
104
102
  _has_run_init: bool = False
@@ -259,7 +257,7 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
259
257
  return obj(**kws)
260
258
 
261
259
 
262
- async def create_async_injector(es: Elements) -> AsyncInjector:
263
- i = AsyncInjectorImpl(ElementCollection(es))
260
+ async def create_async_injector(ce: CollectedElements) -> AsyncInjector:
261
+ i = AsyncInjectorImpl(ce)
264
262
  await i._init() # noqa
265
263
  return i
@@ -75,6 +75,7 @@ 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:
79
80
  if isinstance(obj, KwargsTarget):
80
81
  return obj
@@ -93,11 +94,15 @@ def build_kwargs_target(
93
94
  continue
94
95
 
95
96
  if p.annotation is inspect.Signature.empty:
97
+ if non_strict:
98
+ continue
96
99
  if p.default is not inspect.Parameter.empty:
97
100
  raise KeyError(f'{obj}, {p.name}')
98
101
  continue
99
102
 
100
103
  if p.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY):
104
+ if non_strict:
105
+ continue
101
106
  raise TypeError(sig)
102
107
 
103
108
  ann = p.annotation
@@ -122,7 +127,8 @@ def build_kwargs_target(
122
127
  k = dc.replace(k, tag=pt)
123
128
 
124
129
  if k in seen:
125
- raise ConflictingKeyError(k)
130
+ if not non_strict:
131
+ raise ConflictingKeyError(k)
126
132
  seen.add(k)
127
133
 
128
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,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/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/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))
@@ -342,6 +342,8 @@ def proxy_import(
342
342
  spec: str,
343
343
  package: str | None = None,
344
344
  extras: ta.Iterable[str] | None = None,
345
+ *,
346
+ no_cache: bool = False,
345
347
  ) -> types.ModuleType:
346
348
  """'Legacy' proxy import mechanism."""
347
349
 
@@ -352,12 +354,19 @@ def proxy_import(
352
354
 
353
355
  def __getattr__(att): # noqa
354
356
  nonlocal omod
357
+
355
358
  if omod is None:
356
359
  omod = importlib.import_module(spec, package=package)
357
360
  if extras:
358
361
  for x in extras:
359
362
  importlib.import_module(f'{spec}.{x}', package=package)
360
- return getattr(omod, att)
363
+
364
+ v = getattr(omod, att)
365
+
366
+ if not no_cache:
367
+ setattr(lmod, att, v)
368
+
369
+ return v
361
370
 
362
371
  lmod = types.ModuleType(spec)
363
372
  lmod.__getattr__ = __getattr__ # type: ignore[method-assign]