Как выйти из стандартного перенаправления вывода в Python - PullRequest
0 голосов
/ 22 мая 2019

Я хочу обработать вывод в реальном времени сценария третьей стороны, печатая некоторые строки, которые соответствуют шаблону, но пропуская другие:

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 (который передает строки в мой фиктивный ввод-вывод) ирасширенный 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()

Интересно, можно ли выйти из перенаправления, например, указав *Параметр 1011 * в функции print() позволяет печатать на фактическом стандартном выходе.Я знаю, что могу легко использовать стандартный поток ошибок:

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

Но я специально хочу использовать стандартный вывод.Есть ли хороший, питонный способ сделать это?

Ответы [ 3 ]

3 голосов
/ 22 мая 2019

После перенаправления 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
1 голос
/ 22 мая 2019

Вы можете перенаправить stdout и игнорировать все сообщения, которые начинаются с ignore.Если вы сделаете это таким образом, все print будут перехвачены.Это будет работать лучше, если вы пытаетесь отфильтровать сообщения из кода, к которому у вас нет доступа или который вы не хотите изменять.

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
1 голос
/ 22 мая 2019

После того, как я написал свой вопрос, я понял, что нужно просто сохранить стандартный выходной объект sys.stdout в переменной перед вызовом перенаправления:

stdout = sys.stdout

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

Но как всегда - я был бы рад узнать о других возможных решениях.

...