Table of Contents
In this tutorial we will learn all about handling errors and exceptions in Python programming using try..except
block.
Python built-in exceptions
Before we start with try..except
block, let us go through some of the in-built exceptions from Python. Python ships with a ton of built-in exception classes to cover a lot of error situations so that you do not have to define your own. These classes are divided into Base error classes, from which other error classes are defined, and Concrete error classes, which define the exceptions you are more likely to see from time to time.
Following are some of the common error and exception classes:
- SyntaxError: It occurs when you type a line of code which the Python interpreter is unable to parse
- ImportError: It occurs when an import cannot be resolved.
- KeyError: It occurs when a dictionary key is not found while trying to access it
- TypeError: It occurs if you attempt to do an operation on a value or object of the wrong type
- AttributeError: It is raised when assigning or referencing an attribute fails.
- IndexError: It occurs if you are trying to access an index (for example, in a list) which does not exist.
- NameError: It occurs when a specified name cannot be found either locally or globally
- FileNotFoundError: This error is raised if a file you are attempting to read or write is not found
Handling Errors and Exceptions
Python uses special objects called exceptions to manage errors that arise during a program’s execution. Whenever an error occurs that makes Python unsure what to do next, it creates an exception object. If you write code that handles the exception, the program will continue running. If you don’t handle the exception, the program will halt and show a traceback, which includes a report of the exception that was raised.
Different try statement clauses
When you write a try
statement, a variety of clauses can appear after the try header. Below summarizes all the possible forms which we will cover in this tutorial.
Clause Form | Interpretation |
---|---|
except: |
Catch all (or all other) exception types. |
except name: |
Catch a specific exception only. |
except name as value: |
Catch the listed exception and assign its instance. |
except (name1, name2): |
except (name1, name2): |
except (name1, name2) as value: |
Catch any listed exception and assign its instance. |
else: |
Run if no exceptions are raised in the try block. |
finally: |
Always perform this block on exit. |
try..except block
Exceptions are handled with try-except blocks. A try-except block asks Python to do something, but it also tells Python what to do if an exception is raised. When you use try-except blocks, your programs will continue running even if things start to go wrong. Instead of tracebacks, which can be confusing for users to read, users will see friendly error messages that you write.
Syntax
The basic syntax to use try..except
blocks would be:
try: # do something here except [Exception]: # If there is Exception, then execute this block.
You can define multiple exception blocks to handle different kind of exceptions. We will cover this using different examples to help you understand better.
Example-1: Handling single exception
Before I handle exceptions, let me show you a general scenario where the script can fail while trying to open a file but the file does not exist. In such case you would get FileNotFoundError
exception and the worst part is any code after this exception will not be executed
#!/usr/bin/env python3 with open('input.txt', 'r') as myfile: for line in myfile: print(line) print('Outside the with block')
Output from this script:
As expected we are getting FileNotFoundError
exception. So we will handle this by using try..except
block:
#!/usr/bin/env python3 try: with open('input.txt', 'r') as myfile: for line in myfile: print(line) except: print('Sorry, file doesn\'t exist') print('Outside the with block')
I have updated the code, although I have not defined the type of exception and have used a generic except block so for any kind of error I would get "Sorry, file doesn't exist
"
Output from this script:
So now we don't get any exception and our print statement outside the with
block is also executed.
Example-2: Provide the type of exception
As I mentioned earlier, in the previous example I have not defined the type of exception so for any kind of issue, I will get the same message which can be misleading. For example the file exist although the user is not having enough permission to access the file and still I would get same message.
For the demonstration, I am using a normal user to execute the same script. I have created input.txt as root user and have given only 600
permission so user 'deepak
' has no permission to read this file
[deepak@server ~]$ ls -l input.txt
-rw------- 1 root root 0 Oct 15 14:49 input.txt
Now I execute the same script from Example-1:
[deepak@server ~]$ python3 handle-exceptions.py
Sorry, file doesn't exist
Outside the with block
We still get "Sorry, file doesn't exist
" which is misleading because the file is actually there but I don't have permission to access the file.
To handle such situation we can define the Exception Type with except block:
#!/usr/bin/env python3 try: with open('input.txt', 'r') as myfile: for line in myfile: print(line) except FileNotFoundError: print('Sorry, file doesn\'t exist') except PermissionError: print('Sorry, you don\'t have permission to access the file') print('Outside the with block')
So now I have enhanced the script to handle two separate exceptions for FileNotFoundError
and PermissionError
.
Output from this script:
Example-3: Define multiple exceptions in single block
In the previous example we defined separate block for individual exceptions. We can also combine all the applicable exceptions in single except
block. For example I have combined FileNotFoundError
and PermissionError
into single except block and given a more precise print
statement:
#!/usr/bin/env python3 try: with open('input.txt', 'r') as myfile: for line in myfile: print(line) except (FileNotFoundError, PermissionError): print('Sorry, file doesn\'t exist or you don\'t have permission to access the file') print('Outside the with block')
Output from this script:
So using this approach saved few lines of code.
Example-4: Using a generic exception block
Now we have covered some scenarios where we have manually defined the exception type, but there is also a possibility wherein the exception type doesn't match the ones which we have added in the code in which case it is always a good idea to also add except Exception
to handle unforeseen issues. Additionally we can store the output into a variable and then print that variable as the output.
I have updated our script with except Exception
block to handle any other exceptions which are not defined in the code and storing the output into error
variable, later I will print the content of this variable:
#!/usr/bin/env python3 try: with open('input.txt', 'r') as myfile: for line in myfile: print(line) except FileNotFoundError: print('Sorry, file doesn\'t exist or you don\'t have permission to access the file') except Exception as error: print(error) print(type(error)) print('Outside the with block')
Output from this script:
try..except..else block
The try…except…else
block is a minor modification of the traditional try…except
block so that it can include an else
block.
Syntax
The syntax for try..except..else
block would be:
try: # do something here except [Exception]: # If there is Exception, then execute this block. else: # If there are no exceptions then execute this block
The code in the else
block is always executed if no error has occurred. So, any code that depends on the try
block executing successfully goes in the else
block:
Example: Using try with else block
In this sample script we will prompt user for two numbers and using the python code we will divide first number by the second number
Output from this script:
~]$ python3 handle-exceptions.py Give me two numbers Enter 'q' to quit First number: 10 Second number: 5 You get: 2.0 First number: 10 Second number: 0 Traceback (most recent call last): File "handle-exceptions.py", line 13, in answer = int(first_num) / int(second_num) ZeroDivisionError: division by zero
So if we try to divide the "first number" by 0 then we get ZeroDivisionError
exception. Let me try to handle ZeroDivisionError
with except and else block:
#!/usr/bin/env python3 print("Give me two numbers") print("Enter 'q' to quit") while True: first_num = input("\nFirst number: ") if first_num == 'q': break second_num = input("Second number: ") if second_num == 'q': break try: answer = int(first_num) / int(second_num) except ZeroDivisionError: print("You can not divide by 0") else: print('You get: ', answer)
Output from this script:
So now the else
block is executed for all the SUCCESS condition and except block will handle the ZeroDivisionError
exception.
try..finally block
We use try..finally
when you want exceptions to propagate up but also want to run cleanup code even when exceptions occur. One common usage of try..finally
is for reliably closing file handles
Syntax
The syntax to use try..except..finally
block would be. The except block is optional and you can also use try..finally
without except
block.
try: # do something here except [Exception]: # If there is Exception, then execute this block. finally: # This is executed always
If you wish to combine else
block then the order of the statement should be:
try -> except -> else -> finally
Example-1: Using try with finally block
I will take a simple example to help you understand the usage of try..finally
block. Here we will prompt user for an input with an integer, next we will divide 100 with this number and store the result in re
.
- Now if an exception is raised then we go into
except
block - If there are no exceptions then
else
block is executed - Irrespective of exceptions, the
finally
block will always be executed
#!/usr/bin/env python3 try: num = int(input("Enter the number: ")) re = 100/num except: print("Something is wrong") else: print("result is ",re) finally : print("finally program ends") print('END')
Output from this script (with no exceptions):
~]$ python3 try-finally.py
Enter the number: 10
result is 10.0
finally program ends
END
Output from this script (with exceptions):
~]$ python3 try-finally.py
Enter the number: abcd
Something is wrong
finally program ends
END
So in both the scenarios, the finally
block was executed.
Example-2: Open and close file handle using try..finally block
This is a more practical example where we are opening a file to perform some read and write operation and intentionally we will try to write invalid utf-8 content which will raise UnicodeDecodeError
in which case the finally
block will execute and close the file handle.
open
before the try
block because exceptions that occur when opening the file (like OSError
if the file does not exist) should skip the finally block entirely:Output from this script:
~]$ python3 try-finally.py * Opening file * Reading data * Calling close() Traceback (most recent call last): File "try-finally.py", line 11, in handle.read() # Maybe UnicodeDecodeError File "/usr/lib64/python3.6/codecs.py", line 321, in decode (result, consumed) = self._buffer_decode(data, self.errors, final) UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf1 in position 0: invalid continuation byte
So even though we have received an exception, our code from finally
block was executed to close the file handle.
Manually raise exceptions
To trigger exceptions explicitly, you can code raise statements. Their general form is simple—a raise statement consists of the word raise, optionally followed by the class to be raised or an instance of it:
Syntax:
raise instance # Raise instance of class raise class # Make and raise instance of class: makes an instance raise # Reraise the most recent exception
For example here we have a simple if else condition. If the condition matches then we print something on the console and for else condition we will raise ValueError
with some custom message:
#!/usr/bin/env python3 num = int(input('Enter any number: ')) mylist = [10, 21, 34, 49, 52] if num in mylist: print('Bingo, you hit the jackpot') else: raise ValueError('Ohh, you missed the jackpot')
Output from this script:
~]$ python3 raise-exceptions-1.py Enter any number: 20 Traceback (most recent call last): File "raise-exceptions-1.py", line 9, in raise ValueError('Ohh, you missed the jackpot') ValueError: Ohh, you missed the jackpot
As you see, since the provided number was not part of the mylist
, ValueError
is raised as we had defined. So using raise we can manually create exceptions within python code.
Define and raise custom exceptions
Built-in exceptions cover a wide range of situations. Sometimes, however, you may need to define a custom exception to fit your specific application situation; for example, a MyCustomError
exception.
In this case, Python contains the ability to add custom errors by extending the base Exception class. We will use our existing example and instead of ValueError
, we will create our own exception and execute it inside the else block:
Output from this script when we provide a number which is not present in the list:
~]$ python3 raise-exceptions-1.py Enter any number: 20 Traceback (most recent call last): File "raise-exceptions-1.py", line 12, in raise MyCustomError('Ohh, you missed the jackpot') __main__.MyCustomError: Ohh, you missed the jackpot
The assert statement
Python includes the assert
statement. The next step toward creating real tests is to assert that a particular comparison holds true. Assertions are typically used to verify program conditions during development. For example, here I am using assert to perform the comparison instead of if condition and raising an exception if condition is not matched
#!/usr/bin/env python3 class MyCustomError(Exception): pass num = int(input('Enter any number: ')) mylist = [10, 21, 34, 49, 52] try: assert num in mylist print('Bingo, you hit the jackpot') except: print('Ohh, you missed the jackpot')
Output from this script:
~]$ python3 raise-exceptions-1.py
Enter any number: 20
Ohh, you missed the jackpot
Since the assert
condition didn't matched, the except block was executed.
Conclusion
In this tutorial, we talked about errors and exceptions, what they are, and how to avoid them. We looked at a few built-in errors and the scenarios that would cause them to be raised. We then moved on to handling them by using try…except
and finally blocks. We also covered how to implement our own custom exceptions by using the Exception base class.
This should give you the ability to make your programs more robust by handling both seen and unforeseen issues that may arise during code execution. Handling errors should also help prevent unpleasant usability or security issues from cropping up when your code is in the wild.