Watch at Ease看见世界,读懂生活。

技术 · 实用指南

Uvicorn 无法导入模块?先验证应用字符串、搜索路径与内部回溯

把启动报错拆成三个测试:Python 能否找到模块、指定属性是否存在,以及导入模块时是否触发了另一个异常。

展示 APP 解析、Python 搜索路径证明与直接导入回溯的原创技术图。
原创示意图:先证明目标字符串、模块搜索路径和直接导入,再修改应用代码。 Watch at Ease

“Error loading ASGI app. Could not import module”并不对应唯一修复。Uvicorn 先解析 app.main:app 之类的 APP 目标,Python 再搜索 app.main,而导入该模块时还可能执行并抛出另一个异常。

编辑任何文件前,保留原始命令、工作目录、解释器和第一份完整回溯。下面的流程只修改真正失败的那一层。

1. 固定失败命令和环境

先记录原命令、当前目录与解释器:pwd(PowerShell 用 Get-Location)、python -c "import sys; print(sys.executable)"、python -m uvicorn --version。服务管理器或容器启动时,要记录它们的工作目录和命令。

python -m uvicorn 会使用刚核对的解释器。若该命令连 uvicorn 都无法导入,应先修复环境或调用方式,而不是改应用模块路径。

2. 按 module:attribute 逐字解析 APP

官方格式是 <module>:<attribute>。app.main:app 中,模块是 app.main,属性是 app;不要写 .py、斜杠,或因为目录存在就随意加项目根目录名。

若对象由 create_app 等可调用对象创建,应指向该对象并明确使用 --factory。属性缺失与模块无法导入不是同一个故障。

3. 证明 Python 能找到模块

在相同目录和解释器中运行 python -c "import importlib.util; print(importlib.util.find_spec('app.main'))"。返回 None 表示当前搜索路径无法定位模块,并不证明文件不存在。

Python 的路径查找器遍历 sys.path;Uvicorn 的 --app-dir 会向 PYTHONPATH 加目录。固定一种契约:从项目根运行 app.main:app、从 app 目录运行 main:app,或固定服务目录并传绝对 --app-dir。

4. 直接导入以暴露真正回溯

若 find_spec 能定位模块,运行 python -c "import app.main"。如果完整回溯指向依赖、语法、配置读取或循环导入,说明模块已经找到,只是执行导入体时失败。

通用 Uvicorn 提示可能把内部异常伪装成路径错误。增加目录、重命名 main.py 或创建空文件都不能修复缺失依赖或循环导入。

5. 不启动服务器也能验证属性

导入成功后运行 python -c "import app.main as m; print(m.__file__); print(hasattr(m, 'app')); print(type(getattr(m, 'app', None)))"。文件路径可发现同名遮蔽,hasattr 可区分属性与模块问题。

对象若叫 application,就用 app.main:application;若是工厂则用相应工厂形式。若打印出的文件不是项目文件,先移除同名遮蔽模块。

6. 保持包导入与部署一致

FastAPI 多文件示例使用 app.main:app 之类的包入口。内部导入也要保持同一包边界;把 main.py 当松散脚本运行与把 app.main 当包导入,允许的相对和绝对导入不同。

在真实服务的工作目录、解释器、环境变量和挂载中复现最终命令。只有交互式 shell 先 cd 才成功的命令,并不自动构成部署修复。

7. 以可观察结果作为退出条件

先关闭 reload 和多 worker,只启动一个进程以保留单一回溯。成功标准是预期模块文件和属性均已验证、应用启动完成、已知路由能响应。

若直接导入仍失败,停止调整 Uvicorn 标志并保留回溯;若终端验证通过而服务管理器失败,就比较其目录、解释器、环境与挂载。

区分模块找不到、属性缺失和导入体崩溃的原创技术图。
原创示意图:同一启动提示可能需要路径修复、属性修复或内部回溯修复。 Watch at Ease