Python 如何测试功能cdef';在赛昂的d?

Python 如何测试功能cdef';在赛昂的d?,python,unit-testing,cython,Python,Unit Testing,Cython,我有一个.pyx文件,其中定义了一些函数,例如 cdef double foo(double a) nogil: return 3. * a 如何在pyx文件之外对这些函数的行为进行单元测试?由于它们是cdef,我无法简单地导入它们…要测试cdef-功能性,您需要用Cython编写测试。可以尝试使用cpdef-函数,但是在这种情况下不能使用所有签名(例如使用int*,float*等指针的签名) 要访问cdef函数,您需要通过pxd文件“导出”它们(也可以对以下对象执行此操作): 现在可

我有一个.pyx文件,其中定义了一些函数,例如

cdef double foo(double a) nogil:
    return 3. * a

如何在pyx文件之外对这些函数的行为进行单元测试?由于它们是cdef,我无法简单地导入它们…

要测试
cdef
-功能性,您需要用Cython编写测试。可以尝试使用
cpdef
-函数,但是在这种情况下不能使用所有签名(例如使用
int*
float*
等指针的签名)

要访问cdef函数,您需要通过pxd文件“导出”它们(也可以对以下对象执行此操作):

现在可以在Cython测试仪中对功能进行cimported和测试:

#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
-测试用例,或者直接在cython代码中使用
unittest
(可能是更好的解决方案)

以下是
unittest
的概念证明:

#test_my_module.pyx
cimport my_module
import unittest

class CyTester(unittest.TestCase): 
    def test_foo(self):
        self.assertEqual(my_module.foo(2.0),6.0)
现在,我们只需要在纯python中翻译和导入它,就可以
unittest
it:

#test_cy.py 
import pyximport;
pyximport.install(setup_args = {"script_args" : ["--force"]},
                  language_level=3)

# now drag CyTester into the global namespace, 
# so tests can be discovered by unittest
from test_my_module import *
现在:

>>> python -m unittest test_cy.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
顺便说一句,没有必要显式地对pyx模块进行Cythonization-
pyximport
会自动为我们实现

警告一句:
pyximport
~/.pyxbld
(或其他操作系统上的类似文件)中缓存经过cythonized的c文件,只要
测试我的模块.pyx
没有更改,扩展名就不会重新生成,即使它的依赖项在哪里更改了。当
my_module
发生更改并导致二进制不兼容时(幸运的是,如果是这种情况,python会发出警告),这可能是一个问题

通过传递
setup\u args={“script\u args”:[“--force”]}
我们强制重建

另一个选项是删除缓存文件(可以使用临时目录,例如通过创建的),这样做的好处是保持系统干净

需要明确的
语言级别
()以防止出现警告


如果您使用虚拟环境并通过
setup.py
(或类似工作流)安装cython软件包,则您需要,即您的安装文件需要增加以下内容:

from setuptools import setup, find_packages, Extension
# usual stuff for cython-modules here
...

kwargs = {
      # usual stuff for cython-modules here
      ...

      #ensure pxd-files:
      'package_data' : { 'my_module': ['*.pxd']},
      'include_package_data' : True,
      'zip_safe' : False  #needed because setuptools are used
}

setup(**kwargs)

尽管前面提到过,但最简单的方法是更改cpdefcdef声明:

cpdef double foo(double a) nogil:
    return 3. * a
不需要改变任何其他东西。在大多数情况下,它们实际上是相同的,cpdef的开销稍高,但在继承方面表现更好,请参见:

指令cpdef提供了两种版本的方法;一 Cython的使用速度快,Python的使用速度慢。然后:

这比为cdef提供python包装稍多 方法:与cdef方法不同,cpdef方法完全可由 Python子类中的方法和实例属性。它增加了一点 与cdef方法相比的调用开销


写一个调用
foo
def
cpdef
如何?有没有一个不涉及cpdef的好解决方案?如果我的cdef函数有以下签名:cdef foo(double*a)?那么你可以使用你的答案,这仍然是正确的:)
cpdef double foo(double a) nogil:
    return 3. * a