Как проверить функции cdef'd в Cython?

У меня есть.pyx файл, в котором я определяю некоторые функции, например

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

Как я могу тестировать поведение таких функций вне файла pyx? Поскольку они cdef'd, я не могу просто импортировать их...

Ответ 1

Для тестирования cdef -fuctionality вам нужно написать свои тесты на Cython. Можно попытаться использовать cpdef -functions, однако не все подписи могут быть использованы в этом случае (например, подписи с использованием указателей, таких как int *, float * и т.д.).

Для доступа к cdef -functions вам необходимо "экспортировать" их через pxd файл:

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

#my_module.pxd:
cdef double foo(double a) nogil

Теперь функциональность может быть импортирована и протестирована на Cython -test er:

#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, чтобы цитонизировать/загрузить тест -module, проверить его и преобразовать все тестовые случаи в unittest -test или использовать unittest непосредственно в вашем цитоне. код (возможно, лучшее решение).

Вот подтверждение концепции для unittest:

#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)

Теперь нам нужно только перевести и импортировать его на чистом Python, чтобы иметь возможность unittest:

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

import unittest

А теперь:

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

OK

Кстати, нет необходимости явно цифонизировать pyx -module s - pyximport делает это для нас автоматически.

Предупреждение: pyximport кэширует c файлы, содержащие цитаты, в ~/.pyxbld (или аналогичные в других ОС) и до тех пор, пока test_my_module.pyx не изменился, расширение не перестраивается, даже если его зависимости были изменены, Это может быть проблемой (среди прочих), когда изменяется my_module и это приводит к двоичной несовместимости (к счастью, python предупреждает, если это так).

Проходя setup_args = {"script_args" : ["--force"]}, мы форсируем перестройку.

Другой вариант - удалить кэшированные файлы (можно использовать временный каталог, например, созданный с помощью tempfile.TemporaryDirectory(), через pyximport.install(build_dir=...)), что позволяет сохранить система чистая.

Явное language_level (что такое language_level?) необходимо для предотвращения предупреждений.


Если вы используете виртуальную среду и устанавливаете свой cython-пакет с помощью setup.py (или аналогичного рабочего процесса), вам необходимо , чтобы убедиться, что файлы *.pxd также включены в установочный файл, то есть ваш установочный файл должен быть дополнен:

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)