it-swarm.com.de

Kann ich das stdout in python in eine Art String-Puffer umleiten?

Ich benutze Pythons ftplib, um einen kleinen FTP-Client zu schreiben, aber einige der Funktionen im Paket geben keine Zeichenfolgen aus, sondern geben sie in stdout aus. Ich möchte stdout zu einem Objekt umleiten, von dem ich die Ausgabe lesen kann.

Ich weiß, dass stdout in jede normale Datei umgeleitet werden kann mit:

stdout = open("file", "a")

Ich bevorzuge jedoch eine Methode, bei der das lokale Laufwerk nicht verwendet wird.

Ich suche nach etwas wie dem BufferedReader in Java), das zum Umbrechen eines Puffers in einen Stream verwendet werden kann.

117
Avihu Turzion
from cStringIO import StringIO # Python3 use: from io import StringIO
import sys

old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()

# blah blah lots of code ...

sys.stdout = old_stdout

# examine mystdout.getvalue()
178
Ned Batchelder

Es gibt contextlib.redirect_stdout () - Funktion in Python 3.4:

import io
from contextlib import redirect_stdout

with io.StringIO() as buf, redirect_stdout(buf):
    print('redirected')
    output = buf.getvalue()

Hier ist Codebeispiel, das zeigt, wie es in älteren Python Versionen implementiert wird.

55
jfs

Um die Antwort von Ned oben zu ergänzen: Sie können diese Option verwenden, um die Ausgabe zu jedem Objekt umzuleiten, das eine write (str) -Methode implementiert .

Dies kann effektiv verwendet werden, um die Ausgabe von stdout in einer GUI-Anwendung abzufangen.

Hier ist ein albernes Beispiel in PyQt:

import sys
from PyQt4 import QtGui

class OutputWindow(QtGui.QPlainTextEdit):
    def write(self, txt):
        self.appendPlainText(str(txt))

app = QtGui.QApplication(sys.argv)
out = OutputWindow()
sys.stdout=out
out.show()
print "hello world !"
34

Beginnend mit Python 2.6 können Sie alles verwenden, was die TextIOBase API aus dem io-Modul als Ersatz implementiert. Mit dieser Lösung können Sie auch sys.stdout.buffer.write() in Python 3, um (bereits) codierte Byte-Strings in stdout zu schreiben (siehe stdout in Python ) Die Verwendung von StringIO würde dann nicht funktionieren, da weder sys.stdout.encoding Noch sys.stdout.buffer Verfügbar wären.

Eine Lösung mit TextIOWrapper:

import sys
from io import TextIOWrapper, BytesIO

# setup the environment
old_stdout = sys.stdout
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)

# do something that writes to stdout or stdout.buffer

# get output
sys.stdout.seek(0)      # jump to the start
out = sys.stdout.read() # read output

# restore stdout
sys.stdout.close()
sys.stdout = old_stdout

Diese Lösung funktioniert für Python 2> = 2.6 und Python 3.

Bitte beachten Sie, dass unsere neue sys.stdout.write() nur Unicode-Zeichenfolgen und sys.stdout.buffer.write() nur Byte-Zeichenfolgen akzeptiert. Dies ist möglicherweise nicht der Fall für alten Code, aber häufig für Code, der für die Ausführung auf Python 2 und 3 ohne Änderungen erstellt wurde, wobei wiederum häufig sys.stdout.buffer.

Sie können eine kleine Variation erstellen, die Unicode- und Byte-Zeichenfolgen für write() akzeptiert:

class StdoutBuffer(TextIOWrapper):
    def write(self, string):
        try:
            return super(StdoutBuffer, self).write(string)
        except TypeError:
            # redirect encoded byte strings directly to buffer
            return super(StdoutBuffer, self).buffer.write(string)

Sie müssen die Kodierung des Puffers sys.stdout.encoding nicht festlegen, dies ist jedoch hilfreich, wenn Sie diese Methode zum Testen/Vergleichen der Skriptausgabe verwenden.

6
JonnyJD

Diese Methode stellt sys.stdout wieder her, auch wenn es eine Ausnahme gibt. Es wird auch jede Ausgabe vor der Ausnahme abgerufen.

import io
import sys

real_stdout = sys.stdout
fake_stdout = io.BytesIO()   # or perhaps io.StringIO()
try:
    sys.stdout = fake_stdout
    # do what you have to do to create some output
finally:
    sys.stdout = real_stdout
    output_string = fake_stdout.getvalue()
    fake_stdout.close()
    # do what you want with the output_string

Getestet in Python 2.7.10 mit io.BytesIO()

Getestet in Python 3.6.4 mit io.StringIO()


Bob, hinzugefügt für einen Fall, wenn Sie glauben, dass etwas aus dem Experimentieren mit geändertem/erweitertem Code in irgendeiner Weise interessant werden könnte, andernfalls können Sie es gerne löschen

Ad informandum ... ein paar Bemerkungen aus ausgedehnten Experimenten während der Suche nach praktikablen Mechanismen zum "Greifen" von Ausgaben, die von numexpr.print_versions() direkt an <stdout> Geleitet werden (wenn eine Bereinigung der GUI und das Sammeln von Details erforderlich sind) in Debugging-Bericht)

# THIS WORKS AS HELL: as Bob Stein proposed years ago:
#  py2 SURPRISEDaBIT:
#
import io
import sys
#
real_stdout = sys.stdout                        #           Push <stdout> ( store to REAL_ )
fake_stdout = io.BytesIO()                      #           .DEF FAKE_
try:                                            # FUSED .TRY:
    sys.stdout.flush()                          #           .flush() before
    sys.stdout = fake_stdout                    #           .SET <stdout> to use FAKE_
    # ----------------------------------------- #           +    do what you gotta do to create some output
    print 123456789                             #           + 
    import  numexpr                             #           + 
    QuantFX.numexpr.__version__                 #           + [3] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
    QuantFX.numexpr.print_versions()            #           + [4] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
    _ = os.system( 'echo os.system() redir-ed' )#           + [1] via real_stdout                                 + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
    _ = os.write(  sys.stderr.fileno(),         #           + [2] via      stderr                                 + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
                       b'os.write()  redir-ed' )#  *OTHERWISE, if via fake_stdout, EXC <_io.BytesIO object at 0x02C0BB10> Traceback (most recent call last):
    # ----------------------------------------- #           ?                              io.UnsupportedOperation: fileno
    #'''                                                    ? YET:        <_io.BytesIO object at 0x02C0BB10> has a .fileno() method listed
    #>>> 'fileno' in dir( sys.stdout )       -> True        ? HAS IT ADVERTISED,
    #>>> pass;            sys.stdout.fileno  -> <built-in method fileno of _io.BytesIO object at 0x02C0BB10>
    #>>> pass;            sys.stdout.fileno()-> Traceback (most recent call last):
    #                                             File "<stdin>", line 1, in <module>
    #                                           io.UnsupportedOperation: fileno
    #                                                       ? BUT REFUSES TO USE IT
    #'''
finally:                                        # == FINALLY:
    sys.stdout.flush()                          #           .flush() before ret'd back REAL_
    sys.stdout = real_stdout                    #           .SET <stdout> to use POP'd REAL_
    sys.stdout.flush()                          #           .flush() after  ret'd back REAL_
    out_string = fake_stdout.getvalue()         #           .GET string           from FAKE_
    fake_stdout.close()                         #                <FD>.close()
    # +++++++++++++++++++++++++++++++++++++     # do what you want with the out_string
    #
    print "\n{0:}\n{1:}{0:}".format( 60 * "/\\",# "LATE" deferred print the out_string at the very end reached -> real_stdout
                                     out_string #                   
                                     )
'''
PASS'd:::::
...
os.system() redir-ed
os.write()  redir-ed
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version:   2.5
NumPy version:     1.10.4
Python version:    2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU?     True
VML available?     True
VML/MKL version:   Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
>>>

EXC'd :::::
...
os.system() redir-ed
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version:   2.5
NumPy version:     1.10.4
Python version:    2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU?     True
VML available?     True
VML/MKL version:   Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\

Traceback (most recent call last):
  File "<stdin>", line 9, in <module>
io.UnsupportedOperation: fileno
'''
5
Bob Stein

In Python3.6 sind die Module StringIO und cStringIO weg. Verwenden Sie io.StringIO stattdessen.So sollten Sie dies wie die erste Antwort tun:

import sys
from io import StringIO

old_stdout = sys.stdout
old_stderr = sys.stderr
my_stdout = sys.stdout = StringIO()
my_stderr = sys.stderr = StringIO()

# blah blah lots of code ...

sys.stdout = self.old_stdout
sys.stderr = self.old_stderr

// if you want to see the value of redirect output, be sure the std output is turn back
print(my_stdout.getvalue())
print(my_stderr.getvalue())

my_stdout.close()
my_stderr.close()
4
haofly

Verwenden Sie pipe() und schreiben Sie in den entsprechenden Dateideskriptor.

https://docs.python.org/library/os.html#file-descriptor-operations

Ein Kontextmanager für python3:

import sys
from io import StringIO


class RedirectedStdout:
    def __init__(self):
        self._stdout = None
        self._string_io = None

    def __enter__(self):
        self._stdout = sys.stdout
        sys.stdout = self._string_io = StringIO()
        return self

    def __exit__(self, type, value, traceback):
        sys.stdout = self._stdout

    def __str__(self):
        return self._string_io.getvalue()

verwenden Sie wie folgt:

>>> with RedirectedStdout() as out:
>>>     print('asdf')
>>>     s = str(out)
>>>     print('bsdf')
>>> print(s, out)
'asdf\n' 'asdf\nbsdf\n'
2
Bob