use*_*612 7 python relative-path setuptools setup.py pytest
我的目录布局如下
project\
project\setup.py
project\scripts\foo.py
project\scripts\bar.py
project\scripts\__init__.py
project\tests\test_foo.py
project\tests\__init__.py
Run Code Online (Sandbox Code Playgroud)
我的测试文件如下所示
project\tests\test_fo.py
from ..scripts import foo
def test_one():
assert 0
Run Code Online (Sandbox Code Playgroud)
当我这样做时,我收到以下错误
cd C:\project
C:\virtualenvs\test_env\Scripts\activate
python setup.py install
python setup.py test
Run Code Online (Sandbox Code Playgroud)
E ValueError:尝试在顶级包之外进行相对导入
我究竟做错了什么?这是我的 setup.py
setup(
name = 'project',
setup_requires=['pytest-runner'],
tests_require=['pytest'],
packages = ["scripts","tests"],
package_data={
'scripts': ['*.py'],
'tests': ['*.py'],
},
)
Run Code Online (Sandbox Code Playgroud)
相对导入仅在包内有效。scripts可能是一个包,所以是tests,但project目录不是(也不应该是)。这使得scripts和tests 顶级包。您不能使用相对语法引用其他顶级名称。
此外,测试不与tests包一起运行;测试运行器导入test_foo模块,而不是tests.test_foo模块,因此就 Python 而言,它 test_foo是一个顶级模块。
scripts是顶级名称,直接使用即可。但是,您必须将project目录添加到sys.path。您可以在test_foo.py文件顶部使用:
import os
import sys
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir))
sys.path.insert(0, PROJECT_DIR)
Run Code Online (Sandbox Code Playgroud)
然后从scripts绝对路径导入:
from scripts import foo
Run Code Online (Sandbox Code Playgroud)
但是请注意,当您运行时python setup.py,sys.path无论如何都会将您当前的工作目录添加到其中,因此scripts可以直接使用而无需摆弄sys.path.
此外,pytest已经为您完成了工作;对于任何给定的测试文件,它将确保第一个没有 __init__.py文件的父目录在sys.path. 在您的情况下,这是project/目录,因此scripts可以直接从中导入。请参阅良好做法:
如果
pytest在递归到文件系统时找到“a/b/test_module.py”测试文件,它会按如下方式确定导入名称:
- 确定
basedir:这是第一个不包含__init__.py. 例如,如果 a 和 b 都包含一个__init__.py文件,则 a 的父目录将成为 basedir。- 执行
sys.path.insert(0, basedir)以使测试模块可在完全限定的导入名称下导入。import a.b.test_module其中路径是通过将路径分隔符 / 转换为“.”来确定的。人物。这意味着您必须遵循将目录和文件名直接映射到导入名称的约定。
请注意,为了在使用pytest时实际运行测试setup.py test,您需要在setup.cfg文件中注册一个别名(project/如果没有,请在其中创建):
[aliases]
test = pytest
Run Code Online (Sandbox Code Playgroud)