Windows system, macOS system, or Linux system
An exception is an unexpected event that occurs during the execution of a program that disrupts the normal flow of the program’s instructions.
Carefully analyze the divide.py script being run below. Does the script appear to be executing correctly? Are there any errors? If so, which errors do you see?
linux$ python divide.py
File "/home/expert/Documents/divide.py", line 1
num1 = int(input( 'Enter a number you want to divide: '))
IndentationError: unexpected indent
The above script failed to execute, and an IndentationError
exception was raised.
Exceptions being raised when writing scripts can be a frustrating experience. However, exceptions do serve a purpose. Exceptions notify the developer of the type of problem the script is experiencing by displaying the following:
Before we raise our own exceptions, let’s write a usable script that divides numbers. Write the script below, and save the file as divide.py.
num1 = int(input( 'Enter a number you want to divide: '))
num2 = int(input( 'Enter the number of times you want to divide the previous number by: '))
print(f'{num1} divided by {num2} is: {num1 / num2}')
Run and then input integers into the script to see the normal execution of the script.
linux$ python divide.py
Enter a number you want to divide: 10
Enter the number of times you want to divide the previous number by: 2
10 divided by 2 is: 5.0
Let’s now raise exceptions. Make a syntax error in the divide.py script. Delete the last closed parenthesis in the first line of code.
Save and then run the script. The normal flow of the script should be disrupted and an exception should be raised.
Carefully analyze the output of the exception. Attempt to answer these questions:
linux$ python divide.py
File "/home/expert/Documents/divide.py", line 2
num2 = int(input( 'Enter the number of times you want to divide the previous number by: '))
^
SyntaxError: invalid syntax
In our example, we are receiving a SyntaxError
exception. The exception message is invalid syntax
, and the beginning of line 2
could potentially be causing the exception to be raised.
SyntaxError
exceptions are raised when your Python code does not adhere to Python syntax rules. Our script is missing a parenthesis at the end of line 1, and because of this, our script is raising a SyntaxError
exception.
Resolve the SyntaxError
exception by re-adding the last parenthesis in line 1.
Save and re-run the script to confirm the script executes normally.
linux$ python divide.py
Enter a number you want to divide: 10
Enter the number of times you want to divide the previous number by: 2
10 divided by 2 is: 5.0
A SyntaxError
exception is one of many different types of built-in exceptions that can commonly be raised in Python.
Another type of exception frequently raised is an IndentationError
exception. Indent the code in line 1 a single space to the right to raise an IndentationError
exception.
Save and run the script.
linux$ python divide.py
File "/home/expert/Documents/divide.py", line 1
num1 = int(input( 'Enter a number you want to divide: '))
IndentationError: unexpected indent
Notice the normal flow of the script is disrupted and an IndentationError
exception is raised.
Resolve the IndentationError
exception by deleting the unnecessary indentation in line 1.
Save and re-run the script to confirm the script executes normally.
linux$ python divide.py
Enter a number you want to divide: 10
Enter the number of times you want to divide the previous number by: 2
10 divided by 2 is: 5.0
It is important to note that SyntaxError
and IndentationError
exceptions are due to programming mistakes made by the developer of the script. The developer needs to resolve the issues to clear the exceptions before the script can be run by a user.
Our script is now running. However, exceptions can still occur if the user accidentally misuses the script. Let’s pretend we are a user of the script. Run the script and “accidentally” try to divide letters instead of numbers. An exception should be raised. Carefully analyze the output of the exception. Attempt to answer these questions:
linux$ python divide.py
Enter a number you want to divide: a
Traceback (most recent call last):
File "/home/expert/Documents/divide.py", line 1, in <module>
num1 = int(input( 'Enter a number you want to divide: '))
ValueError: invalid literal for int() with base 10: 'a'
In the previous example, we received a ValueError
exception. ValueError
exceptions are raised when the user inputs an invalid value. In our script, we used the int
class followed by the input
function, which allows the user of the script to only input integers into the script. Because we entered a letter into the script (which python interprets as a string), the ValueError
exception is raised.
The exception message is invalid literal for int() with base 10: 'a'
, and line 1
could potentially be causing the exception to be raised.
Let’s raise a different type of exception. Run the script and accidentally divide a number by zero. Which type of exception is raised?
linux$ python divide.py
Enter a number you want to divide: 5
Enter the number of times you want to divide the previous number by: 0
Traceback (most recent call last):
File "/home/expert/Documents/divide.py", line 4, in <module>
print(f'{num1} divided by {num2} is: {num1 / num2}')
ZeroDivisionError: division by zero
In the previous example, we received a ZeroDivisionError
exception. ZeroDivisionError
exceptions are raised when the user attempts to divide a number by zero.
Let’s test your knowledge. Copy the code below into a file, and save the file as numbers.py.
numbers = [1, 2, 9, 17, 29]
print(numbers[0])
print(numbers[100])
Run the script. Answer the following questions:
Which type of exception is raised?
What is the message in the exception?
What is the offending line of code?
What can you do to resolve the exception?
The solution will be displayed on the next page.
When attempting to run the number.py script, you should see the following:
linux$ python numbers.py
1
Traceback (most recent call last):
File "/home/expert/Documents/numbers.py", line 4, in <module>
print(values[100])
IndexError: list index out of range
Answers to the questions are below:
Which type of exception is raised?
IndexError
exception
What is the message in the exception?
list index out of range
What is the offending line of code?
line 4
What can you do to resolve the exception?
Change the print
function to use a valid index ID used within the numbers
list.
In the script, there is a list named numbers
that has five different values (1, 2, 9, 17, and 29). Each value in a list is referred to as an element. Each element in a list gets a unique index ID that represents the element’s position in the list. The first element gets an index ID of 0, the next element gets an index ID of 1, and so on and so forth. We have five different elements in our list, index IDs 0 to 4 are used for each value within our list. The script is raising an exception in line 4 because we are attempting to print out an element with an index ID of 100 within the numbers
list. An element with index ID 100 does not exist in our list, and thus an exception is raised. To resolve the exception, we should change the print
function to reference a valid index ID within the list. Change line 4 to print out index ID 1 within the numbers
list.
Save and re-run your script to confirm it is running as expected.
linux$ python numbers.py
1
2
Let’s test your knowledge again. Copy the code below into a file, and save the file as year_born.py.
year_born = {'Rebecca Black': 1997, 'Uncle Bob': 1980, 'Alfred Yankovic': 1959}
print(year_born['Robert Van Winkle'])
Run the script. Answer the following questions:
Which type of exception is raised?
What is the message in the exception?
What is the offending line?
What can you do to resolve the exception?
The solution will be displayed on the next page.
When attempting to run the year_born.py script, you should see the following:
linux$ python year_born.py
Traceback (most recent call last):
File "/home/expert/Documents/year_born.py", line 3, in <module>
print(year_born['Robert Van Winkle'])
KeyError: 'Robert Van Winkle'
Answers to the questions are below:
Which type of exception is raised?
KeyError
exception
What is the message in the exception?
'Robert Van Winkle'
What is the offending line?
line 3
What can you do to resolve the exception?
Either change the print
function in line 3 to print a valid key or add the Robert Van Winkle
key to the year_born
dictionary in line 1.
The year_born
dictionary has three different key value pairs. The three keys are Rebecca Black
, Uncle Bob
, and Alfred Yankovic
. Each key has a corresponding value that represents which year that person was born. In line 3, we attempted to print out the Robert Van Winkle
key from the dictionary. However, because there is no Robert Van Winkle
key in the dictionary, a KeyError
exception is raised.
Let’s go ahead and add Robert Van Winkle
and the year he was born to our dictionary.
Save and re-run your script to confirm it is executing as expected.
linux$ python year_born.py
1967
Referring back to our previous divide.py script, we determined users of the script can misuse the script and receive exceptions. In the example below, the user accidentally typed a letter instead of a number. This misuse of the program creates a ValueError
exception.
linux$ python divide.py
Enter a number you want to divide: a
Traceback (most recent call last):
File "/home/expert/Documents/divide.py", line 1, in <module>
num1 = int(input( 'Enter a number you want to divide: '))
ValueError: invalid literal for int() with base 10: 'a'
Imagine being a user of an application, and suddenly the application stops working and raises an exception. The user, who may not be a programmer, would be confused and intimidated by the raised exception. This would lead to a poor user experience. Instead, a well-designed application or script should print a user-friendly message to the user. This will make your script more reliable, easier to work with, and will result in an overall better user experience.
To prevent exceptions from being raised and displayed to the user, you should use exception handling. Exception handling enables your script to detect exceptions. Once an exception is detected, the script will then print out a user-friendly message instead of displaying an intimidating exception.
Exception handling can be done by using a try and except
block in your script. To implement this block, you can use the try
and except
statements.
Update your divide.py script to include a try
statement (line 1 below). Underneath the try
statement is where you place the code that might create an exception (lines 3 to 6 below) . The code within the try
statement must be indented to the right. We are essentially “trying” to run the code within the try
statement, although we are telling Python that an exception might occur.
To complete our try and except
block, we need to implement an except
statement. The block of code within the except
statement will be executed only if there is an exception when running the block of code within the try
statement. Update your script to include the except
statement, then within the except
statement, write code that will print out a user-friendly error if an exception occurs.
Let’s try running the script while putting in valid numbers to divide by.
linux$ python divide.py
Enter a number you want to divide: 10
Enter the number of times you want to divide the previous number by: 2
10 divided by 2 is: 5.0
Because there is no exception when running the code within the try
statement, the scripts executes successfully.
Let’s now raise an exception. Run the script and divide by letters instead of numbers.
linux$ python divide.py
Enter a number you want to divide: a
Sorry you have received a general error. Please email unclebob@unclebob.com for support
Instead of a ValueError
exception being shown to the user of the script, a user-friendly message is printed out to the user: Sorry you have received a general error. Please email unclebob@unclebob.com for support
.
To better understand the try and except
block, let’s walk through the code. When running the script, the code under the try
statement will “try” to be run.
If the code is run successfully, the program executes as expected.
linux$ python divide.py
Enter a number you want to divide: 10
Enter the number of times you want to divide the previous number by: 2
10 divided by 2 is: 5.0
If an exception is raised in the code within the try
statement, then the code within the except
statement is run, and a user-friendly message is printed out.
Let’s try to create a ZeroDivisionError
exception and see what happens.
linux$ python divide.py
Enter a number you want to divide: 5
Enter the number of times you want to divide the previous number by: 0
Sorry you have received a general error. Please email unclebob@unclebob.com for support
Because another exception was raised within the code under the try
statement, the code within the except
statement was run, and a user-friendly message was printed out to the user.
In the previous example, we used one except
statement to handle all exceptions. However, it is not best practice to use this technique. Instead, we want our script to print out different messages for different types of exceptions. Fortunately, you can create multiple except
statements for different types of exceptions to accomplish this.
First, create an except
statement for ValueError
exceptions. In line 8, I added the except
statement and specified the type of exception I want to handle (ValueError
).
Let’s raise a ValueError
exception in our script by trying to divide by a letter.
linux$ python divide.py
Enter a number you want to divide: a
You have entered an incorrect value. Make sure to enter an integer.
Notice when the ValueError
exception is detected, it is then handled by our script, and a user-friendly message is printed out.
Next, create a ZeroDivisionError
exception in our script by trying to divide by zero. What do you think will happen?
linux$ python divide.py
Enter a number you want to divide: 5
Enter the number of times you want to divide the previous number by: 0
Traceback (most recent call last):
File "/home/expert/Documents/divide.py", line 6, in <module>
print(f'{num1} divided by {num2} is: {num1 / num2}')
ZeroDivisionError: division by zero
Oh no! We are getting another scary and intimidating exception. Why is this occurring? Remember, we only implemented exception handling for ValueError
exceptions.
Let’s now implement another except
statement for ZeroDivisionError
exceptions.
Save the script. Run the script and try to divide by zero.
linux$ python divide.py
Enter a number you want to divide: 5
Enter the number of times you want to divide the previous number by: 0
Sorry you cannot divide by zero.
The ZeroDivisionError
exception should be detected and then handled by our script. A user-friendly message is printed out.
You now have a script that works and is able to handle several different types of exceptions. Great job!
Let’s put your newly learned exception handling skills to the test! Below is a script that you can run against the Cisco Always-On IOS XE Sandbox. The Always-On IOS XE Sandbox is essentially a virtual IOS router that is always online that you can send API calls to.
Copy the code below into a file, and save the file as get_interface.py.
import json
import requests
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# the variables below assume the user is leveraging the
# always on sandbox.
# user credentials for your IOS-XE device
username = 'admin'
password = 'C1sco12345'
# header sent in API call to request data in JSON encoding
headers = {'Accept': 'application/yang-data+json'}
# user will input an integer to specify the GigabitEthernet interface # they want to retrieve configuration for
name = int(input('Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: '))
# url to send API call to
url = f'https://sandbox-iosxe-latest-1.cisco.com/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet{name}'
# send API call using requests
response = requests.get(url, auth= (username, password), headers=headers, verify=False)
# convert response to dictionary
data = response.json()
# convert dictionary back to JSON object and print JSON with indentations
print(json.dumps(data, indent=4))
Let’s confirm the script is working as expected.
When using the script, specify the ID of GigabitEthernet interface for which you want to retrieve configuration. For example, enter ‘1’ to get the configuration for interface GigabitEthernet1.
(main) linux$ python get_interface.py
Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: 1
{
"ietf-interfaces:interface": [
{
"name": "GigabitEthernet1",
"description": "MANAGEMENT INTERFACE - DON'T TOUCH ME",
"type": "iana-if-type:ethernetCsmacd",
"enabled": true,
"ietf-ip:ipv4": {
"address": [
{
"ip": "10.10.20.48",
"netmask": "255.255.255.0"
}
]
},
"ietf-ip:ipv6": {}
}
]
}
Enter ‘2’ to get the configuration for interface GigabitEthernet2.
(main) linux$ python get_interface.py
Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: 2
{
"ietf-interfaces:interface": [
{
"name": "GigabitEthernet2",
"description": "Network Interface",
"type": "iana-if-type:ethernetCsmacd",
"enabled": false,
"ietf-ip:ipv4": {},
"ietf-ip:ipv6": {}
}
]
}
Now that your script is working as expected, attempt the following:
How can you break the script from a user perspective? Which exceptions can you raise?
Use a try/except
block within the script to handle any possible exceptions users might experience.
Do this on your own and have fun! The solution will be displayed on the next page.
Your solution might look different than the one given below, and that’s OK.
The example raised a ValueError
exception by inputting a non-integer value into the script.
(main) linux$ python get_interface.py
Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: a
Traceback (most recent call last):
File "/home/expert/Documents/get_interface.py", line 18, in <module>
name = int(input('Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: '))
ValueError: invalid literal for int() with base 10: 'a'
The example raised a KeyboardInterrupt
exception by pressing CTRL+C while the script was running.
(main) linux$ python get_interface.py
Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: ^CTraceback (most recent call last):
File "/home/expert/Documents/get_interface.py", line 18, in <module>
name = int(input('Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: '))
KeyboardInterrupt
try/except
block within the script to handle any possible exceptions users might experience.To handle those two exceptions, the following was done:
try
statement (line 17).try
statement (lines 19 to 32).except
statements—one for ValueError
exceptions and one for KeyboardInterrupt
exceptions (lines 34 to 38).Let’s make sure the script is able to handle ValueError
exceptions.
(main) linux$ python get_interface.py
Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: a
You must enter a number when specifying an interface.
Let’s also make sure the script is able to handle KeyboardInterrupt
exceptions.
(main) linux$ python get_interface.py
Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: ^C
You exited the script by pressing CTRL+C
Finally, let’s confirm the script still runs as expected when entering an integer.
Please enter the ID of the GigabitEthernet interface for which you want to retrieve configuration: 1
{
"ietf-interfaces:interface": [
{
"name": "GigabitEthernet1",
"description": "MANAGEMENT INTERFACE - DON'T TOUCH ME",
"type": "iana-if-type:ethernetCsmacd",
"enabled": true,
"ietf-ip:ipv4": {
"address": [
{
"ip": "10.10.20.48",
"netmask": "255.255.255.0"
}
]
},
"ietf-ip:ipv6": {}
}
]
}
Nice work! You now understand what exceptions are and how to handle them within your Python code.