Prerequisite: This article is an extension to Exception Handling.
In this article, we will try to cover How to Define Custom Exceptions in Python with Examples.
Example:
class CustomError(Exception): pass raise CustomError("Example of Custom Exceptions in Python") Output: CustomError: Example of Custom Exceptions in Python
Python throws errors and exceptions when the code goes wrong, which may cause the program to stop abruptly. Python also provides an exception handling method with the help of try-except. Some of the standard exceptions which are most frequent include IndexError, ImportError, IOError, ZeroDivisionError, TypeError, and FileNotFoundError.
User-Defined Exception in Python
Exceptions need to be derived from the Exception class, either directly or indirectly. Although not mandatory, most of the exceptions are named as names that end in “Error” similar to the naming of the standard exceptions in python. For example,
Python3
class
MyError(Exception):
def
__init__(
self
, value):
self
.value
=
value
def
__str__(
self
):
return
(
repr
(
self
.value))
try
:
raise
(MyError(
3
*
2
))
except
MyError as error:
print
(
'A New Exception occurred: '
, error.value)
Output
('A New Exception occurred: ', 6)
Customizing Exception Classes
To know more about class Exception, run the code below
Python3
Output
Help on class Exception in module exceptions: class Exception(BaseException) | Common base class for all non-exit exceptions. | | Method resolution order: | Exception | BaseException | __builtin__.object | | Methods defined here: | | __init__(...) | x.__init__(...) initializes x; see help(type(x)) for signature | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | __new__ = <built-in method __new__ of type object> | T.__new__(S, ...) -> a new object with type S, a subtype of T | | ---------------------------------------------------------------------- | Methods inherited from BaseException: | | __delattr__(...) | x.__delattr__('name') <==> del x.name | | __getattribute__(...) | x.__getattribute__('name') <==> x.name | | __getitem__(...) | x.__getitem__(y) <==> x[y] | | __getslice__(...) | x.__getslice__(i, j) <==> x[i:j] | | Use of negative indices is not supported. | | __reduce__(...) | | __repr__(...) | x.__repr__() <==> repr(x) | | __setattr__(...) | x.__setattr__('name', value) <==> x.name = value | | __setstate__(...) | | __str__(...) | x.__str__() <==> str(x) | | __unicode__(...) | | ---------------------------------------------------------------------- | Data descriptors inherited from BaseException: | | __dict__ | | args | | message
Example 1: User-Defined class with Multiple Inheritance
In the below article, we have created a class named “Error” derived from the class Exception. This base class is inherited by various user-defined classes to handle different types of python raise an exception with message
Python3
class
Error(Exception):
pass
class
zerodivision(Error):
pass
try
:
i_num
=
int
(
input
(
"Enter a number: "
))
if
i_num
=
=
0
:
raise
zerodivision
except
zerodivision:
print
(
"Input value is zero, try again!"
)
print
()
Output
Enter a number: Input value is zero, try again! ()
Enter a number: Input value is zero, try again!
Example 2: Deriving Error from Super Class Exception
Superclass Exceptions are created when a module needs to handle several distinct errors. One of the common ways of doing this is to create a base class for exceptions defined by that module. Further, various subclasses are defined to create specific exception classes for different error conditions.
Python3
class
Error(Exception):
pass
class
TransitionError(Error):
def
__init__(
self
, prev, nex, msg):
self
.prev
=
prev
self
.
next
=
nex
self
.msg
=
msg
try
:
raise
(TransitionError(
2
,
3
*
2
,
"Not Allowed"
))
except
TransitionError as error:
print
(
'Exception occurred: '
, error.msg)
Output
('Exception occurred: ', 'Not Allowed')
How to use standard Exceptions as a base class?
A runtime error is a class that is a standard exception that is raised when a generated error does not fall into any category. This program illustrates how to use runtime error as a base class and network error as a derived class. In a similar way, an exception can be derived from the standard exceptions of Python.
Python3
class
Networkerror(RuntimeError):
def
__init__(
self
, arg):
self
.args
=
arg
try
:
raise
Networkerror(
"Error"
)
except
Networkerror as e:
print
(e.args)
Output
('E', 'r', 'r', 'o', 'r')
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Raising and Handling Python Exceptions
A Python program terminates as soon as it encounters an error. In Python, an error can be a syntax error or an exception. In this article, you will see what an exception is and how it differs from a syntax error. After that, you will learn about raising exceptions and making assertions. Then, you’ll finish with a demonstration of the try and except block.
Exceptions versus Syntax Errors
Syntax errors occur when the parser detects an incorrect statement. Observe the following example:
>>> print( 0 / 0 ))
File "<stdin>", line 1
print( 0 / 0 ))
^
SyntaxError: invalid syntax
The arrow indicates where the parser ran into the syntax error. In this example, there was one bracket too many. Remove it and run your code again:
>>> print( 0 / 0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
This time, you ran into an exception error. This type of error occurs whenever syntactically correct Python code results in an error. The last line of the message indicated what type of exception error you ran into.
Instead of showing the message exception error
, Python details what type of exception error was encountered. In this case, it was a ZeroDivisionError
. Python comes with various built-in exceptions as well as the possibility to create self-defined exceptions.
Raising an Exception
We can use raise
to throw an exception if a condition occurs. The statement can be complemented with a custom exception.
If you want to throw an error when a certain condition occurs using raise
, you could go about it like this:
x = 10
if x > 5:
raise Exception('x should not exceed 5. The value of x was: {}'.format(x))
When you run this code, the output will be the following:
Traceback (most recent call last):
File "<input>", line 4, in <module>
Exception: x should not exceed 5. The value of x was: 10
The program comes to a halt and displays our exception to screen, offering clues about what went wrong.
The AssertionError
Exception
Instead of waiting for a program to crash midway, you can also start by making an assertion in Python. We assert
that a certain condition is met. If this condition turns out to be True
, then that is excellent! The program can continue. If the condition turns out to be False
, you can have the program throw an AssertionError
exception.
Have a look at the following example, where it is asserted that the code will be executed on a Linux system:
import sys
assert ('linux' in sys.platform), "This code runs on Linux only."
If you run this code on a Linux machine, the assertion passes. If you were to run this code on a Windows machine, the outcome of the assertion would be False
and the result would be the following:
Traceback (most recent call last):
File "<input>", line 2, in <module>
AssertionError: This code runs on Linux only.
In this example, throwing an AssertionError
exception is the last thing that the program will do. The program will come to halt and will not continue. What if that is not what you want?
The try
and except
Block: Handling Exceptions
The try
and except
block in Python is used to catch and handle exceptions. Python executes code following the try
statement as a “normal” part of the program. The code that follows the except
statement is the program’s response to any exceptions in the preceding try
clause.
As you saw earlier, when syntactically correct code runs into an error, Python will throw an exception error. This exception error will crash the program if it is unhandled. The except
clause determines how your program responds to exceptions.
The following function can help you understand the try
and except
block:
def linux_interaction():
assert ('linux' in sys.platform), "Function can only run on Linux systems."
print('Doing something.')
The linux_interaction()
can only run on a Linux system. The assert
in this function will throw an AssertionError
exception if you call it on an operating system other then Linux.
You can give the function a try
using the following code:
try:
linux_interaction()
except:
pass
The way you handled the error here is by handing out a pass
. If you were to run this code on a Windows machine, you would get the following output:
You got nothing. The good thing here is that the program did not crash. But it would be nice to see if some type of exception occurred whenever you ran your code. To this end, you can change the pass
into something that would generate an informative message, like so:
try:
linux_interaction()
except:
print('Linux function was not executed')
Execute this code on a Windows machine:
Linux function was not executed
When an exception occurs in a program running this function, the program will continue as well as inform you about the fact that the function call was not successful.
What you did not get to see was the type of error that was thrown as a result of the function call. In order to see exactly what went wrong, you would need to catch the error that the function threw.
The following code is an example where you capture the AssertionError
and output that message to screen:
try:
linux_interaction()
except AssertionError as error:
print(error)
print('The linux_interaction() function was not executed')
Running this function on a Windows machine outputs the following:
Function can only run on Linux systems.
The linux_interaction() function was not executed
The first message is the AssertionError
, informing you that the function can only be executed on a Linux machine. The second message tells you which function was not executed.
In the previous example, you called a function that you wrote yourself. When you executed the function, you caught the AssertionError
exception and printed it to screen.
Here’s another example where you open a file and use a built-in exception:
try:
with open('file.log') as file:
read_data = file.read()
except:
print('Could not open file.log')
If file.log does not exist, this block of code will output the following:
This is an informative message, and our program will still continue to run. In the Python docs, you can see that there are a lot of built-in exceptions that you can use here. One exception described on that page is the following:
Exception
FileNotFoundError
Raised when a file or directory is requested but doesn’t exist. Corresponds to errno ENOENT.
To catch this type of exception and print it to screen, you could use the following code:
try:
with open('file.log') as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
In this case, if file.log does not exist, the output will be the following:
[Errno 2] No such file or directory: 'file.log'
You can have more than one function call in your try
clause and anticipate catching various exceptions. A thing to note here is that the code in the try
clause will stop as soon as an exception is encountered.
Look at the following code. Here, you first call the linux_interaction()
function and then try to open a file:
try:
linux_interaction()
with open('file.log') as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
except AssertionError as error:
print(error)
print('Linux linux_interaction() function was not executed')
If the file does not exist, running this code on a Windows machine will output the following:
Function can only run on Linux systems.
Linux linux_interaction() function was not executed
Inside the try
clause, you ran into an exception immediately and did not get to the part where you attempt to open file.log. Now look at what happens when you run the code on a Linux machine:
[Errno 2] No such file or directory: 'file.log'
Here are the key takeaways:
- A
try
clause is executed up until the point where the first exception is encountered. - Inside the
except
clause, or the exception handler, you determine how the program responds to the exception. - You can anticipate multiple exceptions and differentiate how the program should respond to them.
- Avoid using bare
except
clauses.
The else
Clause
In Python, using the else
statement, you can instruct a program to execute a certain block of code only in the absence of exceptions.
Look at the following example:
try:
linux_interaction()
except AssertionError as error:
print(error)
else:
print('Executing the else clause.')
If you were to run this code on a Linux system, the output would be the following:
Doing something.
Executing the else clause.
Because the program did not run into any exceptions, the else
clause was executed.
You can also try
to run code inside the else
clause and catch possible exceptions there as well:
try:
linux_interaction()
except AssertionError as error:
print(error)
else:
try:
with open('file.log') as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
If you were to execute this code on a Linux machine, you would get the following result:
Doing something.
[Errno 2] No such file or directory: 'file.log'
From the output, you can see that the linux_interaction()
function ran. Because no exceptions were encountered, an attempt to open file.log was made. That file did not exist, and instead of opening the file, you caught the FileNotFoundError
exception.
Cleaning Up After Using finally
Imagine that you always had to implement some sort of action to clean up after executing your code. Python enables you to do so using the finally
clause.
Have a look at the following example:
try:
linux_interaction()
except AssertionError as error:
print(error)
else:
try:
with open('file.log') as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
finally:
print('Cleaning up, irrespective of any exceptions.')
In the previous code, everything in the finally
clause will be executed. It does not matter if you encounter an exception somewhere in the try
or else
clauses. Running the previous code on a Windows machine would output the following:
Function can only run on Linux systems.
Cleaning up, irrespective of any exceptions.
Summing Up
After seeing the difference between syntax errors and exceptions, you learned about various ways to raise, catch, and handle exceptions in Python. In this article, you saw the following options:
raise
allows you to throw an exception at any time.assert
enables you to verify if a certain condition is met and throw an exception if it isn’t.- In the
try
clause, all statements are executed until an exception is encountered. except
is used to catch and handle the exception(s) that are encountered in the try clause.else
lets you code sections that should run only when no exceptions are encountered in the try clause.finally
enables you to execute sections of code that should always run, with or without any previously encountered exceptions.
Hopefully, this article helped you understand the basic tools that Python has to offer when dealing with exceptions.
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Raising and Handling Python Exceptions
An Exception is raised whenever there is an error encountered, and it signifies that something went wrong with the program. By default, there are many exceptions that the language defines for us, such as TypeError
when the wrong type is passed. In this article, we shall look at how we can create our own Custom Exceptions in Python.
But before we take a look at how custom exceptions are implemented, let us find out how we could raise different types of exceptions in Python.
Raise Exceptions
Python allows the programmer to raise an Exception manually using the raise
keyword.
Format: raise ExceptionName
The below function raises different exceptions depending on the input passed to the function.
def exception_raiser(string): if isinstance(string, int): raise ValueError elif isinstance(string, str): raise IndexError else: raise TypeError
Output:
>>> exception_raiser(123) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in exception_raiser ValueError >>> exception_raiser('abc') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in exception_raiser IndexError >>> exception_raiser([123, 456]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 7, in exception_raiser TypeError
As you can observe, different types of Exceptions are raised based on the input, at the programmer’s choice. This allows for good flexibility of Error Handling as well, since we can actively predict why an Exception can be raised.
Defining Custom Exceptions
Similarly, Python also allows us to define our own custom Exceptions. We are in complete control of what this Exception can do, and when it can be raised, using the raise
keyword. Let us look at how we can define and implement some custom Exceptions.
1. Create a Custom Exception Class
We can create a custom Exception class to define the new Exception. Again, the idea behind using a Class is because Python treats everything as a Class. So it doesn’t seem that outlandish that an Exception can be a class as well!
All Exceptions inherit the parent Exception
Class, which we shall also inherit when creating our class.
We shall create a Class called MyException
, which raises an Exception only if the input passed to it is a list and the number of elements in the list is odd.
class MyException(Exception): pass def list_check(lst): if len(lst) % 2 != 0: raise MyException # MyException will not be raised list_check([1, 2, 3, 4]) # MyException will be raised list_check([1, 3, 5])
Output:
[email protected]:~# python3 exceptions.py Traceback (most recent call last): File "exceptions.py", line 12, in <module> list_check([1, 3, 5]) File "exceptions.py", line 6, in list_check raise MyException __main__.MyException
2. Add a custom Message and Error
We can add our own error messages and print them to the console for our Custom Exception. This involves passing two other parameters in our MyException
class, the message
and error
parameters.
Let us modify our original code to account for a custom Message and Error for our Exception.
class MyException(Exception): def __init__(self, message, errors): # Call Exception.__init__(message) # to use the same Message header as the parent class super().__init__(message) self.errors = errors # Display the errors print('Printing Errors:') print(errors) def list_check(lst): if len(lst) % 2 != 0: raise MyException('Custom Message', 'Custom Error') # MyException will not be raised list_check([1, 2, 3, 4]) # MyException will be raised list_check([1, 3, 5])
Output:
Printing Errors: Custom Error Traceback (most recent call last): File "exceptions.py", line 17, in <module> list_check([1, 3, 5]) File "exceptions.py", line 11, in list_check raise MyException('Custom Message', 'Custom Error') __main__.MyException: Custom Message
We have thus successfully implemented our own Custom Exceptions, including adding custom error messages for debugging purposes! This can be very useful if you are building a Library/API and another programmer wants to know what exactly went wrong when the custom Exception is raised.
Conclusion
In this article, we learned how to raise Exceptions using the raise
keyword, and also build our own Exceptions using a Class and add error messages to our Exception.
References
- JournalDev article on Custom Exceptions
- Exception Handling in Python
- Create a Custom Exception Class in Python
- Execute Exception-Handling Using the
try...except
Block in Python
This tutorial will demonstrate you can create custom exception classes in Python. Here, we’ll show how you can properly perform exception handling, define custom exception classes, and override existing built-in exceptions.
Exceptions are a type of event that occurs whenever something within a program doesn’t go as intended or disrupts the flow of the intended use-case of the program. Without exception handling, the program will cease to execute entirely, and the exception would have to either be fixed or handled.
Create a Custom Exception Class in Python
Creating an Exception Class in Python is done the same way as a regular class. The main difference is you have to include the Python’s base Exception
class to inform the compiler that the class you’re making is an exception class.
Let’s test this method out to create an exception class called DemoException
and use the placeholder control flow keyword pass
inside as a placeholder.
class DemoException(Exception):
pass
Execute Exception-Raising Using the Keyword raise
in Python
To test the DemoException
class and see what it displays when it’s actually triggered, perform exception raising. Exception-raising is synonymous with exception-throwing in other programming languages.
Using the keyword raise
, trigger an exception using the given exception class and outputs an exception message.
class DemoException(Exception):
pass
raise DemoException
Output:
Traceback (most recent call last):
File "/Users/demo/python/demo_exception.py", line 4, in <module>
raise DemoException
__main__.DemoException
A standard exception will look like in the terminal if no custom exception message has been declared.
Declare a Custom Exception Message in Python
To declare a custom exception message for DemoException
, override the __init__()
method of the exception class and include the message that should be outputted for the exception in the parameters, along with the mandatory self-referential parameter self
.
For example, let’s override the __init__()
method and create a custom message for the DemoException
class:
class DemoException(Exception):
def __init__(self, message):
super().__init__(message)
Take note that for the message to be integrated into your exception successfully, call the base Exception
class, __init__()
method, and include the message
as an argument.
Let’s call the exception class again using the raise
keyword, and now, passing a custom message with it:
class DemoException(Exception):
def __init__(self, message):
super().__init__(message)
message = "Exception Triggered! Something went wrong."
raise DemoException(message)
The output should look like this:
Traceback (most recent call last):
File "/Users/demo/python/helloworld.py", line 6, in <module>
raise DemoException(message)
__main__.DemoException: Exception Triggered! Something went wrong.
We’ve now successfully created and triggered an exception class with a custom error message.
For actual situations that may trigger an exception, how do we handle and raise these exceptions? You can solve this problem neatly by implementing exception-handling using the try...except
block.
Execute Exception-Handling Using the try...except
Block in Python
The try...except
block is much like the try-catch
block in other languages like Java.
The try...except
block has 2 main blocks and 2 optional blocks:
try
(required) — The main block responsible for encapsulating the code block where the exception might be triggered. Thetry
block halts the whole process within it whenever an exception is triggered.except
(required) — The block program proceeds whenever a specified exception is triggered. This block typically contains a descriptive error message for the caller or just a simpleprint()
statement. There may be more than oneexcept
block in a singletry
block, each one catching different exceptions.else
(optional) — This optional block is where the program will proceed if thetry
block did not trigger any exceptions.finally
(optional) — This optional block runs once everything from the previous 3 blocks has been performed regardless if an exception is triggered or not.
Let’s use the previous example using the DemoException
class to try a simple try...except
block.
First, wrap the raise
keyword in a function and put it inside the try...except
block.
The function that we’ll create for this example is a function that accepts a number and throws an exception if it sends 0
. If it sends any other number, then the code will proceed as intended. Check the example below:
class DemoException(Exception):
def __init__(self, message):
super().__init__(message)
message = "Exception Triggered! Something went wrong."
def triggerException(num):
if (num == 0):
raise DemoException(message)
else:
print(num)
try:
triggerException(0)
print("Code has successfully been executed.")
except DemoException:
print("Error: Number should not be 0.")
Since the triggerException()
passed 0
as an argument, the code should trigger DemoException
. Here we should expect the raise
keyword message to be overridden with whatever is inside the except
block as the output.
Notice that the print()
line after the triggerException()
function call was not outputted. It’s because the function raised an exception; therefore, it immediately halted all the processes within the try
block and proceeded directly to the except
block.
Output:
Error: Number should not be 0.
Now, let’s try passing a valid number like 20
, for example.
try:
triggerException(20)
print("Code has successfully been executed.")
except DemoException:
print("Error: Number should not be 0.")
Output:
20
Code has successfully been executed.
Let’s try chaining the except
blocks and create another exception. Let’s call the new exception NumberFormatException
, which triggers if the given input is not a number. For this exception class, let’s declare the message inside the class.
class NumberFormatException(Exception, value):
message = f'{value} is not a number'
def __init__(self):
super().__init__(message)
Now, modify the code above to handle the new exception class NumberFormatException
:
class DemoException(Exception):
def __init__(self, message):
super().__init__(message)
class NumberFormatException(Exception):
def __init__(self, message, value):
message = f'{value} is not a number'
super().__init__(message)
message = "Exception occured."
def triggerException(num):
if (not num.isdigit()):
raise NumberFormatException(message, num)
elif (num == 0):
raise DemoException(message)
else:
print(num)
num = "sample string"
try:
triggerException(num)
print("Code has successfully been executed.")
except DemoException:
print("Error: Number should not be 0.")
except NumberFormatException:
print(num+" is not a number.")
In this code, the value of num
that was passed to triggerException()
is a string 'sample string'
so the NumberFormatException
should be triggered.
Output:
sample string is not a number.
In summary, creating custom exceptions in Python is as simple as creating a new class, but with the Exception
class as an extra argument in the class definition. The raise
keyword is used to trigger exceptions given the Exception Class. The try...except
blocks are used to wrap one or more exceptions within a code block and modify what the code does when handling that exception and not just shutting down the program entirely.
In this article let us learn how to manually throw an exception in Python.
How to throw/raise an Exception in Python
An Exception can be manually thrown/raised using the raise statement in Python.
Let us start with a simple example.
raise Exception ('I just raised my 1st ever Exception in Python!')
Running that code will result in an output like below
Traceback (most recent call last):
File "", line 1, in
raise Exception ('I just raised my 1st ever Exception in Python!')
Exception: I just raised my 1st ever Exception in Python!
As you can see, we have managed to throw an exception using the raise statement! This has caused the interpreter to crash the program just like a runtime error would!
Hence the statement we are looking for here is raise. But this is just the short version of the answer. Read on to find out how exactly to employ this raise statement in your code in a variety of situations.
The rest of the article explains
- The 3 major syntax/variants of the raise statement
- When and Where to use each of these syntax and
- The best practices to follow while using each of these syntax
So, let’s begin!
raise Syntax#1: Raise Exception with a Custom Error Message
The syntax shown in the example below (line 3) is the most common syntax you will see with the raise statement.
x = int(input('Please enter a even number: '))
if ( x % 2 != 0 ):
raise ValueError('Input is an odd number, cannot continue Execution')
else:
# continue with whatever you want to do with the even number
In the example above, we are throwing a ValueError with the error message “The value is not as expected”.
For those of you who are very new to Python let us briefly see what ValueError is. According to the official python documentation
ValueError is raised when an operation or function receives an argument that has the right type but an inappropriate value
https://docs.python.org/3/library/exceptions.html#bltin-exceptions
which basically means the passed in argument is of the right type, but the value contained is meaningless to our program.
Running the code in Example-2 above will give you an output like this.
Please enter a even number: 9
Traceback (most recent call last):
File "", line 3, in
raise ValueError('Input is an odd number, cannot continue Execution')
ValueError: Input is an odd number, cannot continue Execution
As you can see, the custom message we coded in is printed along with the exception name.
The syntax used in Example-2 is shown below. Replace the terms with the angle brackets ‘<>’ with whatever your program needs!
raise <ExceptionType> (<error message>)
Use this syntax when you wish to raise an Exception and pass a custom error message through that Exception. This custom message can either be sent to an output device to be seen by the user or to a log file, so that you, the programmer, can see these logs later and use the information there for debugging purposes.
In case you wish to catch the raised Exception from the calling code so that you can deal with that instead of ending your program, you can do so as shown in the example below
def checkForDivisibilityBy2():
x = int(input('Please enter a even number: '))
if ( x % 2 != 0 ):
raise ValueError('Input is an odd number, cannot continue Execution')
else:
print('The entered number is divisible by 2')
try:
checkForDivisibilityBy2()
except ValueError as err:
print (str(err))
The output as you might have expected will look like this.
Please enter a even number: 9
Input is an odd number, cannot continue Execution
The syntax used in the above example is shown below. Replace the terms with the angle brackets ‘<>’ with whatever your program needs!
except <exception_type> as <exception_instance_name>:
print ( str (<exception_instance_name>) )
Best Practices for Syntax#1
If you ever need to use this syntax in your Python code, remember these 2 points
- Be sure to word-smith a very specific error message, so that the ‘future-you’ or anyone else reading these error messages will understand what went wrong and
- Use an appropriate Exception type to raise.
See how ValueError suits this particular program. It is considered best practice to pick an appropriate Exception to raise, else you will end up hiding bugs leading to unreliable programs. We will see an example of such a situation after Syntax#2.
raise Syntax#2: Raise Exception without any Custom messages
Sometime, you may not need to log or print out messages to the user. In those situations you can use the syntax shown in the example below
raise ValueError
The output on the interpreter will look like this
Traceback (most recent call last):
File "", line 1, in
raise ValueError
ValueError
As you can see, the output has just the Exception class name and no error message.
The syntax used in the above example is shown below. As with previous syntax snippets, replace the terms with the angle brackets ‘<>’ with whatever your program needs!
raise <ExceptionType>
What happens behind the scenes when we use this syntax?
The raise statement will expect the first object passed in to be an Exception object. If we pass a Class name as the argument to raise, the python interpreter will convert the code behind the scenes to be
raise ValueError()
which is a call to the constructor of the ValueError class with no arguments.
Similarly, in Syntax#1, what we wrote was raise followed by a call to the constructor with a string argument which is an attribute meant to be used as the error message. This constructor then returns a Exception object, which is what the raise statement needs!
If you wish to learn more about how this string is used in the error message, read my other article on printing exceptions below
Python Exceptions: Getting and Handling Error Messages as strings.
The class which is passed with the raise statement must be a subclass of the BaseException class, which as the name implies is the base class for all the built-in Exception classes in Python.
You can find all the built-in exception classes that python provides in the python documentation here.
If you scroll down till the bottom of that page you can find the Exception Hierarchy tree as shown in the image below.
So just for fun, to see what happens, let us pass a list object to the raise statement!
list1 = [1,2,3,4]
raise list1
If you run this code, the interpreter will raise a TypeError exception as the interpreter was expecting an object of “Exception type” and we passed in a “list type” object.
Traceback (most recent call last):
File "", line 1, in
raise list1
TypeError: exceptions must derive from BaseException
Now that we have understood how to use Syntax#2, let us next see when to use this syntax!
When to use Syntax#2 raise statement
Best time to use Syntax#2 is when you can recover from an Exception through code-substitution or default-value substitution. If you haven’t already, I suggest reading my other article which covers the basics of exception handling in Python.
Exceptions in Python: Everything You Need To Know!
In the article given in the link above, the sub-section “Strategy#2″ in the section “3 ways to handle Exceptions” explains how to recover from exceptions.
Syntax#2 best goes with such problems where it is possible to recover from exceptions and continue with the program execution.
Best Practices for Syntax#2 raise statement
As with Syntax#1 try using an appropriate type of Exception here, also make sure the exception you raise is very specific else you may end-up hiding bugs leading to a unreliable program.
Let us see a simple example of such a situation.
raise: How to avoid Hiding Bugs
Consider the snippet below.
def divide(numerator, denominator):
return int(numerator)/int(denominator)
This function call can produce 2 types of exceptions.
The output below shows the 1st of the 2 types
>> divide(10, 0)
Traceback (most recent call last):
File "", line 1, in
divide(10, 0)
File "", line 2, in divide
return numerator/denominator
ZeroDivisionError: division by zero
Here we passed in the second argument as 0, and division by zero is not allowed in Math, and this resulted in ZeroDivisionError
The output below shows the 2nd type of exception that the code in Example#6 can produce
>> divide('pizza', 6)
Traceback (most recent call last):
File "", line 1, in
divide('pizza', 6)
File "", line 2, in divide
return numerator/denominator
TypeError: unsupported operand type(s) for /: 'str' and 'int'
Here we tried to divide a pizza into 6 pieces using our Math function and this resulted in TypeError!
Say for example, when we wrote the code we forgot about ValueError and we wrote code only to handle division by zeros as shown below.
def divide(numerator, denominator):
if (denominator == 0):
raise Exception
else:
return int(numerator)/int(denominator)
try:
x = input('Please enter the Numerator: ')
y = input('Please enter the Denominator: ')
print(x, ' / ', y, ' = ', str(divide(x,y)))
except Exception:
print("You have entered zero as denominator")
As you can see instead of using ZeroDivisionError type to raise the exception we used Exception type, which is higher in the hierarchy to both TypeError type and ZeroDivisionError type.
Now running the code, to cut a pizza into 6 pieces results in something like this!
Please enter the Numerator: Pizza
Please enter the Denominator: 6
You have entered zero as denominator
As you can see the ValueError exception bug is completely hidden due to our poor choice of Exception type.
Instead if you wrote your code like this using ZeroDivisionError
def divide(numerator, denominator):
if (denominator == 0):
raise ZeroDivisionError
else:
return int(numerator)/int(denominator)
try:
x = input('Please enter the Numerator: ')
y = input('Please enter the Denominator: ')
print(x, ' / ', y, ' = ', str(divide(x,y)))
except ZeroDivisionError:
print("You have entered zero as denominator")
Then if you run the code with bad values, the interpreter will crash the program revealing the ValueError bug as shown below.
Please enter the Numerator: Pizza
Please enter the Denominator: 6
Traceback (most recent call last):
File "", line 4, in
print(x, ' / ', y, ' = ', str(divide(x,y)))
File "", line 5, in divide
return int(numerator)/int(denominator)
ValueError: invalid literal for int() with base 10: 'Pizza'
Hence always go for the Exception type which is specific and appropriate for your situation!
raise and User-Defined Exception Types
Next let us see the best-practices to follow when raising user-defined Exception types. Let us start by learning how to make user-defined exceptions.
How to make your own exception?
In Python making your own exception type is super simple with just 2 lines of code
class MyException (Exception):
pass
Just declare a class that inherits from the built-in Exception class. Then you can raise your exception using Syntax#1 as shown below
>> raise MyException('I am raising my own Exception!!!')
Traceback (most recent call last):
File "", line 1, in
raise MyException('I am raising my own Exception!!!')
MyException: I am raising my own Exception!!!
You can also use Syntax#2 as shown below
>> raise MyException
Traceback (most recent call last):
File "", line 1, in
raise MyException
MyException
When you should Raise your own custom-defined Exception types and when you should use built-in exceptions?
- avoid using user-defined Exception types if possible, as python built-in error types are designed to fit almost every use-case and user-defined Exception types can make the code harder to read and maintain.
- Make sure your exception classes inherit from an appropriate python built-in class
- Generic error are more likely to hide bugs and are considered bad programming practice hence try to go as specific as you can while choosing exceptions.
Let us next move onto the 3rd, final and even shorter syntax of using the raise statement.
raise Syntax#3: without any arguments
The third syntax is to just use the raise statement with no arguments as shown below.
raise
Let us see an example to see how this can be used.
def reRaiseException():
try:
raise ValueError('I am raising a value error for no reason')
except ValueError as err:
print('reRaiseException(): ', str(err))
print ('Re-Raising the exception to the calling code')
raise
try:
reRaiseException()
except ValueError as error:
print('Main code: ', str(error))
Running this code will give an output like this
reRaiseException(): I am raising a value error for no reason
Re-Raising the exception to the calling code
Main code: I am raising a value error for no reason
Here in the function reRaiseException() a ValueError is raised (in Line3) with a custom message and caught by a except clause inside the function. In Line7 the caught exception is re-raised to the calling code.
The except clause in Line10 catches this exception and prints the custom error message passed first on Line3 inside the reRaiseException() function.
One point worth noting is that, if there was no except clause inside the reRaiseException() function, the exception would have been straightaway passed to the main code without the use of the line raise.
Try running the example code below to see what I mean
def reRaiseException():
raise ValueError('I am raising a value error for no reason')
try:
reRaiseException()
except ValueError as error:
print('Main code: ', str(error))
The output of the above code is
Main code: I am raising a value error for no reason
If there was no except clause to catch this one level up, then this exception will crash the program, same as Example 1 in the beginning of the article.
The main reason for the existence of this syntax is because, there are situations, where you might want to do something in the except block inside the reRaiseException() function but you wish to let the the calling code to actually handle that Exception.
In our case we merely printed out 2 more lines before reraising the exception to the main code to demonstrate this point. We will see examples of some situations where this might be useful later in this article.
If no exception is present in the current scope the interpreter will raise a RuntimeError exception indicating that this is an error. This can be easily demonstrated by typing raise and hitting the enter button on the python console.
raise
Traceback (most recent call last):
File "", line 1, in
raise
RuntimeError: No active exception to reraise
As you can see the interpreter prints an appropriate error message namely
No active exception to reraise
And that ends our 3rd and final syntax/variants of using the raise statement.
Exception Chaining and raise Statement
Before ending this article, I would like to point out there there are one more syntax that employ the raise statement. A very common use-case involves Exception Chaining.
This comes in 2 sub-variants as shown below.
raise <exception type 1> as <exception type 2>
raise <exception type 1> as None
This is an advanced topic which needs a separate article to explain. Exception Chaining is typically used in big projects with several layers of abstractions, each layer having a set of user-defined Exceptions.
First try and master the above 3 syntax, as those will be used more that 9 out of 10 times you will ever need to use the raise statement.
And with that I will end this article.
Hope you have enjoyed reading this article and got some value from it.
Feel free to share this article with your friends and colleagues.
Be sure to checkout my other article on Exceptions
Exceptions in Python: Everything You Need To Know!
which covers all the basics of exceptions and strategies to handle them!
In the previous tutorial, we learned about different built-in exceptions in Python and why it is important to handle exceptions. .
However, sometimes we may need to create our own custom exceptions that serve our purpose.
Defining Custom Exceptions
In Python, we can define custom exceptions by creating a new class that is derived from the built-in Exception
class.
Here’s the syntax to define custom exceptions,
class CustomError(Exception):
...
pass
try:
...
except CustomError:
...
Here, CustomError
is a user-defined error which inherits from the Exception
class.
Note:
- When we are developing a large Python program, it is a good practice to place all the user-defined exceptions that our program raises in a separate file.
- Many standard modules define their exceptions separately as
exceptions.py
orerrors.py
(generally but not always).
Example: Python User-Defined Exception
# define Python user-defined exceptions
class InvalidAgeException(Exception):
"Raised when the input value is less than 18"
pass
# you need to guess this number
number = 18
try:
input_num = int(input("Enter a number: "))
if input_num < number:
raise InvalidAgeException
else:
print("Eligible to Vote")
except InvalidAgeException:
print("Exception occurred: Invalid Age")
Output
If the user input input_num is greater than 18,
Enter a number: 45 Eligible to Vote
If the user input input_num is smaller than 18,
Enter a number: 14 Exception occurred: Invalid Age
In the above example, we have defined the custom exception InvalidAgeException
by creating a new class that is derived from the built-in Exception
class.
Here, when input_num is smaller than 18, this code generates an exception.
When an exception occurs, the rest of the code inside the try
block is skipped.
The except
block catches the user-defined InvalidAgeException
exception and statements inside the except
block are executed.
Customizing Exception Classes
We can further customize this class to accept other arguments as per our needs.
To learn about customizing the Exception classes, you need to have the basic knowledge of Object-Oriented programming.
Visit Python Object Oriented Programming to learn about Object-Oriented programming in Python.
Let’s see an example,
class SalaryNotInRangeError(Exception):
"""Exception raised for errors in the input salary.
Attributes:
salary -- input salary which caused the error
message -- explanation of the error
"""
def __init__(self, salary, message="Salary is not in (5000, 15000) range"):
self.salary = salary
self.message = message
super().__init__(self.message)
salary = int(input("Enter salary amount: "))
if not 5000 < salary < 15000:
raise SalaryNotInRangeError(salary)
Output
Enter salary amount: 2000 Traceback (most recent call last): File "<string>", line 17, in <module> raise SalaryNotInRangeError(salary) __main__.SalaryNotInRangeError: Salary is not in (5000, 15000) range
Here, we have overridden the constructor of the Exception
class to accept our own custom arguments salary
and message
.
Then, the constructor of the parent Exception
class is called manually with the self.message
argument using super()
.
The custom self.salary
attribute is defined to be used later.
The inherited __str__
method of the Exception
class is then used to display the corresponding message when SalaryNotInRangeError
is raised.