2010-09-25 13 views
8

Estoy usando ctypes para envolver una biblioteca C (que tengo control) con Python. Quiero ajustar una función C con declaración:Python ctypes: objeto de archivo Python <-> C FILE *

int fread_int(FILE * stream); 

Ahora; Me gustaría abrir el archivo en Python, y luego usar el archivo de Python-objeto (de alguna manera ??) para obtener acceso a la subyacente ARCHIVO * objeto y pasar esa a la C-función:

# Python 
fileH = open(file , "r") 
value = ctypes_function_fread_int(?????) 
fileH.close() 

Es el archivo Python < -> ARCHIVO * mapeo posible?

Joakim

Respuesta

25

un objeto de archivo Python no necesariamente tienen un nivel C subyacente FILE * - al menos, no menos que esté dispuesto a atar su código para las versiones de Python y plataformas muy específica.

Lo que yo recomendaría en su lugar se utiliza el archivo de fileno objeto de Python para obtener un descriptor de archivo, a continuación, utilizar ctypes a la llamada de fdopen la biblioteca de ejecución C para hacer una FILE * de que. Este es un enfoque muy portátil (y usted puede ir por el otro lado, también). El gran problema es que el almacenamiento en búfer será independiente para los dos objetos abiertos en el mismo descriptor de archivo (el objeto de archivo Python, y el C FILE *), así que asegúrese de vaciar dichos objetos tantas veces como sea necesario (o, abrir ambos como no almacenados, si eso es más conveniente y compatible con su uso previsto).

+0

gracias -I muchos tenían la sensación de que el nivel C ARCHIVO * no sería muy robusto. No sabía sobre el atributo fileno del objeto de archivo python, pero parece una forma natural de hacerlo. – user422005

+0

que tenían un problema al importar stdin/stdout/stderr de un SO, y no había ninguna posibilidad de que yo iba a la acumulación de una estructura formal. Tu sugerencia de fdopen() me salvó. Gracias. –

2

Bueno;

Probé la solución basada en fileno, pero me sentí bastante incómodo al abrir el archivo dos veces; Tampoco era claro para mí cómo evitar el valor de retorno de fdopen() a la fuga.

Al final me escribió un microscópico C-extensión:

static PyObject cfile(PyObject * self, PyObject * args) { 
    PyObject * pyfile; 
    if (PyArg_ParseTuple('O' , &pyfile)) { 
     FILE * cfile = PyFile_AsFile(pyfile); 
     return Py_BuildValue("l" , cfile); 
    else 
     return Py_BuildValue(""); 
} 

que utiliza PyFile_AsFile y posteriormente devuelve el puntero FILE * como un valor de puntero puro para Python que pasa esto de nuevo a la función C esperando FILE * de entrada. Funciona al menos.

Joakim

2

Si desea utilizar stdout/stdin/stderr, puede importar esas variables de la biblioteca C estándar.

libc = ctypes.cdll.LoadLibrary('libc.so.6') 
cstdout = ctypes.c_void_p.in_dll(libc, 'stdout') 

O, si se quiere evitar el uso de void* por alguna razón:

class FILE(ctypes.Structure): 
    pass 

FILE_p = ctypes.POINTER(FILE) 

libc = ctypes.cdll.LoadLibrary('libc.so.6') 
cstdout = FILE_p.in_dll(libc, 'stdout') 
0

Adaptado de svplayer

import sys 

from ctypes import POINTER, Structure, py_object, pythonapi 


class File(Structure): 
    pass 

if sys.version_info[0] > 2: 
    convert_file = pythonapi.PyObject_AsFileDescriptor 
    convert_file.restype = c_int 
else: 
    convert_file = pythonapi.PyFile_AsFile 
    convert_file.restype = POINTER(File) 

convert_file.argtypes = [py_object] 

fp = open('path').fp 
c_file = convert_file(fp) 
+0

Excepto que esta equivocada: para py3k '' c_file'' será '' int'', por py2k que será '' '' FILE *. – mcepl

+0

@mcepl Le importaría edición de la respuesta para que sea correcto? – reubano

+0

Creo que la respuesta correcta está en mi solución anterior. – mcepl

0

probado este:

#if PY_MAJOR_VERSION >= 3 
    if (PyObject_HasAttrString(pyfile, "fileno")) { 
     int fd = (int)PyLong_AsLong(PyObject_CallMethod(pyfile, "fileno", NULL)); 
     if (PyObject_HasAttrString(pyfile, "mode")) { 
      char *mode = PyUnicode_AsUTF8AndSize(
        PyObject_CallMethod(pyfile, "mode", NULL), NULL); 
      fp = fdopen(fd, mode); 
     } 
     else { 
      return PyErr_Format(PyExc_ValueError, 
        "File doesn’t have mode attribute!"); 
     } 
    } 
    else { 
     return PyErr_Format(PyExc_ValueError, 
       "File doesn’t have fileno method!"); 
    } 
#else 
    fp = PyFile_AsFile(pyfile); 
#endif 

Es lo Oks como podría estar funcionando.

Cuestiones relacionadas