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
1 change: 1 addition & 0 deletions src/dependency_injector/providers.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ cdef class Dict(Provider):
cdef class ResourceState:
cdef object resource
cdef object shutdowner
cdef bint shutdowner_is_async
cdef bint is_async
cdef bint async_done

Expand Down
21 changes: 19 additions & 2 deletions src/dependency_injector/providers.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3591,9 +3591,20 @@ cdef class ResourceState:
def __cinit__(self, obj, error_callback, /):
self.resource = None
self.shutdowner = None
self.shutdowner_is_async = False
self.is_async = False
self.async_done = False

def __repr__(self):
return (
f"{self.__class__.__name__}("
f"resource={self.resource!r}, "
f"shutdowner={self.shutdowner!r}, "
f"shutdowner_is_async={self.shutdowner_is_async}, "
f"is_async={self.is_async}, "
f"async_done={self.async_done})"
)

def __init__(self, obj, error_callback, /):
if __is_future_or_coroutine(obj):
self.from_coro(obj, error_callback)
Expand All @@ -3612,7 +3623,10 @@ cdef class ResourceState:
self.shutdowner = None

if shutdowner is not None:
await shutdowner(None, None, None)
future = shutdowner(None, None, None)

if self.shutdowner_is_async:
await future

async def from_awaitable(self, awaitable, error_callback, /):
try:
Expand Down Expand Up @@ -3644,6 +3658,7 @@ cdef class ResourceState:
raise

self.shutdowner = acm.__aexit__
self.shutdowner_is_async = True
self.async_done = True
return resource

Expand Down Expand Up @@ -3835,7 +3850,9 @@ cdef class BaseResource(Provider):
if state.is_async:
return state.async_shutdown()
elif state.shutdowner is not None:
state.shutdowner(None, None, None)
result = state.shutdowner(None, None, None)

assert not __isfuture(result), "sync resource with async shutdowner"

if self._async_mode == ASYNC_MODE_ENABLED:
return NULL_AWAITABLE
Expand Down
21 changes: 20 additions & 1 deletion tests/unit/providers/resource/test_async_resource_py35.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import asyncio
import inspect
from contextlib import asynccontextmanager
from contextlib import asynccontextmanager, contextmanager
from typing import Any

from pytest import mark, raises
Expand Down Expand Up @@ -346,3 +346,22 @@ async def _init():

assert result2 is resource
assert _init.counter == 1


@mark.asyncio
async def test_sync_resource_with_async_deps():
@asynccontextmanager
async def resource_async(v):
await asyncio.sleep(0)
yield v

@contextmanager
def resource_sync(_async):
yield _async + 1

_async = providers.Resource(resource_async, 1)
_sync = providers.Resource(resource_sync, _async)

assert (await _sync()) == 2
await _async.shutdown()
await _sync.shutdown()