Skip to content

Commit b17b6d6

Browse files
committed
Merge branch 'release/1.5.3'
2 parents 64547d9 + 56924a7 commit b17b6d6

File tree

5 files changed

+149
-11
lines changed

5 files changed

+149
-11
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "taskiq-dependencies"
3-
version = "1.5.2"
3+
version = "1.5.3"
44
description = "FastAPI like dependency injection implementation"
55
authors = ["Pavel Kirilin <[email protected]>"]
66
readme = "README.md"

taskiq_dependencies/dependency.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import inspect
22
import uuid
3+
from contextlib import _AsyncGeneratorContextManager, _GeneratorContextManager
34
from typing import (
45
Any,
5-
AsyncContextManager,
66
AsyncGenerator,
77
Callable,
8-
ContextManager,
98
Coroutine,
109
Dict,
1110
Generator,
@@ -21,7 +20,7 @@
2120

2221
@overload
2322
def Depends(
24-
dependency: Optional[Callable[..., ContextManager[_T]]] = None,
23+
dependency: Optional[Callable[..., "_GeneratorContextManager[_T]"]] = None,
2524
*,
2625
use_cache: bool = True,
2726
kwargs: Optional[Dict[str, Any]] = None,
@@ -31,7 +30,7 @@ def Depends(
3130

3231
@overload
3332
def Depends(
34-
dependency: Optional[Callable[..., AsyncContextManager[_T]]] = None,
33+
dependency: Optional[Callable[..., "_AsyncGeneratorContextManager[_T]"]] = None,
3534
*,
3635
use_cache: bool = True,
3736
kwargs: Optional[Dict[str, Any]] = None,

taskiq_dependencies/utils.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import inspect
22
import sys
3+
from contextlib import _AsyncGeneratorContextManager, _GeneratorContextManager
34
from typing import Any, AsyncContextManager, ContextManager, Optional
45

56
if sys.version_info >= (3, 10):
@@ -38,9 +39,7 @@ def iscontextmanager(obj: Any) -> TypeGuard[ContextManager[Any]]:
3839
:param obj: object to check.
3940
:return: bool that indicates whether the object is a context manager or not.
4041
"""
41-
if not hasattr(obj, "__enter__") or not hasattr(obj, "__exit__"):
42-
return False
43-
return True
42+
return issubclass(obj.__class__, _GeneratorContextManager)
4443

4544

4645
def isasynccontextmanager(obj: Any) -> TypeGuard[AsyncContextManager[Any]]:
@@ -50,6 +49,4 @@ def isasynccontextmanager(obj: Any) -> TypeGuard[AsyncContextManager[Any]]:
5049
:param obj: object to check.
5150
:return: bool that indicates whether the object is a async context manager or not.
5251
"""
53-
if not hasattr(obj, "__aenter__") or not hasattr(obj, "__aexit__"):
54-
return False
55-
return True
52+
return issubclass(obj.__class__, _AsyncGeneratorContextManager)

tests/test_annotated.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,74 @@ def test_func(
216216
assert id(value[0]) != id(value[1])
217217
assert isinstance(value[0], TestClass)
218218
assert isinstance(value[1], TestClass)
219+
220+
221+
def test_skip_not_decorated_managers() -> None:
222+
"""
223+
Test that synct context skip context managers.
224+
225+
Tests that even is class implements a context manager,
226+
it won't be called during the context resolution,
227+
because it's not annotated with contextmanager decorator.
228+
"""
229+
230+
class TestCM:
231+
def __init__(self) -> None:
232+
self.opened = False
233+
234+
def __enter__(self) -> None:
235+
self.opened = True
236+
237+
def __exit__(self, *args: object) -> None:
238+
pass
239+
240+
test_cm = TestCM()
241+
242+
def get_test_cm() -> TestCM:
243+
nonlocal test_cm
244+
return test_cm
245+
246+
def target(cm: Annotated[TestCM, Depends(get_test_cm)]) -> None:
247+
pass
248+
249+
graph = DependencyGraph(target=target)
250+
with graph.sync_ctx() as ctx:
251+
kwargs = ctx.resolve_kwargs()
252+
assert kwargs["cm"] == test_cm
253+
assert not test_cm.opened
254+
255+
256+
@pytest.mark.anyio
257+
async def test_skip_not_decorated_async_managers() -> None:
258+
"""
259+
Test that synct context skip context managers.
260+
261+
Tests that even is class implements a context manager,
262+
it won't be called during the context resolution,
263+
because it's not annotated with contextmanager decorator.
264+
"""
265+
266+
class TestACM:
267+
def __init__(self) -> None:
268+
self.opened = False
269+
270+
async def __aenter__(self) -> None:
271+
self.opened = True
272+
273+
async def __aexit__(self, *args: object) -> None:
274+
pass
275+
276+
test_acm = TestACM()
277+
278+
def get_test_acm() -> TestACM:
279+
nonlocal test_acm
280+
return test_acm
281+
282+
def target(acm: Annotated[TestACM, Depends(get_test_acm)]) -> None:
283+
pass
284+
285+
graph = DependencyGraph(target=target)
286+
async with graph.async_ctx() as ctx:
287+
kwargs = await ctx.resolve_kwargs()
288+
assert kwargs["acm"] == test_acm
289+
assert not test_acm.opened

tests/test_graph.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,3 +792,74 @@ def target(a: int = A, b: int = B) -> int:
792792
with graph.sync_ctx() as ctx:
793793
kwargs = ctx.resolve_kwargs()
794794
assert target(**kwargs) == 3
795+
796+
797+
def test_skip_not_decorated_managers() -> None:
798+
"""
799+
Test that synct context skip context managers.
800+
801+
Tests that even is class implements a context manager,
802+
it won't be called during the context resolution,
803+
because it's not annotated with contextmanager decorator.
804+
"""
805+
806+
class TestCM:
807+
def __init__(self) -> None:
808+
self.opened = False
809+
810+
def __enter__(self) -> None:
811+
self.opened = True
812+
813+
def __exit__(self, *args: object) -> None:
814+
pass
815+
816+
test_cm = TestCM()
817+
818+
def get_test_cm() -> TestCM:
819+
nonlocal test_cm
820+
return test_cm
821+
822+
def target(cm: TestCM = Depends(get_test_cm)) -> None:
823+
pass
824+
825+
graph = DependencyGraph(target=target)
826+
with graph.sync_ctx() as ctx:
827+
kwargs = ctx.resolve_kwargs()
828+
assert kwargs["cm"] == test_cm
829+
assert not test_cm.opened
830+
831+
832+
@pytest.mark.anyio
833+
async def test_skip_not_decorated_async_managers() -> None:
834+
"""
835+
Test that synct context skip context managers.
836+
837+
Tests that even is class implements a context manager,
838+
it won't be called during the context resolution,
839+
because it's not annotated with contextmanager decorator.
840+
"""
841+
842+
class TestACM:
843+
def __init__(self) -> None:
844+
self.opened = False
845+
846+
async def __aenter__(self) -> None:
847+
self.opened = True
848+
849+
async def __aexit__(self, *args: object) -> None:
850+
pass
851+
852+
test_acm = TestACM()
853+
854+
def get_test_acm() -> TestACM:
855+
nonlocal test_acm
856+
return test_acm
857+
858+
def target(acm: TestACM = Depends(get_test_acm)) -> None:
859+
pass
860+
861+
graph = DependencyGraph(target=target)
862+
async with graph.async_ctx() as ctx:
863+
kwargs = await ctx.resolve_kwargs()
864+
assert kwargs["acm"] == test_acm
865+
assert not test_acm.opened

0 commit comments

Comments
 (0)