如何设置 tox 环境的 sys.path?

5

我有一个需要编译的 Python 项目。使用 tox 成功生成了 sdistbdist_wheel,但是当运行测试时,始终导入源代码模块,导致测试失败(因为那里没有二进制文件)。这是因为项目的根目录始终是 sys.path 上的第一个文件夹,我无法弄清楚为什么会出现这种情况。

project
├── foo
│   ├── __init__.py
│   └── bar.c <-- turns into bar.so in bdist_wheel
├── tests
│   ├── __init__.py
│   └── test_bar.py
├── tox.ini
└── setup.py

tox.ini

[tox]
envlist =
    py37

[testenv]
changedir = {toxinidir}/tests
setenv =
    PYTHONPATH = {envsitepackagesdir}
commands = {envpython} -m pytest --cov=bar --cov-report=html

请注意,我设置了changedir以确保项目根目录不是工作目录。我还将PYTHONPATH设置为仅限于tox环境的site-packages。我还强制运行tox环境的Python。
尽管如此,在测试运行期间,如果我打印出sys.path,我会看到:
[
  '/home/me/foo', # Bad, bad, bad
  '/home/me/foo/tests', # Good, this is the CWD
  '/home/me/foo/.tox/py37/lib/python3.7/site-packages', # Good
  '/home/me/foo/.tox/py37/lib/python37.zip', # Umm, ok, this doesn't exist
  '/home/me/foo/.tox/py37/lib/python3.7', # Good
  '/home/me/foo/.tox/py37/lib/python3.7/lib-dynload', # Probably good
  '/home/me/anaconda/envs/foo/lib/python3.7',  # Bad, why is this here?
]

最大的问题出在/home/me/foo,这会导致源模块foo被加载,而不是安装在tox中的模块。
另一个问题路径是/home/me/anaconda/envs/foo/lib/python3.7。如果在环境中找不到某些东西,我希望tox不要回退到系统解释器。
那么,tox是如何选择这些路径的?我该如何控制它以使其表现更好?

在 tox 输出中有任何警告或异常吗?virtualenv 是否存在?https://github.com/tox-dev/tox/issues/273 和 https://dev59.com/I10a5IYBdhLWcg3wPWlN 暗示 tox 在与 Anaconda 配合使用时可能存在问题。 - ivan_pozdeev
tox没有任何警告,是的,我正在使用Anaconda。 - drhagen
1个回答

5
这不是由tox引起的,而是由pytest引起的。正如pytest文档所描述的那样,已知pytest与像tox这样在隔离环境中安装分发的工具之间存在严重的相互作用。
原因是pytest修改了sys.path,以便将包含测试包(带有__init__.py的测试文件夹)的所有文件夹添加到其中。因为foo/tests目录包含一个__init__.py文件,所以pytest将foo/添加到sys.path中,以便可以使用普通的import语句导入tests包。

即使删除__init__.py文件也可能不足够。foo目录下以python -m pytest的方式运行pytest将导致将foo目录添加到sys.path中。使用pytest命令行工具运行pytest将避免这种情况的发生。

推荐的解决方案是将所有源代码移动到专用的src目录中。这确保了即使项目根目录位于sys.path上,源包也永远不会在其中。

推荐的项目结构

project
├── src
│   └── foo
│       ├── __init__.py
│       └── bar.c
├── tests
│   ├── __init__.py
│   └── test_bar.py
├── tox.ini
└── setup.py

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接