Python ctypes: objeto de archivo Python C FILE *

Estoy usando ctypes para envolver una biblioteca C (sobre la que tengo control) con Python. Quiero envolver una función C con una statement:

int fread_int( FILE * stream ); 

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

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

¿Es posible la asignación del archivo Python FILE *?

Joakim

Me he encontrado con el mismo problema.

Echa un vistazo a este archivo:

http://svn.python.org/projects/ctypes/trunk/ctypeslib/ctypeslib/contrib/pythonhdr.py

Puedes usar PyFile_AsFile desde ahí.

Un objeto de archivo de Python no necesariamente tiene un FILE * nivel C subyacente FILE * , al menos, a menos que esté dispuesto a vincular su código a versiones y plataformas de Python extremadamente específicas.

Lo que recomendaría en su lugar es utilizar el archivo del objeto fileno para obtener un descriptor de archivo, luego usar ctypes para llamar a ctypes de la biblioteca en tiempo de ejecución de fdopen para hacer un FILE * de eso . Este es un enfoque muy portátil (y usted también puede ir al otro lado). El gran problema es que el almacenamiento en búfer será separado para los dos objetos abiertos en el mismo descriptor de archivo (el objeto del archivo Python y el C FILE * C), así que asegúrese de vaciar dichos objetos con la frecuencia que sea necesario (o, abra ambos como no guardados) , si es más conveniente y compatible con su uso previsto).

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 quieres evitar usar 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') 

Bien;

fileno solución basada en fileno , pero me fileno bastante incómodo al abrir el archivo dos veces; Tampoco me quedó claro cómo evitar que el valor de retorno de fdopen() filtre.

Al final escribí una extensión C microscópica:

 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 a Python, que pasa de nuevo a la función C esperando la entrada FILE * . Funciona al menos.

Joakim

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) 

Intenté esto:

 #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 

Parece que podría estar funcionando.