2009-08-02 15 views
94

Estoy usando Python's ftplib para escribir un pequeño cliente FTP, pero algunas de las funciones en el paquete no devuelven resultados de cadenas, pero imprime a stdout. Quiero redirigir stdout a un objeto del que podré leer el resultado.¿Puedo redirigir el stdout en python a algún tipo de buffer de cadena?

stdout puede ser redirigido a cualquier archivo normal con:

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

pero yo prefiero un método que no utiliza la unidad local.

Estoy buscando algo como el BufferedReader en Java que se puede utilizar para envolver un búfer en una secuencia.

+0

no creo que 'stdout = O pen ("file", "a") 'por sí mismo redirigirá cualquier cosa. – Alexey

Respuesta

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, no necesita mantener una referencia al objeto 'stdout' original, ya que siempre está disponible en' sys .__ stdout__'. Ver http://docs.python.org/library/sys.html#sys.__stdout__. –

+71

Bueno, ese es un debate interesante. El stdout original absoluto está disponible, pero al reemplazarlo así, es mejor usar un guardado explícito como lo he hecho, ya que alguien más podría haber reemplazado a stdout y si usa __stdout__, golpearía su reemplazo. –

+5

¿esta operación en un hilo alterará el comportamiento de otros hilos? Quiero decir, ¿es threadsafe? –

34

sólo para añadir a la respuesta de Ned arriba: se puede usar esto para redirigir la salida a cualquier objeto que implemente un método de escritura (str).

Esto se puede utilizar con buenos resultados para "capturar" la salida stdout en una aplicación GUI.

Aquí está un ejemplo tonto en 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

Esto no funciona, bloquea la aplicación, pero no puedo decir por qué. –

+3

Funciona para mí con Python 2.6 y PyQT4. Parece extraño votar código de trabajo cuando no se puede decir por qué no funciona. –

+5

¡no olvide agregar flush() también! – Will

4

A partir de Python 2.6 se puede utilizar cualquier cosa aplicación del TextIOBase API del módulo io como un reemplazo. Esta solución también le permite usar sys.stdout.buffer.write() en Python 3 para escribir (ya) cadenas de bytes codificadas en stdout (consulte stdout in Python 3). El uso de StringIO no funcionaría, porque ni sys.stdout.encoding ni sys.stdout.buffer estarían disponibles.

Una solución usando 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 

Esta solución funciona para Python 2> = 2.6 y Python 3.

Tenga en cuenta que nuestros nuevos sys.stdout.write() sólo acepta cadenas Unicode y sys.stdout.buffer.write() sólo se acepta cadenas de bytes. Este podría no ser el caso para el código anterior, pero a menudo es el caso para el código que está diseñado para ejecutarse en Python 2 y 3 sin cambios, que de nuevo a menudo hace uso de sys.stdout.buffer.

Se puede construir una ligera variación que acepta cadenas Unicode y de bytes 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) 

Usted no tiene que establecer la codificación de la memoria intermedia de la sys.stdout.encoding, pero esto ayuda al utilizar este método para probar/comparar la salida del script.

+0

Esta respuesta me ayudó a configurar un parámetro stdout de un objeto Environment para usar con core.py de Httpie. – fragorl

2

Este método restaura sys.stdout incluso si hay una excepción. También obtiene cualquier salida antes de la excepción.

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 

sólo probado esto en Python 2.7.10, pero io.BytesIO se rumorea que la replacement for StringIO in Python 3.

2

En Python3.6, los módulos StringIO y cStringIO se han ido, usted debe utilizar io.StringIO instead.So usted debe hacer esto como la primera respuesta:

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

Puede mejorar la calidad de su Respuesta explicando cómo funciona el código anterior y cómo esto mejora la situación del Interlocutor. – toonice

+0

Gracias su consejo. – haofly

0

Un gestor de contexto para 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 como esto:

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