|
17 | 17 | from contextlib import asynccontextmanager, contextmanager, suppress |
18 | 18 | from dataclasses import dataclass, field |
19 | 19 | from enum import StrEnum |
20 | | -from functools import partial, partialmethod, singledispatchmethod, update_wrapper |
| 20 | +from functools import partialmethod, singledispatchmethod, update_wrapper |
21 | 21 | from inspect import ( |
22 | 22 | BoundArguments, |
23 | 23 | Signature, |
|
57 | 57 | from injection._core.common.type import ( |
58 | 58 | InputType, |
59 | 59 | TypeInfo, |
60 | | - get_return_types, |
61 | | - get_yield_hint, |
| 60 | + get_yield_hints, |
| 61 | + iter_return_types, |
62 | 62 | ) |
63 | 63 | from injection._core.injectables import ( |
64 | 64 | AsyncCMScopedInjectable, |
@@ -169,14 +169,21 @@ def get_default(cls) -> Priority: |
169 | 169 |
|
170 | 170 | type PriorityStr = Literal["low", "high"] |
171 | 171 |
|
172 | | -type ContextManagerLikeRecipe[**P, T] = ( |
| 172 | +type ContextManagerRecipe[**P, T] = ( |
173 | 173 | Callable[P, ContextManager[T]] | Callable[P, AsyncContextManager[T]] |
174 | 174 | ) |
175 | 175 | type GeneratorRecipe[**P, T] = ( |
176 | 176 | Callable[P, Generator[T, Any, Any]] | Callable[P, AsyncGenerator[T, Any]] |
177 | 177 | ) |
178 | 178 |
|
179 | 179 |
|
| 180 | +@dataclass(repr=False, eq=False, frozen=True, slots=True) |
| 181 | +class _ScopedContext[**P, T]: |
| 182 | + cls: type[ScopedInjectable[Any, T]] |
| 183 | + hints: TypeInfo[T] |
| 184 | + wrapper: Recipe[P, T] | ContextManagerRecipe[P, T] |
| 185 | + |
| 186 | + |
180 | 187 | @dataclass(eq=False, frozen=True, slots=True) |
181 | 188 | class Module(EventListener, InjectionProvider): # type: ignore[misc] |
182 | 189 | name: str = field(default_factory=lambda: f"anonymous@{new_short_key()}") |
@@ -266,29 +273,33 @@ def scoped[**P, T]( |
266 | 273 | def decorator( |
267 | 274 | wrapped: Recipe[P, T] | GeneratorRecipe[P, T], |
268 | 275 | ) -> Recipe[P, T] | GeneratorRecipe[P, T]: |
269 | | - injectable_class: type[ScopedInjectable[Any, T]] |
270 | | - wrapper: Recipe[P, T] | ContextManagerLikeRecipe[P, T] |
271 | | - |
272 | 276 | if isasyncgenfunction(wrapped): |
273 | | - hint = get_yield_hint(wrapped) |
274 | | - injectable_class = AsyncCMScopedInjectable |
275 | | - wrapper = asynccontextmanager(wrapped) |
| 277 | + ctx = _ScopedContext( |
| 278 | + cls=AsyncCMScopedInjectable, |
| 279 | + hints=get_yield_hints(wrapped), |
| 280 | + wrapper=asynccontextmanager(wrapped), |
| 281 | + ) |
276 | 282 |
|
277 | 283 | elif isgeneratorfunction(wrapped): |
278 | | - hint = get_yield_hint(wrapped) |
279 | | - injectable_class = CMScopedInjectable |
280 | | - wrapper = contextmanager(wrapped) |
| 284 | + ctx = _ScopedContext( |
| 285 | + cls=CMScopedInjectable, |
| 286 | + hints=get_yield_hints(wrapped), |
| 287 | + wrapper=contextmanager(wrapped), |
| 288 | + ) |
281 | 289 |
|
282 | 290 | else: |
283 | | - injectable_class = SimpleScopedInjectable |
284 | | - hint = wrapper = wrapped # type: ignore[assignment] |
| 291 | + ctx = _ScopedContext( |
| 292 | + cls=SimpleScopedInjectable, |
| 293 | + hints=(wrapped,), |
| 294 | + wrapper=wrapped, |
| 295 | + ) |
285 | 296 |
|
286 | 297 | self.injectable( |
287 | | - wrapper, |
288 | | - cls=partial(injectable_class, scope_name=scope_name), |
| 298 | + ctx.wrapper, |
| 299 | + cls=ctx.cls.bind_scope_name(scope_name), |
289 | 300 | ignore_type_hint=True, |
290 | 301 | inject=inject, |
291 | | - on=(hint, on), |
| 302 | + on=(ctx.hints, on), |
292 | 303 | mode=mode, |
293 | 304 | ) |
294 | 305 | return wrapped |
@@ -715,7 +726,7 @@ def __build_key_types(input_cls: Any) -> frozenset[Any]: |
715 | 726 | config = MatchingTypesConfig(ignore_none=True) |
716 | 727 | return frozenset( |
717 | 728 | itertools.chain.from_iterable( |
718 | | - iter_matching_types(cls, config) for cls in get_return_types(input_cls) |
| 729 | + iter_matching_types(cls, config) for cls in iter_return_types(input_cls) |
719 | 730 | ) |
720 | 731 | ) |
721 | 732 |
|
|
0 commit comments