views:

490

answers:

2

I'm trying to subclass the built-in file class in Python to add some extra features to stdin and stdout. Here's the code I have so far:

class TeeWithTimestamp(file):
    """
    Class used to tee the output of a stream (such as stdout or stderr) into
    another stream, and to add a timestamp to each message printed.
    """

    def __init__(self, file1, file2):
        """Initializes the TeeWithTimestamp"""
        self.file1 = file1
        self.file2 = file2
        self.at_start_of_line = True

    def write(self, text):
        """Writes text to both files, prefixed with a timestamp"""

        if len(text):
            # Add timestamp if at the start of a line; also add [STDERR]
            # for stderr
            if self.at_start_of_line:
                now = datetime.datetime.now()
                prefix = now.strftime('[%H:%M:%S] ')
                if self.file1 == sys.__stderr__:
                    prefix += '[STDERR] '
                text = prefix + text

            self.file1.write(text)
            self.file2.write(text)

            self.at_start_of_line = (text[-1] == '\n')

The purpose is to add a timestamp to the beginning of each message, and to log everything to a log file. However, the problem I run into is that if I do this:

# log_file has already been opened
sys.stdout = TeeWithTimestamp(sys.stdout, log_file)

Then when I try to do print 'foo', I get a ValueError: I/O operation on closed file. I can't meaningfully call file.__init__() in my __init__(), since I don't want to open a new file, and I can't assign self.closed = False either, since it's a read-only attribute.

How can I modify this so that I can do print 'foo', and so that it supports all of the standard file attributes and methods?

+6  A: 

Calling file.__init__ is quite feasible (e.g., on '/dev/null') but no real use because your attempted override of write doesn't "take" for the purposes of print statements -- the latter internally calls the real file.write when it sees that sys.stdout is an actual instance of file (and by inheriting you've made it so).

print doesn't really need any other method except write, so making your class inherit from object instead of file will work.

If you need other file methods (i.e., print is not all you're doing), you're best advised to implement them yourself.

Alex Martelli
I'm not sure exactly what function I'll need, but I'd certainly like to have some of the more commonly used ones implemented. I guess I'll just bite the bullet and implement the ones I need, and derive from object instead of file.
Adam Rosenfield
What methods besides write are commonly used on sys.stdout? Maybe writelines, close, flush -- all pretty easy (and you'd have to implement them yourself anyway -- how could the file type know to close or flush both of your files anyway?!-)
Alex Martelli
Yeah, you're right, upon looking over the file interface, all I really need are write, writelines, close, and flush. Anything else really should not ever be called on sys.stdout or sys.stderr.
Adam Rosenfield
A: 

You can as well avoid using super :

class SuperFile(file):

    def __init__(self, *args, **kwargs):
        file.__init__(self, *args, **kwargs)

You'll be able to write with it.

e-satis