Компиляция файлов pyx с зависимостями в разных пакетах

У меня возникают проблемы с компиляцией типов cdef-ed в разных пакетах, и я не мог найти объяснения в документах cython.

У меня есть setup.py в корне моего дерева src python:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
   cmdclass = {'build_ext': build_ext},
   ext_modules = [ 
      Extension("flink.pytk.defs.FragIdx", 
         sources = ["flink/pytk/defs/FragIdx.pyx"]),
      Extension("flink.pytk.fragments.STK_idx", 
         sources = ["flink/pytk/fragments/STK_idx.pyx"])
      ]   
)

FragIdx - это тип cdef-ed, определенный в flink/pytk/defs/FragIdx.pyx:

cdef class FragIdx:
   cdef public FragIdx parent
   cdef public FragIdx root
   cdef public tuple label
   ...

И STK_idx является расширением FragIdx, определенным в flink/pytk/fragments/STK_idx.pyx:

from flink.pytk.defs.FragIdx import FragIdx
cdef class STK_idx(FragIdx):
   ...

Когда я пытаюсь скомпилировать, используя setup.py, указанный в начале сообщения, FragIdx скомпилирован правильно, но когда дело доходит до STK_idx, я получаю следующее сообщение об ошибке:

flink/pytk/fragments/STK_idx.pyx:5:5: 'FragIdx' is not a type name

Обратите внимание, что корневой каталог моего исходного дерева указан в $PYTHONPATH.

Я был бы очень признателен, если бы кто-нибудь мог пролить свет на это, спасибо большое!

Даниэла

Ответ 1

О, ну, для тех, у кого есть аналогичная проблема, похоже, что я нашел ответ.

Я ожидал, что python автоматически сканирует символы, скомпилированные в общую библиотеку FragIdx.so, но похоже, что эта информация должна быть явно указана как файл .pxd(который становится C файлом C после запуска Cython).

В процессе есть два этапа:

  • Создание файла определения (.pxd) для суперкласса;
  • Импортирование определения суперкласса через cimport (в отличие от import) в модуле подкласса.

Итак, чтобы сделать его более общим.

Предположим, что вы определили тип cdef-ed A в модуле pkg1.mod1. Затем вы cdef тип B в pkg2.mod2, который подклассы A.

Структура вашего каталога будет выглядеть примерно так:

pkg1/
  mod1.pyx
  mod1.pxd
pkg2/
  mod2.pyx
  mod2.pxd

В pkg1/mod1.pxd вы бы сказали:

cdef class A:
  cdef int a
  cdef int b

И в pkg1/mod1.pyx вы предоставите методы своего класса. В pkg2/mod2.pxd у вас будет:

from pkg1.mod1 cimport A  #note "cimport"!!
cdef class B(A):
  cdef ... # your attributes here

И снова, в pkg2/mod2.pyx вам придется cimport символ A снова:

from pkg1.mod1 cimport A #note "cimport"!!
cdef class B(A):
  ... # your methods here

Интересно, что если вы просто хотите использовать A в своем коде на python, а не использовать его для определения подтипа, файл определений mod1.pxd не нужен. Это связано с тем, что при создании типа расширения вам нужны определения, которые будут доступны для компилятора C, в то время как у вас нет этой проблемы при запуске кода на Python, но поскольку это не очень интуитивно, возможно, важно указать это из.

Эта информация действительно доступна в Документах Cython, хотя, возможно, она может быть немного более явной.

Надеемся, что эта информация может сэкономить кому-то.