web-dev-qa-db-fra.com

Rediriger temporairement stdout/stderr

Est-il possible de rediriger temporairement stdout/stderr en Python (c'est-à-dire pendant la durée d'une méthode)?

Modifier:

Le problème avec les solutions actuelles (dont je me souvenais au départ mais que j'avais ensuite oublié) est qu'elles ne redirect pas; au lieu de cela, ils remplacent simplement les flux dans leur intégralité. Ainsi, si une méthode a une copie local d'une variable pour quelque raison que ce soit (par exemple, le flux a été passé comme paramètre à quelque chose), cela ne fonctionnera pas.

Des solutions?

54
Mehrdad

Pour résoudre le problème selon lequel une fonction peut avoir mis en cache le flux sys.stdout en tant que variable locale et que, par conséquent, le sys.stdout global ne fonctionnera pas dans cette fonction, vous pouvez rediriger au niveau du descripteur de fichier (sys.stdout.fileno()), par exemple:

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() redirige toutes les sorties de sys.stdout.fileno() vers un nom de fichier, un objet de fichier ou un descripteur de fichier donné (os.devnull dans l'exemple).

stdout_redirected() et merged_stderr_stdout() sont définis ici .

17
jfs

Vous pouvez également placer la logique de redirection dans un gestionnaire de contexte.

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

Je ne suis pas sûr de ce que signifie une redirection temporaire. Mais, vous pouvez réaffecter des flux comme celui-ci et le réinitialiser.

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

Également pour écrire à sys.stderr dans stmts d'impression comme ceci. 

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

L'impression régulière sera sur la sortie.

18
Senthil Kumaran

C'est possible avec un décorateur tel que:

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

Utilisé comme:

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

ou, si vous ne souhaitez pas modifier le source de fun, appelez-le directement en tant que

redirect_stderr_stdout(some_logging_stream, the_console)(fun)

Mais notez que ce n'est pas thread-safe.

13
Fred Foo

depuis python 3.4, il y a le gestionnaire de contexte contextlib.redirect_stdout :

from contextlib import redirect_stdout

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

faire taire complètement stdout ceci fonctionne:

from contextlib import redirect_stdout

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

Voici un gestionnaire de contexte que j'ai trouvé utile. Le bon côté de ceci est que vous pouvez l'utiliser avec l'instruction with et qu'il gère également la redirection pour les processus enfants.

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

Le contexte pour lequel j'ai créé ceci est à cet article de blog .

4
Marc Abramowitz

Raymond Hettinger nous montre un meilleur moyen [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

Après le blocage avec, le sys.stdout sera réinitialisé

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

1
Daniel

Nous allons utiliser la syntaxe PHP de ob_start et ob_get_contents functions en python3 et rediriger l'entrée dans un fichier.

Les sorties sont stockées dans un fichier, tout type de flux pouvant également être utilisé.

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

Usage:

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

Serait imprimer

Salut John Au revoir John

0
Uri Goren
0
Cristóbal Ganter