Python Exceptions
January 2, 2017
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 subclasses 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 subclass
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 overview 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.