Python 使用 pytest 正确导入
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/25827160/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Importing correctly with pytest
提问by NcAdams
I just got set up to use pytest with Python 2.6. It has worked well so far with the exception of handling "import" statements: I can't seem to get pytest to respond to imports in the same way that my program does.
我刚刚准备好在 Python 2.6 中使用 pytest。到目前为止,除了处理“导入”语句外,它运行良好:我似乎无法让 pytest 以与我的程序相同的方式响应导入。
My directory structure is as follows:
我的目录结构如下:
src/
    main.py
    util.py
    test/
        test_util.py
    geom/
        vector.py
        region.py
        test/
            test_vector.py
            test_region.py
To run, I call python main.pyfrom src/.
要运行,我python main.py从 src/调用。
In main.py, I import both vector and region with
在 main.py 中,我导入了向量和区域
from geom.region import Region
from geom.vector import Vector
In vector.py, I import region with
在vector.py中,我导入区域
from geom.region import Region
These all work fine when I run the code in a standard run. However, when I call "py.test" from src/, it consistently exits with import errors.
当我在标准运行中运行代码时,这些都可以正常工作。但是,当我从 src/ 调用“py.test”时,它始终以导入错误退出。
Some Problems and My Solution Attempts
一些问题和我的解决方案尝试
My first problem was that, when running "test/test_foo.py", py.test could not "import foo.py" directly. I solved this by using the "imp" tool. In "test_util.py":
我的第一个问题是,在运行“test/test_foo.py”时,py.test 无法直接“导入 foo.py”。我通过使用“imp”工具解决了这个问题。在“test_util.py”中:
import imp
util = imp.load_source("util", "util.py")
This works great for many files. It also seems to imply that when pytest is running "path/test/test_foo.py" to test "path/foo.py", it is based in the directory "path".
这适用于许多文件。这似乎也暗示当 pytest 运行“path/test/test_foo.py”来测试“path/foo.py”时,它基于目录“path”。
However, this fails for "test_vector.py". Pytest can find and import the vectormodule, but it cannotlocate any of vector's imports. The following imports (from "vector.py") both fail when using pytest:
但是,这对于“test_vector.py”失败了。Pytest可以找到并导入vector模块,但它无法找到任何vector的进口。以下导入(来自“vector.py”)在使用 pytest 时都失败:
from geom.region import *
from region import *
These both give errors of the form
这些都给出了形式的错误
ImportError: No module named [geom.region / region]
I don't know what to do next to solve this problem; my understanding of imports in Python is limited.
我不知道下一步该怎么做才能解决这个问题;我对 Python 导入的理解是有限的。
What is the proper way to handle imports when using pytest?
使用 pytest 时处理导入的正确方法是什么?
Edit: Extremely Hacky Solution
编辑:非常hacky的解决方案
In vector.py, I changed the import statement from
在 中vector.py,我将导入语句从
from geom.region import Region
to simply
简单地
from region import Region
This makes the import relative to the directory of "vector.py".
这使得导入相对于“vector.py”目录。
Next, in "test/test_vector.py", I add the directory of "vector.py" to the path as follows:
接下来,在“test/test_vector.py”中,我将“vector.py”的目录添加到路径中,如下所示:
import sys, os
sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/.."))
This enables Python to find "../region.py" from "geom/test/test_vector.py".
这使 Python 能够从“geom/test/test_vector.py”中找到“../region.py”。
This works, but it seems extremely problematic because I am adding a ton of new directories to the path. What I'm looking for is either
这有效,但似乎非常有问题,因为我在路径中添加了大量新目录。我正在寻找的是
1) An import strategy that is compatible with pytest, or
1)与pytest兼容的导入策略,或
2) An option in pytest that makes it compatible with my import strategy
2) pytest 中的一个选项,使其与我的导入策略兼容
So I am leaving this question open for answers of these kinds.
所以我将这个问题留给这些类型的答案。
采纳答案by Jose Varez
importlooks in the following directories to find a module:
import在以下目录中查找模块:
- The home directoryof the program. This is the directory of your root script. When you are running pytest your home directory is where it is installed (/usr/local/bin probably). No matter that you are running it from your src directory because the location of your pytest determines your home directory. That is the reason why it doesn't find the modules.
- PYTHONPATH. This is an environment variable. You can set it from the command line of your operating system. In Linux/Unix systems you can do this by executing: 'export PYTHONPATH=/your/custom/path' If you wanted Python to find your modules from the test directory you should include the src path in this variable.
- The standard librariesdirectory. This is the directory where all your libraries are installed.
- There is a less common option using a pthfile.
- 程序的主目录。这是您的根脚本的目录。当您运行 pytest 时,您的主目录就是它的安装位置(可能是 /usr/local/bin)。无论您是从 src 目录运行它,因为 pytest 的位置决定了您的主目录。这就是它找不到模块的原因。
- 蟒蛇路径。这是一个环境变量。您可以从操作系统的命令行设置它。在 Linux/Unix 系统中,您可以通过执行: ' export PYTHONPATH=/your/custom/path' 如果您希望 Python 从测试目录中找到您的模块,您应该在此变量中包含 src 路径。
- 该标准库目录。这是安装所有库的目录。
- 使用pth文件有一个不太常见的选项。
sys.pathis the result of combining the home directory, PYTHONPATHand the standard librariesdirectory. What you are doing, modifying sys.pathis correct. It is something I do regularly. You could try using PYTHONPATHif you don't like messing with sys.path
sys.path是组合主目录、PYTHONPATH和标准库目录的结果。你在做什么,修改sys.path是正确的。这是我经常做的事情。如果您不喜欢弄乱sys.path,可以尝试使用PYTHONPATH
回答by Joe Dimig
I was wondering what to do about this problem too. After reading this post, and playing around a bit, I figured out an elegant solution. I created a file called "test_setup.py" and put the following code in it:
我也想知道如何解决这个问题。在阅读了这篇文章并玩了一会儿之后,我找到了一个优雅的解决方案。我创建了一个名为“test_setup.py”的文件并将以下代码放入其中:
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
I put this file in the top-level directory (such as src).  When pytestis run from the top-level directory, it will run all test files including this one since the file is prefixed with "test".  There are no tests in the file, but it is still run since it begins with "test".
我把这个文件放在顶级目录下(比如src)。当pytest从顶级目录运行时,它将运行包括这个文件在内的所有测试文件,因为该文件以“test”为前缀。文件中没有测试,但它仍在运行,因为它以“test”开头。
The code will append the current directory name of the test_setup.py file to the system path within the test environment. This will be done only once, so there are not a bunch of things added to the path.
该代码会将 test_setup.py 文件的当前目录名称附加到测试环境中的系统路径。这只会执行一次,因此不会在路径中添加一堆东西。
Then, from within any test function, you can import modules relative to that top-level folder (such as import geom.region) and it knows where to find it since the src directory was added to the path.
然后,从任何测试函数中,您可以导入与该顶级文件夹(例如import geom.region)相关的模块,并且它知道在哪里可以找到它,因为 src 目录已添加到路径中。
If you want to run a single test file (such as test_util.py) instead of all the files, you would use:
如果您想运行单个测试文件(例如 test_util.py)而不是所有文件,您可以使用:
pytest test_setup.py test\test_util.py
This runs both the test_setup and test_util code so that the test_setup code can still be used.
这会同时运行 test_setup 和 test_util 代码,以便仍然可以使用 test_setup 代码。
回答by cjs
The issue here is that Pytest walks the filesystem to discover files that contain tests, but then needs to generate a module name that will cause importto load that file. (Remember, files are not modules.)
这里的问题是 Pytest 遍历文件系统以发现包含测试的文件,但随后需要生成一个模块名称import来加载该文件。(请记住,文件不是模块。)
Pytest comes up with this test package nameby finding the first directory at or above the level of the file that does not include an __init__.pyfile and declaring that the "basedir" for the module tree containing a module generated from this file. It then adds the basedir to sys.pathand imports using the module name that will find that file relative to the basedir.
Pytest通过查找不包含文件的文件级别或更高级别的第一个目录并声明包含从该文件生成的模块的模块树的“basedir”来得出此测试包名称__init__.py。然后将 basedir 添加到sys.path并使用模块名称导入,以找到相对于 basedir 的文件。
There are some implications of this of which you should beware:
您应该注意其中的一些含义:
- The basepath may not match your intended basepath in which case the module will have a name that doesn't match what you would normally use. E.g., what you think of as - geom.test.test_vectorwill actually be named just- test_vectorduring the Pytest run because it found no- __init__.pyin- src/geom/test/and so added that directory to- sys.path.
- You may run into module naming collisions if two files in different directories have the same name. For example, lacking - __init__.pyfiles anywhere, adding- geom/test/test_util.pywill conflict with- test/test_util.pybecause both are loaded as- import test_util.py, with both- test/and- geom/test/in the path.
- basepath 可能与您预期的 basepath 不匹配,在这种情况下,模块的名称将与您通常使用的名称不匹配。例如,你所认为的 - geom.test.test_vector将实际上只是被命名- test_vector期间Pytest运行,因为它没有发现- __init__.py在- src/geom/test/等补充目录- sys.path。
- 如果不同目录中的两个文件具有相同的名称,您可能会遇到模块命名冲突。例如,缺少 - __init__.py文件的任何地方,加入- geom/test/test_util.py与意志的冲突- test/test_util.py,因为两者都为加载- import test_util.py,既- test/和- geom/test/路径。
The system you're using here, without explicit __init__.pymodules, is having Python create implicit namespace packagesfor your directories. (A package is a module with submodules.) Ideally we'd configure Pytest with a path from which it would also generate this, but it doesn't seem to know how to do that.
您在这里使用的系统没有显式__init__.py模块,它让 Python为您的目录创建隐式命名空间包。(一个包是一个带有子模块的模块。)理想情况下,我们会用一个路径配置 Pytest,它也将生成它,但它似乎不知道如何做到这一点。
The easiest solution here is simply to add empty __init__.pyfiles to all of the subdirectories under src/; this will cause Pytest to import everything using package/module names that start with directory names under src/.
这里最简单的解决方案是简单地将空__init__.py文件添加到src/;下的所有子目录中。这将导致 Pytest 使用以目录名称开头的包/模块名称导入所有内容src/。
The question How do I Pytest a project using PEP 420 namespace packages?discusses other solutions to this.
问题如何使用 PEP 420 命名空间包对项目进行 Pytest?讨论其他解决方案。
回答by officialgupta
If you include an __init__.pyfile inside your tests directory, then when the program is looking to set a home directory it will walk 'upwards' until it finds one that does not contain an init file. In this case src/.
如果您在测试目录中包含一个__init__.py文件,那么当程序要设置主目录时,它将“向上”移动,直到找到不包含 init 文件的主目录。在这种情况下 src/.
From here you can import by saying :
从这里你可以通过说导入:
from geom.region import *
you must also make sure that you have an init file in any other subdirectories, such as the other nested test directory
您还必须确保在任何其他子目录中都有一个 init 文件,例如其他嵌套的测试目录

