我刚刚设置为使用python与Python 2.6 . 到目前为止,除了处理“导入”语句之外,它运行良好:我似乎无法以与我的程序相同的方式响应导入 .
我的目录结构如下:
src/
main.py
util.py
test/
test_util.py
geom/
vector.py
region.py
test/
test_vector.py
test_region.py
要运行,我从src /调用 python main.py
.
在main.py中,我导入了vector和region
from geom.region import Region
from geom.vector import Vector
在vector.py中,我导入了区域
from geom.region import Region
当我在标准运行中运行代码时,这些都可以正常工作 . 但是,当我从src /调用“py.test”时,它会一直退出导入错误 .
一些问题和我的解决方案尝试
我的第一个问题是,当运行“test / test_foo.py”时,py.test无法直接“导入foo.py” . 我通过使用“imp”工具解决了这个问题 . 在“test_util.py”中:
import imp
util = imp.load_source("util", "util.py")
这适用于许多文件 . 它似乎也暗示当pytest运行“path / test / test_foo.py”来测试“path / foo.py”时,它基于目录“path” .
但是,"test_vector.py"失败了 . Pytest可以找到并导入 vector
模块,但它 cannot 找到任何 vector
的导入 . 使用pytest时,以下导入(来自"vector.py")都会失败:
from geom.region import *
from region import *
这些都给出了表格的错误
ImportError: No module named [geom.region / region]
我不知道接下来要做什么来解决这个问题;我对Python中导入的理解是有限的 .
What is the proper way to handle imports when using pytest?
编辑:非常黑客的解决方案
在 vector.py
中,我更改了import语句
from geom.region import Region
简单地说
from region import Region
这使得导入相对于“vector.py”目录 .
接下来,在“test / test_vector.py”中,我将“vector.py”目录添加到路径中,如下所示:
import sys, os
sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/.."))
这使Python能够从“geom / test / test_vector.py”中找到“../region.py” .
这是有效的,但它似乎非常有问题,因为我在路径中添加了大量的新目录 . 我正在寻找的是
1)与pytest兼容的导入策略,或
2)pytest中的一个选项,使其与我的导入策略兼容
所以我将这个问题留给这些问题的答案 .
3 回答
import 在以下目录中查找模块:
该程序的 home directory . 这是根脚本的目录 . 当您运行pytest时,您的主目录是安装它的位置(可能是/ usr / local / bin) . 无论你是从src目录运行它,因为pytest的位置决定了你的主目录 . 这就是它找不到模块的原因 .
PYTHONPATH . 这是一个环境变量 . 您可以从操作系统的命令行进行设置 . 在Linux / Unix系统中,您可以通过执行以下命令来执行此操作:'export PYTHONPATH = / your / custom / path'如果您希望Python从测试目录中找到您的模块,则应在此变量中包含src路径 .
standard libraries 目录 . 这是安装所有库的目录 .
使用 pth 文件有一个不太常见的选项 .
sys.path 是组合 home directory , PYTHONPATH 和 standard libraries 目录的结果 . 你正在做什么,修改 sys.path 是正确的 . 这是我经常做的事情 . 你可以尝试使用 PYTHONPATH ,如果你不喜欢弄乱 sys.path
这里的问题是Pytest遍历文件系统以发现包含测试的文件,但随后需要生成一个模块名称,该名称将导致
import
加载该文件 . (记住,files are not modules . )Pytest通过查找不包含
__init__.py
文件的文件级别或更高级别的第一个目录,并声明包含从该文件生成的模块的模块树的"basedir"来提供此test package name . 然后它将basedir添加到sys.path
的前面,并使用将在相对于basedir找到该文件的模块名称导入 .您应该注意这一点的一些含义:
基本路径可能与您预期的基本路径不匹配,在这种情况下,模块的名称与您通常使用的名称不匹配 . 例如,您认为
geom.test.test_vector
在Pytest运行期间实际上只会被命名为test_vector
,因为它在src/geom/test/
中找不到__init__.py
,因此将其添加到路径中 .如果不同目录中的两个文件具有相同的名称,则可能会遇到模块命名冲突 . 例如,在任何地方缺少
__init__.py
文件,添加geom/test/test_util.py
将与test/test_util.py
冲突,因为两者都被加载为import test_util.py
,路径中包含test/
和geom/test/
.你在这里使用的系统,没有明确的
__init__.py
模块,正在为你的目录创建implicit namespace packages . (包是一个带有子模块的模块 . )理想情况下,我们似乎知道如何做到这一点 .这里最简单的解决方案就是将空
__init__.py
文件添加到src/
下的所有子目录中;这将导致Pytest使用以src/
下的目录名开头的包/模块名称导入所有内容 .我想知道该怎么办这个问题 . 看了这篇文章,并玩了一下,我想出了一个优雅的解决方案 . 我创建了一个名为“test_setup.py”的文件,并将以下代码放入其中:
我把这个文件放在顶级目录(例如src)中 . 当从顶级目录运行
pytest
时,它将运行包括此文件在内的所有测试文件,因为该文件的前缀为"test" . 文件中没有测试,但它仍然运行,因为它以"test"开头 .代码将test_setup.py文件的当前目录名附加到测试环境中的系统路径 . 这只会进行一次,因此路径中没有添加任何东西 .
然后,从任何测试函数中,您可以导入相对于该顶级文件夹的模块(例如
import geom.region
),并且它知道从src目录添加到路径后的位置 .如果要运行单个测试文件(例如test_util.py)而不是所有文件,则可以使用:
这将运行test_setup和test_util代码,以便仍可以使用test_setup代码 .