2012-06-21 4 views
18

Tengo un script de Python que está usando 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 salida a mi terminal de Linux que me gustaría suprimir. He intentado redirigir stdout/stderr a través de;Suprimir la impresión de stdout/stderr de las funciones de Python

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

pero esto no alcanza la salida. Creo que las funciones a las que llamo vía Python (rogue_function() desde arriba) son realmente envoltorios para código C compilado, que en realidad están haciendo la impresión.

¿Alguien sabe de una forma en que puedo hacer una "captura profunda" de cualquier impresión entregada a stdout/stderr por una función (y cualquier subfunción que funcione con llamadas)?

ACTUALIZACIÓN:

que terminó tomando el método descrito en la respuesta de su elección y escribir un gestor 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 utilizar este que acaba:

with suppress_stdout_stderr(): 
    rogue_function() 

Esto funciona "bastante bien". Suprime la impresión de las funciones deshonestas que atestaban mi script. Al probarlo, me di cuenta de que permite excepciones sobresalientes, así como algunas impresiones de registradores, y no estoy del todo claro por qué. Creo que tiene algo que ver con cuando se envían estos mensajes a stdout/stderr (creo que sucede después de que sale mi gestor de contexto). Si alguien puede confirmar esto, yo estaría interesado en escuchar los detalles ...

+1

las hace [este enfoque] (http://stackoverflow.com/a/978264/344821) (de la barra lateral relacionada) ¿funciona? – Dougal

+0

En lugar de establecer 'sys.stdout' en' StringIO() ', ¿ha intentado configurarlo en un archivo? es decir 'sys.stdout = open ('log.txt', 'w')' – carmenism

+0

Dougal, gracias, parece prometedor, lo intentaré mañana. nullpointer, intenté dirigirlo a una clase NullPointer() personalizada, y eso tampoco funcionó. – jeremiahbuddha

Respuesta

5

This approach (encontrado a través de la barra lateral relacionada) podría funcionar. Reasigna los descriptores de archivo en lugar de solo las envolturas a ellos en sys.stdout, etc.

-2

Si está ejecutando este script en una máquina basada en Linux, debe ser capaz de:

$> ./runscript.py > output.txt 
+0

No deseo suprimir todos los resultados generados por el script, solo el resultado espurio generado por estas funciones particulares. De lo contrario, sí, esta sería la solución más simple ... – jeremiahbuddha

+0

¿Ayudaría esto? Agregar una impresión antes y después de esa función en particular. Analizar la salida con una expresión regular para deshacerse de todo entre las copias agregadas por encima de – GeneralBecos

1

¿Intentaste redirigir a stderr también? p.

sys.stdout = StringIO(); 
sys.stderr = StringIO(); 
foo(bar); 
sys.stdout = sys.__stdout__; # These are provided by python 
sys.stderr = sys.__stderr__; 

También utilizando StringIO podría utilizar memoria extra. Puede usar un dispositivo ficticio en su lugar (por ejemplo, http://coreygoldberg.blogspot.com/2009/05/python-redirect-or-turn-off-stdout-and.html).

+0

Sí, probé stdout y stderr, no hay dados ... – jeremiahbuddha

+0

Es posible que el código C escriba directamente en stdout; probablemente no puedas redirigir eso. Lo siento. – Bob

1

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

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 para eso he creado esto es a this blog post. Similar a la tuya, creo.

que utilizar de esta manera en un setup.py:

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

realmente no solicitadas por el OP, pero necesitaba para ocultar y almacenar la salida, e hicieron como sigue:

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 sólo con Pitón n 3)

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

+0

¡esta debería ser la respuesta aceptada para python 3! también se podría agregar seld._stderr == sys.stderr; sys.stderr = auto <...> – neok

+0

@neok: generalizado un poco, gracias por la sugerencia –

Cuestiones relacionadas