简体   繁体   中英

uvicorn and fastAPI with pyinstaller problem when uvicorn workers>1

I had checked PyInstaller and FastAPI (maximum recursion depth exceeded) and Pyinstaller-compiled Uvicorn server does not start correctly

FastAPI demo main.py :

import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
    return {"hello": "world"}

if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=58000, reload=False)

Run pyinstaller first pyinstaller -F main.py --clean and add hidden_imports in spec:

hidden_imports=[
                'uvicorn.logging',
                'uvicorn.loops',
                'uvicorn.loops.auto',
                'uvicorn.protocols',
                'uvicorn.protocols.http',
                'uvicorn.protocols.http.auto',
                'uvicorn.protocols.websockets',
                'uvicorn.protocols.websockets.auto',
                'uvicorn.lifespan',
                'uvicorn.lifespan.on',
            ]

It works good, but the app must be string when workers greater than 1:

WARNING: You must pass the application as an import string to enable 'reload' or 'workers'.

So I change to:

if __name__ == '__main__':
    uvicorn.run("main:app", host="0.0.0.0", port=58000, reload=False, workers=2)

After doing that, I ran the app dist/main and it created many apps like below, using 100% CPUs and 100% memories rapidly:

error message

Works on Python 3.8.3 and pyinstaller 4.0

It is important to call (on Windows) mutiprocessing.freeze_support() in the beginning, see official docs .

import multiprocessing
...
...
...
if __name__ == '__main__':
    mutiprocessing.freeze_support()
    uvicorn.run("main:app", host="0.0.0.0", port=58000, reload=False, workers=2)

Additionally, it might be needed to add the module main as a hidden import.

It looks like an infinite recursion to me. I suspect the cause is related to the self-reference at main:app and some PyInstaller sys dark magic that sets __name__ as __main__ .

I recommend moving app into a separate module and referencing it from that module in uvicorn.run() :

# app.py
from fastapi import FastAPI


app = FastAPI()

@app.get("/")
def root():
    return {"hello": "world"}
# main.py
import uvicorn


if __name__ == "__main__":
    uvicorn.run("app:app", host="0.0.0.0", port=58000, reload=False, workers=2)

Also, don't forget to add app.py as a hidden import for PyInstaller:

hidden_imports=[
    'uvicorn.logging',
    'uvicorn.loops',
    'uvicorn.loops.auto',
    'uvicorn.protocols',
    'uvicorn.protocols.http',
    'uvicorn.protocols.http.auto',
    'uvicorn.protocols.websockets',
    'uvicorn.protocols.websockets.auto',
    'uvicorn.lifespan',
    'uvicorn.lifespan.on',
    'app',
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM