如何逃脱Python中的标准输出重定向



我想处理第三部分脚本的实时输出,打印一些与图案相匹配但跳过其他的行:

def thirdparty_code():
    from random import choice
    stuff = ['keep: important stuff', 'ignore: boring stuff']
    while True:
        chosen_line = choice(stuff)
        print(chosen_line)

我使用redirect_stdout(将行传递到我的虚拟IO(和一个扩展的StringIO(用作IO,但也称为我的过滤功能(。但是,当我在处理功能的内部调用print()时,我会得到一个RecursionError-这不是出乎意料的:

from io import StringIO
from contextlib import redirect_stdout
class StringIOWithCallback(StringIO):
    def __init__(self, callback, **kwargs):
        super().__init__(**kwargs)
        self.callback = callback
    def write(self, s):
        super().write(s)
        self.callback(s)
def filter_boring_stuff_out(line):
    if line.startswith('ignore'):
        return
    print(line)
my_io = StringIOWithCallback(filter_boring_stuff_out)
with redirect_stdout(my_io):
    thirdparty_code()

我想知道是否有可能逃离重定向,例如在print()功能中指定file参数,以便将其打印到实际的标准输出。我知道我可以轻松地使用标准错误流:

import sys
print(line, file=sys.stderr)

,但我特别想使用标准输出。是否有一种很好的Pythonic方法可以做到?

重定向stdout后,您可以轻松地将其重置,这要归功于__stdout__,它可以保存原始值。

sys.stdout = redirected_stdout
...
...
sys.stdout = sys.__stdout__

如果您不断地发现自己切换了这些输出流,则应该创建一个函数以输出到重定向流:

def redirect(*args):
    print(*args, file=redirected_file)
redirect(...)  # redirect this output
print(...)  # use standard stream

一旦我写了我的问题,我才意识到,在调用重定向之前,只需将标准输出对象sys.stdout保存到变量:

stdout = sys.stdout
def filter_boring_stuff_out(line):
    if line.startswith('ignore'):
        return
    print(line, file=stdout)

,但是一如既往 - 我很乐意了解其他可能的解决方案。

您可以重定向stdout并忽略以ignore开头的所有消息。如果这样做,所有print S将被拦截。如果您试图从无法访问或不想更改的代码中过滤消息,这将更好。

import sys
from contextlib import redirect_stdout
class Filter_Out:
    def __init__(self, *_, start=None, anywhere=None, end=None):
        self.last_ignore = False
        self.start = start
        self.anywhere = anywhere
        self.end = end
        self.terminal = sys.stdout
    def write(self, txt):
        if (self.start and txt.startswith(self.start)) or 
           (self.end and txt.endswith(self.end)) or 
           (self.anywhere and self.anywhere in txt):
            self.last_ignore = True
            return
        if self.last_ignore and txt == 'n':
            self.last_ignore = False
        else:
            self.terminal.write(txt)
    def flush(self):
        pass
with redirect_stdout(Filter_Out(start='ignore', anywhere='4')):
    print("test")
    print("test2")
    print("ignore: test2") # will not print because it started with ignore
    print("test1")
    print("test42") # will not print because it had 4 in it

最新更新