web-dev-qa-db-fra.com

Comment capturer la sortie stdout à partir d'un appel de fonction Python?

J'utilise une bibliothèque Python qui fait quelque chose avec un objet

do_something(my_object)

et le change. Ce faisant, certaines statistiques sont imprimées sur stdout, et j'aimerais bien maîtriser ces informations. La bonne solution serait de changer do_something() pour renvoyer les informations pertinentes,

out = do_something(my_object)

mais il faudra un certain temps avant que les développeurs de do_something() ne parviennent à ce problème. En guise de solution de contournement, j'ai pensé à analyser tout ce que do_something() écrit sur stdout.

Comment puis-je capturer la sortie stdout entre deux points du code, par exemple,

start_capturing()
do_something(my_object)
out = end_capturing()

?

86
Nico Schlömer

Essayez ce gestionnaire de contexte:

# import StringIO for Python 2 or 3
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO

import sys

class Capturing(list):
    def __enter__(self):
        self._stdout = sys.stdout
        sys.stdout = self._stringio = StringIO()
        return self
    def __exit__(self, *args):
        self.extend(self._stringio.getvalue().splitlines())
        del self._stringio    # free up some memory
        sys.stdout = self._stdout

Usage:

with Capturing() as output:
    do_something(my_object)

output est maintenant une liste contenant les lignes imprimées par l'appel de fonction.

Utilisation avancée:

Ce qui n’est peut-être pas évident, c’est que cela peut être fait plus d’une fois et que les résultats sont concaténés:

with Capturing() as output:
    print 'hello world'

print 'displays on screen'

with Capturing(output) as output:  # note the constructor argument
    print 'hello world2'

print 'done'
print 'output:', output

Sortie:

displays on screen                     
done                                   
output: ['hello world', 'hello world2']

Update : Ils ont ajouté redirect_stdout() à contextlib in Python 3.4 (avec redirect_stderr()). Vous pouvez donc utiliser io.StringIO Avec cela pour obtenir un résultat similaire (bien que Capturing soit une liste et un contexte gestionnaire est sans doute plus pratique).

147
kindall

Dans python> = 3.4, la contextlib contient un redirect_stdout décorateur. Il peut être utilisé pour répondre à votre question comme suit:

import io
from contextlib import redirect_stdout

f = io.StringIO()
with redirect_stdout(f):
    do_something(my_object)
out = f.getvalue()

De les docs :

Gestionnaire de contexte pour la redirection temporaire de sys.stdout vers un autre fichier ou un objet de type fichier.

Cet outil ajoute de la flexibilité aux fonctions ou aux classes existantes dont la sortie est câblée sur stdout.

Par exemple, la sortie de help () est normalement envoyée à sys.stdout. Vous pouvez capturer cette sortie dans une chaîne en redirigeant la sortie vers un objet io.StringIO:

  f = io.StringIO() 
  with redirect_stdout(f):
      help(pow) 
  s = f.getvalue()

Pour envoyer la sortie de help () dans un fichier sur le disque, redirigez la sortie vers un fichier normal:

 with open('help.txt', 'w') as f:
     with redirect_stdout(f):
         help(pow)

Pour envoyer la sortie de help () à sys.stderr:

with redirect_stdout(sys.stderr):
    help(pow)

Notez que l'effet secondaire global sur sys.stdout signifie que ce gestionnaire de contexte ne convient pas pour une utilisation dans le code de bibliothèque et dans la plupart des applications à threads. Cela n'a également aucun effet sur la sortie des sous-processus. Cependant, cela reste une approche utile pour de nombreux scripts d’utilitaires.

Ce gestionnaire de contexte est réentrant.

62
ForeverWintr