2

I'm trying to prevent resource leakage using a with statement. Let's consider the following simplified example. We can uncomment 2 lines to make open or close raise exceptions.

class Test:

    def open(self):
        print('open')
        self.f = open('test.txt', 'w')
        #raise Exception('Open crashed')

    def close(self):
        print('close')
        self.f.close()
        #raise Exception('Close crashed')

    def __enter__(self):
        print('enter')
        self.open()
        return self

    def __exit__(self, e_type, e_value, e_traceback):
        print('exit')
        self.close()
        return False

with Test() as test:
    raise Exception('Ooops!')

Running it as is results in the following output. Everything is nice and the file descriptor does not leak.

enter
open
exit
close
Traceback (most recent call last):
  File "test.py", line 24, in <module>
    raise Exception('Ooops!')
Exception: Ooops!

However if open raises an exception we get the following output. The file descriptor leaks.

enter
open
Traceback (most recent call last):
  File "test.py", line 23, in <module>
    with Test() as test:
  File "test.py", line 15, in __enter__
    self.open()
  File "test.py", line 6, in open
    raise Exception('Open crashed')
Exception: Open crashed

Now if close raises an exception we get the following output. The file description does not leak, but the trace makes me think there might be a better way to handle this.

enter
open
exit
close
Traceback (most recent call last):
  File "test.py", line 24, in <module>
    raise Exception('Ooops!')
Exception: Ooops!

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 24, in <module>
    raise Exception('Ooops!')
  File "test.py", line 20, in __exit__
    self.close()
  File "test.py", line 11, in close
    raise Exception('Close crashed')
Exception: Close crashed

What's the right way to do this please?

For what it's worth here are my real open and close functions. I'm opening and configuring a SQLite connection in open, and closing it in close.

def open(self):
    self.connection = sqlite3.connect(
        self.database_file, isolation_level=None)
    self.cursor = self.connection.cursor()
    self.cursor.execute('PRAGMA foreign_keys=ON')
    self.cursor.execute('PRAGMA journal_mode=WAL')

def close(self):
    self.connection.close()
marcv81
  • 850
  • 8
  • 22

0 Answers0