Eli Bendersky已经详细解释了如何“Redirecting all kinds of stdout in Python”,特别是重定向C级流,例如stdout共享库(dll)。但是,该示例在Linux中并不适用于Windows,主要是由于以下几行:
libc = ctypes.CDLL(None)
c_stdout = ctypes.c_void_p.in_dll(libc = ctypes.CDLL(None), 'stdout')
我们如何让它在Windows中运行?
答案 0 :(得分:3)
我发现答案埋没在Drekin's code中。基于此,我对Eli Bendersky's example进行了一些小改动:
更新:此代码已经在Windows上的64位64位和Linux上的Python 3.5 64位上进行了测试。对于Windows上的Python 3.5,请参阅eryksun的评论。
from contextlib import contextmanager
import ctypes
import io
import os
import sys
import tempfile
import ctypes.util
from ctypes import *
import platform
if platform.system() == "Linux":
libc = ctypes.CDLL(None)
c_stdout = ctypes.c_void_p.in_dll(libc, 'stdout')
if platform.system() == "Windows":
class FILE(ctypes.Structure):
_fields_ = [
("_ptr", c_char_p),
("_cnt", c_int),
("_base", c_char_p),
("_flag", c_int),
("_file", c_int),
("_charbuf", c_int),
("_bufsize", c_int),
("_tmpfname", c_char_p),
]
# Gives you the name of the library that you should really use (and then load through ctypes.CDLL
msvcrt = CDLL(ctypes.util.find_msvcrt())
libc = msvcrt # libc was used in the original example in _redirect_stdout()
iob_func = msvcrt.__iob_func
iob_func.restype = POINTER(FILE)
iob_func.argtypes = []
array = iob_func()
s_stdin = addressof(array[0])
c_stdout = addressof(array[1])
@contextmanager
def stdout_redirector(stream):
# The original fd stdout points to. Usually 1 on POSIX systems.
original_stdout_fd = sys.stdout.fileno()
def _redirect_stdout(to_fd):
"""Redirect stdout to the given file descriptor."""
# Flush the C-level buffer stdout
libc.fflush(c_stdout)
# Flush and close sys.stdout - also closes the file descriptor (fd)
sys.stdout.close()
# Make original_stdout_fd point to the same file as to_fd
os.dup2(to_fd, original_stdout_fd)
# Create a new sys.stdout that points to the redirected fd
sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb'))
# Save a copy of the original stdout fd in saved_stdout_fd
saved_stdout_fd = os.dup(original_stdout_fd)
try:
# Create a temporary file and redirect stdout to it
tfile = tempfile.TemporaryFile(mode='w+b')
_redirect_stdout(tfile.fileno())
# Yield to caller, then redirect stdout back to the saved fd
yield
_redirect_stdout(saved_stdout_fd)
# Copy contents of temporary file to the given stream
tfile.flush()
tfile.seek(0, io.SEEK_SET)
stream.write(tfile.read())
finally:
tfile.close()
os.close(saved_stdout_fd)
if __name__ == '__main__':
f = io.BytesIO()
print('...')
with stdout_redirector(f):
print('foobar')
print(12)
libc.puts(b'this comes from C')
os.system('echo and this is from echo')
print('Got stdout:"\n{0}\n"'.format(f.getvalue().decode('utf-8')))
print('Resuming normal operation...')