22

I'm testing aiohttp and asyncio. I want the same event loop to have a socket, http server, http client.

I'm using this sample code:

@routes.get('/')
async def hello(request):
    return web.Response(text="Hello, world")

app = web.Application()
app.add_routes(routes)
web.run_app(app)

The problem is run_app is blocking. I want to add the http server into an existing event loop, that I create using:

asyncio.get_event_loop()

2 Answers 2

27

The problem is run_app is blocking. I want to add the http server into an existing event loop

run_app is just a convenience API. To hook into an existing event loop, you can directly instantiate the AppRunner:

loop = asyncio.get_event_loop()
# add stuff to the loop
...

# set up aiohttp - like run_app, but non-blocking
runner = aiohttp.web.AppRunner(app)
loop.run_until_complete(runner.setup())
site = aiohttp.web.TCPSite(runner)    
loop.run_until_complete(site.start())

# add more stuff to the loop
...

loop.run_forever()

In asyncio 3.8 and later you can use asyncio.run():

async def main():
    # add stuff to the loop, e.g. using asyncio.create_task()
    ...

    runner = aiohttp.web.AppRunner(app)
    await runner.setup()
    site = aiohttp.web.TCPSite(runner)    
    await site.start()

    # add more stuff to the loop, if needed
    ...

    # wait forever
    await asyncio.Event().wait()

asyncio.run(main())
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, any reason why the server wont terminate on SIGINT (pressing CTRL+C)? I'm using windows powershell
@user3599803 Not using Windows, sorry, but googling possibly reveals it as a long-standing issue in asyncio.
@user3599803 stackoverflow.com/a/37420223/2955584 check this answer for terminating the script in windows machine
Just a note how to specify IP and port, which takes some googling: site = web.TCPSite(runner, host='0.0.0.0', port=80)
@Derkades What would be the point of invoking runner.cleanup() in the finally clause, as in your edit? wait() on a freshly created event can never be interrupted AFAIK.
13

For the future traveler from Google, here is a simpler way.

async def main():
    await aio.gather(
        web._run_app(app, port=args.port),
        SomeotherTask(),
        AndAnotherTask()
    )

aio.run(main())

Explanation: web.runapp is a thin wrapper over internal function web._runapp. The function uses the old style way of getting the eventloop and then calling loop.run_until_complete.

We replace it with aio.gather alongside other tasks that we want to run concurrently and use the aio.run to schedule them

Source

3 Comments

They now provide the Application Runner API to allow for this use case: docs.aiohttp.org/en/stable/…
Please avoid using private APIs in StackOverflow answers. Methods starting with _, such as _run_app, can be removed, renamed, or change meaning without prior notice.
I had trouble using this private member function but was able to use the app runner example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.