首页 文章

使用pytest正确导入

提问于
浏览
23

我刚刚设置为使用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 回答

  • 6

    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 directoryPYTHONPATHstandard libraries 目录的结果 . 你正在做什么,修改 sys.path 是正确的 . 这是我经常做的事情 . 你可以尝试使用 PYTHONPATH ,如果你不喜欢弄乱 sys.path

  • 5

    这里的问题是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/ 下的目录名开头的包/模块名称导入所有内容 .

  • 9

    我想知道该怎么办这个问题 . 看了这篇文章,并玩了一下,我想出了一个优雅的解决方案 . 我创建了一个名为“test_setup.py”的文件,并将以下代码放入其中:

    import sys, os
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    

    我把这个文件放在顶级目录(例如src)中 . 当从顶级目录运行 pytest 时,它将运行包括此文件在内的所有测试文件,因为该文件的前缀为"test" . 文件中没有测试,但它仍然运行,因为它以"test"开头 .

    代码将test_setup.py文件的当前目录名附加到测试环境中的系统路径 . 这只会进行一次,因此路径中没有添加任何东西 .

    然后,从任何测试函数中,您可以导入相对于该顶级文件夹的模块(例如 import geom.region ),并且它知道从src目录添加到路径后的位置 .

    如果要运行单个测试文件(例如test_util.py)而不是所有文件,则可以使用:

    pytest test_setup.py test\test_util.py
    

    这将运行test_setup和test_util代码,以便仍可以使用test_setup代码 .

相关问题