Python 3:无缓冲流与缓冲流
Python 3: Unbuffered vs Buffered Streams
我一直在使用以下代码片段来静音(重定向输出)在我的 Python 脚本中调用的 C 代码:
from ctypes import CDLL, c_void_p
import os
import sys
# Code
class silence(object):
def __init__(self, stdout=os.devnull):
self.outfile = stdout
def __enter__(self):
# Flush
sys.__stdout__.flush()
# Save
self.saved_stream = sys.stdout
self.fd = sys.stdout.fileno()
self.saved_fd = os.dup(self.fd)
# Open the redirect
self.new_stream = open(self.outfile, 'wb', 0)
self.new_fd = self.new_stream.fileno()
# Replace
os.dup2(self.new_fd, self.fd)
def __exit__(self, *args):
# Flush
self.saved_stream.flush()
# Restore
os.dup2(self.saved_fd, self.fd)
sys.stdout = self.saved_stream
# Clean up
self.new_stream.close()
os.close(self.saved_fd)
# Test case
libc = CDLL('libc.so.6')
# Silence!
with silence():
libc.printf(b'Hello from C in silence\n')
想法是重定向与 stdout
关联的 fd,并将其替换为与打开的空设备关联的 fd。不幸的是,它在 Python 3:
下无法正常工作
$ python2.7 test.py
$ python3.3 -u test.py
$ python3.3 test.py
Hello from C in silence
在 Python 2.7 和 3.3 下,无缓冲输出 可以工作。但是,我不确定根本原因是什么。即使 stdout
被缓冲,对 sys.saved_stream.flush()
的调用应该最终在 C 级别调用 fflush(stdout)
(将输出刷新到空设备)。
我对 Python 3 I/O 模型的哪一部分有误解?
我也不是 100% 确定我理解 Py3 I/O 模型,但是添加
sys.stdout = os.fdopen(self.fd, 'wb', 0)
在你分配给 self.fd
后立即在 Python 3.4 中为我修复了它(在添加此语句之前我能够在 3.4 中重现该问题)。
我也不完全确定发生了什么,但在我的系统上有两种方法可以解决这个问题:
- 将
__exit__
中对 self.saved_stream.flush()
的调用替换为 libc.fflush(None)
。
在调用silence()
之前用任意字符串调用libc.printf
,例如:
libc = CDLL('/bin/cygwin1.dll')
libc.printf(b'')
此外,只有第二种方式 Python 的 print
和 libc.printf
的输出在 with silence():
块之后保持同步。
我一直在使用以下代码片段来静音(重定向输出)在我的 Python 脚本中调用的 C 代码:
from ctypes import CDLL, c_void_p
import os
import sys
# Code
class silence(object):
def __init__(self, stdout=os.devnull):
self.outfile = stdout
def __enter__(self):
# Flush
sys.__stdout__.flush()
# Save
self.saved_stream = sys.stdout
self.fd = sys.stdout.fileno()
self.saved_fd = os.dup(self.fd)
# Open the redirect
self.new_stream = open(self.outfile, 'wb', 0)
self.new_fd = self.new_stream.fileno()
# Replace
os.dup2(self.new_fd, self.fd)
def __exit__(self, *args):
# Flush
self.saved_stream.flush()
# Restore
os.dup2(self.saved_fd, self.fd)
sys.stdout = self.saved_stream
# Clean up
self.new_stream.close()
os.close(self.saved_fd)
# Test case
libc = CDLL('libc.so.6')
# Silence!
with silence():
libc.printf(b'Hello from C in silence\n')
想法是重定向与 stdout
关联的 fd,并将其替换为与打开的空设备关联的 fd。不幸的是,它在 Python 3:
$ python2.7 test.py
$ python3.3 -u test.py
$ python3.3 test.py
Hello from C in silence
在 Python 2.7 和 3.3 下,无缓冲输出 可以工作。但是,我不确定根本原因是什么。即使 stdout
被缓冲,对 sys.saved_stream.flush()
的调用应该最终在 C 级别调用 fflush(stdout)
(将输出刷新到空设备)。
我对 Python 3 I/O 模型的哪一部分有误解?
我也不是 100% 确定我理解 Py3 I/O 模型,但是添加
sys.stdout = os.fdopen(self.fd, 'wb', 0)
在你分配给 self.fd
后立即在 Python 3.4 中为我修复了它(在添加此语句之前我能够在 3.4 中重现该问题)。
我也不完全确定发生了什么,但在我的系统上有两种方法可以解决这个问题:
- 将
__exit__
中对self.saved_stream.flush()
的调用替换为libc.fflush(None)
。 在调用
silence()
之前用任意字符串调用libc.printf
,例如:libc = CDLL('/bin/cygwin1.dll') libc.printf(b'')
此外,只有第二种方式 Python 的 print
和 libc.printf
的输出在 with silence():
块之后保持同步。