tipos propias modulos lista librerias globales funciones basicas python stdout

propias - tipos de funciones en python



Suprimir la impresiĆ³n stdout/stderr de las funciones de Python (7)

Tengo un script de Python que utiliza algunas funciones de Python de caja cerrada (es decir, no puedo editar estas funciones) proporcionadas por mi empleador. Cuando llamo a estas funciones, están imprimiendo la salida a mi terminal de Linux que me gustaría suprimir. He intentado redireccionar stdout / stderr vía;

orig_out = sys.stdout sys.stdout = StringIO() rogue_function() sys.stdout = orig_out

pero esto no logra captar la salida. Creo que las funciones a las que llamo via-Python (rogue_function () desde arriba) son realmente envoltorios para el código C compilado, que en realidad están imprimiendo.

¿Alguien sabe de alguna manera que pueda hacer una "captura profunda" de cualquier impresión que se le dé a stdout / stderr mediante una función (y cualquier subfunción que la función llame)?

ACTUALIZACIÓN :

Terminé tomando el método descrito en la respuesta seleccionada a continuación y escribiendo un administrador de contexto para suprimir stdout y stderr:

# Define a context manager to suppress stdout and stderr. class suppress_stdout_stderr(object): '''''' A context manager for doing a "deep suppression" of stdout and stderr in Python, i.e. will suppress all print, even if the print originates in a compiled C/Fortran sub-function. This will not suppress raised exceptions, since exceptions are printed to stderr just before a script exits, and after the context manager has exited (at least, I think that is why it lets exceptions through). '''''' def __init__(self): # Open a pair of null files self.null_fds = [os.open(os.devnull,os.O_RDWR) for x in range(2)] # Save the actual stdout (1) and stderr (2) file descriptors. self.save_fds = [os.dup(1), os.dup(2)] def __enter__(self): # Assign the null pointers to stdout and stderr. os.dup2(self.null_fds[0],1) os.dup2(self.null_fds[1],2) def __exit__(self, *_): # Re-assign the real stdout/stderr back to (1) and (2) os.dup2(self.save_fds[0],1) os.dup2(self.save_fds[1],2) # Close all file descriptors for fd in self.null_fds + self.save_fds: os.close(fd)

Para usar esto solo tienes que:

with suppress_stdout_stderr(): rogue_function()

Esto funciona "bastante bien". No suprime la impresión de las funciones deshonestas que saturaban mi script. Noté que, al probarlo, permite detectar excepciones así como algunas impresiones de registradores, y no estoy del todo claro por qué. Creo que tiene algo que ver con el momento en que estos mensajes se envían a stdout / stderr (creo que ocurre después de que se cierre el administrador de contexto). Si alguien puede confirmar esto, me interesaría escuchar los detalles ...



A partir de Python 3.5 podemos hacer esto con un trabajo mínimo utilizando incorporaciones en contextlib , a saber, redirect_stdout y redirect_stderr . Solo necesitamos combinar estos dos gestores de contexto incorporados en un gestor de contexto personalizado, lo que se puede hacer fácilmente usando el bonito patrón en la respuesta de Martijn aquí . La redirección de ambas salidas a os.devnull debe ser lo suficientemente segura y portátil.

from contextlib import contextmanager,redirect_stderr,redirect_stdout from os import devnull @contextmanager def suppress_stdout_stderr(): """A context manager that redirects stdout and stderr to devnull""" with open(devnull, ''w'') as fnull: with redirect_stderr(fnull) as err, redirect_stdout(fnull) as out: yield (err, out)

Tenga en cuenta que la supresión de stderr todavía le dará un seguimiento completo cuando algo se rompa, lo cual es bueno:

import sys def rogue_function(): print(''spam to stdout'') print(''important warning'', file=sys.stderr) 1 + ''a'' return 42 with suppress_stdout_stderr(): rogue_function()

Cuando se ejecute lo anterior solo se imprimen.

Traceback (most recent call last): File "tmp.py", line 20, in <module> rogue_function() File "foo.py", line 16, in rogue_function 1 + ''a'' TypeError: unsupported operand type(s) for +: ''int'' and ''str''

a la terminal. Las excepciones no manejadas nunca deben pasar desapercibidas.


En realidad no fue solicitado por el OP, pero necesitaba ocultar y almacenar la salida, e hice lo siguiente:

from io import StringIO import sys class Hider: def __init__(self, channels=(''stdout'',)): self._stomach = StringIO() self._orig = {ch : None for ch in channels} def __enter__(self): for ch in self._orig: self._orig[ch] = getattr(sys, ch) setattr(sys, ch, self) return self def write(self, string): self._stomach.write(string) def flush(self): pass def autopsy(self): return self._stomach.getvalue() def __exit__(self, *args): for ch in self._orig: setattr(sys, ch, self._orig[ch])

Uso:

with Hider() as h: spammy_function() result = h.autopsy()

(probado solo con Python 3)

EDITAR: ahora permite seleccionar stderr , stdout o ambos, como en Hider([stdout, stderr])


Mi solución es similar a la tuya, pero usa contextlib y es un poco más corta y fácil de entender (IMHO).

import contextlib @contextlib.contextmanager def stdchannel_redirected(stdchannel, dest_filename): """ A context manager to temporarily redirect stdout or stderr e.g.: with stdchannel_redirected(sys.stderr, os.devnull): if compiler.has_function(''clock_gettime'', libraries=[''rt'']): libraries.append(''rt'') """ 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 de blog . Al igual que el tuyo creo.

Lo uso así en un setup.py :

with stdchannel_redirected(sys.stderr, os.devnull): if compiler.has_function(''clock_gettime'', libraries=[''rt'']): libraries.append(''rt'')


Python 3.6 versión de trabajo, probada con millones de supresiones sin ningún error

import os import sys class suppress_stdout_stderr(object): def __enter__(self): self.outnull_file = open(os.devnull, ''w'') self.errnull_file = open(os.devnull, ''w'') self.old_stdout_fileno_undup = sys.stdout.fileno() self.old_stderr_fileno_undup = sys.stderr.fileno() self.old_stdout_fileno = os.dup ( sys.stdout.fileno() ) self.old_stderr_fileno = os.dup ( sys.stderr.fileno() ) self.old_stdout = sys.stdout self.old_stderr = sys.stderr os.dup2 ( self.outnull_file.fileno(), self.old_stdout_fileno_undup ) os.dup2 ( self.errnull_file.fileno(), self.old_stderr_fileno_undup ) sys.stdout = self.outnull_file sys.stderr = self.errnull_file return self def __exit__(self, *_): sys.stdout = self.old_stdout sys.stderr = self.old_stderr os.dup2 ( self.old_stdout_fileno, self.old_stdout_fileno_undup ) os.dup2 ( self.old_stderr_fileno, self.old_stderr_fileno_undup ) os.close ( self.old_stdout_fileno ) os.close ( self.old_stderr_fileno ) self.outnull_file.close() self.errnull_file.close()


Si está ejecutando este script en una máquina basada en Linux, debería poder:

$> ./runscript.py > output.txt


Este enfoque (que se encuentra a través de la barra lateral relacionada) podría funcionar. Reasigna los descriptores de archivo en lugar de solo los envoltorios a ellos en sys.stdout, etc.