#python #c #ctypes #cython #cpython
#python #c #ctypes #cython #cpython
Вопрос:
Я использую ctypes для работы с библиотекой, написанной на C. Эта библиотека C позволяет мне регистрировать функцию обратного вызова, которую я реализую на Python.
Вот тип функции обратного вызова в соответствии с API ctypes:
_command_callback = CFUNCTYPE(
UNCHECKED(c_int),
POINTER(vedis_context),
c_int,
POINTER(POINTER(vedis_value)))
Вот декоратор, который я написал, чтобы пометить функцию как обратный вызов:
def wrap_callback(fn):
return _command_callback(fn)
Чтобы использовать это, я могу просто написать:
@wrap_callback
def my_callback(*args):
print args
return 1 # Needed by C library to indicate OK response.
c_library_func.register_callback(my_callback)
Теперь я могу вызвать свой обратный вызов ( my_callback
) из C, и это работает отлично.
Проблема, с которой я сталкиваюсь, заключается в том, что будет некоторое шаблонное поведение, которое я хотел бы выполнить как часть этих обратных вызовов (например, возврат флага успеха и т. Д.). Чтобы минимизировать шаблонность, я попытался написать декоратор:
def wrap_callback(fn):
def inner(*args, **kwargs):
return fn(*args, **kwargs)
return _command_callback(inner)
Обратите внимание, что это функционально эквивалентно предыдущему примеру.
@wrap_callback
def my_callback(*args):
print args
return 1
Однако, когда я пытаюсь вызвать обратный вызов, используя этот подход, я получаю следующее исключение, исходящее из _ctypes/callbacks.c
:
Traceback (most recent call last):
File "_ctypes/callbacks.c", line 314, in 'calling callback function'
File "/home/charles/tmp/scrap/z1/src/vedis/vedis/core.py", line 28, in inner
return fn(*args, **kwargs)
SystemError: Objects/cellobject.c:24: bad argument to internal function
Я не уверен, что здесь происходит, что может привести к тому, что первый пример сработает, а второй пример завершится неудачей. Кто-нибудь может пролить свет на это? Бонусные баллы, если вы можете помочь мне найти способ украсить эти обратные вызовы, чтобы я мог сократить шаблонный код!
Комментарии:
1.
inner.__closure__
является кортежем с acell
, который содержит ссылку наfn
. Эта ячейка загружается во фрейме оценки. Чтобы получить ссылку вcell
,LOAD_DEREF
вызывается кодPyCell_Get
операции . Ошибка, которую вы получаете, означаетPyCell_Get
, что был вызван нежелательный указатель, который не указывает на ячейку. Это означает__closure__
, что кортеж поврежден.2. Сохраняете ли вы ссылку на
my_callback
после передачи ее в свою библиотеку? В противном случае обратный вызов и thunk освобождаются, принимая ссылки наinner
них иfn
вместе с ними. Память все еще может быть частично нетронутой, что приводит к странным ошибкам, когда библиотека, наконец, вызывает обратный вызов.3. Я посмотрю, смогу ли я что-то сделать, чтобы убедиться, что есть ссылка
my_callback
, но я думаю, что вы здесь на правильном пути.4. Если я создаю список в области модуля, а затем добавляю
inner
в список, это исправляет ошибку. Спасибо! Теперь я буду работать над поиском немного более чистого способа исправить это.
Ответ №1:
Благодаря eryksyn я смог исправить эту проблему. Исправление выглядит так:
def wrap_callback(fn):
def inner(*args, **kwargs):
return fn(*args, **kwargs)
return _command_callback(inner), inner
def my_callback(*args):
print args
return 1
ctypes_cb, my_callback = wrap_callback(my_callback)
Комментарии:
1. Вы должны сохранить ссылку на
ctypes_cb
, но вам не нужна другая ссылкаinner
.CThunkObject
Inctypes_cb._objects
уже ссылаетсяinner
.