Python 3 TypeError: muss str sein, keine Bytes mit sys.stdout.write ()

69

Ich suchte nach einer Möglichkeit, einen externen Prozess über ein Python-Skript auszuführen und seine Standardnachrichten während der Ausführung zu drucken.
Der folgende Code funktioniert, druckt jedoch zur Laufzeit keine Standardausgabe. Beim Beenden wird folgende Fehlermeldung angezeigt:

sys.stdout.write (nächste Zeile) TypeError: muss str sein, keine Bytes

p = subprocess.Popen(["demo.exe"],stdout = subprocess.PIPE, stderr= subprocess.PIPE)    
# Poll process for new output until finished
while True:
    nextline = p.stdout.readline()
    if nextline == '' and p.poll() != None:
        break
    sys.stdout.write(nextline)
    sys.stdout.flush()

output = p.communicate()[0]
exitCode = p.returncode

Ich benutze Python 3.3.2

Michael IV
quelle
Mögliches Duplikat von Builtins.TypeError: muss str sein, nicht Bytes
200_success

Antworten:

114

Python 3 behandelt Strings etwas anders. Ursprünglich gab es nur einen Typ für Zeichenfolgen : str. Als Unicode in den 90er Jahren an Bedeutung gewann, wurde der neue unicodeTyp hinzugefügt, um Unicode zu handhaben, ohne den bereits vorhandenen Code 1 zu beschädigen . Dies ist praktisch dasselbe wie, strjedoch mit Multibyte-Unterstützung.

In Python 3 gibt es zwei verschiedene Typen:

  • Der bytesTyp. Dies ist nur eine Folge von Bytes. Python weiß nichts darüber, wie man dies als Zeichen interpretiert.
  • Der strTyp. Dies ist auch eine Folge von Bytes, aber Python weiß, wie diese Bytes als Zeichen interpretiert werden .
  • Der separate unicodeTyp wurde gelöscht. strunterstützt jetzt Unicode.

In Python 2 kann die implizite Annahme einer Codierung viele Probleme verursachen. Möglicherweise verwenden Sie die falsche Codierung, oder die Daten haben möglicherweise überhaupt keine Codierung (z. B. ein PNG-Bild).
Python explizit zu sagen, welche Codierung verwendet werden soll (oder explizit zu raten), ist oft viel besser und entspricht viel mehr der "Python-Philosophie" von " explizit ist besser als implizit ".

Diese Änderung ist nicht mit Python 2 kompatibel, da sich viele Rückgabewerte geändert haben, was zu subtilen Problemen wie diesem führt. Dies ist wahrscheinlich der Hauptgrund, warum die Einführung von Python 3 so langsam war. Da Python keine statische Typisierung 2 hat, ist es unmöglich, dies automatisch mit einem Skript (wie dem gebündelten 2to3) zu ändern .

  • Sie können konvertieren strzu bytesmit bytes('h€llo', 'utf-8'); das sollte produzieren b'H\xe2\x82\xacllo'. Beachten Sie, wie ein Zeichen in drei Bytes konvertiert wurde.
  • Sie können konvertieren byteszu strmit b'H\xe2\x82\xacllo'.decode('utf-8').

Natürlich ist UTF-8 in Ihrem Fall möglicherweise nicht der richtige Zeichensatz. Verwenden Sie daher unbedingt den richtigen.

In Ihrem spezifischen Code nextlineist vom Typ bytes, nicht strvom Lesen stdoutund stdinvon subprocessin Python 3 von strbis geändert bytes. Dies liegt daran, dass Python nicht sicher sein kann, welche Codierung dies verwendet. Es verwendet wahrscheinlich dasselbe wie sys.stdin.encoding(die Codierung Ihres Systems), kann aber nicht sicher sein.

Sie müssen ersetzen:

sys.stdout.write(nextline)

mit:

sys.stdout.write(nextline.decode('utf-8'))

oder vielleicht:

sys.stdout.write(nextline.decode(sys.stdout.encoding))

Sie werden auch ändern müssen , if nextline == ''um if nextline == b''da:

>>> '' == b''
False

Siehe auch Python 3 ChangeLog , PEP 358 und PEP 3112 .


1 Es gibt einige nette Tricks, die Sie mit ASCII machen können, die Sie mit Multibyte-Zeichensätzen nicht machen können. Das bekannteste Beispiel ist das "xor mit Leerzeichen zum Umschalten der Groß- chr(ord('a') ^ ord(' ')) == 'A'und Kleinschreibung" (z. B. ) und "Setzen Sie das 6. Bit, um ein Steuerzeichen zu erstellen" (z ord('\t') + ord('@') == ord('I'). B. ). ASCII wurde in einer Zeit entwickelt, in der die Manipulation einzelner Bits eine Operation mit nicht zu vernachlässigenden Auswirkungen auf die Leistung war.

2 Ja, Sie können Funktionsanmerkungen verwenden, aber es ist eine vergleichsweise neue Funktion, die nur wenig verwendet wird.

Martin Tournoij
quelle
+1 für den Tipp! Übrigens sehe ich, dass ich den Standard erst am Ende des Programms bekomme. Können Sie mir sagen, warum er nicht in Echtzeit gedruckt wird?
Michael IV
Ohne zu wissen, welche demo.exeAusgaben und was genau Sie wollen, ist es schwierig, Ihre Frage zu beantworten, da nicht klar ist, was die Frage ist :-) Beachten Sie jedoch, dass readline()eine Zeile gelesen wird , das heißt, sie liest weiter, bis sie auf eine neue Zeile stößt. was unter Windows, glaube ich, ist \r\nund nicht\n
Martin Tournoij
Ich versuche, demo.exe stdout-Nachrichten zu drucken, während es in Python ausgeführt wird. Derzeit druckt Python demo.exe stdout nur bei Beendigung des Unterprozesses
Michael IV
@MichaelIV weiß nicht, warum Sie keine Ausgabe haben. Ich habe es getestet und es funktioniert für mich. Ich habe es auch unter Windows getestet, um sicherzugehen. Achten Sie darauf , dass demo.exeAusgänge \r\nZeilenende, und das demo.exeist auch stdout Spülung.
Martin Tournoij
32

Während die akzeptierte Antwort gut funktioniert, wenn die Bytes, die Sie aus Ihrem Unterprozess haben, mit sys.stdout.encoding(oder einer kompatiblen Codierung, wie das Lesen von einem Tool, das ASCII ausgibt und Ihr Standardout UTF-8 verwendet) codiert werden , ist die richtige Methode, beliebige Bytes in Standardausgabe zu schreiben ist:

sys.stdout.buffer.write(some_bytes_object)

Dadurch werden die Bytes einfach so ausgegeben, wie sie sind, ohne zu versuchen, sie als Text-in-Some-Codierung zu behandeln.

Wooble
quelle
Im Rahmen einer Migration würde ich diese Antwort als die richtige akzeptieren. Zum Beispiel kann ich nicht ändern, wie das CSV-Modul seine Daten schreibt. Meiner Meinung nach ist der beste Weg, Daten zu übergeben, wie sie zu stdout sind. Danke für dieses Stück!
Davidmpaz