Stream tee in Python: saving STDOUT to file while keeping the console alive

Today, I was debugging within the Simics virtual platform debugger when I needed to log my console to file, while still being able to work with it.

Usually, I would have done:

c:\prompt> [commands to start Simics] > out.txt

However, this kills the console since stdout is fully redirected to a file.

I quickly coded a short hack that allows me to redirect Simics’ built-in Python interpreter’s standard-out stream to a file, without it becoming invisible. The trick is to make a delegate stream that keeps a copy of sys.stdout and emits every calls to the stream to both a log stream and sys.stdout. This trick works to redirect stdout in any Python interpreter or program, not just Simics. The hack to catch all method calls (ie: open(), close(), write()) on the stream_tee instance is derived from a post on GitHub from Anand Kunal.

The class that does this in Python is as follows:

class stream_tee(object):
    # Based on https://gist.github.com/327585 by Anand Kunal
    def __init__(self, stream1, stream2):
        self.stream1 = stream1
        self.stream2 = stream2
        self.__missing_method_name = None # Hack!
 
    def __getattribute__(self, name):
        return object.__getattribute__(self, name)
 
    def __getattr__(self, name):
        self.__missing_method_name = name # Could also be a property
        return getattr(self, '__methodmissing__')
 
    def __methodmissing__(self, *args, **kwargs):
            # Emit method call to the log copy
            callable2 = getattr(self.stream2, self.__missing_method_name)
            callable2(*args, **kwargs)
 
            # Emit method call to stdout (stream 1)
            callable1 = getattr(self.stream1, self.__missing_method_name)
            return callable1(*args, **kwargs)

To use it to redirect standard out, simply do:

import sys
from stream_tee import *
 
logfile = file("blah.txt", "w+")
sys.stdout = stream_tee(sys.stdout, logfile)
# Now, every operation on sys.stdout is also mirrored on logfile

In a Simics script, this can be done like so:

@import sys
@from stream_tee import *
@logfile = file("blah.txt", "w+")
@sys.stdout = stream_tee(sys.stdout, logfile)

This short hack allows me to save my entire sessions, while being able to work interactively in the shell. For some reason, Simics 4.0 does not have such an auto-logging feature…

As a side effect, the method just shown can be used and extended to replicate method calls on multiple instances of a class (not just streams), by wrapping them in a generalized “tee”. The only caveat is that only one of the instances can actually be used to return the value from the method call. This seems to work good on streams though 😉

3 thoughts on “Stream tee in Python: saving STDOUT to file while keeping the console alive

  1. @Alexandre

    I don’t want to use a Unix-like shell and this particular implementation allows you to programmatically enable/disable streaming to a file at any arbitrary point during the execution of the program linked with an internal Python interpreter.

Leave a Reply