Cython: объявить PyCapsule_Destructor в файле pyx - PullRequest
1 голос
/ 13 января 2020

Я не знаю python и пытаюсь обернуть существующую библиотеку C, которая предоставляет 200 функций инициализации для некоторых объектов и 200 деструкторов с помощью PyCapsule. Поэтому моя идея - вернуть PyCapsule из упаковщиков функций init и забыть о деструкторах, которые должны вызываться автоматически.

Согласно документации PyCapsule_New() принимает:
typedef void (*PyCapsule_Destructor)(PyObject *);
, в то время как C -библиотека имеет деструкторы в виде:
int foo(void*);

I ' Я пытаюсь сгенерировать C функцию в .pyx файле с помощью cdef, которая сгенерирует C -функцию, которая обернет библиотечный деструктор, скроет возвращаемый тип и передаст указатель, взятый с PyCapsule_GetPointer, в деструктор , (файл pyx генерируется программно для 200 функций).

После нескольких экспериментов я получаю следующий файл .pyx:

from cpython.ref cimport PyObject
from cpython.pycapsule cimport PyCapsule_New, PyCapsule_IsValid, PyCapsule_GetPointer

cdef void stateFree( PyObject *capsule ):
     cdef:
        void * _state
     # some code with PyCapsule_GetPointer

def stateInit():
    cdef:
        void * _state
    return PyCapsule_New(_state, "T", stateFree)

И когда я пытаюсь скомпилировать его с помощью Cython Я получаю: Cannot assign type 'void (PyObject *)' to 'PyCapsule_Destructor'
использование PyCapsule_New(_state, "T", &stateFree) не помогает.

Есть идеи, что не так?

UPD:

Хорошо, думаю, я нашел решение. По крайней мере, это компилируется. Посмотрим, будет ли это работать. Я выделю места, которые, как мне кажется, я допустил:

из cpython .ref cimport PyObject
из cpython .pycapsule cimport PyCapsule_New, PyCapsule_IsValid, PyCapsule_GetPointer , PyCapsule_Destructor

cpdef void stateFree ( объект капсула):
cdef:
void * _state
_state = PyCapsule_GetPointer ( капсула, "T")
print ('уничтожено')

def stateInit ():
cdef:
int _state = 1
print ("initialized")
return PyCapsule_New (_state, "T", stateFree)

1 Ответ

2 голосов
/ 13 января 2020

Проблема в том, что Cython различает

  • object - объект Python, о котором он знает и обрабатывает подсчет ссылок, и
  • PyObject* , что, насколько это касается, является загадочным типом, о котором в основном ничего не говорится, за исключением того, что это указатель на структуру.

Это несмотря на тот факт, что код C, сгенерированный для * 1011 Cython * заканчивается написанием в терминах PyObject*.

Подпись, используемая Cython cimport, равна ctypedef void (*PyCapsule_Destructor)(object o) (что не совсем совпадает с определением C Поэтому деструктор определите как

cdef void stateFree( object capsule ):

Практически в этом случае различие не имеет значения. Это имеет большее значение в случаях, когда функция крадет ссылку или возвращает заимствованную ссылку. Здесь capsule имеет то же самое количество ссылок на вход и выход функции независимо от того, управляет ли это Cython.

С точки зрения вашего отредактированного решения:

  • cpdef неверно для stateFree. Использовать cdef поскольку это не функция, которая должна отображаться в интерфейсе Python (и если вы используете cpdef, неясно, передается ли версия Python или C в качестве указателя на функцию).
  • Вам не нужно использовать приведение к PyCapsule_Destructor и избегать его, потому что приведение может легко скрывать ошибки.

Могу ли я просто уделить минутку express моей общей неприязни для PyCapsule (иногда полезно передавать непрозрачный тип через код Python, не касаясь его, но для чего-то большего я думаю, что обычно лучше правильно обернуть его в cdef class). Возможно, вы подумали об этом, и это правильный инструмент для работы, но я помещаю это предупреждение, чтобы попытаться отговорить людей в будущем, которые могут попытаться использовать его для более «копирования и вставки» Основа.

...