python3中pytest的布局和导入

onl*_*egg 8 python pytest python-3.x conda

我无法从pytest函数导入模块。我知道有100万个问题,但是我已经读了一堆,但仍然难以理解。

$ 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
...
Run Code Online (Sandbox Code Playgroud)

我希望那能奏效。“代码”是一个包,对吗?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
Run Code Online (Sandbox Code Playgroud)

我也尝试修改的sys.path如图所示这里,我指定PYTHONPATH,并添加初始化代码和测试.py文件。

我可以在不使用setuptools之类的情况下使导入工作吗?这只是为了进行实验,因此我宁愿不处理开销。

可能还需要注意的是我正在使用conda,因为当我将python 2 pip安装的pytest与init .py文件一起使用时,它似乎可以正常工作。

hoe*_*ing 5

__init__.py首先关于没有文件的目录的一些注意事项:

隐式命名空间包

尽管没有 的目录__init__.py在 Python 3 中是有效的导入源,但它不是常规包,而是隐式命名空间包(有关详细信息,请参阅PEP 420)。在其他属性中,隐式命名空间包在导入时是二等公民,这意味着当 Python 有两个同名的包 in 时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()"
Run Code Online (Sandbox Code Playgroud)

这将打印spam from regular package:虽然implicit_namespace是第一个 in sys.path,但mypkg.mymodfromregular是导入的,因为它regular/mypkg是一个常规包。


现在你知道了,因为你的包code是一个隐式命名空间包,code如果遇到一个,Python 会更喜欢你的常规导入。对您来说不幸的code, stdlib 中有一个模块,所以它实际上是一个“反向名称阴影”问题:您有一个与 stdlib 同名的导入对象,但它不是隐藏 stdlib 导入,而是隐藏您的。

因此,您需要做两件事才能使您的布局可用:

  1. code目录一个唯一的名字(让它成为mycode这个答案的例子)
  2. 在那之后,您仍然需要修复sys.path从项目根目录运行测试时的问题,因为它sys.path本身并不存在。你有一些可能性:
    • 将一个空conftest.py文件添加到根目录(除了mycode目录)。这将指示pytest将根目录添加到sys.path(有关解释,请参见此处)。您现在可以pytest照常运行,导入将得到解决;
    • 通过python -m pytest调用解释器直接将当前目录添加到sys.path
    • sys.path通过环境PYTHONPATH变量将当前目录添加到,例如 run PYTHONPATH=. pytest