Python Exceptions

Posted by Mike Barkas on Monday January 02, 2017

Basic Overview of Python Exceptions

One of the major error types in Python are exceptions.

Not including syntax errors, errors that are encountered when your program runs, are called exceptions and if no handler for this error is found, it will be an unhandled exception and this will stop your program.

You can guard against exceptions stopping your program by wrapping the code that may cause the issue with a try/except statement.

The basic idea is to try the code you think may cause an error, and if it does cause an error, handle the exception with except and then you can change the flow of your code or logic.

Example:

try:
  file = open('file_name.txt')
except IOError as error:
  print('File not found')

The code example above specifically checks for an input output error using the IOError exception class. If the file cannot be opened, show a message to the user.

You can also handle different types of exceptions with multiple except clauses.

Example:

try:
  file = open('file_name.txt')
except IOError as error:
  print('File not found')
except ValueError:
  print('Data must be an integer')

The examples above are only to show exception syntax examples. There are additional clauses, you can use except clauses, else clause, and a finally clause that is always executed.

Exception Inheritance Hierarchy

Exceptions are basically classes that handle a specific type of error and are set in a hierarchy. These are called built-in exceptions.

The first class that all other exception classes are built on is the BaseException class. Other exception classes extend this class and provide specific error checking. Two examples that directly extend the base class are SystemExit and KeyboardInterrupt.

Another exception class that is derived from the base class is the Exception class. This class has many sub-classes. Two examples are ImportError and ValueError. There are many others, check the Python documentation for your interpreter version.

Specifying Exception Handlers

When handling exceptions in your code, if you don't specify an exception class you will handle all exceptions. If you don't handle these classes properly this will cause problems with your program including Python itself. If you catch all exceptions you will be removing SystemExit and KeyboardInterrupt.

Bad example:

try:
  file = open('file_name.txt')
except:
  continue

Handling all exceptions from the Exception class is also not a good idea because you will be hiding many exceptions that find common mistakes like SyntaxError and NameError exceptions. Try to find a specific error type you want to check for in your code.

When you specify an exception class in an except statement, all exceptions that are sub-classes of that class will also be caught.

Use the method resolution order method to help determine base classes with mro().

Example:

>>> ValueError.mro()
[<type 'exceptions.ValueError'>, <type 'exceptions.StandardError'>,
<type 'exceptions.Exception'>, <type 'exceptions.BaseException'>,
<type 'object'>]
>>>

Creating A Custom Exception

When creating a custom exception class it is recommended to sub-class the Exception class. This will inherit all the necessary functionality from the base class Exception.

This will provide the minimum needed to have a custom exception class name.

Example:

class MyCustomError(Exception):
  pass

Then use this custom exception class in your function definition:

if variable_one > variable_two:
  raise MyCustomError('Custom error message')

You can also override the __init__() in your custom exception class to provide a custom error message.

Exception Payloads

Exception objects will contain information of why the exception happened. Most exceptions will accept a single string as their payload. You can supply a more informative error message as a single string argument when you handle an exception.

Example:

raise ValueError('Value must be greater than 5')

Access the exception payload via the args attribute or convert the exception message to a string str(e) then print the message.

Some exceptions have additional attributes that can provide more information, UnicodeError for example.

Raising Exceptions

Raising an exception in your code is done by the keyword raise and then specify the exception class.

Example:

if variable is None:
  raise ValueError('Provide a value')

Exception Chaining

Implicit chaining: One exception causes another exception.

The first exception’s information is available in the stack trace because it is stored in the __context__ attribute of the last exception class.

Explicit chaining: Associate a new exception when an exception is raised.

Used to translate one exception type to another. The original exception type is stored in the __cause__ attribute.

This is done with the from keyword with the new exception name.

Example:

class CustomError(Exception):
  pass

def my_function(x, y):
  try:
    return math()
  except ZeroDivisionError as e:
    raise CustomError('Custom error message') from e

The first exception is defined as e and then the second exception is from e.

Traceback

Traceback information can be found on an exception class in the __traceback__ attribute.


I tried to keep this article a simple over view of some of the basics with Python exceptions. There are many more details and customization options you can learn more about in the Python documentation.

tags: python 3