Describe the bug
When an exception is raised with a normal (Async)ExitStack used as a context manager (see fastapi), __aexit__ is called with exception details. fastapi_injectable has no way to mirror this behavior, always forcing us to use an ugly workaround (see below)
To Reproduce
from collections.abc import AsyncGenerator
from typing import Annotated
from fastapi import Depends
from fastapi_injectable import cleanup_all_exit_stacks, injectable
async def _get_dbconnection() -> AsyncGenerator[object, None]:
conn = object()
try:
print("yield connection")
yield conn
except Exception:
print("conn.rollback()")
raise
else:
print("conn.commit()")
finally:
print("conn.close()")
@injectable()
async def as_dependency(connection: Annotated[object, Depends(_get_dbconnection)]):
raise Exception("boom")
async def main():
try:
await as_dependency()
except Exception:
# BUG HERE
# in the face of an exception, we should be able to call __aexit__ with exception
# information so that we can properly unwind the exit stack and call rollback() on the
# dependency
print("got exception")
finally:
await cleanup_all_exit_stacks()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Expected behavior
I want to be able to unwind an exit stack with an error as would happen if it were used as a context manager.
My suggestion is to add an exc: Exception parameter to the _close_stack method such that it calls
if exc:
await stack.__aexit__(type(exc), exc, exc.__traceback__)
else:
await stack.aclose()
and plumb it all the way through from the close_exit_stacks methods
Ugly workaround
Looks something like this...
async def _injectable_wrapper(callable):
scope = InjectableScope()
await scope.__aenter__()
try:
result = callable(*args, **kwargs)
if asyncio.iscoroutine(result):
result = await result
await scope.__aexit__(None, None, None)
return result
except Exception as exc:
await scope.__aexit__(type(exc), exc, exc.__traceback__)
raise
Describe the bug
When an exception is raised with a normal (Async)ExitStack used as a context manager (see fastapi),
__aexit__is called with exception details. fastapi_injectable has no way to mirror this behavior, always forcing us to use an ugly workaround (see below)To Reproduce
Expected behavior
I want to be able to unwind an exit stack with an error as would happen if it were used as a context manager.
My suggestion is to add an
exc: Exceptionparameter to the _close_stack method such that it callsand plumb it all the way through from the close_exit_stacks methods
Ugly workaround
Looks something like this...