Error Handling in Python using: With, Try, Exception and Finally

In this article we will discuss error handling using Python With Statements Try/Except/Finally statements, show how to use these in combination, and compare how it works to try/catch code blocks in other languages.

What is error handling?

Error handling is when you put in some extra code to tell your script what to do when things don’t go totally to plan. perhaps you try to open a file that isn’t there. Or perhaps a user puts in unexpected input.

Without any error handling, your program or script will simply crash, throw an error, and quit running. It is important to at least put in a minimal amount of error handling to ensure that your script/program will run reliably.

Try/Catch Statements

In many languages, you use Try/Catch statements for error handling. In C# for example, you might write some code that looks like this:

Try{
string text = System.IO.File.ReadAllText(@"C:\Users\Public\TestFolder\WriteText.txt");

}
Catch(exception e){
console.writeline(e);
}

The above code will attempt to read the txt file. If it cannot read the file, it will throw an exception. The catch code block then catches that exception into variable e. And uses the console.writeline method to print the exception onto the console. That way you can see what happened.

If you didn’t put in the try/catch blocks, the exception would have still been shown on the screen, but the application would have crashed and you have had to re-launch it. Sometimes on your computer, you might get an error about an un-caught exception right before your program closes. Those are cases like this one where the system.io.file.readalltext method was not in a try block of code.

Python Try, Exception & Finally Statements

Python does not have try/catch blocks. Instead, python has try/exception blocks. They really do the same thing but have different names.

Let’s look at an example written in python:

try:
  f = open(“test.txt", 'r')
  data = f.read()
  print("Trying")
except:
  print("Fiddlesticks! Failed")
finally:
  print("Finally!")
  print("All Done")

In the above script, we start the try statement and we attempt to execute the code in that section. If it succeeds, it will print the text trying. It will then jump down to the finally section and print Finally! followed by “All Done” on the console.

If the above script is not able to open the test.txt file, it will throw an exception and print “FIddleSticks!” to the console, followed by Finally.

The next logical question about these statements are what are the roles of each section?

  • The Try code block is the code you really want to execute.
  • The exception code block is the bit of code that you want to execute in the event you get an error while executing the code in the try code block.
  • The Finally code block is code that you want to execute regardless of the outcome.

More helpful errors

You may find that simply writing fiddlesticks when you have an error is not all that helpful. You need to capture the error so you can write it to a log file, or maybe you want to display the error on the screen.

Lets try executing this code in our python script:

f = open(“test.txt", 'r')
data = f.read()
print(data)

The above script will read test.txt and print the contents to the console. Unfortunately, if test.txt does not exist, you will get an error like this one:

IOError: [Errno 2] No such file or directory: 'test.txt'

Notice the type of error is IOError. That is helpful because we can create an exception block of code specifically around IOErrors. Lets look at how we would write that code:

try:
  f = open(“test.txt", 'r')
  data = f.read()
  print(data)
Except IOError as e:
  Print(e)
except:
  print("Fiddlesticks! Failed")
finally:
  print("Finally!")
print("All Done")

The above code will attempt to run what is in the try block. If it failed with an IOError, it will run the code in the except block. In this case, it will print out the error saying something about how it could not open or close a file. It will then run the finally code block when everything is finished.

If we want to have different logic for different kinds of exceptions, we could keep adding similar code like the code below.  Notice we call out each type of exception. Then we have the option to have a different remediation step for each exception type.

try:
  f = open("test.txt", 'r')
    Try:
      data = f.read()
      print(data)
    except IOError as e:
      print(e)
    except ValueError as e:
      print(e)
    except EOFError as e:
      print(e)
    Except:
      print(“unknown error”)
    Finally:
      f.close()
except:
  print("Fiddlesticks! Failed")
finally:
  print("All Done")

In the case of our example above, we are doing the exact same logic for each exception, so we might as well consolidate the exception handling into a single exception line.  That would look like this:

try:
  f = open("test.txt", 'r')
  data = f.read()
  print(data)
except (IOError, ValueError, EOFError) as e:
  print(e)
except:
  print("Fiddlesticks! Failed")
finally:
  print("All Done")

In the above example, we will print out the exception if it matches IOError, Valueerror, or EOFError. If it does not match any of those, it will print out Fiddlesticks. Here are a few of the most common exceptions you may want to handle:

  • IOError – file cannot be opened
  • ImportError – cannot find the specified module
  • EOFError – When the input reaches the end of a file and no more data can be read
  • ValueError – function receives an argument that has the right type but an invalid value
  • KeyboardInterrupt – User hits the interrupt key (Ctrl+D or Ctrl+C)

Or if you want a more comprehensive list of Python Exceptions You check look here.

Creating Custom Exceptions

In the previous section, we. were focused on handling exceptions using the exceptions that are built-in to Python. However, as you are developing your application, you will most likely encounter situations where you want to handle exceptions a bit differently. This is when you would create your own custom exceptions.

To handle your own custom Exceptions, you have to create a class for each exception type. Then put in some code for what to do when that exception has occurred. The most basic of exception classes looks like this:

class MyError(Exception):
    pass

raise MyError("Test Exception!")

**Note the pass statement is there just for syntax reasons. You could put additional code there instead.

If you run the above code, you will see output that says Test Exception, like this:

Now that we know the basics to create our own custom exception. Let’s start with a new example. We have a basic math function that just adds numbers together and returns the sum:

def addnumbers(x,y):
    return x+y

print(addnumbers(3,2))

When we run the code above, the output is the number 5. Now let’s say that we want to throw an exception if someone passes in the number 3. We can create a custom exception that lets the user of our function know that we don’t allow the number 3 as an input. Then we can add some code to check if the number 3 was passed in, and raise an exception in that case.

class BadNumbersError(Exception):
    pass

def addnumbers(x,y):
    if x ==3:
        raise BadNumbersError("We don't like the number 3")
    return x+y

print(addnumbers(3,2))

In the code above, you can see we created a class called BadNumbrersError. Then in our function, we added a check. If x==3, then raise a BadNumbersError exception and pass in the text “We don’t like the number 3”.

Next, we call our function and pass in values of 3 and 2. WIthout this new exception, the output would be the number 5. But now that we have added in our exception when you run the code, you should see what our new custom exception looks like.

As you can see, we trigger our exception and present a message to the user that says that we don’t like the number 3 when calling this function.

Python With statements

With statements can be used with try/catch statements to reduce the amount of code you need to write for handling different kinds of errors.

With statements call the __Enter__ and __Exit__ functions that are part of a given class. An example of this is with the File open class.

To properly handle errors when opening files, you would need some code similar to this:

try:
  f = open(“test.txt", 'r')
    Try:
      data = f.read()
      print(data)
    except IOError as e:
      print(e)
    Except:
      print(“unknown error”)
    Finally:
      f.close()
except:
  print("Fiddlesticks! Failed")
finally:
  print("All Done")

Before we get into the with statements, lets examine this code a bit. In the previous sections, we wrote some code to catch exceptions when opening a file. But the other issue is what happens if we have an issue after the file is already open. We need to make sure we close the file. We could put that in the finally section at the very bottom. But that will throw an exception if the original file never successfully opened. The result is this big mess of nested try/except statements to hopefully catch all of the different scenarios you may encounter.

Lucky for us, the makers of Python came out with a With Statement. Like I said previously, a with statement has an __enter__ and an __exit__ function that it calls at the beginning and the end of the statement. This allows some of that code to be removed from the big try/except mess I demonstrated above. An example of a with statement can be seen below:

with open(“test.txt”,r) as f:
text=f.read()
Print(text)

The above text will still throw an exception if test.txt does not exist. However, we no longer have to remember to call f.close when we are all finished with the file. If we do have the file open and an error occurs, the with statement will handle closing out the file handles for me. What this means is we can use the with statement in conjunction with our try/except code blocks to give us cleaner looking code. Let’s look at another example:

try:
  with open(“test.txt", 'r’) as f:
    data = f.read()
    print(data)
Except IOError as e:
  Print(e)
except:
  print("Fiddlesticks! Failed")
finally:
  print("Finally!")
print("All Done")

Notice the above example looks a lot like our original example. It is the same number of lines, and it is still very readable. However, it gives us similar functionality to the second example with the nested try/except loops.

Summary

In today’s article we discussed What is Error handling, What is the role of Try/Catch code blocks. How to set up exceptions, how to create our own custom extensions, and what a with statement does for us.

Error handling is a very important part of writing good software. Software without proper error handling will be unstable, and may not give good output when invalid input is entered into it.