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.

Several Exceptions

So far we have been dealing with only one possible exception, FileNotFoundError. However, we know that the code will raise two different exceptions, the second one being an  AttributeError. If you are not sure about which errors can be raised, you can generate them on purpose. For instance, if you run this code:

file = open('my_data.dat', 'a')
file.readfile()

You will get the following message:

AttributeError: '_io.TextIOWrapper' object has no attribute 'readfile'

The first string is the type of exception, AttributeError, while the second part is the message. The same exception can have different messages, which describe better what has happened. What we want is to catch the  AttributeError, but also we want to catch the FileNotFound . Therefore, our code would look like this:

filename = 'my_data.dat'

try:
    file = open(filename)
    data = file.readfile()
except FileNotFoundError:
    file = open(filename, 'w')
    print('Created file')
except AttributeError:
    print('Attribute Error')

Now, you are dealing with several exceptions. Remember that when an exception is raised within the try block, the rest of the code will not be executed, and Python will go through the different  except blocks. Therefore, only one exception is raised at a time. In the case where the file doesn't exist, the code will deal only with the  FileNotFoundError .

Of course, you can also add a final exception to catch all other possible errors in the program, like this:

filename = 'my_data.dat'

try:
    file = open(filename)
    data = file.read()
    important_data = data[0]
except FileNotFoundError:
    file = open(filename, 'w')
    print('Created file')
except AttributeError:
    print('Attribute Error')
except:
    print('Unhandled exception')

In this case, if the file exists but it is empty, we are going to have a problem trying to access data[0] . We are not prepared for that exception and therefore we are going to print a message saying Unhandled exception. It would be, however, more interesting to let the user know what exception was actually raised. 

We can do the following:

filename = 'my_data.dat'

try:
    file = open(filename)
    data = file.read()
    important_data = data[0]
except Exception as e:
    print('Unhandled exception')
    print(e)

Which will output the following message:

Unhandled exception
string index out of range

The exception also has a type, which you can use. For example:

filename = 'my_data.dat'

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

print(important_data)

Which will print the first letter of Information, i.e. I . The pattern above has a very important drawback, and is that important_data may end up not being defined. For example, if the file  my_data.dat doesn't exist, we will get another error:

NameError: name 'important_data' is not defined