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.