aio-libs / aiohttp

Asynchronous HTTP client/server framework for asyncio and Python
https://docs.aiohttp.org
Other
15.15k stars 2.02k forks source link

BaseSite can be awaited for closure, like asyncio.Server #4617

Closed TBBle closed 2 months ago

TBBle commented 4 years ago

🐣 Is your feature request related to a problem? Please describe.

When using the asyncio.run(main()) idiom introduced in Python 3.7, the general aiohttp examples of web.run_app, or AppRunner + TCPSite + loop.run_until_complete aren't well-suited.

The idiom that seems to match the various examples is something like

async def main():
  app = make_some_app()
  runner = web.AppRunner(app)
  await runner.setup()
  site = web.TCPSite(runner, 'localhost', 8080)
  await site.start()
  await asyncio.gather(site._server.wait_closed())
  return 0

This allows, for example, hanging other random asyncio-based things off this level, e.g., an outgoing watchdog call, and also gives us a useful place to hang the Python 3.7 asyncio Windows KeyboardInterrupt workaround.

It's also what lets your process terminate cleanly if someone calls an endpoint that does a site.stop() or runner.cleanup() as part of its logic.

It is also how you would explore aiohttp in a REPL, using the Python 3.8 -m asyncio feature.

However, it's a bit nasty to have to wait on site._server's wait_closed. It also has the risk of actually running before the part of BaseSite.stop that follows its own await self._server.wait_closed() call.

💡 Describe the solution you'd like

In the above, something like

  await asyncio.gather(site.wait_stop())

where site.wait_stop() is a future fulfilled at the end of BaseSite.stop.

Describe alternatives you've considered

We could also consider making BaseSite._server be exposed as a property BaseSite.server, returning an asyncio.Server.

As well as racing with BaseSite.stop, I feel that would be exposing internals of aiohttp.web needlessly.

Dreamsorcerer commented 2 months ago

I think this is a duplicate of #2746.