Могу ли я перенаправить stdout в python в какой-то строковый буфер?
Я использую питона ftplib чтобы написать небольшой FTP-клиент, но некоторые функции в пакете не возвращают вывод строки, а печатают в stdout. Я хочу перенаправить stdout к объекту, из которого я смогу прочитать вывод.
Я знаю stdout можно перенаправить в любой обычный файл с помощью:
stdout = open("file", "a")
но я предпочитаю метод, который не использует локальный диск.
Я ищу что-то вроде BufferedReader в Java, который может быть использован для оберните буфер в поток.
8 ответов:
from cStringIO import StringIO import sys old_stdout = sys.stdout sys.stdout = mystdout = StringIO() # blah blah lots of code ... sys.stdout = old_stdout # examine mystdout.getvalue()
здесь contextlib.redirect_stdout (функции) в Python 3.4:
import io from contextlib import redirect_stdout with io.StringIO() as buf, redirect_stdout(buf): print('redirected') output = buf.getvalue()здесь пример кода, который показывает, как реализовать его на старых версиях Python.
просто добавьте к ответу Неда выше: вы можете использовать это для перенаправления вывода на любой объект, реализующий метод write (str).
Это может быть использовано для хорошего эффекта, чтобы" поймать " вывод stdout в графическом приложении.
вот глупый пример в 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 !"
начиная с Python 2.6 вы можете использовать все, что реализует
TextIOBaseAPI от модуля ввода-вывода в качестве замены. Это решение также позволяет использоватьsys.stdout.buffer.write()в Python 3 для записи (уже) закодированных байтовых строк в stdout (см. stdout в Python 3). ИспользуяStringIOне будет работать тогда, потому что ниsys.stdout.encoding, ниsys.stdout.bufferбудет доступен.решение с помощью 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это решение работает для Python 2 >= 2.6 и Python 3.
обратите внимание, что наш новый
sys.stdout.write()принимает только строки в юникоде иsys.stdout.buffer.write()принимает только строки байтов. Это может быть не так для старого кода, но часто имеет место для кода, который построен для работы на Python 2 и 3 без изменений, что снова часто используетsys.stdout.buffer.вы можете построить небольшое изменение, которое принимает unicode и байтовые строки для
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)вам не нужно устанавливать кодировку буфера sys.стандартный вывод.кодирование, но это помогает при использовании этого метода для тестирования / сравнения вывода скрипта.
этот метод восстанавливает функции sys.stdout, даже если есть исключение. Он также получает любые выходные данные перед исключением.
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протестировано в Python 2.7.10 с помощью
io.BytesIO()протестировано в Python 3.6.4 с помощью
io.StringIO()
Боб, добавлено для случая, если вы чувствуете, что что-то из измененного / расширенного кода экспериментирования может стать интересным в любом смысле, в противном случае не стесняйтесь удалять это
в Ad informandum ... несколько замечаний от расширенного экспериментирования во время нахождения некоторых жизнеспособных механиков для "захвата" выходов, направленных
numexpr.print_versions()непосредственно<stdout>( при необходимости очистки GUI и сбора деталей в отладку-отчет)# 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 '''
использовать
pipe()и записать в соответствующий файловый дескриптор.https://docs.python.org/library/os.html#file-descriptor-operations
в Python3. 6,
StringIOиcStringIOмодули ушли, вы должны использоватьio.StringIOinstead.So вы должны сделать это, как первый ответ: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()
контекстный менеджер для python3:
import sys from io import StringIO class redirected_stdout: 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 @property def string(self): return self._string_io.getvalue()использовать такой:
>>> with redirected_stdout() as out: >>> print('asdf') >>> s = out.string >>> print('bsdf') >>> print(s, out.string) 'asdf\n' 'asdf\nbsdf\n'
Comments