Handling a File Error

As already pointed out in the exceptions lesson, Python contains a vast set of built-in exceptions. One important class has to do with file handling. The example presented in the lesson will help to review file handling methods and put them in the context of handling exceptions.

The else Block

To prevent what we just saw in the previous section, we can add one more block to the sequence: finally. This block is always going to be executed, regardless of whether an exception was raised or not. For example:

filename = 'my_data.dat'

try:
    file = open(filename)
    data = file.read()
    important_data = data[0]
except Exception as e:
    if isinstance(e, IndexError):
        print(e)
        data = 'Information'
        important_data = data[0]
    else:
        print('Unhandled exception')
finally:
    important_data = 'A'

print(important_data)


This is, in the end, a very silly example, because we are setting important_data to a special value, but I hope you can see the use of finally. If there is something that you must absolutely be sure that is executed, you can include it in a finally statement.

finally is very useful to be sure that you are closing a connection, the communication with a device, closing a file, etc. Generally speaking, releasing the resources. Finally has a very interesting behavior, because it is not executed always at the same moment. Let's see the following code:

filename = 'my_data.dat'

try:
    print('In the try block')
    file = open(filename)
    data = file.read()
    important_data = data[0]
except FileNotFoundError:
    print('File not found, creating one')
    file = open(filename, 'w')
finally:
    print('Finally, closing the file')
    file.close()
    important_data = 'A'

print(important_data)

First, run the code when the file my_data.dat doesn't exist. You should see the following output:

In the try block
File not found, creating one
Finally, closing the file
A

So, you see you went from the try to the except  to the finally. If you run the code again, the file will exist, and therefore the output will be completely different:

In the try block
Finally, closing the file
Traceback (most recent call last):
  File "JJ_exceptions.py", line 7, in 
    important_data = data[0]
IndexError: string index out of range

What you can see here is that when an unhandled exception is raised, the first block to be executed is the finally. You close the file immediately. And then, the error is re-raised. This is very handy because it prevents any kind of conflict with downstream code. You open, you close the file and then the rest of the program has to deal with the problem of the  IndexError. If you want to try a program without exceptions, just write something into my_data.dat  and you will see the output.