How to run FastAPI application inside Jupyter?

Question:

I am learning FastAPI and I have this example.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

I saved the script as main.ipynb

The tutorial says to run this line of code in the command line: uvicorn main:app --reload

I am getting this error:

(venv) PS C:UsersxxxDesktopPython YamedSaadAPI> uvicorn main:app --reload
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [21304] using WatchFiles
ERROR:    Error loadinimport module "main".INFO:     Stopping reloader process [21304]

The reason is because I am using .ipynb as opposed to .py.

How can i fix this error while using .ipynb.

Thanks so much

Asked By: bravopapa

||

Answers:

If you attempted to start the server as usual inside Jupyter, for example:

import uvicorn

if __name__ == "__main__":
    uvicorn.run(app)

you would get the following error:

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

This is due to Jupyter already running an event loop, and once Uvicorn calls asyncio.run() internally, the above error is raised.

As per asyncio.run() documentation:

This function cannot be called when another asyncio event loop is
running in the same thread (see relevant asyncio implementation, where the error is raised).

[…]

This function always creates a new event loop and closes it at the
end. It should be used as a main entry point for asyncio programs, and
should ideally only be called once.

Solution 1

If you wouldd like to run uvicorn from an already running async environment, use uvicorn.Server.serve() instead (you could add the below to a new code cell in your Jupyter notebook, and then run it):

import asyncio
import uvicorn

if __name__ == "__main__":
    config = uvicorn.Config(app)
    server = uvicorn.Server(config)
    await server.serve()

or, get the current event loop (using asyncio.get_event_loop()), and call loop.create_task() to create a task inside the event loop for the current thread:

import asyncio
import uvicorn

if __name__ == "__main__":
    config = uvicorn.Config(app)
    server = uvicorn.Server(config)
    loop = asyncio.get_event_loop()
    loop.create_task(server.serve())

Solution 2

Alternatively, you can use nest_asyncio, which allows nested use of asyncio.run() and loop.run_until_complete():

import nest_asyncio
import uvicorn

if __name__ == "__main__":
    nest_asyncio.apply()
    uvicorn.run(app)
Answered By: Chris