How to run Uvicorn FastAPI server as a module from another Python file?

Question:

I want to run FastAPI server using Uvicorn from A different Python file.

uvicornmodule/main.py

import uvicorn
import webbrowser
from fastapi import FastAPI
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

app = FastAPI()

import os
script_dir = os.path.dirname(__file__)
st_abs_file_path = os.path.join(script_dir, "static/")
app.mount("/static", StaticFiles(directory=st_abs_file_path), name="static")

@app.get("/")
async def index():
    return FileResponse('static/index.html', media_type='text/html')

def start_server():
    # print('Starting Server...')       

    uvicorn.run(
        "app",
        host="0.0.0.0",
        port=8765,
        log_level="debug",
        reload=True,
    )
    # webbrowser.open("http://127.0.0.1:8765")

if __name__ == "__main__":
    start_server()

So, I want to run the FastAPI server from the below test.py file:

from uvicornmodule import main
main.start_server()

Then, I run python test.py.

But I am getting the below error:

RuntimeError:
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.

What I am doing wrong? I need to run this module as package.

Asked By: Rakesh Shetty

||

Answers:

When spawning new processes from the main process (as this is what happens when uvicorn.run() is called), it is important to protect the entry point to avoid recursive spawning of subprocesses, etc. As described in this article:

If the entry point was not protected with an if-statement idiom
checking for the top-level environment, then the script would execute
again directly
, rather than run a new child process as expected.

Protecting the entry point ensures that the program is only started
once, that the tasks of the main process are only executed by the main
process and not the child processes.

Basically, your code that creates the new process must be under if __name__ == '__main__':. Hence:

from uvicornmodule import main

if __name__ == "__main__":
    main.start_server()

Additionally, running uvicorn programmatically and having reload and/or workers flag(s) enabled, you must pass the application as an import string in the format of "<module>:<attribute>". For example:

# main.py
import uvicorn
from fastapi import FastAPI

app = FastAPI()

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
    # the below would also work, if `reload` and/or `workers` flags are not used
    # uvicorn.run(app, host="0.0.0.0", port=8000)

Also, as per FastAPI documentation, when running the server from a terminal in the following way (the default port is 8000. Have a look at all the available command line options):

> uvicorn main:app --reload

the command uvicorn main:app refers to:

  • main: the file main.py (the Python "module").
  • app: the object created inside of main.py with the line app = FastAPI().
  • --reload: make the server restart after code changes. Only use for development.
Answered By: Chris
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.