webentwicklung-frage-antwort-db.com.de

Stdout/stderr vorübergehend umleiten

Ist es möglich, stdout/stderr vorübergehend in Python umzuleiten (d. H. Für die Dauer einer Methode)?

Bearbeiten:

Das Problem mit den aktuellen Lösungen (an die ich mich zuerst erinnerte, aber dann vergaß) ist, dass sie nicht redirect; Sie ersetzen lediglich die Streams in ihrer Gesamtheit. Wenn eine Methode aus irgendeinem Grund (zum Beispiel weil der Stream als Parameter an etwas übergeben wurde) eine local - Kopie der Variable hat, funktioniert sie nicht.

Irgendwelche Lösungen?

54
Mehrdad

Um das Problem zu beheben, dass eine Funktion sys.stdout stream als lokale Variable zwischengespeichert hat und das Ersetzen des globalen sys.stdout in dieser Funktion nicht möglich ist, können Sie auf Dateideskriptorebene (sys.stdout.fileno()) umleiten, z.

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() leitet die gesamte Ausgabe für sys.stdout.fileno() auf einen bestimmten Dateinamen, ein Dateiobjekt oder einen Dateideskriptor um (im Beispiel os.devnull).

stdout_redirected() und merged_stderr_stdout() werden hier definiert .

17
jfs

Sie können die Umleitungslogik auch in einem Kontextmanager ablegen.

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 __== '__main__':

    devnull = open(os.devnull, 'w')
    print('Fubar')

    with RedirectStdStreams(stdout=devnull, stderr=devnull):
        print("You'll never see me")

    print("I'm back!")
85
Rob Cowie

Ich bin nicht sicher, was eine temporäre Weiterleitung bedeutet. Sie können jedoch Streams wie diese neu zuweisen und sie zurücksetzen.

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

Auch um in sys.stderr innerhalb von Printstmts wie diesem zu schreiben. 

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

Regelmäßiger Druck wird zu stdout.

18
Senthil Kumaran

Mit einem Dekorateur wie dem folgenden ist es möglich:

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

Benutzen als:

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

wenn Sie die Quelle für fun nicht ändern möchten, rufen Sie sie direkt als auf

redirect_stderr_stdout(some_logging_stream, the_console)(fun)

Beachten Sie jedoch, dass dies nicht fadensicher ist.

13
Fred Foo

ab Python 3.4 gibt es den Kontextmanager contextlib.redirect_stdout :

from contextlib import redirect_stdout

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

stdout vollständig zum Schweigen bringen:

from contextlib import redirect_stdout

with redirect_stdout(None):
    # do stuff...
5

Hier ist ein Kontextmanager, den ich nützlich fand. Das Schöne daran ist, dass Sie es mit der with-Anweisung verwenden können und auch die Umleitung für untergeordnete Prozesse übernimmt.

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):
        ...
    """

    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, warum ich das erstellt habe, ist unter diesem Blogbeitrag .

4
Marc Abramowitz

Raymond Hettinger zeigt uns einen besseren Weg [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

Nach dem with-Block wird die sys.stdout zurückgesetzt

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

1
Daniel

Wir verwenden die PHP-Syntax der Funktionen ob_start und ob_get_contents in python3 und leiten die Eingabe in eine Datei um.

Die Ausgaben werden in einer Datei gespeichert, es können auch beliebige Streams verwendet werden.

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

Verwendungszweck:

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

Würde drucken

Hallo John Tschüss John

0
Uri Goren
0