拦截分配给系统标准输出以及系统标准

2024-10-03 11:24:01 发布

您现在位置:Python中文网/ 问答频道 /正文

sys模块有两个我感兴趣的全局属性:sys.stdout和{}。在

我正在构建一个自己的模块,它(除其他外)用自己的包装器替换sys.stdout和{},这些包装器拦截尝试的输出,修改它,然后将其转发给原始的。我的方法是这样的:

_orig_stdout = sys.stdout
_orig_stderr = sys.stderr
sys.stdout = MyFakeStdoutClass()
sys.stderr = MyFaleStderrClass()

这正如预期的那样工作——在导入模块后的任何时候,尝试使用sys.stdout或{}执行任何操作都会通过我的类来完成。在


现在,我的模块有一个既得利益,就是要确保这种安排保持现在的样子——它希望永久地控制sys.stdout和{}。其他模块可以像我的模块那样重新分配sys.stdout,但我的模块不希望它们这样做。相反,我的模块想拦截他们的企图。在

对于常规类,这样做很容易-我只需覆盖该类的__setattr__()方法:

^{pr2}$

但是,我已经尝试为sys模块本身执行此操作,但是它不起作用(即使在我执行sys.__setattr__ = my_setattr操作之后,我发现my_setattr永远不会被调用)。在

另外,虽然other answers指出了为sys模块创建自己的包装类并将其分配给sys.modules['sys']的可能解决方案,但这行不通——如果在我的模块被另一个模块导入之前,sys是在该模块导入我的模块之前导入的,那么我的更改将无法坚持。在

此外,使用一些helper方法设置sys.stdout = property(stdout_getter, stdout_setter)来返回/修改我的_orig_stdout变量也不起作用。即使后来在同一个文件中,我也可以只做sys.stdout = sys.__stdout__,它就恢复正常了。我不想这成为可能。在

有没有一个好办法可以绕过这个限制?在


Tags: 模块方法属性mystderrstdoutsys全局
1条回答
网友
1楼 · 发布于 2024-10-03 11:24:01

在python上完全重写stdout的唯一真正方法是实际重写stdout文件描述符(1)。这可以使用^{}系统调用来完成。在

下面是一个跨平台的例子,展示了如何重写stdout,允许对写入它的所有数据使用自定义逻辑。在这个例子中,逻辑只是复制写入stdout的所有字符。在

import os
import sys
import threading

def handle_stdout(fake_stdout, real_stdout):
    while not fake_stdout.closed and not real_stdout.closed:
        char = fake_stdout.read(1)
        real_stdout.write(char * 2)

def override_stdout():
    stdout_fd = 1
    pipe_reader_fd, pipe_writer_fd = os.pipe()
    pipe_reader = os.fdopen(pipe_reader_fd, 'r')
    original_stdout_writer = os.fdopen(os.dup(stdout_fd), 'w')
    os.dup2(pipe_writer_fd, stdout_fd)
    return pipe_reader, original_stdout_writer

# Override stdout
pipe_reader, original_stdout_writer = override_stdout()
thread = threading.Thread(target=handle_stdout, args=(pipe_reader, original_stdout_writer))
thread.start()

# Write stuff to stdout
print('foobar')

# Cleanup to allow background thread to shut down
pipe_reader.close()
original_stdout_writer.close()

运行此示例将输出:

^{pr2}$

对于某些版本的python,必须将PYTHONLEGACYWINDOWSSTDIO环境变量设置为非空字符串,才能使此示例在windows上运行。在

相关问题 更多 >