Improve async mode exceptions handling to prevent infinite hanging when exception occurs

This commit is contained in:
Roman Mogylatov 2021-02-16 08:15:20 -05:00
parent f470400bbe
commit 131373227c
4 changed files with 1147 additions and 762 deletions

View File

@ -9,7 +9,8 @@ follows `Semantic versioning`_
Development version Development version
------------------- -------------------
- Fix double printing of exception when initializing resource causes error. - Improve async mode exceptions handling to prevent infinite hanging when exception occurs.
- Fix double printing of exception when resource initialization causes an error.
4.23.1 4.23.1
------ ------

File diff suppressed because it is too large Load Diff

View File

@ -455,10 +455,14 @@ cdef inline void __async_prepare_args_kwargs_callback(
object awaitables, object awaitables,
object future, object future,
): ):
awaited = future.result() try:
for value, (key, _) in zip(awaited, awaitables): awaited = future.result()
args[key] = value except Exception as exception:
future_result.set_result(args) future_result.set_exception(exception)
else:
for value, (key, _) in zip(awaited, awaitables):
args[key] = value
future_result.set_result(args)
@cython.boundscheck(False) @cython.boundscheck(False)
@ -560,19 +564,28 @@ cdef inline object __call(
cdef inline void __async_call_callback(object future_result, object call, object future): cdef inline void __async_call_callback(object future_result, object call, object future):
args, kwargs = future.result() try:
result = call(*args, **kwargs) args, kwargs = future.result()
except Exception as exception:
future_result.set_exception(exception)
else:
result = call(*args, **kwargs)
if __isawaitable(result): if __isawaitable(result):
result = asyncio.ensure_future(result) result = asyncio.ensure_future(result)
result.add_done_callback(functools.partial(__async_result_callback, future_result)) result.add_done_callback(functools.partial(__async_result_callback, future_result))
return return
future_result.set_result(result) future_result.set_result(result)
cdef inline object __async_result_callback(object future_result, object future): cdef inline object __async_result_callback(object future_result, object future):
future_result.set_result(future.result()) try:
result = future.result()
except Exception as exception:
future_result.set_exception(exception)
else:
future_result.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):

View File

@ -190,6 +190,25 @@ class FactoryTests(AsyncTestCase):
self.assertIsNot(service1.client, service2.client) self.assertIsNot(service1.client, service2.client)
def test_kwargs_error_injection(self):
async def init_resource():
raise Exception('Something went wrong')
class Container(containers.DeclarativeContainer):
resource_with_error = providers.Resource(init_resource)
client = providers.Factory(
Client,
resource1=resource_with_error,
resource2=None,
)
container = Container()
with self.assertRaises(Exception) as context:
self._run(container.client())
self.assertEqual(str(context.exception), 'Something went wrong')
def test_attributes_injection(self): def test_attributes_injection(self):
class ContainerWithAttributes(containers.DeclarativeContainer): class ContainerWithAttributes(containers.DeclarativeContainer):
resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1)) resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))