IT TIP

일시적으로 stdout / stderr 리디렉션

itqueen 2021. 1. 5. 20:47
반응형

일시적으로 stdout / stderr 리디렉션


파이썬에서 stdout / stderr을 일시적으로 리디렉션 할 수 있습니까 (즉, 메서드 기간 동안)?

편집하다:

현재 솔루션의 문제 (처음에는 기억했지만 잊어 버렸음)는 리디렉션 하지 않는다는 것입니다 . 오히려 스트림 전체를 대체합니다. 따라서 메서드 에 어떤 이유로 든 변수 중 하나의 로컬 복사본 이있는 경우 (예 : 스트림이 매개 변수로 무언가에 전달 되었기 때문에) 작동하지 않습니다.

해결책이 있습니까?


일부 함수가 sys.stdout스트림을 로컬 변수로 캐시 하여 전역 sys.stdout대체하는 것이 해당 함수 내에서 작동하지 않을 수있는 문제를 해결하려면 파일 설명자 수준 ( sys.stdout.fileno()) 에서 리디렉션 할 수 있습니다 ( 예 :

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()모든 출력을 sys.stdout.fileno()지정된 파일 이름, 파일 객체 또는 파일 설명자 ( os.devnull예제)로 리디렉션합니다 .

stdout_redirected()그리고 merged_stderr_stdout()여기에 정의되어 있습니다 .


컨텍스트 관리자에 리디렉션 논리를 넣을 수도 있습니다.

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 __name__ == '__main__':

    devnull = open(os.devnull, 'w')
    print('Fubar')

    with RedirectStdStreams(stdout=devnull, stderr=devnull):
        print("You'll never see me")

    print("I'm back!")

임시 리디렉션이 무엇을 의미하는지 잘 모르겠습니다. 그러나 이와 같이 스트림을 재 할당하고 다시 재설정 할 수 있습니다.

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

또한 이와 같은 인쇄 stmts 내에서 sys.stderr에 쓰십시오.

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

일반 인쇄는 표준 출력됩니다.


다음과 같은 데코레이터를 사용하면 가능합니다.

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

로 사용:

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

또는의 소스를 수정하지 않으려면 다음 fun과 같이 직접 호출하십시오.

redirect_stderr_stdout(some_logging_stream, the_console)(fun)

그러나 이것은 스레드로부터 안전하지 않습니다.


파이썬 3.4부터 컨텍스트 관리자가 있습니다 contextlib.redirect_stdout.

from contextlib import redirect_stdout

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

stdout이 작동 을 완전히 침묵시키기 위해 :

from contextlib import redirect_stdout

with redirect_stdout(None):
    # do stuff...

Here's a context manager that I found useful. The nice things about this are that you can use it with the with statement and it also handles redirecting for child processes.

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

The context for why I created this is at this blog post.


Raymond Hettinger shows us a better way[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

After the with block the sys.stdout will be reset

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


We'll use the PHP syntax of ob_start and ob_get_contents functions in python3, and redirect the input into a file.

The outputs are being stored in a file, any type of stream could be used as well.

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

Would print

Hi John Bye John


Look at contextlib.redirect_stdout(new_target) and contextlib.redirect_stderr(new_target). contextlib.redirect_stderr(new_target) is new in Python 3.5.

ReferenceURL : https://stackoverflow.com/questions/6796492/temporarily-redirect-stdout-stderr

반응형

'IT TIP' 카테고리의 다른 글

Xcode, 중복 기호 _main  (0) 2021.01.05
숫자 범위를 다른 범위에 매핑  (0) 2021.01.05
왜 벡터  (0) 2021.01.05
numpy의 반복되지 않는 난수  (0) 2021.01.05
Cordova 초기화 오류 : 클래스를 찾을 수 없음  (0) 2021.01.05