J'utilise ftplib
de python pour écrire un petit client FTP, mais certaines fonctions du paquet ne renvoient pas de chaîne de caractères, mais affichent sur stdout
. Je veux rediriger stdout
vers un objet dont je pourrai lire le résultat.
Je sais que stdout
peut être redirigé vers n'importe quel fichier normal avec:
stdout = open("file", "a")
Mais je préfère une méthode qui n’utilise pas le lecteur local.
Je cherche quelque chose comme le BufferedReader
dans Java qui peut être utilisé pour envelopper un tampon dans un flux.
from cStringIO import StringIO # Python3 use: from io import StringIO
import sys
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
# blah blah lots of code ...
sys.stdout = old_stdout
# examine mystdout.getvalue()
Il y a la fonction contextlib.redirect_stdout () dans Python 3.4:
import io
from contextlib import redirect_stdout
with io.StringIO() as buf, redirect_stdout(buf):
print('redirected')
output = buf.getvalue()
Voici exemple de code qui montre comment l'implémenter sur des versions plus anciennes Python) .
Juste pour ajouter à la réponse de Ned ci-dessus: vous pouvez l'utiliser pour rediriger la sortie vers tout objet implémentant une méthode write (str).
Ceci peut être utilisé à bon escient pour "attraper" la sortie stdout dans une application graphique.
Voici un exemple stupide dans PyQt:
import sys
from PyQt4 import QtGui
class OutputWindow(QtGui.QPlainTextEdit):
def write(self, txt):
self.appendPlainText(str(txt))
app = QtGui.QApplication(sys.argv)
out = OutputWindow()
sys.stdout=out
out.show()
print "hello world !"
À partir de Python 2.6, vous pouvez utiliser tout ce qui implémente l'API TextIOBase
API du module io. Cette solution vous permet également d'utiliser sys.stdout.buffer.write()
in Python 3 pour écrire des chaînes d'octets (déjà) codées sur stdout (voir stdout in Python ) Utiliser StringIO
ne fonctionnerait pas alors, car ni sys.stdout.encoding
Ni sys.stdout.buffer
Ne seraient disponibles.
Une solution utilisant TextIOWrapper:
import sys
from io import TextIOWrapper, BytesIO
# setup the environment
old_stdout = sys.stdout
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)
# do something that writes to stdout or stdout.buffer
# get output
sys.stdout.seek(0) # jump to the start
out = sys.stdout.read() # read output
# restore stdout
sys.stdout.close()
sys.stdout = old_stdout
Cette solution fonctionne pour Python 2> = 2.6 et Python 3.
Veuillez noter que notre nouvelle sys.stdout.write()
n'accepte que les chaînes unicode et que sys.stdout.buffer.write()
n'accepte que les chaînes d'octets. Ce n'est peut-être pas le cas pour l'ancien code, mais c'est souvent le cas pour le code conçu pour être exécuté sur Python 2 et 3 sans modifications, ce qui utilise à nouveau souvent sys.stdout.buffer
.
Vous pouvez créer une légère variante qui accepte les chaînes unicode et octets pour write()
:
class StdoutBuffer(TextIOWrapper):
def write(self, string):
try:
return super(StdoutBuffer, self).write(string)
except TypeError:
# redirect encoded byte strings directly to buffer
return super(StdoutBuffer, self).buffer.write(string)
Vous n'avez pas besoin de définir le codage du tampon sys.stdout.encoding, mais cela vous aide lorsque vous utilisez cette méthode pour tester/comparer la sortie du script.
Cette méthode restaure sys.stdout même s'il y a une exception. Il obtient également toute sortie avant l'exception.
import io
import sys
real_stdout = sys.stdout
fake_stdout = io.BytesIO() # or perhaps io.StringIO()
try:
sys.stdout = fake_stdout
# do what you have to do to create some output
finally:
sys.stdout = real_stdout
output_string = fake_stdout.getvalue()
fake_stdout.close()
# do what you want with the output_string
Testé dans Python 2.7.10 en utilisant io.BytesIO()
)
Testé dans Python 3.6.4 en utilisant io.StringIO()
)
Bob, ajouté pour un cas si vous sentez que quelque chose provenant de l'expérimentation de code modifié/étendu pourrait devenir intéressant dans tous les sens, sinon n'hésitez pas à le supprimer
Ad informandum ... quelques remarques issues d'expérimentations approfondies lors de la recherche de mécanismes viables permettant de "saisir" les sorties, dirigées par
numexpr.print_versions()
directement vers le<stdout>
(Sur la nécessité de nettoyer l'interface graphique et de collecter des détails dans le rapport de débogage)
# THIS WORKS AS HELL: as Bob Stein proposed years ago:
# py2 SURPRISEDaBIT:
#
import io
import sys
#
real_stdout = sys.stdout # Push <stdout> ( store to REAL_ )
fake_stdout = io.BytesIO() # .DEF FAKE_
try: # FUSED .TRY:
sys.stdout.flush() # .flush() before
sys.stdout = fake_stdout # .SET <stdout> to use FAKE_
# ----------------------------------------- # + do what you gotta do to create some output
print 123456789 # +
import numexpr # +
QuantFX.numexpr.__version__ # + [3] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
QuantFX.numexpr.print_versions() # + [4] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
_ = os.system( 'echo os.system() redir-ed' )# + [1] via real_stdout + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
_ = os.write( sys.stderr.fileno(), # + [2] via stderr + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
b'os.write() redir-ed' )# *OTHERWISE, if via fake_stdout, EXC <_io.BytesIO object at 0x02C0BB10> Traceback (most recent call last):
# ----------------------------------------- # ? io.UnsupportedOperation: fileno
#''' ? YET: <_io.BytesIO object at 0x02C0BB10> has a .fileno() method listed
#>>> 'fileno' in dir( sys.stdout ) -> True ? HAS IT ADVERTISED,
#>>> pass; sys.stdout.fileno -> <built-in method fileno of _io.BytesIO object at 0x02C0BB10>
#>>> pass; sys.stdout.fileno()-> Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# io.UnsupportedOperation: fileno
# ? BUT REFUSES TO USE IT
#'''
finally: # == FINALLY:
sys.stdout.flush() # .flush() before ret'd back REAL_
sys.stdout = real_stdout # .SET <stdout> to use POP'd REAL_
sys.stdout.flush() # .flush() after ret'd back REAL_
out_string = fake_stdout.getvalue() # .GET string from FAKE_
fake_stdout.close() # <FD>.close()
# +++++++++++++++++++++++++++++++++++++ # do what you want with the out_string
#
print "\n{0:}\n{1:}{0:}".format( 60 * "/\\",# "LATE" deferred print the out_string at the very end reached -> real_stdout
out_string #
)
'''
PASS'd:::::
...
os.system() redir-ed
os.write() redir-ed
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version: 2.5
NumPy version: 1.10.4
Python version: 2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU? True
VML available? True
VML/MKL version: Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
>>>
EXC'd :::::
...
os.system() redir-ed
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version: 2.5
NumPy version: 1.10.4
Python version: 2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU? True
VML available? True
VML/MKL version: Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
Traceback (most recent call last):
File "<stdin>", line 9, in <module>
io.UnsupportedOperation: fileno
'''
En Python3.6, les modules StringIO
et cStringIO
ont disparu, vous devez utiliser io.StringIO
à la place. Vous devriez donc procéder comme pour la première réponse:
import sys
from io import StringIO
old_stdout = sys.stdout
old_stderr = sys.stderr
my_stdout = sys.stdout = StringIO()
my_stderr = sys.stderr = StringIO()
# blah blah lots of code ...
sys.stdout = self.old_stdout
sys.stderr = self.old_stderr
// if you want to see the value of redirect output, be sure the std output is turn back
print(my_stdout.getvalue())
print(my_stderr.getvalue())
my_stdout.close()
my_stderr.close()
Utilisez pipe()
et écrivez dans le descripteur de fichier approprié.
https://docs.python.org/library/os.html#file-descriptor-operations
Un gestionnaire de contexte pour python3:
import sys
from io import StringIO
class RedirectedStdout:
def __init__(self):
self._stdout = None
self._string_io = None
def __enter__(self):
self._stdout = sys.stdout
sys.stdout = self._string_io = StringIO()
return self
def __exit__(self, type, value, traceback):
sys.stdout = self._stdout
def __str__(self):
return self._string_io.getvalue()
utiliser comme ceci:
>>> with RedirectedStdout() as out:
>>> print('asdf')
>>> s = str(out)
>>> print('bsdf')
>>> print(s, out)
'asdf\n' 'asdf\nbsdf\n'