Transitive import error: ModuleNotFoundError: No module named '...'

Transitive import error: ModuleNotFoundError: No module named '...'

我现在很困惑。这是项目树:

project
- source
- - lib
- - - __init__.py
- - - utils.py
- - - stats.py
- test
- - lib
- - - test_stats.py

stats.pyimport utils,如果自己执行 stats.py,它确实有效。现在 test_stats.pyimport lib.stats 但如果在 project 目录中作为 PYTHONPATH=source pytest 执行会导致 ModuleNotFoundError: No module named 'utils' 错误:

==================================== ERRORS ====================================
___________________ ERROR collecting test/lib/test_stats.py ___________________
ImportError while importing test module '/lib/test_stats.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test/lib/test_stats.py:40: in <module>
    import lib.stats
source/lib/__init__.py:42: in <module>
    from .stats import Stats
source/lib/stats.py:40: in <module>
    import utils
E   ModuleNotFoundError: No module named 'utils'
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.19 seconds ============================

这是怎么回事,如何正确执行具有这种目录结构的测试?

P.S. 我知道(开发模式)pip install ...tox 技巧(用于模块搜索路径),但是我想了解它们是否绝对有必要让这件事继续下去,或者我已经假设这个简单的设置有问题。

import utils in Python 3 是绝对导入,Python 查找模块 utils in sys.path.

当您 运行 stats.py 作为脚本时 Python 添加目录 project/source/lib/sys.path。所以脚本中的 import utils 有效。

但是当你 运行 test_stats.py Python 不添加 project/source/lib/sys.path。所以 import utils 不起作用。

克服这个问题的一种方法是使用相对导入:from . import utils。在 stats.py 中表示:不要搜索 sys.path,从与 stats.py 相同的目录中导入模块 utils。但是你失去了 运行 stats.py 作为脚本的能力。因此,将主要代码从 stats.py 移动到 lib/.

之外的单独脚本

一个略有不同的解决方案是将主代码从 stats.py 移动到模块 __main__.py 并使用 python -m lib 执行模块(project/source/ 必须在 sys.path).