asyncio.run() cannot be called from a running event loop asyncio.run() cannot be called from a running event loop python-3.x python-3.x

asyncio.run() cannot be called from a running event loop


The asyncio.run() documentation says:

This function cannot be called when another asyncio event loop is running in the same thread.

In your case, jupyter (IPython ≥ 7.0) is already running an event loop:

You can now use async/await at the top level in the IPython terminal and in the notebook, it should — in most of the cases — “just work”. Update IPython to version 7+, IPykernel to version 5+, and you’re off to the races.

Therefore you don't need to start the event loop yourself and can instead call await main(url) directly, even if your code lies outside any asynchronous function.

Jupyter / IPython

async def main():    print(1)    await main()

Python (≥ 3.7)

import asyncioasync def main():    print(1)    asyncio.run(main())

In your code that would give:

url = ['url1', 'url2']result = await main(url)for text in result:    pass # text contains your html (text) response

Caution

There is a slight difference on how Jupyter use the loop compared to IPython.


To add to cglacet's answer - if one wants to detect whether a loop is running and adjust automatically (ie run main() on the existing loop, otherwise asyncio.run()), here is one suggestion that I tried (if indeed one wants to do that):

try:    loop = asyncio.get_running_loop()except RuntimeError:  # if cleanup: 'RuntimeError: There is no current event loop..'    loop = Noneif loop and loop.is_running():    print('Async event loop already running')    tsk = loop.create_task(main())    # ^-- https://docs.python.org/3/library/asyncio-task.html#task-object    tsk.add_done_callback(                                          # optional        lambda t: print(f'Task done: '                              # optional                        f'{t.result()=} << return val of main()'))  # optional (using py38)else:    print('Starting new event loop')    asyncio.run(main())