popen2 und bufsize

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
divinity
User
Beiträge: 3
Registriert: Dienstag 4. Juli 2006, 09:35

Hi allerseits,

habe eine ganze Weile im Forum gestöbert, bin auch über eine Reihe von Threads gestolpert, wo mein Problem zumindest periphär behandelt wurde, ne Lösung hab ich aber nicht gefunden.
Daher brech ich das jetzt hier mal runter auf den Kern und bring ein Beispiel:

Ich starte aus Python heraus ein externes Programm. Ich benutze dazu popen2.Popen3, weil ich so a) an die PID komme, b) mein Python-Skript weiterläuft und ich nur gelegentlich mit poll() checken kann, ob das externe Programm fertig ist und ich c) saubere filedescriptors für stdout, stdin und stderr habe.
Funktioniert auch alles ganz hervorragend, solange nicht ein spezieller Fall eintritt. Wenn mein externes Programm mehr als 8192 Zeichen an stdout schreibt, dann ist der Puffer voll und das Programm wartet, bis irgendwer kommt und den Puffer leermacht. Ich sitzte aber auf der Parent-Seite und wundere mich, warum mir ein poll() erzählt, das Child würde nie fertig werden.
Jetzt könnte ich einfach mit fromchild.read() den Puffer leerlesen, read() wartet aber auch ein EOF, bevor es zurückkehrt, ich blockiere mir also den Parent. Ausserdem kann ich ja erstmal gar nicht sagen, ob das Child noch läuft oder wegen eines vollen Buffers schläft...

Zum Beispiel:
Erstmal eine mögliche Child-Applikation:

Code: Alles auswählen

import sys, time
output_size = 8191
for x in range(output_size):
	sys.stdout.write("x")
time.sleep(5)
dann eine einfache Parent-Applikation:

Code: Alles auswählen

import popen2, time
app = popen2.Popen3("child.py")
while app.poll() == -1:
	time.sleep(1)
Setze ich output_size auf < 8192 endet auch der Parent nach ein paar Sekunden, setzte ich es allerdings auf >8192, dann werden weder Parent noch Child je enden. Child wartet, dass der Puffer geleert wird, Parent wartet, dass Child endlich einen Exit-Code liefert.

Nun könnte ich Parent erweitern:

Code: Alles auswählen

import popen2, time
app = popen2.Popen3("child.py")
stdout = ''
while app.poll() == -1:
	stdout += app.fromchild.read()
	time.sleep(1)
Das Problem hier ist nur, dass ich beim ersten read() in eine blockierende Funktion reinrutsche, die wartet, bis Child ein EOF sendet. Genau das will ich ja gerade nicht.

Im Übrigen scheint (zumindest unter HP-UX 11) die Angabe der bufsize (3. Parm beim Popen3) keinen Einfluss auf den tatsächlichen Puffer haben.

Gibt es irgendeine Möglichkeit, read() mitzugeben, dass es nur soviel lesen soll, wie gerade im Puffer ist? Kann man read() alternativ einen timeout mitgeben? Alternativ kann man Popen3 tatsächlich den Puffer vergrößern?
Ich bin bei meiner Recherche hier im Forum mehrmals über select.select() gestolpert. Leider bin ich nicht so tief in Python, als dass ich das Konzept verstanden hätte...

Am liebsten wär mir, wenn ich nicht gelegentlich noch den Puffer leeren müsste, sondern Popen3 einen entsprechend Großen zur Verfügung stellt.
Kann mir da jemand weiterhelfen?

Gruß
D
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Spontane Idee: Was, wenn du immer nur ein einzelnes Zeichen liest? Also in deinem letzten Codeschnippsel das hier verwendest:

Code: Alles auswählen

stdout += app.fromchild.read(1)
divinity
User
Beiträge: 3
Registriert: Dienstag 4. Juli 2006, 09:35

Leider kann ich auch damit schon mein Parent blockieren, wenn z.B. das Child eine Stunde kein einziges Byte schreibt...
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Dann also select:

Code: Alles auswählen

[read, write, error] = select.select([mein_read_objekt], [], [], timeout=5);
if mein_read_objekt in read:
    #lesen...
else:
    #es kann nicht gelesen werden
Man kann bei select auf verschiedene Objeke warten, oben nur der Fall, dass man von einem Objekt auf input wartet.
divinity
User
Beiträge: 3
Registriert: Dienstag 4. Juli 2006, 09:35

Genau das ist es...

Dank dir für das brauchbare Beispiel. Jetzt bin ich endlich dahinter gestiegen, was select wirklich macht.

Super - Problem gelöst!
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

versuch es mal mit sys.stdout.flush()

das zwingt sys.stdout zur ausgabe und leert dadurch den speicher...

mfg cime
Antworten