我有一个.pyx文件,我在其中定义了一些函数,例如
cdef double foo(double a) nogil:
return 3. * a
我如何在pyx文件之外对这些函数的行为进行单元测试?由于它们是cdef,我无法简单地导入它们......
答案 0 :(得分:10)
要测试cdef
功能,您需要用Cython编写测试。可以尝试使用cpdef
函数,但是在这种情况下,并非所有签名都可以使用(例如,使用诸如int *
,float *
之类的指针的签名)。
要访问cdef功能,您需要通过pxd文件“导出”它们:
#my_module.pyx:
cdef double foo(double a) nogil:
return 3. * a
#my_module.pxd:
cdef double foo(double a) nogil
现在可以在Cython测试器中导入并测试功能:
#test_my_module.pyx
cimport my_module
def test_foo():
assert my_module.foo(2.0)==6.0
print("test ok")
test_foo()
现在
>>> cythonize -i my_module.pyx
>>> cythonize -i test_my_module.pyx
>>> python -c "import test_my_module"
test ok
从那里去哪里取决于您的测试基础结构。
例如,如果您使用unittest
-module,则可以使用pyximport cythonize /加载测试模块,检查它并将所有测试用例转换为unittest
-test用例或使用{{1 }}直接在您的cython代码中(可能是更好的解决方案)。
这是unittest
的概念证明:
unittest
现在我们只需要翻译和导入纯python即可#test_my_module.pyx
cimport my_module
import unittest
class CytTester(unittest.TestCase):
def test_foo(self):
self.assertEqual(my_module.foo(2.0),6.0)
:
unittest
现在:
#test_cy.py
import pyximport; pyximport.install()
from test_my_module import *
import unittest
顺便说一句,不需要显式地对pyx模块进行cythonize->>> python -m unittest test_cy.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
自动为我们完成。
警告提示::pyximport
会在pyximport
(或其他操作系统中类似)中缓存经过cythonized处理的c文件,只要~/.pyxbld
不变该扩展程序不会重建。当test_my_module.pyx
更改并导致二进制不兼容时,可能会出现问题(幸运的是,如果出现这种情况,python会发出警告)。
必须在测试环境中确保重建pyx文件(基于时间戳)或删除缓存文件(可以使用临时目录,例如使用{{ 3}},通过tempfile.TemporaryDirectory()
)。
如果您使用虚拟环境并通过my_module
(或类似的工作流程)安装cython软件包,则需要pyximport.install(build_dir=...)
,即您的安装文件需要添加:
setup.py
答案 1 :(得分:0)
尽管前面提到过,最简单的方法是更改 cpdef 的 cdef 声明:
cpdef double foo(double a) nogil:
return 3. * a
无需更改任何其他内容。对于大多数用途,它们实际上是相同的,cpdef 的开销略高,但在继承方面表现更好,请参阅 details here:
<块引用>指令 cpdef 使该方法的两个版本可用;一 在 Cython 中使用速度快,在 Python 中使用速度慢。然后:
这比为 cdef 提供一个 python 包装器做的稍微多一点 方法:与 cdef 方法不同,cpdef 方法可以完全覆盖 Python 子类中的方法和实例属性。它增加了一点 与 cdef 方法相比的调用开销。