2010-09-25 6 views
8

Sto usando i tipi ctyp per racchiudere una libreria C (che ho il controllo su) con Python. Voglio racchiudere una funzione C con dichiarazione:Tipi di codice Python: oggetto file Python <-> FILE C *

int fread_int(FILE * stream); 

Ora; Vorrei aprire il file in python, e quindi utilizzare il file-oggetto Python (in qualche modo ??) per ottenere l'accesso all'oggetto sottostante FILE * e passare che per il C-funzione di:

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

Is il file Python < -> Mappare FILE * è possibile?

Joakim

risposta

-1

che ho incontrato lo stesso problema.

Date un'occhiata a questo file:

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

È possibile utilizzare PyFile_AsFile da esso.

+0

Grazie - quella era una soluzione migliore della mia. – user422005

+5

Sfortunatamente PyFile_AsFile non esiste nell'API Python 3 C. –

+2

@MikhailKorobov: Ho chiesto [una domanda] (http://stackoverflow.com/q/16130268/309483) per risolvere questo problema. –

25

Un oggetto file Python non necessariamente avere un sottostante di livello C FILE * - almeno, a meno che non si è disposti a legare il codice per le versioni di Python e piattaforme estremamente specifici.

Quello che mi sento di raccomandare invece utilizza il fileno oggetto file Python per ottenere un descrittore di file, quindi utilizzare ctypes per chiamare la libreria di runtime del fdopen C per fare una FILE * di che. Questo è un approccio molto portabile (e puoi anche andare dall'altra parte). Il grosso problema è che il buffering sarà separato per i due oggetti aperti sullo stesso descrittore di file (l'oggetto file Python e C FILE *), quindi assicurati di svuotare detti oggetti tutte le volte che è necessario (oppure, aprire entrambi come unbuffered, se è più comodo e compatibile con l'uso previsto).

+0

Grazie mille. Ho avuto la sensazione che il file di livello C * non fosse molto robusto. Non sapevo dell'attributo fileno dell'oggetto file python, ma mi sembra un modo naturale di procedere. – user422005

+0

Ho avuto un problema nell'importare stdin/stdout/stderr da un SO, e non c'era alcuna possibilità che stavo per costruire una struttura formale. Il tuo suggerimento fdopen() mi ha salvato. Grazie. –

2

Bene;

Ho provato la soluzione basata su fileno, ma ero piuttosto scomodo nell'aprire il file due volte; Inoltre, non mi è stato chiaro come evitare la perdita del valore di ritorno da fdopen().

Alla fine ho scritto un microscopico C-estensione:

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(""); 
} 

che utilizza PyFile_AsFile e successivamente restituisce il puntatore FILE * come valore puntatore pura a Python che passa questo ritorno alla funzione C in attesa FILE * ingresso. Funziona almeno.

Joakim

2

Se si desidera utilizzare stdout/stdin/stderr, è possibile importare tali variabili dalla libreria C standard.

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

Oppure, se si vuole evitare di utilizzare void* per qualche motivo:

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

Adattato da 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

Eccetto questo sbagliato: per py3k '' c_file'' sarà '' int'', per py2k sarà '' FILE * ''. – mcepl

+0

@mcepl ti dispiace modificare la risposta per renderla corretta? – reubano

+0

Credo che la risposta corretta sia nella mia soluzione sopra. – mcepl

0

provato questo:

#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 

E lo piace come potrebbe funzionare.