Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you subclass the file type in Python?

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?

like image 427
Adam Rosenfield Avatar asked Jul 04 '09 19:07

Adam Rosenfield


2 Answers

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.

like image 72
Alex Martelli Avatar answered Oct 01 '22 22:10

Alex Martelli


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.

like image 43
e-satis Avatar answered Oct 01 '22 23:10

e-satis