From d31308ccedcf1d5522b72e9d534e451d2dcd9662 Mon Sep 17 00:00:00 2001 From: remimd Date: Sat, 14 Jun 2025 13:44:43 +0200 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=F0=9F=90=9B=20`@constant`=20now=20w?= =?UTF-8?q?orks=20with=20async=20recipes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- injection/_core/common/lazy.py | 33 +++++++++++++++++++++------------ injection/_core/module.py | 6 +++--- tests/test_constant.py | 29 ++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/injection/_core/common/lazy.py b/injection/_core/common/lazy.py index dc15564..0226673 100644 --- a/injection/_core/common/lazy.py +++ b/injection/_core/common/lazy.py @@ -1,41 +1,50 @@ -from collections.abc import Callable, Iterator +from collections.abc import AsyncIterator, Awaitable, Callable, Iterator from functools import partial -from injection._core.common.invertible import Invertible, SimpleInvertible +from injection._core.common.invertible import Invertible -def lazy[T](factory: Callable[..., T]) -> Invertible[T]: +def lazy[T](factory: Callable[..., T]) -> Callable[[], T]: def cache() -> Iterator[T]: - nonlocal factory value = factory() - del factory + while True: + yield value + + return partial(next, cache()) + +def alazy[T](factory: Callable[..., Awaitable[T]]) -> Callable[[], Awaitable[T]]: + async def cache() -> AsyncIterator[T]: + value = await factory() while True: yield value - getter = partial(next, cache()) - return SimpleInvertible(getter) + return partial(__anext, cache()) class Lazy[T](Invertible[T]): - __slots__ = ("__invertible", "__is_set") + __slots__ = ("__get", "__is_set") - __invertible: Invertible[T] + __get: Callable[[], T] __is_set: bool def __init__(self, factory: Callable[..., T]) -> None: @lazy - def invertible() -> T: + def get() -> T: value = factory() self.__is_set = True return value - self.__invertible = invertible + self.__get = get self.__is_set = False def __invert__(self) -> T: - return ~self.__invertible + return self.__get() @property def is_set(self) -> bool: return self.__is_set + + +async def __anext[T](async_iterator: AsyncIterator[T]) -> T: + return await anext(async_iterator) diff --git a/injection/_core/module.py b/injection/_core/module.py index 9a73075..1b69bcc 100644 --- a/injection/_core/module.py +++ b/injection/_core/module.py @@ -50,7 +50,7 @@ from injection._core.common.event import Event, EventChannel, EventListener from injection._core.common.invertible import Invertible, SimpleInvertible from injection._core.common.key import new_short_key -from injection._core.common.lazy import Lazy, lazy +from injection._core.common.lazy import Lazy, alazy, lazy from injection._core.common.threading import get_lock from injection._core.common.type import ( InputType, @@ -512,9 +512,9 @@ def constant[**P, T]( mode: Mode | ModeStr = Mode.get_default(), ) -> Any: def decorator(wp: Recipe[P, T]) -> Recipe[P, T]: - lazy_instance = lazy(wp) + recipe: Recipe[[], T] = alazy(wp) if iscoroutinefunction(wp) else lazy(wp) # type: ignore[arg-type] self.injectable( - lambda: ~lazy_instance, + recipe, ignore_type_hint=True, inject=False, on=(wp, on), diff --git a/tests/test_constant.py b/tests/test_constant.py index 39a0d2c..7dfc86c 100644 --- a/tests/test_constant.py +++ b/tests/test_constant.py @@ -1,6 +1,6 @@ import pytest -from injection import constant, get_instance +from injection import aget_instance, constant, get_instance class TestConstant: @@ -12,6 +12,33 @@ class SomeInjectable: ... instance_2 = get_instance(SomeInjectable) assert instance_1 is instance_2 is not None + def test_constant_with_recipe(self): + class SomeClass: ... + + @constant + def recipe() -> SomeClass: + return SomeClass() + + instance_1 = get_instance(SomeClass) + instance_2 = get_instance(SomeClass) + assert instance_1 is instance_2 + assert isinstance(instance_1, SomeClass) + + async def test_constant_with_async_recipe(self): + class SomeClass: ... + + @constant + async def recipe() -> SomeClass: + return SomeClass() + + with pytest.raises(RuntimeError): + get_instance(SomeClass) + + instance_1 = await aget_instance(SomeClass) + instance_2 = await aget_instance(SomeClass) + assert instance_1 is instance_2 + assert isinstance(instance_1, SomeClass) + def test_constant_with_on(self): class A: ... From 4d0012aa39ec46143fcf85dc4036da926ea08039 Mon Sep 17 00:00:00 2001 From: remimd Date: Sat, 14 Jun 2025 13:47:11 +0200 Subject: [PATCH 2/2] ?? --- injection/_core/common/lazy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/injection/_core/common/lazy.py b/injection/_core/common/lazy.py index 0226673..8a8089f 100644 --- a/injection/_core/common/lazy.py +++ b/injection/_core/common/lazy.py @@ -19,7 +19,7 @@ async def cache() -> AsyncIterator[T]: while True: yield value - return partial(__anext, cache()) + return partial(_anext, cache()) class Lazy[T](Invertible[T]): @@ -46,5 +46,5 @@ def is_set(self) -> bool: return self.__is_set -async def __anext[T](async_iterator: AsyncIterator[T]) -> T: +async def _anext[T](async_iterator: AsyncIterator[T]) -> T: return await anext(async_iterator)