파이썬의 stdout을 일종의 문자열 버퍼로 리디렉션 할 수 있습니까?


138

ftplib작은 FTP 클라이언트를 작성하기 위해 파이썬을 사용 하고 있지만 패키지의 일부 함수는 문자열 출력을 반환하지 않지만로 인쇄합니다 stdout. stdout출력을 읽을 수있는 객체 로 리디렉션하고 싶습니다 .

다음 stdout을 사용하여 일반 파일로 리디렉션 할 수 있다는 것을 알고 있습니다.

stdout = open("file", "a")

그러나 로컬 드라이브를 사용하지 않는 방법을 선호합니다.

BufferedReader버퍼를 스트림으로 래핑하는 데 사용할 수있는 Java 와 같은 것을 찾고 있습니다.


stdout = open("file", "a")그 자체로는 아무것도 리디렉션 하지 않을 것이라고 생각 합니다.
Alexey

답변:


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

52
+1, 원본 stdout개체는 항상에서 사용할 수 있으므로 원본 개체에 대한 참조를 유지할 필요가 없습니다 sys.__stdout__. docs.python.org/library/sys.html#sys.__stdout__을 참조하십시오 .
Ayman Hourieh

92
글쎄, 그것은 흥미로운 토론입니다. 절대 원래의 표준 출력을 사용할 수 있지만 다음과 같이 교체 할 때 다른 사람이 표준 출력을 대체 한 수 있기 때문에, 그것은 내가했던 다른 이름으로 저장 명시를 사용하는 것이 좋습니다 당신이 사용하는 경우 표준 출력을 , 당신이 그들의 교체 소지품 것입니다.
Ned Batchelder

5
한 스레드에서이 작업이 다른 스레드의 동작을 변경합니까? 스레드 안전합니까?
Anuvrat Parashar

6
이전 stdout을 finally:블록 에 다시 할당하는 것이 좋습니다 . 따라서 예외가 발생하면 다시 할당됩니다. try: bkp = sys.stdout ... ... finally: sys.stdout = bkp
Matthias Kuhn

20
파이썬 3에서 이것을 사용하려면 cStringIO를 io로 바꾸십시오.
Anthony Labarre

80

Python 3.4 에는 contextlib.redirect_stdout () 함수 가 있습니다.

import io
from contextlib import redirect_stdout

with io.StringIO() as buf, redirect_stdout(buf):
    print('redirected')
    output = buf.getvalue()

이전 Python 버전에서 구현하는 방법을 보여주는 코드 예제는 다음과 같습니다 .


3
도 있습니다 redirect_stderr너무 최신 파이썬에!
CMCDragonkai

이 솔루션에 try / finally 블록을 추가 할 필요가 없다고 생각합니다.
snr

35

위의 Ned의 답변에 추가하기 위해 : write (str) 메소드를 구현하는 객체로 출력을 리디렉션 할 수 있습니다 .

이것은 GUI 응용 프로그램에서 stdout 출력을 "catch"하는 데 효과적입니다.

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 !"

5
python 2.6 및 PyQT4에서 작동합니다. 왜 작동하지 않는지 알 수 없을 때 투표 작업 코드를 다운시키는 것이 이상해 보입니다!
니콜라스 레 페브르

9
flush ()도 추가하는 것을 잊지 마십시오!

6

Python 2.6부터는 io 모듈에서 TextIOBaseAPI 를 구현하는 모든 것을 대체물로 사용할 수 있습니다 . 이 솔루션은 또한 사용할 수 있습니다 sys.stdout.buffer.write()(참조 표준 출력에 쓰기로 파이썬 3에서 (이미) 인코딩 된 바이트 문자열을 파이썬 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.

다음에 대해 유니 코드 및 바이트 문자열을 허용하는 약간의 변형을 작성할 수 있습니다 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.stdout.encoding으로 설정할 필요는 없지만 스크립트 출력을 테스트 / 비교하기 위해이 방법을 사용할 때 도움이됩니다.


이 답변은 Httpie의 core.py와 함께 사용하기 위해 Environment 객체의 stdout 매개 변수를 설정할 때 도움이되었습니다.
fragorl

6

이 방법은 예외가 있어도 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()


Bob은 수정 / 확장 코드 실험에서 어떤 것이 든 어떤 의미에서 흥미로울 수 있다고 생각되면 사례를 추가했습니다. 그렇지 않으면 자유롭게 삭제하십시오.

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
'''

6

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

이처럼 사용하십시오 :

>>> with RedirectedStdout() as out:
>>>     print('asdf')
>>>     s = str(out)
>>>     print('bsdf')
>>> print(s, out)
'asdf\n' 'asdf\nbsdf\n'

4

Python3.6에서 StringIOcStringIO모듈이 사라지면 io.StringIO대신 사용해야합니다. 따라서 첫 번째 답변과 같이해야합니다.

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

1
위의 코드가 작동하는 방식과 질문자의 상황에 대한 개선 방법을 설명하여 답변의 품질을 향상시킬 수 있습니다.
toonice


1

여기에 또 다른 내용이 있습니다. contextlib.redirect_stdoutio.StringIO()같이 문서화 매우 중요하지만, 여전히 매일 사용을위한 자세한 조금입니다. 서브 클래 싱으로 하나의 라이너로 만드는 방법은 다음과 같습니다 contextlib.redirect_stdout.

import sys
import io
from contextlib import redirect_stdout

class capture(redirect_stdout):

    def __init__(self):
        self.f = io.StringIO()
        self._new_target = self.f
        self._old_targets = []  # verbatim from parent class

    def __enter__(self):
        self._old_targets.append(getattr(sys, self._stream))  # verbatim from parent class
        setattr(sys, self._stream, self._new_target)  # verbatim from parent class
        return self  # instead of self._new_target in the parent class

    def __repr__(self):
        return self.f.getvalue()  

__enter__가 자체를 리턴하므로 with 블록이 종료 된 후 컨텍스트 관리자 오브젝트를 사용할 수 있습니다. 또한 __repr__ 메소드 덕분에 컨텍스트 관리자 객체의 문자열 표현은 실제로 stdout입니다. 이제는

with capture() as message:
    print('Hello World!')
print(str(message)=='Hello World!\n')  # returns True
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.