2009-08-02 7 views
94

Sto usando python ftplib per scrivere un piccolo client FTP, ma alcune delle funzioni nel pacchetto non restituiscono l'output di stringa, ma stampano su stdout. Voglio reindirizzare stdout su un oggetto da cui potrò leggere l'output.Posso reindirizzare lo stdout in python in una sorta di buffer di stringa?

so stdout può essere reindirizzato a qualsiasi file regolare con:

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

ma io preferisco un metodo che non utilizza l'unità locale.

Sto cercando qualcosa come il BufferedReader in Java che può essere utilizzato per avvolgere un buffer in un flusso.

+0

non credo 'stdout = o pen ("file", "a") 'di per sé reindirizzerà qualsiasi cosa. – Alexey

risposta

148
from cStringIO import StringIO 
import sys 

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

# blah blah lots of code ... 

sys.stdout = old_stdout 

# examine mystdout.getvalue() 
+40

+1, non è necessario mantenere un riferimento all'oggetto 'stdout' originale, poiché è sempre disponibile in' sys .__ stdout__'. Vedi http://docs.python.org/library/sys.html#sys.__stdout__. –

+71

Bene, questo è un dibattito interessante. Lo stdout assoluto assoluto è disponibile, ma quando si sostituisce in questo modo, è meglio usare un salvataggio esplicito come ho fatto, dal momento che qualcun altro potrebbe aver sostituito lo stdout e se si usa __stdout__, si sostituirà la sostituzione. –

+5

questa operazione in un thread altererebbe il comportamento di altri thread? Voglio dire che è sicuro? –

34

solo per aggiungere alla risposta di Ned sopra: è possibile utilizzare questo per reindirizzare l'output qualsiasi oggetto che implementa un metodo write (str).

Questo può essere usato con buoni risultati per "catturare" l'output di stdout in un'applicazione GUI.

Ecco un esempio stupido 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 !" 
+0

Questo non funziona, blocca l'app, ma non posso dire perché. –

+3

Funziona per me con Python 2.6 e PyQT4. Sembra strano votare giù codice funzionante quando non si può capire perché non funziona! –

+5

non dimenticare di aggiungere anche flush()! – Will

4

A partire da Python 2.6 è possibile utilizzare qualsiasi cosa l'attuazione del TextIOBase API dal modulo IO come una sostituzione. Questa soluzione consente inoltre di utilizzare sys.stdout.buffer.write() in Python 3 per scrivere (già) stringhe di byte codificate su stdout (vedere stdout in Python 3). L'utilizzo di StringIO non funzionerebbe quindi, perché né né sys.stdout.buffer sarebbero disponibili.

Una soluzione con 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 

Questa soluzione funziona per Python 2> = 2.6 e Python 3.

Si prega di notare che i nostri nuovi sys.stdout.write() accetta solo le stringhe Unicode e sys.stdout.buffer.write() accetta solo stringhe di byte. Questo potrebbe non essere il caso per il vecchio codice, ma è spesso il caso per il codice che è stato creato per funzionare su Python 2 e 3 senza modifiche, che spesso utilizza lo sys.stdout.buffer.

È possibile costruire una piccola variazione che accetta Unicode e byte stringhe per write():

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) 

Non è necessario per impostare la codifica del tampone del sys.stdout.encoding, ma questo aiuta quando si utilizza questo metodo per testare/confrontare l'output dello script.

+0

Questa risposta mi ha aiutato durante l'impostazione di un parametro stdout dell'oggetto Environment da utilizzare con core.py di Httpie. – fragorl

2

Questo metodo ripristina sys.stdout anche se c'è un'eccezione. Ottiene anche qualsiasi output prima dell'eccezione.

real_stdout = sys.stdout 
fake_stdout = io.BytesIO() 
try: 
    sys.stdout = fake_stdout 
    # do what you gotta 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 

ho provato solo questo in Python 2.7.10, ma io.BytesIO si dice di essere il replacement for StringIO in Python 3.

2

In Python3.6, i moduli StringIO e cStringIO ci sono più, si dovrebbe usare io.StringIO instead.So si dovrebbe fare come la prima risposta:

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() 
+0

Potresti migliorare la qualità della tua risposta spiegando come funziona il codice sopra e come questo è un miglioramento rispetto alla situazione del Questionario. – toonice

+0

Grazie a voi. – haofly

0

Un responsabile di contesto per python3:

import sys 
from io import StringIO 


class redirected_stdout: 
    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 

    @property 
    def string(self): 
     return self._string_io.getvalue() 

uso in questo modo:

>>> with redirected_stdout() as out: 
>>>  print('asdf') 
>>>  s = out.string 
>>>  print('bsdf') 
>>> print(s, out.string) 
'asdf\n' 'asdf\nbsdf\n' 
Problemi correlati