Search code examples
pythondestructorpysqlite

Cleaning up an internal pysqlite connection on object destruction


I have an object with an internal database connection that's active throughout its lifetime. At the end of the program's run, the connection has to be committed and closed. So far I've used an explicit close method, but this is somewhat cumbersome, especially when exceptions can happen in the calling code.

I'm considering using the __del__ method for closing, but after some reading online I have concerns. Is this a valid usage pattern? Can I be sure that the internal resources will be freed in __del__ correctly?

This discussion raised a similar question but found no satisfactory answer. I don't want to have an explicit close method, and using with isn't an option, because my object isn't used as simply as open-play-close, but is kept as a member of another, larger object, that uses it while running in a GUI.

C++ has perfectly working destructors where one can free resources safely, so I would imagine Python has something agreed-upon too. For some reason it seems not to be the case, and many in the community vow against __del__. What's the alternative, then?


Solution

  • You can make a connection module, since modules keep the same object in the whole application, and register a function to close it with the atexit module

    # db.py:
    import sqlite3
    import atexit
    
    con = None
    
    def get_connection():
        global con
        if not con:
            con = sqlite3.connect('somedb.sqlite')
        atexit.register(close_connection, con)
        return con
    
    def close_connection(some_con):
        some_con.commit()
        some_con.close()
    
    # your_program.py
    import db
    con = db.get_connection()
    cur = con.cursor()
    cur.execute("SELECT ...")
    

    This sugestion is based on the assumption that the connection in your application seems like a single instance (singleton) which a module global provides well.

    If that's not the case, then you can use a destructor.

    However destructors don't go well with garbage collectors and circular references (you must remove the circular reference yourself before the destructor is called) and if that's not the case (you need multiple connections) then you can go for a destructor. Just don't keep circular references around or you'll have to break them yourself.

    Also, what you said about C++ is wrong. If you use destructors in C++ they are called either when the block that defines the object finishes (like python's with) or when you use the delete keyword (that deallocates an object created with new). Outside that you must use an explicit close() that is not the destructor. So it is just like python - python is even "better" because it has a garbage collector.