Showing Posts From

PyInstaller

PyInstaller 打包 FastAPI + uvicorn:能跑起来的最小写法

想把 FastAPI 项目打包成一个 exe 发给非技术用户,直接: pyinstaller -F main.py跑一下崩得七七八八。因为 uvicorn 走的是字符串式导入,PyInstaller 环境下拿不到;再加上一堆子模块(uvicorn.loops.*、starlette.*)静态分析捞不全。 正确起步:写个 start.py 不要直接打 main.py,写个显式导入 app 对象的启动文件: # start.py from main import app # 直接拿对象,不用字符串 import uvicornif __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)对应的 main.py: from fastapi import FastAPI app = FastAPI()@app.get("/") async def root(): return {"hello": "world"}打包 start.py: pyinstaller -F start.pyCould not import module "main" 的原因 如果 start.py 里写: uvicorn.run("main:app", host="0.0.0.0", port=8000)main:app 是字符串,uvicorn 会在运行时 importlib.import_module("main")。PyInstaller 打包后 main 不在 sys.path 里,直接崩: Error loading ASGI app. Could not import module "main"改成直接 import + 传对象最省事: from main import app uvicorn.run(app, ...)或者告诉 PyInstaller 也带上 main: pyinstaller -F start.py --hidden-import=mainuvicorn 子模块丢失 跑起来抛: ModuleNotFoundError: No module named 'uvicorn.loops'uvicorn 有一堆按需加载的 loop / protocol / logger 子模块,PyInstaller 全漏了。一次性带上: pyinstaller -F start.py \ --collect-all fastapi \ --collect-all uvicorn \ --collect-all starlette \ --collect-all pydantic--collect-all X = 把 X 及其所有子模块、数据文件、二进制全打进去。 静态文件 / 模板 用了 StaticFiles 或 Jinja2Templates 得加数据: Windows: pyinstaller -F start.py ^ --add-data "templates;templates" ^ --add-data "static;static"Linux/macOS: pyinstaller -F start.py \ --add-data "templates:templates" \ --add-data "static:static"代码里读路径要兼容 PyInstaller 临时目录: import sys, os from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templatesdef base_path(): return getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))app.mount("/static", StaticFiles(directory=os.path.join(base_path(), "static")), name="static") templates = Jinja2Templates(directory=os.path.join(base_path(), "templates"))后台运行、无 CMD 窗口 想双击不弹黑框: pyinstaller -F -w start.py --collect-all uvicorn ...-w 关控制台。注意:关了控制台,print / uvicorn 的日志就看不到了,改成写日志文件: uvicorn.run( app, host="0.0.0.0", port=8000, log_config={ "version": 1, "handlers": { "file": {"class": "logging.FileHandler", "filename": "app.log"}, }, "root": {"handlers": ["file"], "level": "INFO"}, }, )完整命令模板 一次搞定: pyinstaller -F -w start.py ^ --name myapi ^ --icon app.ico ^ --add-data "templates;templates" ^ --add-data "static;static" ^ --collect-all fastapi ^ --collect-all uvicorn ^ --collect-all starlette ^ --collect-all pydantic产物:dist/myapi.exe,双击就是一个后台服务。 什么时候别打包生产环境线上服务:Docker / systemd 更好 要热更新:exe 每次改代码都要重打 依赖 GPU / CUDA:打包体动辄几百 MB 多用户 / 高并发:exe 里塞 uvicorn 效率一般PyInstaller 更适合:内网小工具、给客户发一个"点两下就跑"的本地 API、演示 demo。 一句话总结 写 start.py + 直接 import app + --collect-all 四个关键字。字符串导入是坑,把 uvicorn 家族收全就通了。

PyInstaller 打包 GUI 程序:无 CMD、文件夹形式

用 PyInstaller 打包一个 Python GUI 程序,想要"启动不弹 CMD 黑框、也不打成单个大文件",一条命令搞定: pyinstaller -w -D main.py参数解释 -w(或 --noconsole):不开控制台窗口。适合 Tkinter、PyQt、PySide、wxPython 之类的 GUI。 -D(或 --onedir):文件夹形式打包。产物结构: dist/ └── main/ ├── main.exe ├── python3x.dll └── ...(依赖 dll、pyd、资源)跟 -F 单文件相比,-D 启动快、崩溃少、易调试,就是需要发一整个文件夹。 加图标 pyinstaller -w -D -i app.ico main.py.ico 文件建议 256×256 或多尺寸 ico。 常见错误组合 GUI 忘了 -w: pyinstaller -D main.py # 会弹黑色 CMD 窗口GUI 只想单文件反而用了 -F -w: pyinstaller -F -w main.py能跑,但每次启动都要把打包体解压到临时目录,启动慢;sys._MEIPASS 拿资源要处理路径。 用 spec 文件更可控 第一次 pyinstaller 会生成 main.spec,之后修改这个文件重新构建: pyinstaller main.specspec 里控制"无 CMD"的字段: exe = EXE( pyz, a.scripts, name='main', console=False, # 无 CMD 窗口 icon='app.ico', ... )有 -w -D 起步足够,需要精细控制 hidden imports、数据文件、加签名时再上 spec。 加静态资源 程序里读了 templates/、assets/ 之类的目录,-D 只会打二进制,不带资源,运行时找不到。加 --add-data: Windows(分隔符 ;): pyinstaller -w -D main.py ^ --add-data "templates;templates" ^ --add-data "assets;assets"macOS / Linux(分隔符 :): pyinstaller -w -D main.py \ --add-data "templates:templates" \ --add-data "assets:assets"代码里读资源要用 PyInstaller 兼容路径: import sys, osdef resource_path(rel): base = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__))) return os.path.join(base, rel)with open(resource_path("templates/index.html")) as f: ...找不到某个包 装了包但打包运行时抛 ModuleNotFoundError——PyInstaller 静态分析漏了动态导入。补 hidden-import: pyinstaller -w -D main.py --hidden-import=some_dynamic_module大量子模块可以直接 collect-all: pyinstaller -w -D main.py --collect-all fastapi --collect-all uvicorn一句话总结 pyinstaller -w -D main.py 是 GUI 程序打包最省心的一条命令。资源要 --add-data、动态导入要 --hidden-import 或 --collect-all。