I want to share my database connection between functions.
In aiohttp you must initialize connection in on_startup field, cause of framework async nature.
I could solve this and share between all my handlers using subapp field of aiohttp and doing this:
app = connexion.AioHttpApp(__name__)
api = app.add_api(os.path.join(os.getcwd(), API_SPECS_PATH),
pass_context_arg_name='request',
strict_validation=True)
app.app.on_startup.append(setup_db)
I access on handlers:
async def fake_handler(*_null, **extra_args):
db_connection = extra_args['request'].app['db']
...
When I want to access this in, for example, a basic auth handler, I cannot.
A method to access my database/something connection.
I can access in handlers but I cannot access in, for example, x-basicInfoFunc.
Here is the solution I use to handle this issue:
import connexion
from functools import partial
class StartupWrapper:
def __init__(self, app: connexion.AioHttpApp, api: connexion.AioHttpApi):
self.app = app
self.api = api
self.app.app.on_startup.append(partial(self.on_startup))
self.app.app.on_shutdown.append(partial(self.on_shutdown))
self.app.app.on_cleanup.append(partial(self.on_cleanup))
self.http_session = None
async def on_startup(self, app):
""" Called after application initialization, but before to server first request """
self.api.subapp['db'] = MyDB()
async def on_shutdown(self, app):
""" Called when a shutdown signal is received, just after serving the last request """
pass
async def on_cleanup(self, app):
""" Called after on_shutdown, when all requests has been canceled """
pass
def main():
specification_dir = os.path.join(os.path.dirname(__file__), 'openapi')
app = connexion.AioHttpApp(__name__, specification_dir=specification_dir)
api = app.add_api('openapi.yaml', pass_context_arg_name='request')
StartupWrapper(app, api)
app.run(port=8080)
Then in my handlers:
async def hello(request: web.Request) -> web.Response:
db = request.app['db']
#...
Not ideal, but it works.
I'm thinking of patch to add on_startup & co argument in add_api function.
@Jyhess Thanks for your response but that's the same that I'm doing and I can't access to my db connections in middlewares like authentication.
Maybe you can have a look to aiotask-context.
I managed to get this done via a context that sets the DB inside the application + copying the app['db'] to request['db'] inside the middleware. Actually, @Jyhess solution looks nice as well, but I wanted to ensure that api handlers see only what connexion supplied them via API definition + extras (such as DB) that I am controlling explicitly.
aiohttp has a nice feature: app.cleanup_ctx. However, the keys assigned in the signal handlers are not present in the request.app of the handler. The StartupWrapper solution is too verbose.
I never understood why people hate verbose solutions.
At least everything is visible and easy to understand compared to magic things happening in easy to use solutions where a lot is done for the user and nobody knows how that's done.
@kornicameister it's a high level programming: one wants the main idea of his program to shine, not to be hidden in the tons of boilerplate technical details. But that's for another debate (python vs assembly language). Nevertheless, connexion should be an extension to aiohttp. But besides the brought enhancements, it suppresses one of the aiohttp's basic features: accessing the application config from request handlers. When you do app['foo'] = 'bar' in the app creation procedure, you get KeyError in a handler if you want to access app['foo'].
ok, it is possible, the add_api result is just not documented:
async def db_ctx(subapp, _app):
subapp['db'] = await create_db_connection()
yield
subapp['db'].disconnect()
api = app.add_api(...)
subapp = api.subapp
subapp['foo'] = 'bar'
app.cleanup_ctx(functools.partial(db_ctx, subapp))
Now I can see that @Jyhess has the subapp part in his code, I have just missed the main magic of the snippet, sorry.