Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to / doc: async configuration / awaitable loop.start() #2575

Closed
dimaqq opened this issue Jan 24, 2019 · 4 comments
Closed

How to / doc: async configuration / awaitable loop.start() #2575

dimaqq opened this issue Jan 24, 2019 · 4 comments
Labels

Comments

@dimaqq
Copy link

dimaqq commented Jan 24, 2019

User guide advocates following to start a web server:

application = ...
application.listen(...)
IOLoop.current().start()

Now let's bring in some async library and use that to get application config:

config = IOLoop.current().run_sync(get_config_from_async_db)
application = SomeApp(config, ...)
application.listen(config["port"])
IOLoop.current().start()

Then comes aio-libs/aiohttp#3331 where iohttp (kinda rightfully) decided that asyncio.get_event_loop().is_running() should be True.

Now SomeApp constructor is going to create a aiohttp.TCPConnector(...) (a transitive dependency), which makes aiohttp unhappy (warning only, nothing is awaited at this point, but iohttp takes a reference to current event loop).

I wish I could express application setup and start as:

async def main():
    config = await get_config_from_async_db()
    application = SomeApp(config, ...)
    application.listen(config["port"])
    await application.serve_forever()

asyncio.run(main())  # Or IOLoop.current().run(main())

I this something like this would also be useful in tests, where a server may be started and stopped for each test.

P.S. I apologise if this was covered somewhere and I couldn't find a doc or past issue.

@ploxiln
Copy link
Contributor

ploxiln commented Jan 24, 2019

You can pretty much do that:

async def main():
    config = await get_config()
    application = SomeApp(config)
    application.listen(config["port"])

mainloop = IOLoop.current()
mainloop.spawn_callback(main)
mainloop.start()

You then probably want to add a signal handler which calls IOLoop.stop() ... here's one way which may be sufficient: #1791 (comment)

@bdarnell
Copy link
Member

Yeah, this is probably worth documenting as an example; I use async def main with run_sync a lot of the time now. My version of await serve_forever() is await tornado.locks.Event().wait() (and then when/if I add graceful shutdown I set that event instead of just stopping the event loop; the rest of my cleanup happens in main()).

@dimaqq
Copy link
Author

dimaqq commented Jan 28, 2019

You can pretty much do that:

async def main():
    config = await get_config()
    application = SomeApp(config)
    application.listen(config["port"])

mainloop = IOLoop.current()
mainloop.spawn_callback(main)
mainloop.start()

You then probably want to add a signal handler which calls IOLoop.stop() ... here's one way which may be sufficient: #1791 (comment)

This is cool, but hangs if startup code raises an exception:

async def foo(): 1/0
IOLoop.current().spawn_callback(foo)
IOLoop.current().start()
ERROR:tornado.application:Exception ...
Traceback ...
<hangs>

Yeah, this is probably worth documenting as an example; I use async def main with run_sync a lot of the time now. My version of await serve_forever() is await tornado.locks.Event().wait() (and then when/if I add graceful shutdown I set that event instead of just stopping the event loop; the rest of my cleanup happens in main()).

This is better, as run_sync propagates exceptions. And there's a chance to cleanup in main().

I was kinda hoping to use try: finally: for cleanup... I'll try to bolt this on top of Event().wait().

@bdarnell bdarnell added the docs label Feb 2, 2019
@bdarnell
Copy link
Member

Deprecation changes in python 3.10 forced us to update all the examples to use asyncio.run() and an async main as discussed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants