How to get all previous print in Python

119 views Asked by At

I'm currently using some functions to store all print in Python

And it get all print AFTER the function has finished

Here is a sample snippet

import io
import sys
import time
import traceback

def return_std(func):
    def wrapper(*args, **kwargs):
        old_stdout = sys.stdout
        sys.stdout = buffer = io.StringIO()
        try:
            func(*args, **kwargs)
        except:
            print(traceback.format_exc())
        finally:
            sys.stdout = old_stdout
            body = buffer.getvalue()
            buffer.close()
        return body
    return wrapper

@return_std
def some_useful_function():
    print('so this line is executed at ' + time.strftime('%X'))
    time.sleep(5)

print('program execute at ' + time.strftime('%X'))
std_of_some_useful_function = some_useful_function()
print(std_of_some_useful_function)
print('but we can not know if it succeeded or failed before ' + time.strftime('%X'))

output

program execute at 11:11:11
so this line is executed at 11:11:11

but we can not know if it succeeded or failed before 11:11:16

This way captures all print and error in some_useful_function, and stores it as the function return. But print will not show BEFORE this function has finished

How should I modify the wrapper return_std, so I can see the output at 11:11:11 instantly (like it was really printed), and get all previous print stored at 11:11:16

2

There are 2 answers

3
Iain Shelvington On BEST ANSWER

You can create a subclass of io.StringIO and override the write() method so that it writes to a saved version of sys.stdout aswell as writing to it's internal buffer

class SavedStdOut(io.StringIO):

    def __init__(self, stdout):
        super().__init__()
        self.stdout = stdout

    def write(self, s: str):
        self.stdout.write(s)
        return super().write(s)

The class then writes to the passed file and saves the written data

foo = SavedStdOut(sys.stdout)
foo.write('Hello\n')  # Hello
print('Buffer:', foo.getvalue())  # Buffer: Hello

A complete example

import io
import sys
import time
import traceback


class SavedStdOut(io.StringIO):

    def __init__(self, stdout):
        super().__init__()
        self.stdout = stdout

    def write(self, s: str):
        self.stdout.write(s)
        return super().write(s)


def return_std(func):
    def wrapper(*args, **kwargs):
        sys.stdout = buffer = SavedStdOut(sys.stdout)
        try:
            func(*args, **kwargs)
        except:
            print(traceback.format_exc())
        finally:
            sys.stdout = buffer.stdout
            body = buffer.getvalue()
            buffer.close()
        return body
    return wrapper


@return_std
def some_useful_function():
    print('so this line is executed at ' + time.strftime('%X'))
    time.sleep(5)


print('program execute at ' + time.strftime('%X'))
std_of_some_useful_function = some_useful_function()
print('saved stdout:', std_of_some_useful_function)
print('but we can not know if it succeeded or failed before ' + time.strftime('%X'))

Output:

program execute at 03:52:01
so this line is executed at 03:52:01  # Printed as soon as print() is called
saved stdout: so this line is executed at 03:52:01

but we can not know if it succeeded or failed before 03:52:06
3
Ajinkya Kamat On

Not convinced that whatever you are trying to do is a good idea but you can do it like so.

Create a my_print class with __call__ operator and overwrite the builtins.print method with your class like this.

class MyPrint():

    def __init__(self):
        self.__buffer = io.StringIO()
        self.__old_print = __builtin__.print

    def getvalue(self):
        return self.__buffer.getvalue()

    def close(self):
        self.__buffer.close()
        __builtin__.print = self.__old_print

    def __call__(self, *args, **kwargs):
        self.__buffer.write(args[0])
        self.__old_print(*args, **kwargs)

def return_std(func):
    def wrapper(*args, **kwargs):
        my_print = MyPrint()
        __builtin__.print = my_print
        try:
            func(*args, **kwargs)
        except:
            print(traceback.format_exc())
        finally:
            body = my_print.getvalue()
            my_print.close()
        return body
    return wrapper

@return_std
def some_useful_function():
    print('so this line is executed at ' + time.strftime('%X'))
    time.sleep(1)

print('program execute at ' + time.strftime('%X'))
std_of_some_useful_function = some_useful_function()
print(std_of_some_useful_function)
print('but we can not know if it succeeded or failed before ' + time.strftime('%X'))