Pytest运行正常但直接执行py文件报No module named错误如何解决
问题表现
项目目录结构如下:
generate ├── generate │ ├── __init__.py │ ├── write_sql.py │ └── utils.py └── tests └── test_write_sql.py
test_write_sql.py与write_sql.py中均使用绝对导入语句引用依赖:
from generate import utils
两种执行场景表现不一致:
- 在最外层generate目录(项目根目录)执行
pytest命令,测试可正常运行 - 在同个根目录下执行
python3 generate/write_sql.py,直接抛出模块找不到错误:
root@637572f508b9:~/generate# python3 generate/write_sql.py Traceback (most recent call last): File "/root/generate/generate/write_sql.py", line 5, in <module> from generate import utils ModuleNotFoundError: No module named 'generate'
如果将write_sql.py中的导入语句改为import utils,直接执行脚本可正常运行,但执行pytest时又会触发导入错误。
认知纠正:Python不会因为子目录包含__init__.py就自动将其加入模块搜索路径,只有存在于sys.path列表中的目录下的合法Python包,才能被导入语句识别。
根因说明
两种场景的sys.path初始化逻辑不同,是冲突的核心原因:
- 执行pytest时,pytest会自动将当前执行命令的工作目录(即项目根目录)加入
sys.path,根目录下的generate子目录是合法Python包,因此from generate import utils可以正常生效。 - 直接通过文件路径执行
python3 generate/write_sql.py时,Python会将被执行脚本自身所在的目录(即内层generate/generate目录)加入sys.path,而非命令执行时的工作目录,此时搜索路径中不存在顶层generate包,因此导入失败。 - 改为
import utils后,直接执行脚本时可以在同目录下找到utils.py,但pytest运行时测试文件的搜索起点是项目根目录,无法找到与测试文件不同层级的utils模块,因此会反向报错。
可行解决方案
方案1:以模块模式运行脚本(零代码修改,最推荐)
放弃文件路径执行方式,在项目根目录下使用-m参数以模块形式调用脚本,命令如下:
python3 -m generate.write_sql
这种执行方式下,Python会将当前工作目录(项目根目录)加入sys.path,和pytest的路径逻辑完全一致,不需要修改任何现有导入语句,完全符合Python包的导入规范,两种场景可直接兼容。
方案2:手动补全脚本搜索路径(适合临时调试)
如果必须使用文件路径方式执行脚本,可以在write_sql.py的所有业务导入语句之前,手动将项目根目录加入sys.path,示例代码:
import sys from pathlib import Path # 将当前文件的上一级目录(项目根目录)加入模块搜索路径 sys.path.append(str(Path(__file__).parent.parent.resolve())) # 原有导入语句保持不变 from generate import utils
该写法会根据脚本自身的存储位置自动定位项目根目录,不会影响pytest运行时的路径逻辑,但属于手动修改解释器搜索路径的临时方案,不建议在正式项目中长期使用。
方案3:本地可编辑模式安装包(正式项目标准方案)
在项目根目录添加基础打包配置,将当前包安装到本地Python环境,彻底消除路径依赖:
- 在项目根目录新建
pyproject.toml,写入基础配置:
[build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "generate" version = "0.0.1" packages = ["generate"]
- 在项目根目录执行可编辑安装命令:
pip install -e .
安装完成后,无论在哪个目录执行脚本、无论是跑pytest还是直接运行文件,Python都能从环境的包搜索路径中找到generate模块,不存在路径冲突问题,适合正式开发、项目分发场景使用。
内容的提问来源于stack exchange,提问作者Ruben Flam-Shepherd

