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.

Try ... Except

Imagine that you are reading data from a file. You can simply write something like this:

f = open('my_file.dat')
data = f.readfile()
print('Data loaded')

If you run the code, you will see a message like the following:

FileNotFoundError: [Errno 2] No such file or directory: 'my_file.dat'

When an error is raised, the code that follows will not be executed. That is why you don't see the statement Data loaded on the console. This is a somewhat innocent problem but imagine that you are communicating with a device. If there is an error in your program, you won't have the chance to close the communication with the device, or you won't be able to close a shutter to prevent damages to the detectors, etc.

Dealing with this kind of errors is normally done within a try / except  block. This means that if there is an error inside the try, the block except  will be executed. In the example above, we can do the following:

    f = open('my_file.dat')
    print('Loaded data')
    print('Data not loaded')

If you run the code, you will see a nice message being printed to screen saying Data not loaded. This is great! Now our program is not crashing and we can close the shutter, or stop the communication with our device. However, we don't know the reason why the data was not loaded.

Before continuing, create an empty file called my_file.dat, and run the script again. You will see that data is not being loaded, regardless whether the file exists or not. With this trivial example, you are seeing the risks around unspecific  except blocks. If we make a simpler script:

f = open('my_file.dat')
print('Loaded data')

The output will be:

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

Which is telling us that the problem is the method that we tried to use, readfile doesn't exist. When you use a plain  try/except block, you are sure you are handling all possible exceptions, but you have no way of knowing what actually went wrong. In simple cases like the one above, you have only two lines of code to explore. However, if you are building a package or a function, some errors will propagate downstream, and you don't know how they are going to affect the rest of the program.

For you to have an idea of the importance of correct handling of errors, I will tell you what I have witnessed using a program that ships with a very sophisticated lab instrument. The program that controls the Nano Sight has a very nice user interface. However, when you are saving data, if the filename you choose has a dot in it, the data will not be saved. Sadly, the data will also be lost and the user will never know that the problem was having a simple . in the filename.

Handling all possible errors in a graceful way is very complicated and sometimes almost impossible. However, you can see that even the software that ships with very expensive instruments (in this case I mean instruments with a price tag similar to a small apartment), also has to deal with all kinds of exceptions, and not always in the most user-friendly way.