-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from modern-python/feature/injecting-factory
implement factories injecting
- Loading branch information
Showing
3 changed files
with
83 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
packages/modern-di/modern_di/providers/injected_factory.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import functools | ||
import typing | ||
|
||
from modern_di import Container | ||
from modern_di.providers.abstract import AbstractProvider | ||
|
||
|
||
T_co = typing.TypeVar("T_co", covariant=True) | ||
P = typing.ParamSpec("P") | ||
|
||
|
||
class InjectedFactory(typing.Generic[T_co]): | ||
__slots__ = ("_factory_provider",) | ||
|
||
def __init__(self, factory_provider: AbstractProvider[T_co]) -> None: | ||
self._factory_provider = factory_provider | ||
|
||
async def async_resolve(self, container: Container) -> typing.Callable[[], T_co]: | ||
await self._factory_provider.async_resolve(container) | ||
return functools.partial(self._factory_provider.sync_resolve, container) | ||
|
||
def sync_resolve(self, container: Container) -> typing.Callable[[], T_co]: | ||
self._factory_provider.sync_resolve(container) | ||
return functools.partial(self._factory_provider.sync_resolve, container) |
54 changes: 54 additions & 0 deletions
54
packages/modern-di/tests_core/providers/test_injected_factory.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import dataclasses | ||
import datetime | ||
|
||
import pytest | ||
from modern_di import Container, Scope, providers | ||
|
||
from tests_core.creators import create_async_resource, create_sync_resource | ||
|
||
|
||
@dataclasses.dataclass(kw_only=True, slots=True) | ||
class DependentCreator: | ||
dep1: datetime.datetime | ||
|
||
|
||
async_resource = providers.Resource(Scope.APP, create_async_resource) | ||
sync_resource = providers.Resource(Scope.APP, create_sync_resource) | ||
request_sync_factory = providers.Factory(Scope.REQUEST, DependentCreator, dep1=sync_resource.cast) | ||
request_async_factory = providers.Factory(Scope.REQUEST, DependentCreator, dep1=async_resource.cast) | ||
|
||
|
||
async def test_injected_async_factory() -> None: | ||
async with ( | ||
Container(scope=Scope.APP) as app_container, | ||
app_container.build_child_container(scope=Scope.REQUEST) as request_container, | ||
): | ||
factory = await request_async_factory.factory_provider.async_resolve(request_container) | ||
instance1, instance2 = factory(), factory() | ||
assert instance1 is not instance2 | ||
assert isinstance(instance1, DependentCreator) | ||
assert isinstance(instance2, DependentCreator) | ||
|
||
|
||
async def test_injected_sync_factory() -> None: | ||
with ( | ||
Container(scope=Scope.APP) as app_container, | ||
app_container.build_child_container(scope=Scope.REQUEST) as request_container, | ||
): | ||
factory = request_sync_factory.factory_provider.sync_resolve(request_container) | ||
instance1, instance2 = factory(), factory() | ||
assert instance1 is not instance2 | ||
assert isinstance(instance1, DependentCreator) | ||
assert isinstance(instance2, DependentCreator) | ||
|
||
|
||
async def test_injected_async_factory_in_sync_mode() -> None: | ||
with ( | ||
Container(scope=Scope.APP) as app_container, | ||
app_container.build_child_container(scope=Scope.REQUEST) as request_container, | ||
): | ||
with pytest.raises(RuntimeError, match="Resolving async resource in sync container is not allowed"): | ||
await request_async_factory.factory_provider.async_resolve(request_container) | ||
|
||
with pytest.raises(RuntimeError, match="Async resource cannot be resolved synchronously"): | ||
request_async_factory.factory_provider.sync_resolve(request_container) |