Ich verwende das Unterprozessmodul , um einen Unterprozess zu starten und eine Verbindung zu seinem Ausgabestream (stdout) herzustellen. Ich möchte in der Lage sein, nicht blockierende Lesevorgänge auf seinem Standard auszuführen. Gibt es eine Möglichkeit, .readline nicht zu blockieren oder zu überprüfen, ob sich Daten im Stream befinden, bevor ich sie aufrufe .readline
? Ich möchte, dass dies portabel ist oder zumindest unter Windows und Linux funktioniert.
Hier ist, wie ich es jetzt mache (es blockiert, .readline
wenn keine Daten verfügbar sind):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
python
io
subprocess
nonblocking
Mathieu Pagé
quelle
quelle
To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Antworten:
fcntl
,select
,asyncproc
Wird in diesem Fall nicht helfen.Eine zuverlässige Methode zum Lesen eines Streams ohne Blockierung unabhängig vom Betriebssystem ist die Verwendung von
Queue.get_nowait()
:quelle
out.readline
blockiert der Aufruf, den Thread und den Hauptthread zu blockieren, und ich muss warten, bis readline zurückkehrt, bevor alles andere fortgesetzt wird. Irgendein einfacher Weg, das zu umgehen? (Ich lese mehrere Zeilen aus meinem Prozess, der auch eine andere .py-Datei ist, die DB und Dinge tut)shelljob
pypi.python.org/pypi/shelljobIch hatte oft ein ähnliches Problem; Python-Programme, die ich häufig schreibe, müssen in der Lage sein, einige primäre Funktionen auszuführen und gleichzeitig Benutzereingaben über die Befehlszeile (stdin) zu akzeptieren. Das einfache Einfügen der Funktionen zur Verarbeitung von Benutzereingaben in einen anderen Thread löst das Problem nicht, da es
readline()
blockiert und keine Zeitüberschreitung aufweist. Wenn die primäre Funktionalität vollständig ist und nicht mehr auf weitere Benutzereingaben gewartet werden muss, möchte ich normalerweise, dass mein Programm beendet wird, dies ist jedoch nicht möglich, dareadline()
der andere Thread immer noch blockiert und auf eine Zeile wartet. Eine Lösung, die ich für dieses Problem gefunden habe, besteht darin, stdin mit dem Modul fcntl zu einer nicht blockierenden Datei zu machen:Meiner Meinung nach ist dies etwas sauberer als die Verwendung der Select- oder Signalmodule, um dieses Problem zu lösen, aber es funktioniert auch nur unter UNIX ...
quelle
buffer_size
definiert als?Python 3.4 führt eine neue vorläufige API für das asynchrone E / A-
asyncio
Modul ein .Der Ansatz ähnelt der
twisted
Antwort von @Bryan Ward. Definieren Sie ein Protokoll, und seine Methoden werden aufgerufen, sobald die Daten bereit sind:Siehe "Unterprozess" in den Dokumenten .
Es gibt eine übergeordnete Schnittstelle
asyncio.create_subprocess_exec()
, dieProcess
Objekte zurückgibt , mit denen eine Zeile mithilfe vonStreamReader.readline()
Coroutine (mitasync
/await
Python 3.5+ -Syntax ) asynchron gelesen werden kann :readline_and_kill()
führt folgende Aufgaben aus:Jeder Schritt kann bei Bedarf durch Timeout-Sekunden begrenzt werden.
quelle
print(text, flush=True)
sodass der gedruckte Text dem anrufenden Beobachter sofort zur Verfügung stehtreadline
. Als ich es mit der Fortran-basierten ausführbaren Datei getestet habe, die ich eigentlich umschließen / beobachten möchte, puffert es die Ausgabe nicht und verhält sich daher wie erwartet.readline_and_kill
funktioniert in Ihrem zweiten Skript sehr ähnlichsubprocess.comunicate
, da es den Prozess nach einem Lese- / Schreibvorgang beendet. Ich sehe auch, dass Sie eine einzelne Pipe verwendenstdout
, die der Unterprozess als nicht blockierend behandelt. Der Versuch , beide zu verwenden ,stdout
undstderr
ich finde , ich Blockierung am Ende .Probieren Sie das asyncproc- Modul aus. Zum Beispiel:
Das Modul kümmert sich um das gesamte Einfädeln, wie von S.Lott vorgeschlagen.
quelle
Sie können dies wirklich einfach in Twisted tun . Abhängig von Ihrer vorhandenen Codebasis ist dies möglicherweise nicht so einfach zu verwenden. Wenn Sie jedoch eine verdrehte Anwendung erstellen, werden solche Dinge fast trivial. Sie erstellen eine
ProcessProtocol
Klasse und überschreiben dieoutReceived()
Methode. Twisted (abhängig vom verwendeten Reaktor) ist normalerweise nur eine großeselect()
Schleife mit installierten Rückrufen, um Daten von verschiedenen Dateideskriptoren (häufig Netzwerk-Sockets) zu verarbeiten. DieoutReceived()
Methode installiert also einfach einen Rückruf für die Verarbeitung von Daten, die von kommenSTDOUT
. Ein einfaches Beispiel für dieses Verhalten lautet wie folgt:Die Twisted-Dokumentation enthält einige gute Informationen dazu.
Wenn Sie Ihre gesamte Anwendung auf Twisted aufbauen, wird die asynchrone Kommunikation mit anderen lokalen oder Remote-Prozessen so elegant. Wenn Ihr Programm jedoch nicht auf Twisted basiert, ist dies nicht wirklich hilfreich. Hoffentlich kann dies für andere Leser hilfreich sein, auch wenn es für Ihre spezielle Anwendung nicht gilt.
quelle
select
sollte nicht unter Windows mit Dateideskriptoren funktionieren, laut docsselect()
, auf das er sich bezieht, dasselbe ist, das du bist. Ich gehe davon aus, weil esTwisted
unter Windows funktioniert ...asyncio
stdlib hinzugefügt .select()
ist der portabelste unter Unixen und Unix-Likes, aber es gibt auch zwei Reaktoren für Windows: twistedmatrix.com/documents/current/core/howto/…Verwenden Sie select & read (1).
Für readline () - wie:
quelle
select
sollte nicht unter Windows mit Dateideskriptoren funktionieren, laut docsproc.stdout.read()
egal wie klein das Argument ist ein blockierender Anruf.OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
Eine Lösung besteht darin, einen anderen Prozess zu erstellen, um das Lesen des Prozesses durchzuführen, oder einen Thread des Prozesses mit einer Zeitüberschreitung zu erstellen.
Hier ist die Thread-Version einer Timeout-Funktion:
http://code.activestate.com/recipes/473878/
Müssen Sie jedoch den Standard lesen, wenn er eingeht? Eine andere Lösung könnte darin bestehen, die Ausgabe in eine Datei zu kopieren und mit p.wait () zu warten, bis der Prozess abgeschlossen ist .
quelle
Haftungsausschluss: Dies funktioniert nur für Tornados
Sie können dies tun, indem Sie den fd so einstellen, dass er nicht blockiert, und dann ioloop verwenden, um Rückrufe zu registrieren. Ich habe dies in einem Ei namens tornado_subprocess verpackt und Sie können es über PyPI installieren:
Jetzt können Sie so etwas tun:
Sie können es auch mit einem RequestHandler verwenden
quelle
threading.Thread
neue nicht blockierende Prozesse erstellen? Ich habe es inon_message
einer Tornado-Websocket-Instanz verwendet, und es hat den Job gut gemacht.select
mit Dateideskriptoren nicht )select
Aufruf nicht. Ich habe dies unter Windows nicht versucht, aber Sie würden wahrscheinlich auf Probleme stoßen, da die Bibliothek dasfcntl
Modul verwendet. Kurz gesagt: Nein, dies wird unter Windows wahrscheinlich nicht funktionieren.Bestehende Lösungen haben bei mir nicht funktioniert (Details unten). Was schließlich funktionierte, war die Implementierung von readline mit read (1) (basierend auf dieser Antwort ). Letzteres blockiert nicht:
Warum vorhandene Lösungen nicht funktionierten:
quelle
q.get_nowait()
von meiner Antwort darf niemals blockieren, das ist der Sinn der Verwendung. 2. Der Thread, der readline (enqueue_output()
function ) ausführt, wird auf EOF beendet, z. B. einschließlich des Falls, in dem der ausgabeproduzierende Prozess beendet wird. Wenn Sie glauben, dass es nicht so ist; Bitte geben Sie ein vollständiges Beispiel für einen minimalen Code an , das etwas anderes anzeigt (möglicherweise als neue Frage ).dcmpid = myprocess
.Hier ist mein Code, der verwendet wird, um jede Ausgabe des Unterprozesses so schnell wie möglich abzufangen, einschließlich Teilzeilen. Es pumpt gleichzeitig und stdout und stderr in fast korrekter Reihenfolge.
Getestet und korrekt unter Python 2.7 Linux & Windows.
quelle
Ich füge dieses Problem hinzu, um einen Unterprozess zu lesen. Öffnen Sie stdout. Hier ist meine nicht blockierende Leselösung:
quelle
msvcrt.kbhit()
stattdessenDiese Version des nicht blockierenden Lesens erfordert keine speziellen Module und funktioniert bei den meisten Linux-Distributionen sofort.
quelle
Hier ist eine einfache Lösung basierend auf Threads, die:
select
).stdout
undstderr
asynchron.asyncio
(was zu Konflikten mit anderen Bibliotheken führen kann).Drucker.py
reader.py
quelle
Fügen Sie diese Antwort hier hinzu, da nicht blockierende Pipes unter Windows und Unix festgelegt werden können.
Alle
ctypes
Details sind der Antwort von @ techtonik zu verdanken .Es gibt eine leicht modifizierte Version, die sowohl auf Unix- als auch auf Windows-Systemen verwendet werden kann.
Auf diese Weise können Sie dieselbe Funktion und Ausnahme für Unix- und Windows-Code verwenden.
Um das Lesen unvollständiger Daten zu vermeiden, habe ich meinen eigenen Readline-Generator geschrieben (der die Byte-Zeichenfolge für jede Zeile zurückgibt).
Es ist ein Generator, so dass Sie zum Beispiel ...
quelle
readline()
er mit nicht blockierenden Pipes (wie z. B. set usingfcntl
) unter Python 2 nicht funktioniert. Glauben Sie, dass er nicht mehr korrekt ist? (Meine Antwort enthält den Link (fcntl
), der die gleichen Informationen enthält, aber jetzt gelöscht zu sein scheint.) (2) Sehen Sie, wiemultiprocessing.connection.Pipe
verwendetSetNamedPipeHandleState
Ich habe das Problem des ursprünglichen Fragestellers, wollte aber keine Threads aufrufen. Ich mischte Jesses Lösung mit einem direkten read () aus der Pipe und meinem eigenen Buffer-Handler für Zeilenlesevorgänge (mein Unterprozess - ping - schrieb jedoch immer vollständige Zeilen <eine Systemseitengröße). Ich vermeide das Warten, indem ich nur eine von einem Objekt registrierte Io-Uhr einlese. Heutzutage führe ich normalerweise Code innerhalb eines Gobjects MainLoop aus, um Threads zu vermeiden.
Der Beobachter ist
Das Hauptprogramm richtet einen Ping ein und ruft dann die Gobject-Mail-Schleife auf.
Alle anderen Arbeiten sind mit Rückrufen in gobject verbunden.
quelle
In modernem Python sieht es viel besser aus.
Hier ist ein einfaches Kinderprogramm, "hello.py":
Und ein Programm, um damit zu interagieren:
Das druckt aus:
Beachten Sie, dass das eigentliche Muster, das auch hier und in verwandten Fragen von fast allen vorherigen Antworten verwendet wird, darin besteht, den stdout-Dateideskriptor des Kindes auf nicht blockierend zu setzen und ihn dann in einer Art Auswahlschleife abzufragen. Heutzutage wird diese Schleife natürlich von asyncio bereitgestellt.
quelle
Die Auswahl Sie feststellen, wo sich die nächste nützliche Eingabe befindet.
Mit separaten Threads sind Sie jedoch fast immer zufriedener. Einer blockiert das stdin, ein anderer tut, wo immer Sie nicht blockiert werden möchten.
quelle
Warum Thread & Queue stören? Im Gegensatz zu readline () blockiert BufferedReader.read1 () das Warten auf \ r \ n nicht und gibt so schnell wie möglich zurück, wenn eine Ausgabe eingeht.
quelle
read1
wird blockiert, wenn die ersten zugrunde liegenden Leseblöcke, was passiert, wenn die Pipe noch offen ist, aber keine Eingabe verfügbar ist.In meinem Fall benötigte ich ein Protokollierungsmodul, das die Ausgabe der Hintergrundanwendungen abfängt und erweitert (Hinzufügen von Zeitstempeln, Farben usw.).
Am Ende hatte ich einen Hintergrund-Thread, der die eigentliche E / A ausführt. Der folgende Code gilt nur für POSIX-Plattformen. Ich habe nicht wesentliche Teile entfernt.
Wenn jemand dieses Biest für längere Zeit verwenden wird, sollten Sie offene Deskriptoren verwalten. In meinem Fall war es kein großes Problem.
quelle
Mein Problem ist etwas anders, da ich sowohl stdout als auch stderr aus einem laufenden Prozess sammeln wollte, aber letztendlich dasselbe, da ich die Ausgabe in einem Widget so rendern wollte, wie sie generiert wurde.
Ich wollte nicht auf viele der vorgeschlagenen Problemumgehungen mit Warteschlangen oder zusätzlichen Threads zurückgreifen, da diese nicht erforderlich sein sollten, um eine so allgemeine Aufgabe wie das Ausführen eines anderen Skripts und das Sammeln seiner Ausgabe auszuführen.
Nachdem ich die vorgeschlagenen Lösungen und Python-Dokumente gelesen hatte, löste ich mein Problem mit der folgenden Implementierung. Ja, es funktioniert nur für POSIX, da ich den
select
Funktionsaufruf verwende.Ich bin damit einverstanden, dass die Dokumente verwirrend sind und die Implementierung für eine solche häufig verwendete Skriptaufgabe umständlich ist. Ich glaube, dass ältere Versionen von Python unterschiedliche Standardeinstellungen
Popen
und Erklärungen haben, was zu großer Verwirrung geführt hat. Dies scheint sowohl für Python 2.7.12 als auch für 3.5.2 gut zu funktionieren.Der Schlüssel bestand darin, die
bufsize=1
Zeilenpufferung festzulegen und dannuniversal_newlines=True
als Textdatei anstelle einer Binärdatei zu verarbeiten, die bei der Einstellung als Standard zu gelten scheintbufsize=1
.ERROR, DEBUG und VERBOSE sind einfach Makros, die die Ausgabe an das Terminal drucken.
Diese Lösung ist meiner Meinung nach zu 99,99% effektiv, da sie weiterhin die Blockierungsfunktion
readline
verwendet. Wir gehen daher davon aus, dass der Unterprozess gut ist und vollständige Zeilen ausgibt.Ich freue mich über Feedback zur Verbesserung der Lösung, da ich Python noch nicht kenne.
quelle
Ich habe eine Bibliothek erstellt, die auf der Lösung von JF Sebastian basiert . Du kannst es benutzen.
https://github.com/cenkalti/what
quelle
Ausgehend von der Antwort von JF Sebastian und mehreren anderen Quellen habe ich einen einfachen Subprozessmanager zusammengestellt. Es bietet das nicht blockierende Lesen der Anforderung sowie das parallele Ausführen mehrerer Prozesse. Es verwendet keinen betriebssystemspezifischen Aufruf (den ich kenne) und sollte daher überall funktionieren.
Es ist bei pypi erhältlich, also einfach
pip install shelljob
. Beispiele und vollständige Dokumente finden Sie auf der Projektseite .quelle
EDIT: Diese Implementierung blockiert immer noch. Verwenden Sie stattdessen die Antwort von JFSebastian .
Ich habe die beste Antwort versucht , aber das zusätzliche Risiko und die Wartung des Thread-Codes waren besorgniserregend.Beim Durchsehen des io-Moduls (und der Beschränkung auf 2.6) fand ich BufferedReader. Dies ist meine threadlose, nicht blockierende Lösung.quelle
for line in iter(p.stdout.readline, ""): # do stuff with the line
? Es ist threadlos (einzelner Thread) und blockiert, wenn Ihr Code blockiert.Ich bin kürzlich auf das gleiche Problem gestoßen, bei dem ich jeweils eine Zeile aus dem Stream (Tail Run im Unterprozess) im nicht blockierenden Modus lesen muss. Ich wollte die nächsten Probleme vermeiden: CPU nicht brennen, Stream nicht um ein Byte lesen ( wie readline) usw.
Hier ist meine Implementierung https://gist.github.com/grubberr/5501e1a9760c3eab5e0a Es unterstützt keine Windows (Umfrage), behandelt EOF nicht, aber es funktioniert gut für mich
quelle
timeout
wie in Ihrer Lösung) und.readline()
lesen mehr als ein Byte zu einem Zeitpunkt (bufsize=1
Mittel linie -gepufferter (nur relevant für das Schreiben)). Welche anderen Probleme haben Sie gefunden? Nur-Link-Antworten sind nicht sehr nützlich.Dies ist ein Beispiel für die Ausführung eines interaktiven Befehls im Unterprozess. Der Standardausgang ist mithilfe eines Pseudo-Terminals interaktiv. Sie können auf folgende Adresse verweisen: https://stackoverflow.com/a/43012138/3555925
quelle
Diese Lösung verwendet die
select
Modul, um "verfügbare Daten zu lesen" aus einem E / A-Stream. Diese Funktion blockiert zunächst, bis Daten verfügbar sind, liest dann jedoch nur die verfügbaren Daten und blockiert sie nicht weiter.Da das
select
Modul verwendet wird, funktioniert dies nur unter Unix.Der Code ist vollständig PEP8-kompatibel.
quelle
Ich habe mich auch dem von Jesse beschriebenen Problem gestellt und es gelöst, indem ich "select" verwendet habe, wie es Bradley , Andy und andere getan haben, aber in einem Blockierungsmodus, um eine Besetztschleife zu vermeiden. Es verwendet eine Dummy-Pipe als Fake-Standard. Die Auswahl blockiert und wartet, bis entweder stdin oder die Pipe fertig sind. Wenn eine Taste gedrückt wird, entsperrt stdin die Auswahl und der Tastenwert kann mit read (1) abgerufen werden. Wenn ein anderer Thread in die Pipe schreibt, entsperrt die Pipe die Auswahl und dies kann als Hinweis darauf angesehen werden, dass die Notwendigkeit für stdin vorbei ist. Hier ist ein Referenzcode:
quelle
Probieren Sie wexpect aus , die Windows-Alternative von pexpect .
quelle
Auf Unix-ähnlichen Systemen und Python 3.5+ gibt
os.set_blocking
es genau das, was es sagt.Dies gibt aus:
Mit
os.set_blocking
kommentiert ist es:quelle
Hier ist ein Modul, das nicht blockierende Lese- und Hintergrundschreibvorgänge in Python unterstützt:
https://pypi.python.org/pypi/python-nonblock
Bietet eine Funktion,
nonblock_read, das Daten aus dem Stream liest, falls verfügbar, andernfalls eine leere Zeichenfolge zurückgibt (oder None, wenn der Stream auf der anderen Seite geschlossen ist und alle möglichen Daten gelesen wurden).
Sie können auch das Modul python-subprocess2 in Betracht ziehen.
https://pypi.python.org/pypi/python-subprocess2
Dies fügt dem Unterprozessmodul hinzu. Für das von "subprocess.Popen" zurückgegebene Objekt wird eine zusätzliche Methode hinzugefügt, runInBackground. Dadurch wird ein Thread gestartet und ein Objekt zurückgegeben, das automatisch ausgefüllt wird, wenn Inhalte in stdout / stderr geschrieben werden, ohne den Hauptthread zu blockieren.
Genießen!
quelle