Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions injection/_core/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
ModuleLockError,
ModuleNotUsedError,
NoInjectable,
SkipInjectable,
)

"""
Expand Down Expand Up @@ -620,7 +621,7 @@ async def aget_instance[T](
async def aget_instance(self, cls, default=None): # type: ignore[no-untyped-def]
try:
return await self.afind_instance(cls)
except KeyError:
except (KeyError, SkipInjectable):
return default

@overload
Expand All @@ -640,7 +641,7 @@ def get_instance[T](
def get_instance(self, cls, default=None): # type: ignore[no-untyped-def]
try:
return self.find_instance(cls)
except KeyError:
except (KeyError, SkipInjectable):
return default

@overload
Expand Down Expand Up @@ -866,29 +867,28 @@ class Dependencies:
lazy_mapping: Lazy[Mapping[str, Injectable[Any]]]

def __iter__(self) -> Iterator[tuple[str, Any]]:
for name, injectable in self.mapping.items():
instance = injectable.get_instance()
yield name, instance
for name, injectable in self.items():
with suppress(SkipInjectable):
yield name, injectable.get_instance()

async def __aiter__(self) -> AsyncIterator[tuple[str, Any]]:
for name, injectable in self.mapping.items():
instance = await injectable.aget_instance()
yield name, instance
for name, injectable in self.items():
with suppress(SkipInjectable):
yield name, await injectable.aget_instance()

@property
def are_resolved(self) -> bool:
return self.lazy_mapping.is_set

@property
def mapping(self) -> Mapping[str, Injectable[Any]]:
return ~self.lazy_mapping

async def aget_arguments(self) -> dict[str, Any]:
return {key: value async for key, value in self}

def get_arguments(self) -> dict[str, Any]:
return dict(self)

def items(self) -> Iterator[tuple[str, Injectable[Any]]]:
return iter((~self.lazy_mapping).items())

@classmethod
def from_iterable(cls, iterable: Iterable[tuple[str, Injectable[Any]]]) -> Self:
lazy_mapping = Lazy(lambda: dict(iterable))
Expand Down
6 changes: 5 additions & 1 deletion injection/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"ScopeAlreadyDefinedError",
"ScopeError",
"ScopeUndefinedError",
"SkipInjectable",
)


Expand All @@ -30,6 +31,9 @@ def cls(self) -> type[T]:
return self.__class


class SkipInjectable(InjectionError): ...


class ModuleError(InjectionError): ...


Expand All @@ -42,7 +46,7 @@ class ModuleNotUsedError(KeyError, ModuleError): ...
class ScopeError(InjectionError): ...


class ScopeUndefinedError(LookupError, ScopeError): ...
class ScopeUndefinedError(LookupError, SkipInjectable, ScopeError): ...


class ScopeAlreadyDefinedError(ScopeError): ...
Expand Down
28 changes: 28 additions & 0 deletions tests/test_scoped.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections.abc import AsyncIterator, Iterator
from dataclasses import dataclass

import pytest

Expand Down Expand Up @@ -173,3 +174,30 @@ async def some_injectable_recipe() -> AsyncIterator[SomeInjectable]:
instance_2 = await afind_instance(SomeInjectable)

assert instance_1 is instance_2

async def test_scoped_with_scope_not_defined(self):
@scoped("test")
class A: ...

@injectable
@dataclass
class B:
a: A | None = None

# sync
b = find_instance(B)
assert b.a is None

with define_scope("test"):
b = find_instance(B)

assert isinstance(b.a, A)

# async
b = await afind_instance(B)
assert b.a is None

async with adefine_scope("test"):
b = await afind_instance(B)

assert isinstance(b.a, A)
Loading