2012-06-21 11 views
18

Ho uno script Python che utilizza alcune funzioni Python a scatola chiusa (ovvero non posso modificare queste funzioni) fornite dal mio datore di lavoro. Quando chiamo queste funzioni, stanno stampando l'output sul mio terminale Linux che vorrei eliminare. Ho provato a reindirizzare stdout/stderr tramite;Elimina stampa stdout/stderr dalle funzioni Python

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

ma questo non riesce a rilevare l'output. Penso che le funzioni che sto chiamando via-Python (rogue_function() da sopra) siano realmente wrapper per C-code compilato, che in realtà stanno facendo la stampa.

Qualcuno sa di un modo in cui posso eseguire una "cattura profonda" di qualsiasi stampa consegnata a stdout/stderr da una funzione (e da eventuali sottofunzioni che funzionano chiamate)?

UPDATE:

ho finito per prendere il metodo descritto nella risposta selezionata in basso e la scrittura di un gestore di contesto per sopprimere stdout e 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) 

Per utilizzare questo è sufficiente:

with suppress_stdout_stderr(): 
    rogue_function() 

Questo funziona "abbastanza bene". Sopprime la stampa dalle funzioni canaglia che ingombrano il mio script. Ho notato che durante il test è stato possibile superare le eccezioni sollevate e alcune stampe del logger, e non sono del tutto chiaro perché. Penso che abbia qualcosa a che fare con quando questi messaggi vengono inviati a stdout/stderr (penso che accada dopo che il mio gestore di contesto è uscito). Se qualcuno può confermare questo, sarei interessato a sentire i dettagli ...

+1

fa [questo approccio] (http://stackoverflow.com/a/978264/344821) (dal barra laterale correlata) lavoro? – Dougal

+0

Invece di impostare 'sys.stdout' su' StringIO() ', hai provato a impostarlo su un file? per esempio 'sys.stdout = open ('log.txt', 'w')' – carmenism

+0

Dougal, grazie, sembra promettente, lo proverò domani. nullpointer, ho provato a indirizzarlo a una classe NullPointer() personalizzata, e anche questo non ha funzionato. – jeremiahbuddha

risposta

5

This approach (trovato tramite la barra laterale correlata) potrebbe funzionare. Riassegna i descrittori di file anziché solo i wrapper in sys.stdout, ecc.

-2

Se si esegue questo script su una macchina basata su Linux, si dovrebbe essere in grado di:

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

Non voglio sopprimere tutto l'output generato dallo script, solo l'output spuri generato da queste particolari funzioni. Altrimenti, sì, questa sarebbe la soluzione più semplice ... – jeremiahbuddha

+0

Questo aiuto: aggiungi una stampa prima e dopo quella particolare funzione. Analizza l'output con una regex per eliminare tutto tra le stampe aggiunte sopra – GeneralBecos

1

hai provato reindirizzare anche lo stderr? ad es.

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

Anche l'utilizzo di StringIO potrebbe utilizzare memoria aggiuntiva. Puoi invece utilizzare un dispositivo fittizio (ad esempio http://coreygoldberg.blogspot.com/2009/05/python-redirect-or-turn-off-stdout-and.html).

+0

Sì, ho provato sia stdout che stderr, senza dadi ... – jeremiahbuddha

+0

Potrebbe essere che il codice C stia scrivendo direttamente su stdout; probabilmente non saresti in grado di reindirizzare quello. Scusate. – Bob

1

La mia soluzione è simile alla tua ma utilizza contextlib ed è un po 'più breve e più facile da capire (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() 

Il contesto per il motivo per cui ho creato questo è a this blog post. Simile al tuo, penso.

lo uso come questo in un setup.py:

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

Non proprio richiesto dal PO, ma avevo bisogno di nascondere e memorizzare l'uscita, e fatto come segue:

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

(testato solo con Pytho n 3)

EDIT: ora permette di selezionare stderr, stdout o entrambi, come in Hider([stdout, stderr])

+0

questa dovrebbe essere la risposta accettata per python 3! si potrebbe anche aggiungere seld._stderr == sys.stderr; sys.stderr = self <...> – neok

+0

@neok: generalizzato un po ', grazie per il suggerimento –

Problemi correlati