Ich habe eine Bibliotheksfunktion (in C geschrieben), die Text erzeugt, indem die Ausgabe in FILE *
geschrieben wird. Ich möchte das in Python (2.7.x) mit Code umwandeln, der eine temporäre Datei oder eine Pipe erstellt, diese in die Funktion übergibt, das Ergebnis aus der Datei liest und es als Python-String zurückgibt.
Hier ist ein vereinfachtes Beispiel, um zu veranschaulichen, wonach ich suche:
%Vor%Python-Wrapper:
%Vor% Ich frage mich, was meine beste Wette für MAGIC_HERE()
ist.
Ich bin versucht, einfach ctypes
zu verwenden und einen libc.fdopen()
-Wrapper zu erstellen, der ein Python c_void_t zurückgibt und dann in die Bibliotheksfunktion übergibt. Ich bin der Meinung, dass das in der Theorie sicher sein sollte - ich frage mich nur, ob es Probleme mit diesem Ansatz oder einem existierenden Python-System gibt, um dieses Problem zu lösen.
Auch das wird in einem lang andauernden Prozess laufen (nehmen wir einfach "für immer" an), so dass alle durchgesickerten Dateideskriptoren problematisch werden.
Beachten Sie zunächst, dass FILE*
eine stdio-spezifische Entität ist. Es existiert nicht auf Systemebene. Die Dinge, die auf Systemebene existieren, sind Deskriptoren (abgerufen mit file.fileno()
) in UNIX ( os.pipe()
gibt bereits einfache Deskriptoren zurück) und handle (abgerufen mit msvcrt.get_osfhandle()
) in Windows. Daher ist es eine schlechte Wahl als Interbibliothek-Austauschformat, wenn mehr als eine C-Laufzeit aktiv sein kann. Sie werden Probleme bekommen, wenn Ihre Bibliothek gegen eine andere C-Laufzeit als Ihre Kopie kompiliert wird Python: 1) binäre Layouts der Struktur können sich unterscheiden (z. B. aufgrund von Alignment oder zusätzlichen Membern zu Debugging-Zwecken oder sogar unterschiedlichen Schriftgrößen); 2) In Windows sind Dateideskriptoren, mit denen die Struktur verknüpft ist, ebenfalls C-spezifische Entitäten, und ihre Tabelle wird intern von einer C-Laufzeit verwaltet 1 .
Außerdem wurde I / O in Python 3 überarbeitet, um es von stdio
zu entwirren. Also, FILE*
ist dem Python-Geschmack (und wahrscheinlich auch den meisten Nicht-C-Geschmacksrichtungen) fremd.
Nun, was Sie brauchen, ist
fdopen()
(oder gleichwertig). (Einer der Python-Mottos ist "mach das richtige Ding einfach und das falsche Ding schwer", immerhin)
Die sauberste Methode besteht darin, genau die Instanz zu verwenden, mit der die Bibliothek verknüpft ist (beten Sie, dass sie dynamisch mit ihr verknüpft ist oder dass kein exportierendes Symbol aufgerufen wird)
Zum ersten Punkt konnte ich keine Python-Module finden, die die Metadaten der geladenen dynamischen Module analysieren könnten, um herauszufinden, mit welchen DLLs sie verknüpft sind (nur ein Name oder gar ein Name + Version reicht nicht aus, Sie wissen, aufgrund der möglichen mehreren Instanzen der Bibliothek auf dem System). Obwohl es definitiv möglich ist, da die Informationen über sein Format weit verbreitet sind.
Für den zweiten Punkt ist es ein triviales ctypes.cdll('path').fdopen
( _fdopen
für MSVCRT).
Zweitens können Sie ein kleines Hilfsmodul erstellen, das mit derselben (oder einer mit der Laufzeit kompatiblen) Laufzeit wie die Bibliothek kompiliert wird und die Konvertierung von dem oben genannten Deskriptor / Handle für Sie übernimmt. Das ist effektiv eine Problemumgehung zum Bearbeiten der Bibliothek.
Schließlich gibt es die einfachste (und schmutzigste) Methode, die Pythons C-Laufzeit-Instanz verwendet (so dass alle obigen Warnungen vollständig gelten) über die Python-C-API, die über ctypes.pythonapi
. Es nutzt die Vorteile von
stdio
s FILE*
sind (Python 3's nicht) PyFile_AsFile
API, die das umgebrochene FILE*
( Beachten Sie, dass in Python 3 fehlt )
fd
müssen Sie zuerst ein dateiähnliches Objekt erstellen (so dass ein FILE*
zurückgegeben wird;)) die Tatsache, dass id()
eines Objekts seine Speicheradresse ist (CPython-spezifisch) 2
Beachten Sie, dass Sie mit den fd
s- und C-Zeigern die richtigen Objektlebensdauern von Hand sicherstellen müssen!
os.fdopen()
zurückgegeben werden, schließen den Deskriptor auf .close()
os.dup()
, wenn Sie sie brauchen, nachdem ein Dateiobjekt geschlossen wurde / Garbage Collected PyFile_IncUseCount()
/ PyFile_DecUseCount()
. iter(f)
/ for l in f
, internes Caching ist unabhängig von stdio
's Caching) / li>