运行测试触发ImportError:相对导入无已知父包问题咨询
项目结构
| .gitignore | README.md | call_test_outside.py | main.py | setup.cfg | setup.py |- src |- PropTools | __init__.py | PropTools | __init__.py | SQLiteWrapper.py | dates.py | prices.py | transactions.py | config | __init__.py | settings.py | tests | __init__.py | transactions_tests.py
问题场景
直接在tests目录下运行transactions_tests.py时,出现以下导入错误:
from ..PropTools.transactions import TransactionType, InstrumentType, get_transactions ImportError: attempted relative import with no known parent package
但从项目根目录通过call_test_outside.py调用该测试文件则可正常运行。
相关代码:transactions_tests.py(tests目录内):
from ..PropTools.transactions import TransactionType, InstrumentType, get_transactions print(get_transactions)
call_test_outside.py(根目录):
from src.PropTools.tests import transactions_tests print(transactions_tests.get_transactions)
解决方案
1. 从根目录以模块方式运行测试
直接在项目根目录执行以下命令,Python会自动识别完整的包结构,支持相对导入:
python -m src.PropTools.tests.transactions_tests
原理:-m参数会将根目录加入Python的模块搜索路径(sys.path),此时src.PropTools作为一个完整的包存在,相对导入的..能正确找到父级包。
2. 改用绝对导入
将测试文件中的相对导入改为基于项目根目录的绝对导入:
# 替换原来的相对导入 from src.PropTools.PropTools.transactions import TransactionType, InstrumentType, get_transactions
这种方式不需要依赖特定的运行目录,只要根目录在sys.path中即可。
3. 安装开发模式包(推荐)
通过setup.py将包安装为可编辑模式,这样包会被链接到Python环境中,修改代码实时生效,且任何目录都能正确导入包:
# 在项目根目录执行 pip install -e .
之后测试文件可以改用基于包名的绝对导入:
from PropTools.PropTools.transactions import TransactionType, InstrumentType, get_transactions
此时无论在哪运行测试文件,都不会出现导入错误。
4. 使用专业测试框架(如pytest)
直接在项目根目录使用pytest运行测试,框架会自动处理路径问题:
# 运行单个测试文件 pytest src/PropTools/tests/transactions_tests.py -v # 运行整个tests目录下的所有测试 pytest src/PropTools/tests/ -v
pytest会自动将项目根目录加入模块搜索路径,无需手动调整导入或运行方式,同时还能提供更丰富的测试功能。
报错原因说明
直接在tests目录下运行transactions_tests.py时,Python会将tests目录视为顶级模块,此时相对导入中的..无法找到对应的父包(因为PropTools不在当前模块的搜索路径中)。而从根目录运行时,根目录被加入sys.path,src.PropTools作为完整的包结构被识别,相对导入才能正常工作。
内容的提问来源于stack exchange,提问作者Oliver Mohr Bonometti

