如果扩展程序是分发的一部分,除了运行
poetry install
之外,您不需要做任何事情-
poetry
将在可编辑安装项目的过程中就地构建扩展程序。
在其他情况下,您可以将调用
distutils
命令嵌入到测试中,作为套件设置/拆卸的一部分。我对
nose
不是很熟悉,但这里有一个简单的示例。想象一下,我有一个
fib.pyx
(这是来自Cython书籍的示例):
def fib(long n):
'''Returns the nth Fibonacci number.'''
cdef long a=0, b=1, i
for i in range(n):
a, b = a + b, a
return a
一个test_fib.py
模块,它构建fib
库并在测试成功后删除它:
from distutils.dist import Distribution
from distutils.core import Extension
from pathlib import Path
from Cython.Build import cythonize
fib_source = Path('fib.pyx')
dist = Distribution(attrs={'ext_modules': cythonize(fib_source.name)})
build_ext_cmd = dist.get_command_obj('build_ext')
build_ext_cmd.ensure_finalized()
build_ext_cmd.inplace = 1
build_ext_cmd.run()
fib_obj = Path(build_ext_cmd.get_ext_fullpath(fib_source.stem))
from fib import fib
def teardown_module():
fib_obj.unlink()
from distutils.dir_util import remove_tree
remove_tree(build_ext_cmd.build_lib)
remove_tree(build_ext_cmd.build_temp)
def test_zero():
assert fib(0) == 0
def test_ten():
assert fib(10) == 55
您可能正在定制自定义 build.py
中的 setup_kwargs
。要重用此代码,请调整 dist
初始化,例如:
from build import build
setup_kwargs = {}
build(setup_kwargs)
dist = Distribution(attrs=setup_kwargs)
...
pytest
示例
使用pytest
可以更方便地组织测试,创建名为conftest.py
的文件,并将设置/拆卸代码提取到钩子中:
from distutils.core import Extension
from distutils.dist import Distribution
from distutils.dir_util import remove_tree
from pathlib import Path
from Cython.Build import cythonize
def pytest_sessionstart(session):
fib_source = Path('fib.pyx')
dist = Distribution(attrs={'ext_modules': cythonize(fib_source.name)})
build_ext_cmd = dist.get_command_obj('build_ext')
build_ext_cmd.ensure_finalized()
build_ext_cmd.inplace = 1
build_ext_cmd.run()
session.fib_obj = Path(build_ext_cmd.get_ext_fullpath(fib_source.stem))
def pytest_sessionfinish(session):
session.fib_obj.unlink()
现在测试变得更加清晰,设置代码仅会在整个测试会话中运行一次。以上测试示例已更新:
from fib import fib
def test_zero():
assert fib(0) == 0
def test_ten():
assert fib(10) == 55
__init__.py
中使用setup_module
函数而不是在conftest.py
中使用pytest_sessionstart
时,它也可以构建二进制文件,但无法导入并出现访问被拒绝的错误。你有什么想法可能是错的吗? - Ivan Mishalkinsetup_module
执行得太晚了(或者如果您愿意,导入过早)。 我猜测nose
在收集时会导入测试模块,因此如果在测试模块级别上有import fib
,则将执行导入。 仅在此之后才会执行固定装置(顺便说一下,pytest
也是这样做的-这就是为什么我将init代码放在钩子中而不是在固定装置中的原因)。 - hoeflingsetup_module
(在__init__.py
中显式调用setup_module()
),或者将setup_module
中的代码提取出来在模块级别上运行(如我在答案中的示例)。或者另一种方式:延迟库导入,将它们从模块级别移动到测试内部。但是,这些方法都不太优雅。 - hoefling