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()