2012-06-21 16 views
18

Ich habe ein Python-Skript, das einige geschlossene Python-Funktionen (d. H. Ich kann diese Funktionen nicht bearbeiten) von meinem Arbeitgeber zur Verfügung gestellt. Wenn ich diese Funktionen aufrufen, drucken sie die Ausgabe an mein Linux-Terminal, die ich gerne unterdrücken möchte. Ich habe versucht, stdout/stderr umzuleiten;Unterdrücken stdout/stderr Drucken von Python-Funktionen

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

aber dies schlägt die Ausgabe nicht. Ich denke, die Funktionen, die ich via-Python (rogue_function() von oben) anrufe, sind wirklich Wrapper für kompilierten C-Code, die eigentlich das Drucken machen.

Kennt jemand eine Art, wie ich eine "Deep-Capture" von jedem Druck an stdout/stderr von einer Funktion (und alle Unterfunktionen, die Anrufe Funktion) machen kann tun?

UPDATE:

Ich beendete das Verfahren in der ausgewählten Antwort skizzierte Aufnahme unten und das Schreiben eines Kontext-Manager stdout und stderr zu unterdrücken:

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

dies nur Sie verwenden:

with suppress_stdout_stderr(): 
    rogue_function() 

Das funktioniert "ziemlich gut". Es unterdrückt den Ausdruck von den Rogue-Funktionen, die mein Skript überladen haben. Ich habe es beim Testen festgestellt, dass es sowohl erhöhte Ausnahmen als auch einen Logger-Druck durchlässt, und mir ist nicht ganz klar, warum. Ich denke, es hat etwas mit zu tun, wenn diese Nachrichten an stdout/stderr gesendet werden (ich denke, es passiert, nachdem mein Kontextmanager beendet). Wenn jemand dies bestätigen können, ich Interesse hätte, um die Details zu hören ...

+1

Does [dieser Ansatz] (http://stackoverflow.com/a/978264/344821) (von der verwandte Seitenleiste) arbeiten? – Dougal

+0

Anstatt 'sys.stdout' auf' StringIO() 'zu setzen, haben Sie versucht, es in eine Datei zu setzen? d. h. 'sys.stdout = offen ('log.txt', 'w')' – carmenism

+0

Dougal, danke, das sieht vielversprechend aus, ich werde es morgen ausprobieren. Nullpointer, ich habe versucht, es an eine benutzerdefinierte NullPointer() -Klasse zu richten, und das hat auch nicht funktioniert. – jeremiahbuddha

Antwort

5

This approach (gefunden über die zugehörige Seitenleiste) könnte funktionieren. Es ordnet die Dateideskriptoren und nicht nur die Wrapper ihnen in sys.stdout usw. zu.

-2

Wenn Sie dieses Skript auf einem Linux-basierten Computer ausführen, sollten Sie in der Lage sein:

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

Ich möchte nicht die gesamte vom Skript erzeugte Ausgabe unterdrücken, sondern nur die fehlerhafte Ausgabe, die von diesen bestimmten Funktionen erzeugt wird. Ansonsten, ja, das wäre die einfachste Lösung ... – jeremiahbuddha

+0

Würde dies helfen: Fügen Sie einen Druck vor und nach dieser bestimmten Funktion hinzu. Parse die Ausgabe mit einem Regex, um alles zwischen den Prints über – GeneralBecos

1

Haben Sie versucht, stderr auch umleiten? z.B.

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

Auch die Verwendung von StringIO könnte zusätzlichen Speicher benötigen. Sie können stattdessen ein Dummy-Gerät verwenden (z. B. http://coreygoldberg.blogspot.com/2009/05/python-redirect-or-turn-off-stdout-and.html).

+0

loszuwerden Yeah, ich habe versucht sowohl stdout und stderr, keine Würfel ... – jeremiahbuddha

+0

Es könnte sein, dass der C-Code direkt auf stdout schreibt; Sie könnten das wahrscheinlich nicht umleiten. Es tut uns leid. – Bob

1

Meine Lösung ist ähnlich wie Ihre, aber verwendet contextlib und ist ein wenig kürzer und leichter zu verstehen (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() 

Der Kontext dafür, warum ich schuf diese bei this blog post ist. Ähnlich wie deiner, denke ich.

Ich benutze es so in einem setup.py:

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

wirklich nicht vom OP angefordert, aber ich brauchte und speichern die Ausgabe zu verbergen, und tat wie folgt:

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

Verbrauch:

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

(getestet nur mit Pytho n 3)

EDIT: erlaubt nun stderr auszuwählen, stdout oder beides, wie in Hider([stdout, stderr])

+0

Dies sollte die akzeptierte Antwort für Python 3 sein! man könnte auch seld._stderr == sys.stderr; sys.stderr = self <...> – neok

+0

@neok: verallgemeinert ein bisschen, danke für den Vorschlag –

Verwandte Themen