Redireccionar temporalmente stdout / stderr

¿Es posible redireccionar temporalmente stdout / stderr en Python (es decir, por la duración de un método)?

Editar:

El problema con las soluciones actuales (que primero recordé pero luego olvidé) es que no se redirigen ; más bien, simplemente reemplazan las streams en su totalidad. Por lo tanto, si un método tiene una copia local de una variable por cualquier razón (por ejemplo, porque la secuencia se pasó como un parámetro a algo), no funcionará.

¿Alguna solución?

Para resolver el problema de que alguna función podría haber almacenado la secuencia sys.stdout como una variable local y, por lo tanto, reemplazar la sys.stdout global no funcionará dentro de esa función, podría redirigir a un nivel de descriptor de archivo ( sys.stdout.fileno() ) p.ej:

 from __future__ import print_function import os import sys def some_function_with_cached_sys_stdout(stdout=sys.stdout): print('cached stdout', file=stdout) with stdout_redirected(to=os.devnull), merged_stderr_stdout(): print('stdout goes to devnull') some_function_with_cached_sys_stdout() print('stderr also goes to stdout that goes to devnull', file=sys.stderr) print('stdout is back') some_function_with_cached_sys_stdout() print('stderr is back', file=sys.stderr) 

stdout_redirected() redirige todos los resultados de sys.stdout.fileno() a un nombre de archivo, objeto de archivo o descriptor de archivo os.devnull ( os.devnull en el ejemplo).

stdout_redirected() y merged_stderr_stdout() se definen aquí .

También puede poner la lógica de redirección en un administrador de contexto.

 import os import sys class RedirectStdStreams(object): def __init__(self, stdout=None, stderr=None): self._stdout = stdout or sys.stdout self._stderr = stderr or sys.stderr def __enter__(self): self.old_stdout, self.old_stderr = sys.stdout, sys.stderr self.old_stdout.flush(); self.old_stderr.flush() sys.stdout, sys.stderr = self._stdout, self._stderr def __exit__(self, exc_type, exc_value, traceback): self._stdout.flush(); self._stderr.flush() sys.stdout = self.old_stdout sys.stderr = self.old_stderr if __name__ == '__main__': devnull = open(os.devnull, 'w') print('Fubar') with RedirectStdStreams(stdout=devnull, stderr=devnull): print("You'll never see me") print("I'm back!") 

No estoy seguro de lo que significa la redirección temporal. Pero, puedes reasignar secuencias como esta y restablecerlas.

 temp = sys.stdout sys.stdout = sys.stderr sys.stderr = temp 

También para escribir en sys.stderr dentro de stmts de impresión como este.

  print >> sys.stderr, "Error in atexit._run_exitfuncs:" 

La impresión regular será a la salida estándar.

Es posible con un decorador como el siguiente:

 import sys def redirect_stderr_stdout(stderr=sys.stderr, stdout=sys.stdout): def wrap(f): def newf(*args, **kwargs): old_stderr, old_stdout = sys.stderr, sys.stdout sys.stderr = stderr sys.stdout = stdout try: return f(*args, **kwargs) finally: sys.stderr, sys.stdout = old_stderr, old_stdout return newf return wrap 

Usar como:

 @redirect_stderr_stdout(some_logging_stream, the_console): def fun(...): # whatever 

o, si no desea modificar la fuente por fun , llámela directamente como

 redirect_stderr_stdout(some_logging_stream, the_console)(fun) 

Pero tenga en cuenta que esto no es seguro para subprocesos.

a partir de python 3.4 existe el administrador de contexto contextlib.redirect_stdout :

 from contextlib import redirect_stdout with open('yourfile.txt', 'w') as f: with redirect_stdout(f): # do stuff... 

para silenciar completamente stdout esto funciona:

 from contextlib import redirect_stdout with redirect_stdout(None): # do stuff... 

Aquí hay un administrador de contexto que encontré útil. Lo bueno de esto es que puede usarlo con la statement with y también maneja la redirección de procesos secundarios.

 import contextlib @contextlib.contextmanager def stdchannel_redirected(stdchannel, dest_filename): """ A context manager to temporarily redirect stdout or stderr eg: with stdchannel_redirected(sys.stderr, os.devnull): ... """ try: oldstdchannel = os.dup(stdchannel.fileno()) dest_file = open(dest_filename, 'w') os.dup2(dest_file.fileno(), stdchannel.fileno()) yield finally: if oldstdchannel is not None: os.dup2(oldstdchannel, stdchannel.fileno()) if dest_file is not None: dest_file.close() 

El contexto por el que creé esto está en esta publicación del blog .

Raymond Hettinger nos muestra una mejor manera [1]:

 import sys with open(filepath + filename, "w") as f: #replace filepath & filename with f as sys.stdout: print("print this to file") #will be written to filename & -path 

Después del bloque with, se restablecerá sys.stdout.

[1]: http://www.youtube.com/watch?v=OSGv2VnC0go&list=PLQZM27HgcgT-6D0w6arhnGdSHDcSmQ8r3

Usaremos la syntax PHP de las funciones ob_start y ob_get_contents en python3, y redirigiremos la entrada a un archivo.

Las salidas se almacenan en un archivo, también se puede utilizar cualquier tipo de flujo.

 from functools import partial output_buffer = None print_orig = print def ob_start(fname="print.txt"): global print global output_buffer print = partial(print_orig, file=output_buffer) output_buffer = open(fname, 'w') def ob_end(): global output_buffer close(output_buffer) print = print_orig def ob_get_contents(fname="print.txt"): return open(fname, 'r').read() 

Uso:

 print ("Hi John") ob_start() print ("Hi John") ob_end() print (ob_get_contents().replace("Hi", "Bye")) 

Imprimiría

Hola John Bye John

Mire contextlib.redirect_stdout (new_target) y contextlib.redirect_stderr (new_target) . contextlib.redirect_stderr (new_target) es nuevo en Python 3.5.