mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-02-27 17:25:03 +03:00
Add first dirty async injections implementation
This commit is contained in:
parent
c51eb52053
commit
caee7f6b41
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,8 @@
|
||||||
"""Providers module."""
|
"""Providers module."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import inspect
|
||||||
|
|
||||||
cimport cython
|
cimport cython
|
||||||
|
|
||||||
|
|
||||||
|
@ -353,6 +356,7 @@ cdef inline tuple __provide_positional_args(
|
||||||
return tuple(positional_args)
|
return tuple(positional_args)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: refactor
|
||||||
@cython.boundscheck(False)
|
@cython.boundscheck(False)
|
||||||
@cython.wraparound(False)
|
@cython.wraparound(False)
|
||||||
cdef inline dict __provide_keyword_args(
|
cdef inline dict __provide_keyword_args(
|
||||||
|
@ -363,14 +367,19 @@ cdef inline dict __provide_keyword_args(
|
||||||
cdef int index
|
cdef int index
|
||||||
cdef object name
|
cdef object name
|
||||||
cdef object value
|
cdef object value
|
||||||
cdef dict prefixed
|
cdef dict prefixed = {}
|
||||||
|
cdef list awaitables = []
|
||||||
cdef NamedInjection kw_injection
|
cdef NamedInjection kw_injection
|
||||||
|
|
||||||
if len(kwargs) == 0:
|
if len(kwargs) == 0:
|
||||||
for index in range(inj_kwargs_len):
|
for index in range(inj_kwargs_len):
|
||||||
kw_injection = <NamedInjection>inj_kwargs[index]
|
kw_injection = <NamedInjection>inj_kwargs[index]
|
||||||
name = __get_name(kw_injection)
|
name = __get_name(kw_injection)
|
||||||
kwargs[name] = __get_value(kw_injection)
|
value = __get_value(kw_injection)
|
||||||
|
if inspect.isawaitable(value):
|
||||||
|
awaitables.append((name, value))
|
||||||
|
else:
|
||||||
|
kwargs[name] = value
|
||||||
else:
|
else:
|
||||||
kwargs, prefixed = __separate_prefixed_kwargs(kwargs)
|
kwargs, prefixed = __separate_prefixed_kwargs(kwargs)
|
||||||
|
|
||||||
|
@ -387,9 +396,12 @@ cdef inline dict __provide_keyword_args(
|
||||||
else:
|
else:
|
||||||
value = __get_value(kw_injection)
|
value = __get_value(kw_injection)
|
||||||
|
|
||||||
|
if inspect.isawaitable(value):
|
||||||
|
awaitables.append((name, value))
|
||||||
|
else:
|
||||||
kwargs[name] = value
|
kwargs[name] = value
|
||||||
|
|
||||||
return kwargs
|
return {'kwargs': kwargs, 'awaitables': awaitables}
|
||||||
|
|
||||||
|
|
||||||
@cython.boundscheck(False)
|
@cython.boundscheck(False)
|
||||||
|
@ -424,15 +436,49 @@ cdef inline object __call(
|
||||||
injection_args,
|
injection_args,
|
||||||
injection_args_len,
|
injection_args_len,
|
||||||
)
|
)
|
||||||
keyword_args = __provide_keyword_args(
|
kw_return = __provide_keyword_args(
|
||||||
kwargs,
|
kwargs,
|
||||||
injection_kwargs,
|
injection_kwargs,
|
||||||
injection_kwargs_len,
|
injection_kwargs_len,
|
||||||
)
|
)
|
||||||
|
keyword_args, awaitable_keyword_args = kw_return['kwargs'], kw_return['awaitables']
|
||||||
|
|
||||||
|
# TODO: Refactor
|
||||||
|
if awaitable_keyword_args:
|
||||||
|
call_future = asyncio.Future()
|
||||||
|
|
||||||
|
future = asyncio.Future()
|
||||||
|
future.set_result(
|
||||||
|
(
|
||||||
|
call_future,
|
||||||
|
call,
|
||||||
|
positional_args,
|
||||||
|
keyword_args,
|
||||||
|
awaitable_keyword_args,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
kwargs_ready = asyncio.gather(future, *[value for _, value in awaitable_keyword_args])
|
||||||
|
kwargs_ready.add_done_callback(__async_call_callback)
|
||||||
|
asyncio.ensure_future(kwargs_ready)
|
||||||
|
|
||||||
|
return call_future
|
||||||
|
|
||||||
return call(*positional_args, **keyword_args)
|
return call(*positional_args, **keyword_args)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: refactor
|
||||||
|
cdef inline object __async_call_callback(object injections):
|
||||||
|
(call_future, call, positional_args, keyword_args, awaitable_keyword_args), *awaited_keyword_args = injections.result()
|
||||||
|
|
||||||
|
for value, (name, _) in zip(awaited_keyword_args, awaitable_keyword_args):
|
||||||
|
keyword_args[name] = value
|
||||||
|
|
||||||
|
result = call(*positional_args, **keyword_args)
|
||||||
|
|
||||||
|
call_future.set_result(result)
|
||||||
|
|
||||||
|
|
||||||
cdef inline object __callable_call(Callable self, tuple args, dict kwargs):
|
cdef inline object __callable_call(Callable self, tuple args, dict kwargs):
|
||||||
return __call(
|
return __call(
|
||||||
self.__provides,
|
self.__provides,
|
||||||
|
|
|
@ -2564,7 +2564,9 @@ cdef class Dict(Provider):
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs):
|
cpdef object _provide(self, tuple args, dict kwargs):
|
||||||
"""Return result of provided callable's call."""
|
"""Return result of provided callable's call."""
|
||||||
return __provide_keyword_args(kwargs, self.__kwargs, self.__kwargs_len)
|
# TODO: hotfix, remove
|
||||||
|
kwargs = __provide_keyword_args(kwargs, self.__kwargs, self.__kwargs_len)
|
||||||
|
return kwargs['kwargs']
|
||||||
|
|
||||||
|
|
||||||
cdef class Resource(Provider):
|
cdef class Resource(Provider):
|
||||||
|
|
89
tests/unit/providers/test_async_py36.py
Normal file
89
tests/unit/providers/test_async_py36.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
# Runtime import to get asyncutils module
|
||||||
|
import os
|
||||||
|
_TOP_DIR = os.path.abspath(
|
||||||
|
os.path.sep.join((
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
'../',
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
import sys
|
||||||
|
sys.path.append(_TOP_DIR)
|
||||||
|
|
||||||
|
from asyncutils import AsyncTestCase
|
||||||
|
|
||||||
|
|
||||||
|
RESOURCE1 = object()
|
||||||
|
RESOURCE2 = object()
|
||||||
|
|
||||||
|
|
||||||
|
async def init_resource(resource):
|
||||||
|
await asyncio.sleep(random.randint(1, 10) / 1000)
|
||||||
|
yield resource
|
||||||
|
await asyncio.sleep(random.randint(1, 10) / 1000)
|
||||||
|
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def __init__(self, resource1: object, resource2: object) -> None:
|
||||||
|
self.resource1 = resource1
|
||||||
|
self.resource2 = resource2
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
def __init__(self, client: Client) -> None:
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))
|
||||||
|
resource2 = providers.Resource(init_resource, providers.Object(RESOURCE2))
|
||||||
|
|
||||||
|
client = providers.Factory(
|
||||||
|
Client,
|
||||||
|
resource1=resource1,
|
||||||
|
resource2=resource2,
|
||||||
|
)
|
||||||
|
|
||||||
|
service = providers.Factory(
|
||||||
|
Service,
|
||||||
|
client=client,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FactoryTests(AsyncTestCase):
|
||||||
|
|
||||||
|
def test_direct_injection(self):
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
client1 = self._run(container.client())
|
||||||
|
client2 = self._run(container.client())
|
||||||
|
|
||||||
|
self.assertIsInstance(client1, Client)
|
||||||
|
self.assertIs(client1.resource1, RESOURCE1)
|
||||||
|
self.assertIs(client1.resource2, RESOURCE2)
|
||||||
|
|
||||||
|
self.assertIsInstance(client2, Client)
|
||||||
|
self.assertIs(client2.resource1, RESOURCE1)
|
||||||
|
self.assertIs(client2.resource2, RESOURCE2)
|
||||||
|
|
||||||
|
def test_children_injection(self):
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
service1 = self._run(container.service())
|
||||||
|
service2 = self._run(container.service())
|
||||||
|
|
||||||
|
self.assertIsInstance(service1, Service)
|
||||||
|
self.assertIsInstance(service1.client, Client)
|
||||||
|
self.assertIs(service1.client.resource1, RESOURCE1)
|
||||||
|
self.assertIs(service1.client.resource2, RESOURCE2)
|
||||||
|
|
||||||
|
self.assertIsInstance(service2, Service)
|
||||||
|
self.assertIsInstance(service2.client, Client)
|
||||||
|
self.assertIs(service2.client.resource1, RESOURCE1)
|
||||||
|
self.assertIs(service2.client.resource2, RESOURCE2)
|
||||||
|
|
||||||
|
self.assertIsNot(service1.client, service2.client)
|
Loading…
Reference in New Issue
Block a user