SystemError: Объекты / cellobject.c:24: неверный аргумент для внутренней функции

#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__ является кортежем с a cell , который содержит ссылку на 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 In ctypes_cb._objects уже ссылается inner .