Search code examples
pythonwith-statementcontextmanagerdelresource-cleanup

__del__ is unreliable, but the object does not persist if I try and use a context manager


I have a script that instantiates a number of child objects. When the script (and these objects) end...the objects need to do a little cleanup (especially temp file close and delete).

I keep reading how __del__ is unreliable - however a context manage does not appear to work a the child objects will not persist. They need to hang around (to do things like file reads and writes)

An example:

WRITER.PY

import os

class Writer(object):
    def __init__(self, filename):
        self.filename = filename
        self.open()

    def open(self):
        self.fh = open(self.filename, "w+", 0)

    def write(self, msg):
        print >>self.fh, str(msg)

    def close(self):
        self.fh.close()
        os.remove(self.filename)

    def __enter__(self):
        print "entered"
#         self.open()


    def __exit__(self, type, value, traceback):
        print "__exit__"
        self.close()

MAIN.PY

def myfunc(filename):
    with WRITER.Writer(filename) as writeit:
        # do some stuff
        writeit.write("hallo")
        # do some more stuff
        writeit.write("more results")
        # even more stuff
        writeit.write("goodbye")

But when I run myfunc(), the object is garbage collected as soon as it finishes the __init__(). It goes straight from enter to exit and performs none of the duties after the with statement. It doesn't seem to matter if I put the open in the __init__ or the __enter__.

OUTPUT:

Traceback (most recent call last):
  File "/shared/GitHub/Tesera/MRAT_Refactor/bin/MAIN.py", line 13, in <module>
    myfunc("./tempfile")
  File "/shared/GitHub/Tesera/MRAT_Refactor/bin/MAIN.py", line 6, in myfunc
    writeit.write("hallo")
AttributeError: 'NoneType' object has no attribute 'write'
entered
__exit__            

Is there a way to use a context manager in this way, a better way to use __del__ ...or is there a third option to accomplish this?


Solution

  • The problem has nothing to do with garbage collection. As documented, the as clause binds the return value of the __enter__ method. You aren't returning anything, so you get None.

    If you want the Writer object to be returned, you need to do return self at the end of the __enter__ method.