One of the struggles developers face is how to catch all Python exceptions. Developers often categorize exceptions as coding mistakes that lead to errors when running the program. Some developers still fail to distinguish between errors and exceptions.
In the case of Python application development, a python program terminates as soon as it encounters an unhandled error. So, to establish the difference between errors and exceptions, there are two types of errors:
First, let’s examine the syntax errors. Python syntax errors are caused by not following the proper structure (syntax) of the language. It is also known as a parsing error.
Here’s an example:
>>> ages = {
‘jj’: 2,
‘yoyo’: 4
}
print(f’JJ is {ages[“jj”]} years old.’)
The output:
JJ is 2 years old.
This is a simple code with no syntax error. Then we will add another variable tomtom:
>>> ages = {
‘jj’: 2,
‘yoyo’: 4
‘tomtom’: 6
}
print(f’JJ is {ages[“jj”]} years old.’)
Upon inspection, you can see an invalid syntax on the second entry, yoyo, inside the array with a missing comma. Try to run this code, and you will get a traceback:
File “<pyshell>”, line 1
>>> ages = {
^
SyntaxError: invalid syntax
As you notice, the traceback message didn’t pinpoint the exact line where the syntax error occurs. The Python interpreter only attempts to locate where the invalid syntax is. It only points to where it first noticed the problem. So, when you get a SyntaxError traceback, it means that you should visually inspect whether the interpreter is pointing to the right error.
In the example above, the interpreter encounters an invalid syntax inside an array called ages. Thus, it points out that there is something wrong inside the array.
This is a syntax error.
In most cases, a Python developer writes an impressive piece of code that is ready to execute. The program becomes a robust machine learning model, but during execution, Python throws up an unexpected error. Unfortunately, it is no longer the typical syntax error. Developers are now dealing with logical errors, also known as exceptions.
Let’s delve into exceptions.
Exceptions are errors that occur at runtime. Mostly, these errors are logical. For example, when you try to divide a number by zero, you will get a ZeroDivisionError. When you open a file(read) that doesn’t exist, you will receive FileNotFoundError. Further, when you try to import a module that doesn’t exist, you will get ImportError.
Here is how Python treats the above-mentioned errors:
ZeroDivisionError
>>> 2 / 0
Traceback (most recent call last):
File “<pyshell>”, line 1, in <module>
ZeroDivisionError: division by zero
FileNotFoundError
>>> open(“stack.txt”)
Traceback (most recent call last):
File “<pyshell>”, line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: ‘stack.txt’
ImportError
>>> from collections import qwerty
Traceback (most recent call last):
File “<pyshell>”, line 1, in <module>
ImportError: cannot import name ‘qwerty’
Whenever a runtime error occurs, Python creates an exception object. It then creates and prints a traceback of that error with details on why the error happened.
Here are common exceptions in Python:
You can visit the official Python documentation site to have in-depth knowledge about Python built-in exceptions. While the Python community offers great support, Python application deployment discussions can be improved. Application Performance Management (APM) like Retrace, is a great tool to deal with Python exceptions. It helps you find all the exceptions thrown and identify its root cause. You can try it for free today!
A direct logic is followed to catch exceptions in Python. When an exception occurs, the Python interpreter stops the current process. It is handled by passing through the calling process. If not, the program will crash.
For instance, a Python program has a function X that calls function Y, which in turn calls function Z. If an exception exists in function Z but is not handled within Z, the exception passes to Y and then to X. Simply, it will have a domino effect.
An unhandled exception displays an error message and the program suddenly crashes. To avoid such a scenario, there are two methods to handle Python exceptions:
Let’s start with the try statement to handle exceptions. Place the critical operation that can raise an exception inside the try clause. On the other hand, place the code that handles the exceptions in the except clause.
Developers may choose what operations to perform once it catches these exceptions. Take a look at the sample code below:
import sys
list = [‘x’, 1e-15, 5]
for result in list:
try:
print(“The result is”, result)
y = 1/int(result)
break
except:
print(“Whew!”, sys.exc_info()[0], “occurred.”)
print(“Next input please.”)
print()
print(“The answer of”, result, “is”, y)
The program has an array called list with three elements. Next, the line that causes an exception is placed inside the try block. If there are no exceptions, the except block will skip, and the logic flow will continue until the last element. However, if an exception occurs, the except block will catch it.
The output:
The result is x
Whew! <class ‘ValueError’> occurred.
Next input, please.
The result is 1e-15
Whew! <class ‘ZeroDivisionError’> occurred.
Next input, please.
The result is 5
The answer to 5 is 0.2
To print the name of the exception, we use the exc_info()function inside the sys module. The first two elements cause exceptions since an integer can’t be divided with string and a zero value. Hence, it raised ValueError and ZeroDivisionError exceptions.
What if you want to deal with a specific exception? In the previous example, it didn’t mention any specific exception in the except block. That is not a good programming practice because it will catch all exceptions.
Additionally, it will handle all exceptions in the same manner, which is an incorrect approach. Therefore, maximize the use of the except block and specify which exceptions it should catch.
To execute this concept, we can use a tuple of values to specify multiple exceptions. Here is a sample pseudo-code:
try:
#do something
#your statements
pass
except Exception_1:
#handle Exception_1 and execute this block statement
pass
except Exception_2:
handle Exception_2 and execute this block statement
pass
except Exception_3:
#handle Exception_3 and execute this block statement
pass
except:
#handles all other exceptions
pass
Try Stackify’s free code profiler, Prefix, to write better code on your workstation. Prefix works with .NET, Java, PHP, Node.js, Ruby, and Python.
Another way to catch all Python exceptions when it occurs during runtime is to use the raise keyword. It is a manual process wherein you can optionally pass values to the exception to clarify the reason why it was raised.
>>> raise IndexError
Traceback (most recent call last):
File “<pyshell>”, line 1, in <module>
IndexError
>>> raise OverflowError(“Arithmetic operation is too large”)
Traceback (most recent call last):
File “<pyshell>”, line 1, in <module>
OverflowError: Arithmetic operation is too large
Let’s have a simple code to illustrate how raise keyword works:
try:
x = int(input(“Enter a positive integer: “))
if x <= 0:
raise ValueError(“It is not a positive number!”)
except ValueError as val_e:
print(val_e)
The output:
Enter a positive integer: -6
It is not a positive number!
Enter a positive integer: 6
In dealing with exceptions, there are instances that a certain block of code inside try ran without any errors. As such, use the optional else keyword with the try statement.
Here is a sample code:
try:
number = int(input(“Enter a number: “))
assert number % 2 == 0
except:
print(“This is an odd number!”)
else:
print(“This is an even number!”)
rem = number % 2
print(“The remainder is “, rem)
The output:
Enter a number: 3
This is an odd number!
Enter a number: 2
This is an even number!
The remainder is 0
The try statement in Python has an optional finally block. It means that it executes the block by all means. At the same time, it releases external resources.
Some examples include a connection between a mobile app and a remote data center via a distributed network. All resources should be clean once the program stops, whether it successfully runs or not.
It is the job of the finally block to guarantee execution. Such actions include closing a file or GUI and disconnecting from the network.
Check on the example below to illustrate how the operation works:
try:
file = open(“myFile.txt”,encoding = ‘utf-8’)
# this performs file operations
finally:
f.close()
The f.close()function ensures that the file is closed even if an exception happens during the program execution.
Bugs and errors are part of every developer’s journey. As mentioned above, errors and exceptions are different. So, why is it that Python developers should know how to catch Python exceptions and master exception handling?
The answers to this are not theoretical. Instead, let’s have a sample use case.
For example, you’re dealing with a vast amount of data. So, you build a program that reads thousands of files across multiple directories. There is no way that you will not encounter an error here. Possible errors may include wrong or missing file type, invalid file format, or dealing with different file extensions.
On that note, it is not feasible to open all the files and write a script that can cater to it accordingly. With exception handling, developers can define multiple conditions. For instance, resolve an invalid format by establishing the correct format first and then reading the file afterward.
Another option is to continue reading the data and skip those files with an invalid format. Then, create a log file that can deal with it later. To help you with log files, APMs like Retrace is a great tool in dealing with logs. It has logging features and actionable insights to support you with your Python applications.
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]