Search code examples
pythonhtmlfileflushcontextmanager

By using context manager, my code writes the text in descending order (second line first instead of first line) into the file


I am trying to write html using class in Python. Some guidance have been given by my lecturer. When I try to use with function to write text into the HTML file, the lines below the first initiation are written into the file first. Can I know how to solve it?

Code:

class DOM:
    class HtmlTable:
        def __init__(self, indent, tag_name):
            self.indent = indent
            self.tag_name = tag_name
            
        def __enter__(self):
            self.file = open('test.html', 'a')
            self.file.write(f'{self.indent*"  "}<{self.tag_name} >\n')
            return self.file

        def __exit__(self, exception_type, exception_value, traceback):
            self.file.write(f'{self.indent*"  "}</{self.tag_name}>\n')
            self.file.close()
    
    def __init__(self):
        self.indent = -2

    def tag(self, tag_name):
        self.indent += 2
        return self.HtmlTable(self.indent, tag_name)

Test:

if __name__ == '__main__':
    doc = DOM()
    with doc.tag('html'):
        with doc.tag('head'):
            #remaining code

Output:

    <head >
    </head>
<html >
</html>

Desired Output:

<html >
    <head >
    </head>
</html>

Solution

  • You may want to .flush() your file - only on "flush" the actual file content is written to disc - until then the file object caches what needs to be done in its internal buffer:

    class DOM: 
        class HtmlTable:
            def __init__(self, indent, tag_name):
                self.indent = indent
                self.tag_name = tag_name
                
            def __enter__(self):
                self.file = open('test.html', 'a+')
                self.file.write(f'{self.indent*"  "}<{self.tag_name} >\n') 
                self.file.flush()  ########## here ##########
                return self.file
    
    
            def __exit__(self, exception_type, exception_value, traceback):
                self.file.write(f'{self.indent*"  "}</{self.tag_name}>\n')
                self.file.close()
    
        # fixed indentation        
        def __init__(self):
            self.indent = -2
    
        # fixed indentation        
        def tag(self, tag_name):
            self.indent += 2        
            return self.HtmlTable(self.indent, tag_name)
    
    
    def main():
        with open("test.html","w") as f:
            f.write("\n")
    
        doc = DOM()
        with doc.tag('html'):
            pass
            with doc.tag('head'):
                pass
    
        print(open("test.html").read())
    
    if __name__ == '__main__':
        main()
    

    ==>

    <html >
        <head >
        </head>
    </html>
    

    Currently the file itself flushes its buffer to disc when needed - it "is needed" when you hit self.file.close() in your __exit__(). The first __exit__ is done for the tag "head".

    Until the file is "flushed" the "things to be written" are kept in the file objects internal buffer - that is the reason for the output you are getting.