python3 中 pytest 的布局和导入
Layout and importing for pytest in python3
我在从我的 pytest 函数导入模块时遇到问题。我知道这方面有一百万个问题,但我已经阅读了很多,但我仍然无法理解。
$ tree
.
└── code
├── eight_puzzle.py
├── missionaries_and_cannibals.py
├── node.py
├── search.py
└── test
├── test_eight_puzzle.py
└── test_search.py
2 directories, 6 files
$
$ grep import code/test/test_search.py
import sys
import pytest
import code.search
$
$ pytest
...
ImportError while importing test module '~/Documents/code/test/test_search.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
code/test/test_search.py:14: in <module>
import code.search
E ModuleNotFoundError: No module named 'code.search'; 'code' is not a package
...
我希望它能起作用。 'code'是一个包吧? Python 3 中的包是任何包含 .py 文件的目录。
我也尝试过使用相对导入 - from .. import search
- 我得到以下结果。
ImportError while importing test module '~/Documents/code/test/test_search.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
code/test/test_search.py:14: in <module>
from .. import search
E ImportError: attempted relative import with no known parent package
我也试过修改 sys.path,如图 ,指定我的 PYTHONPATH,并在代码和测试中添加 init.py 文件。
我可以在不使用 setuptools 的情况下使这个导入工作吗?这只是为了试验,所以我不想处理开销。
注意我正在使用 conda 可能也很重要,因为当我使用 python 2 pip 安装版本的 pytest 和 init[ 时它似乎可以工作=29=.py 文件。
关于没有 __init__.py
个文件的目录的一些注意事项:
隐式命名空间包
虽然没有 __init__.py
的目录是 Python 3 中的有效导入源,但它不是常规包,而是隐式命名空间包(请参阅 PEP 420 了解细节)。在其他属性中,隐式命名空间包在导入时是第二个 class 公民,这意味着当 Python 在 sys.path
中有两个同名的包时,一个是常规包,另一个是隐式命名空间包,无论哪个包先出现,都会优先使用常规包。自己查一下:
$ mkdir -p implicit_namespace/mypkg
$ echo -e "def spam():\n print('spam from implicit namespace package')" > implicit_namespace/mypkg/mymod.py
$ mkdir -p regular/mypkg
$ touch regular/mypkg/__init__.py
$ echo -e "def spam():\n print('spam from regular package')" > regular/mypkg/mymod.py
$ PYTHONPATH=implicit_namespace:regular python3 -c "from mypkg.mymod import spam; spam()"
这将打印 spam from regular package
:虽然 implicit_namespace
在 sys.path
中排在第一位,但 regular
中的 mypkg.mymod
是导入的,因为 regular/mypkg
是普通包裹。
现在你知道了,因为你的包 code
是一个隐式命名空间包,所以 Python 如果遇到一个 code
会更喜欢定期导入 code
。不幸的是,there is a module code
in the stdlib,所以这实际上是一个 "reverse name shadowing" 问题:您有一个与来自 stdlib 的对象同名的导入对象,但它没有隐藏 stdlib 导入,而是隐藏了您的导入。
因此,为了使布局可用,您需要做两件事:
- 给
code
目录一个唯一的名称(对于这个答案的例子,让它成为 mycode
)
- 在那之后,当 运行 从项目根目录进行测试时,您仍然需要修复
sys.path
,因为它本身不在 sys.path
中。你有一些可能性:
- 添加一个空的
conftest.py
文件到根目录(除了 mycode
目录)。这将指示 pytest
将根目录添加到 sys.path
(有关解释,请参阅 here)。您现在可以像往常一样 运行 pytest
导入将得到解决;
- 运行 通过
python -m pytest
的测试 - 调用解释器直接将当前目录添加到 sys.path
;
- 通过
PYTHONPATH
环境变量将当前目录添加到 sys.path
,例如运行 PYTHONPATH=. pytest
.
我在从我的 pytest 函数导入模块时遇到问题。我知道这方面有一百万个问题,但我已经阅读了很多,但我仍然无法理解。
$ tree
.
└── code
├── eight_puzzle.py
├── missionaries_and_cannibals.py
├── node.py
├── search.py
└── test
├── test_eight_puzzle.py
└── test_search.py
2 directories, 6 files
$
$ grep import code/test/test_search.py
import sys
import pytest
import code.search
$
$ pytest
...
ImportError while importing test module '~/Documents/code/test/test_search.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
code/test/test_search.py:14: in <module>
import code.search
E ModuleNotFoundError: No module named 'code.search'; 'code' is not a package
...
我希望它能起作用。 'code'是一个包吧? Python 3 中的包是任何包含 .py 文件的目录。
我也尝试过使用相对导入 - from .. import search
- 我得到以下结果。
ImportError while importing test module '~/Documents/code/test/test_search.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
code/test/test_search.py:14: in <module>
from .. import search
E ImportError: attempted relative import with no known parent package
我也试过修改 sys.path,如图
我可以在不使用 setuptools 的情况下使这个导入工作吗?这只是为了试验,所以我不想处理开销。
注意我正在使用 conda 可能也很重要,因为当我使用 python 2 pip 安装版本的 pytest 和 init[ 时它似乎可以工作=29=.py 文件。
关于没有 __init__.py
个文件的目录的一些注意事项:
隐式命名空间包
虽然没有 __init__.py
的目录是 Python 3 中的有效导入源,但它不是常规包,而是隐式命名空间包(请参阅 PEP 420 了解细节)。在其他属性中,隐式命名空间包在导入时是第二个 class 公民,这意味着当 Python 在 sys.path
中有两个同名的包时,一个是常规包,另一个是隐式命名空间包,无论哪个包先出现,都会优先使用常规包。自己查一下:
$ mkdir -p implicit_namespace/mypkg
$ echo -e "def spam():\n print('spam from implicit namespace package')" > implicit_namespace/mypkg/mymod.py
$ mkdir -p regular/mypkg
$ touch regular/mypkg/__init__.py
$ echo -e "def spam():\n print('spam from regular package')" > regular/mypkg/mymod.py
$ PYTHONPATH=implicit_namespace:regular python3 -c "from mypkg.mymod import spam; spam()"
这将打印 spam from regular package
:虽然 implicit_namespace
在 sys.path
中排在第一位,但 regular
中的 mypkg.mymod
是导入的,因为 regular/mypkg
是普通包裹。
现在你知道了,因为你的包 code
是一个隐式命名空间包,所以 Python 如果遇到一个 code
会更喜欢定期导入 code
。不幸的是,there is a module code
in the stdlib,所以这实际上是一个 "reverse name shadowing" 问题:您有一个与来自 stdlib 的对象同名的导入对象,但它没有隐藏 stdlib 导入,而是隐藏了您的导入。
因此,为了使布局可用,您需要做两件事:
- 给
code
目录一个唯一的名称(对于这个答案的例子,让它成为mycode
) - 在那之后,当 运行 从项目根目录进行测试时,您仍然需要修复
sys.path
,因为它本身不在sys.path
中。你有一些可能性:- 添加一个空的
conftest.py
文件到根目录(除了mycode
目录)。这将指示pytest
将根目录添加到sys.path
(有关解释,请参阅 here)。您现在可以像往常一样 运行pytest
导入将得到解决; - 运行 通过
python -m pytest
的测试 - 调用解释器直接将当前目录添加到sys.path
; - 通过
PYTHONPATH
环境变量将当前目录添加到sys.path
,例如运行PYTHONPATH=. pytest
.
- 添加一个空的