Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to close file while using with and try..except? [duplicate]

Tags:

python

I wrote this example to show myself that __exit__ is not being run when an exception occurs:

class A(object):
    def __enter__(self):
        print('enter')
    def __exit__(self):
        print('exit')
try:
    with A() as a:
        raise RunTimeError()
except Exception as e:
    print('except')

Output:

enter
except

That said, what is the correct way to use a with statement and catch exceptions, while making sure __exit__ is being run in the end? Thanks!

like image 409
Idan Avatar asked Oct 17 '22 01:10

Idan


1 Answers

The __exit__ function is called, regardless whether the with body raises errors or not. Your function needs to have additional parameters exc_type (the type of the exception), exc_value (the exception object), and traceback (the traceback that is generated).

In case the with body did not raise an Error, the three parameters are None. In case there is an error, they take the values described above.

But you can for instance close a file, regardless whether there is an error and then later handle the error.

So we can here implement it for instance as:

class A(object):
    def __enter__(self):
        self.file = open('some_file.txt')
        return self.file

    def __exit__(self, exc_type, exc_value, traceback):
        print(('exit', exc_type, exc_value, traceback))
        # close the file, regardless of exceptions
        self.file.close()
        return False  # silence the exception?

If we now write something like:

with A():
    raise Exception

We will obtain the exception, the __exit__ function will print:

('exit', <class 'Exception'>, Exception(), <traceback object at 0x7fc512c924c8>)

We can inspect the exception class, exception value and traceback, and handle accordingly. For instance based on the exception, we might decide to close a file, send a bug report, abort the SQL transaction or not.

The __exit__ function also has a return value (if not specified, a Python function returns None). In case the __exit__ function returns an object with truthiness True, it will surpress the exception: the exception will not raise out of the with block. Otherwise it will be raised out of the with block. For instance in our example, we still receive the exception.

class SilenceExceptions(object):
    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_value, traceback):
        return True  # silence ALL exceptions

If we now call:

with SilenceExceptions():
    raise Error

we will not see the exception, since it is "catched" in the __exit__ function.

like image 150
Willem Van Onsem Avatar answered Oct 21 '22 05:10

Willem Van Onsem