Error con Python ctypes y librsvg

Estoy tratando de envolver la función básica de librsvg con ctypes para Python, pero estoy obteniendo una falla de seguridad.

DO:

// pycairo excerpt typedef struct { PyObject_HEAD cairo_t *ctx; PyObject *base; /* base object used to create context, or NULL */ } PycairoContext; // librsvg excerpt RsvgHandle * rsvg_handle_new_from_file (const gchar * file_name, GError ** error); // ... gboolean rsvg_handle_render_cairo (RsvgHandle * handle, cairo_t * cr); 

Tipos de python:

 from ctypes import * from ctypes import util librsvg = cdll.LoadLibrary('/brew/lib/librsvg-2.2.dylib') libgobject = cdll.LoadLibrary('/brew/lib/libgobject-2.0.dylib') libgobject.g_type_init() class RSVGDimensionData(Structure): _fields_ = ( ('width', c_int), ('height', c_int), ('em', c_double), ('ex', c_double) ) class PycairoContext(Structure): _fields_ = ( ('PyObject_HEAD', c_byte * object.__basicsize__), ('ctx', c_void_p), ('base', c_void_p) ) class RSVGHandle(object): def __init__(self, path): self.path = path self.error = '' self.handle = librsvg.rsvg_handle_new_from_file(self.path, self.error) def render_cairo(self, context): context.save() z = PycairoContext.from_address(id(context)) librsvg.rsvg_handle_render_cairo(self.handle, z.ctx) context.restre() import cairo h = RSVGHandle('bank.svg') s = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100) ctx = cairo.Context(s) # segmentation fault.... h.render_cairo(ctx) 

El error está ocurriendo en esta línea: librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)

¿Alguna idea de lo que está mal con esto?

El problema es que la especificación del tipo de retorno no está definida; El uso de c_void_p en el resultado no es suficiente para resolver el problema en ese caso. Necesitas poner

 librsvg.rsvg_handle_new_from_file.restype = c_void_p 

en un lugar apropiado. Entonces (también) funciona en OSX ya sea en 32 bits o 64 bits.

Pero he encontrado más útil boost el ajuste básico para manejar posibles errores al crear un identificador desde un archivo. A continuación hay una envoltura básica que hace eso. También replica de manera casi idéntica el uso básico de los enlaces rsvg estándar.

 from ctypes import CDLL, POINTER, Structure, byref, util from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p class _PycairoContext(Structure): _fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__), ("ctx", c_void_p), ("base", c_void_p)] class _RsvgProps(Structure): _fields_ = [("width", c_int), ("height", c_int), ("em", c_double), ("ex", c_double)] class _GError(Structure): _fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)] def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None): if rsvg_lib_path is None: rsvg_lib_path = util.find_library('rsvg-2') if gobject_lib_path is None: gobject_lib_path = util.find_library('gobject-2.0') l = CDLL(rsvg_lib_path) g = CDLL(gobject_lib_path) g.g_type_init() l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))] l.rsvg_handle_new_from_file.restype = c_void_p l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p] l.rsvg_handle_render_cairo.restype = c_bool l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)] return l _librsvg = _load_rsvg() class Handle(object): def __init__(self, path): lib = _librsvg err = POINTER(_GError)() self.handle = lib.rsvg_handle_new_from_file(path.encode(), byref(err)) if self.handle is None: gerr = err.contents raise Exception(gerr.message) self.props = _RsvgProps() lib.rsvg_handle_get_dimensions(self.handle, byref(self.props)) def render_cairo(self, ctx): """Returns True is drawing succeeded.""" z = _PycairoContext.from_address(id(ctx)) return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx) 

Ejemplo de uso se puede ver en https://stackoverflow.com/a/14928770/1832154 .

librsvg.rsvg_handle_render_cairo espera punteros y obtiene enteros en su lugar. No estoy seguro de toda la historia aquí, pero esta modificación al menos no falla.

Prueba esto

  librsvg.rsvg_handle_render_cairo(c_void_p(self.handle), c_void_p(z.ctx)) 

Note que envolví los dos parámetros en c_void_p para convertirlos en punteros nulos *. No es ideal, pero parece funcionar.