0

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

Dacian Peng
  • 75
  • 1
  • 6

2 Answers2

2

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
Iain Shelvington
  • 31,030
  • 3
  • 31
  • 50
1

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'))
  • Thanks, Ajinkya. You helped me and prefectly solved my problem. Your script functions well even if error occurs. In my project, the `some_useful_function` is part of another decorator, which emails some scheduled functions' debug info. And the email decorator can be added to any function, so I can get a notifying email when an assigned task (function) is done or break. But sometimes the task lasts too long to wait, and I'm eager to know what happened. Unfortunately, I'm to check nothing in log before receiving email. Now I can check the output while task running, thanks! – Dacian Peng Mar 23 '23 at 07:09
  • @DacianPeng Consider using `import logging` instead of using print statements. It allows you to `addHandler()` to add capabilities like sending email notifications. – Ajinkya Kamat Mar 23 '23 at 17:02
  • Good idea, I'll explore the built-in `logging` further. Thanks! – Dacian Peng Mar 24 '23 at 03:24