¿Cómo usar las vistas de memoria escritas con Cython para aceptar cadenas de Python?

¿Cómo puedo escribir una función de Cython que toma un objeto de cadena de bytes (una cadena normal, un bytearray u otro objeto que sigue el protocolo del búfer ) como una vista de memoria escrita ?

De acuerdo con el tutorial de Unicode y Passing Strings Cython, lo siguiente debería funcionar:

cpdef object printbuf(unsigned char[:] buf): chars = [chr(x) for x in buf] print repr(''.join(chars)) 

Funciona para bytearrays y otros buffers de escritura:

 $ python -c 'import test; test.printbuf(bytearray("test\0ing"))' 'test\x00ing' 

Pero no funciona para cadenas normales y otros objetos de búfer de solo lectura:

 $ python -c 'import test; test.printbuf("test\0ing")' Traceback (most recent call last): File "", line 1, in  File "test.pyx", line 1, in test.printbuf (test.c:1417) File "stringsource", line 614, in View.MemoryView.memoryview_cwrapper (test.c:6795) File "stringsource", line 321, in View.MemoryView.memoryview.__cinit__ (test.c:3341) BufferError: Object is not writable. 

Mirando el código C generado, Cython siempre pasa el indicador PyObject_GetBuffer() a PyObject_GetBuffer() , lo que explica la excepción.

Puedo ver manualmente el objeto del búfer, pero no es tan conveniente:

 from cpython.buffer cimport \ PyBUF_SIMPLE, PyBUF_WRITABLE, \ PyObject_CheckBuffer, PyObject_GetBuffer, PyBuffer_Release cpdef object printbuf(object buf): if not PyObject_CheckBuffer(buf): raise TypeError("argument must follow the buffer protocol") cdef Py_buffer view PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE) try: chars = [chr((view.buf)[i]) for i in range(view.len)] print repr(''.join(chars)) finally: PyBuffer_Release(&view) 
 $ python -c 'import test; test.printbuf(bytearray("test\0ing"))' 'test\x00ing' $ python -c 'import test; test.printbuf("test\0ing")' 'test\x00ing' 

¿Estoy haciendo algo mal o Cython no admite la coacción de objetos de búfer de solo lectura (como cadenas normales) en objetos de vista de memoria escritos?

A pesar de la documentación que sugiere lo contrario, Cython (al menos hasta la versión 0.22) no admite la coacción de objetos de búfer de solo lectura en objetos de vista de memoria con tipo. Cython siempre pasa el indicador PyBUF_WRITABLE a PyObject_GetBuffer() , incluso cuando no necesita acceso de escritura. Esto hace que los objetos de búfer de solo lectura generen una excepción.

Presenté este problema en la lista de correo de desarrolladores de Cython e incluso incluí un parche (muy aproximado). Nunca recibí una respuesta, así que asumo que los desarrolladores de Cython no están interesados ​​en solucionar este error.

Este problema se solucionó en Cython 0.28, publicado 2018-03-13 ( PR # 1869 ). El registro de cambios dice:

El modificador const se puede aplicar a las declaraciones de vista de memoria para permitir buffers de solo lectura como entrada.

También hay una nueva sección en la documentación .

El ejemplo que dio funcionará en Cython 0.28 si escribe su función así:

 cpdef object printbuf(const unsigned char[:] buf): chars = [chr(x) for x in buf] print repr(''.join(chars))