// Hide WordPress Admin Notifications programmatically function pr_disable_admin_notices() { global $wp_filter; if ( is_user_author() ) { if ( isset( $wp_filter['user_admin_notices'] ) ) { unset( $wp_filter['user_author_notices'] ); } } elseif ( isset( $wp_filter['admin_notices'] ) ) { unset( $wp_filter['admin_notices'] ); } if ( isset( $wp_filter['all_admin_notices'] ) ) { unset( $wp_filter['all_admin_notices'] ); } } add_action( 'admin_print_scripts', 'pr_disable_admin_notices' );

A Walkthrough With Examples – Real Python

Python has a complete set of built-in exceptions that provide a quick and efficient way to handle errors and exceptional situations that may happen in your code. Knowing the most commonly used built-in exceptions is key for you as a Python developer. This knowledge will help you debug code because each exception has a specific meaning that can shed light on your debugging process.
You’ll also be able to handle and raise most of the built-in exceptions in your Python code, which is a great way to deal with errors and exceptional situations without having to create your own custom exceptions.
In this tutorial, you’ll:

Learn what errors and exceptions are in Python
Understand how Python organizes the built-in exceptions in a class hierarchy
Explore the most commonly used built-in exceptions
Learn how to handle and raise built-in exceptions in your code

To smoothly walk through this tutorial, you should be familiar with some core concepts in Python. These concepts include Python classes, class hierarchies, exceptions, try … except blocks, and the raise statement.

Errors and Exceptions in Python
Errors and exceptions are important concepts in programming, and you’ll probably spend a considerable amount of time dealing with them in your programming career. Errors are concrete conditions, such as syntax and logical errors, that make your code work incorrectly or even crash.
Often, you can fix errors by updating or modifying the code, installing a new version of a dependency, checking the code’s logic, and so on.
For example, say you need to make sure that a given string has a certain number of characters. In this case, you can use the built-in len() function:

In this example, you use the wrong operator. Instead of using the equality comparison operator, you use the assignment operator. This code raises a SyntaxError, which represents a syntax error as its name describes.

Note: In the above code, you’ll note how nicely the error message suggests a possible solution for correcting the code. Starting in version 3.10, the Python core developers have put a lot of effort into improving the error messages to make them more friendly and useful for debugging.

To fix the error, you need to localize the affected code and correct the syntax. This action will remove the error:

Now the code works correctly, and the SyntaxError is gone. So, your code won’t break, and your program will continue its normal execution.
There’s something to learn from the above example. You can fix errors, but you can’t handle them. In other words, if you have a syntax error like the one in the example, then you won’t be able to handle that error and make the code run. You need to correct the syntax.
On the other hand, exceptions are events that interrupt the execution of a program. As their name suggests, exceptions occur in exceptional situations that should or shouldn’t happen. So, to prevent your program from crashing after an exception, you must handle the exception with the appropriate exception-handling mechanism.
To better understand exceptions, say that you have a Python expression like a + b. This expression will work if a and b are both strings or numbers:

In this example, the code works correctly because a and b are both numbers. However, the expression raises an exception if a and b are of types that can’t be added together:

Because a is a string and b is a number, your code fails with a TypeError exception. Since there is no way to add text and numbers, your code has faced an exceptional situation.
Python uses classes to represent exceptions and errors. These classes are generically known as exceptions, regardless of what a concrete class represents, an exception or an error. Exception classes give us information about an exceptional situation and also errors detected during the program’s execution.
The first example in this section shows a syntax error in action. The SyntaxError class represents an error but it’s implemented as a Python exception. This could be confusing, but Python uses exception classes for both errors and exceptions.
Another example of an exception could be when you’re working on a piece of code that processes a text file, and that file doesn’t exist. In this case, you don’t have an error in your code. You have an exceptional situation that you need to handle to prevent the program from crashing. You have no control over the problem because you can’t ensure that the file exists by modifying your code. You need to handle the exception.
You can use try … except blocks to handle exceptions in Python. In the following section, you’ll learn the basics of how to do that handling.
Handling Exceptions
If you have a piece of code that raises an exception, and you don’t provide a handler code for that exception, then your program will stop running. After that, an exception traceback will appear on the standard output, your screen.

In Python, you can handle exceptions using the try … except statement, which allows you to catch the exception and provide recuperative actions.
Consider the following example. A common exception that you’ll see when you start using Python’s lists and tuples is IndexError. This exception happens when you try to access an index that’s out of range:

In this example, the list of numbers only has three values. So, when you try to access index 5 in an indexing operation, you get an IndexError that breaks the code. You can wrap up this code in a try … except block to prevent the breakdown:

Now, the code doesn’t break with an exception. Instead, it prints a message to the screen. Note that the call to print() is just a placeholder action for the sake of the example. In real-world code, you may do other things here.
The above example illustrates the most basic construct to handle exceptions in Python. You can check out the tutorial suggested above to dive deeper into exception handling. Now, it’s time to learn about the other side of the coin. You can also raise exceptions in Python.
Raising Exceptions
Python has the raise statement as part of its syntax. You can use this statement to raise exceptions in your code as a response to exceptional situations.

As an example of how to use the raise statement, say that you need to write a function for calculating the average grade of students. You come up with the following function:

This function works okay. However, when the list of grades is empty, you get a zero division error because len(grades) will be 0. When the user of the code sees the error message, they may get confused. A zero division error? What’s causing this?
A better approach would probably be to make sure that the input list isn’t empty and raise a more appropriate exception if that’s the case:

In this updated version of average_grade(), you add a conditional statement that checks whether the input data is empty. If it is, you raise a ValueError with an explicit error message that clearly communicates what’s wrong with the code.
The IndexError and ValueError exceptions are examples of commonly used built-in exceptions in Python. In the following sections, you’ll learn more about these and several other built-in exceptions.
This knowledge will help you in several ways. First, you’ll be able to quickly figure out the type of error you may have in your code, which improves your debugging skills. Second, you’ll be armed with a wide arsenal of already available exceptions to raise in your own code, freeing yourself from creating custom exceptions.
Python Built-in Exceptions
Python has over sixty built-in exceptions that represent a wide range of common errors and exceptional situations. These exceptions are organized into two groups:

Base class exceptions
Concrete exceptions

The first group of exceptions comprises exception classes that are primarily used as base classes for other exceptions. For example, in this group, you have the Exception class, which is specially designed to allow you to create custom exceptions.
The second group contains exceptions that you’ll commonly see in Python code or get while executing Python code. For example, you’ve probably seen some of the following concrete exceptions in your day-to-day coding:

This table is just a small sample of Python’s built-in exceptions. You can find a comprehensive list of all the built-in exceptions in the Built-in Exceptions page of Python’s documentation.
Glancing at the Exceptions Hierarchy
As you already know, you’ll find many built-in exceptions in Python. You can explore them by inspecting the builtins namespace from a REPL session:

In this example, you first import the builtins namespace. Then, you use the built-in dir() function to list the names that this module defines. Note that you’ll get the complete list of built-in names. Between them, you’ll find the built-in exceptions.
Python’s built-in exceptions are coded as classes and organized in a class hierarchy that includes the following levels:

Base classes: They provide the base classes for other exceptions. You should only use them as parent classes. However, you might find some of these exceptions, such as the Exception class, in some codebases.
Concrete exceptions: They’re exceptions that Python will raise as a response to different exceptional situations. They also provide a great base of concrete exceptions that you can raise in your own code when appropriate.
OS exceptions: They provide exceptions that the operating system generates. Python passes them along to your application. In most cases, you’ll be catching these exceptions but not raising them in your code.
Warnings: They provide warnings about unexpected events or actions that could result in errors later. These particular types of exceptions don’t represent errors. Ignoring them can cause you issues later, but you can ignore them.

A diagram of the exception hierarchy is below:
BaseException
├── BaseExceptionGroup
├── GeneratorExit
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ExceptionGroup [BaseExceptionGroup]
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── StopAsyncIteration
├── StopIteration
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
├── BytesWarning
├── DeprecationWarning
├── EncodingWarning
├── FutureWarning
├── ImportWarning
├── PendingDeprecationWarning
├── ResourceWarning
├── RuntimeWarning
├── SyntaxWarning
├── UnicodeWarning
└── UserWarning

Note that most classes in the hierarchy inherit from Exception. This is also the base class that you should use in those situations when you need to create a custom exception.
Knowing the Base Exceptions
In the built-in exception hierarchy, you’ll find a few classes that are designed to be base classes. The BaseException class is on the top. Then you have five subclasses:

As you can see, all of these base exceptions have their specific use case. It’s important to note that you should use Exception and not BaseException when you create custom exceptions. That’s because BaseException should be used for exceptions that should never be caught in real code.
In practice, when catching and raising exceptions, you should use the most specific exception for the problem at hand.
For example, if you have a piece of code that can potentially raise a ValueError, then you should explicitly handle this exception. If you use Exception instead of ValueError, then your code will catch Exception and all its subclasses, including ValueError. If your code ends up raising something different from ValueError, then that error will be mishandled.
Getting to Know Warnings
At the bottom of the exception hierarchy, you’ll find warnings. These are particular types of exceptions that denote something that can cause problems in the near future. Warnings are exceptions that pertain to the warning categories.

Note: Because warnings are a type of exception with specific use cases and dedicated documentation, you won’t cover them in detail in this tutorial. You can check the Warning control page in Python’s documentation for a complete walkthrough of warnings.

Probably the most common warning that you’ll see while executing Python code is DeprecationWarning. This warning appears when you use deprecated features of the language. For example, Python 3.12 has deprecated the calendar.January and calendar.February constants:

Even though the code works because the constants haven’t been removed yet, Python lets you know that the feature is deprecated to prevent you from having issues in the future. As the warning message says, now you should use calendar.JANUARY.
Syntax Errors
The first exception that you’ll probably see in Python is the SyntaxError exception. Even though Python uses an exception class for this type of issue, you should be clear that they represent errors rather than exceptions. So you won’t be handling them but fixing them, as you already learned.
You’ll also find that Python defines a couple of additional exceptions that inherit from SyntaxError:

These exceptions can be expected when you’re starting to learn Python, and they can confuse you. Fortunately, modern code editors and IDE include features that spot and often remove the conditions that generate these exceptions. In the following sections, you’ll learn about this group of exceptions.
SyntaxError
When Python detects an invalid syntax in a piece of code, it raises a SyntaxError exception. The exception prints a traceback with helpful information that you can use to debug the error and fix your code.

There are many situations where you can end up with a syntax error in your code. You can forget a comma or a closing bracket, misuse an operator or a keyword, and more.
Here are a few examples:

These are common syntax errors that you can fall into at times. The good news is that with the improved error messages that Python provides these days, you can quickly track down the error and fix it.
IndentationError
Unlike other programming languages, indentation is part of Python syntax. To delimit a code block in Python, you use indentation. Therefore, you may get an error if the indentation isn’t correct. Python uses the IndentationError exception to denote this problem.
You may encounter this exception when you’re starting to learn Python. It can also appear when you copy and paste code from intricate places. Fortunately, the error isn’t common nowadays because modern code editors can automatically fix code indentation.
To see the exception in action, consider the following example of a function whose code block uses non-uniform indentation:

In this example, the highlighted lines have different indentations. The first line is indented at four spaces, while the second line is indented at two spaces. This indentation mismatch triggers an IndentationError. Again, because this is a syntax error, Python reports it immediately. You can quickly fix the issue by correcting the indentation.
TabError
Another possible issue for those who come from other programming languages is to mix tab and space characters while indenting Python code. The exception for this issue is TabError, which is a subclass of IndentationError. So, it’s another syntax error exception.

Note: The Style Guide for Python Code (PEP 8) explicitly says:

Spaces are the preferred indentation method.
Tabs should be used solely to remain consistent with code that is already indented with tabs.
Python disallows mixing tabs and spaces for indentation. (Source)

Here’s an example where Python raises a TabError exception:

In this example, the first line in greet() is indented using a tab character, while the second line is indented using eight spaces, which is the common number of spaces that replaces a tab character.

Note: If you don’t use eight spaces in the second line, then you’ll get an IndentationError instead of a TabError.

To try out the example, go ahead and run the file from the command line:

Because the code indentation mixes tabs and spaces, you get a TabError indentation error. Again, TabError exceptions aren’t common nowadays because modern code editors can fix them automatically. So, if you have your editor well-set for Python development, then you probably won’t see this exception in action.

Sometimes, when importing packages, modules and their contents, your code fails with an error telling you that the target file wasn’t found. In practice, you can get one of two import-related exceptions:

Note that the ModuleNotFoundError exception is a subclass of ImportError with a more specific meaning or goal, as you’ll learn in a moment.
In the following sections, you’ll learn about these two exceptions and when Python raises them. This knowledge will help you fix the problem and make your code work.
ModuleNotFoundError
As you’ve already learned, the ModuleNotFoundError exception is a subclass of ImportError with a more specific goal. Python raises this exception when it can’t find the module from which you want to import something:

When Python doesn’t find the target module in its import search path, it raises a ModuleNotFoundError exception. To fix this problem, you must ensure your module is listed in sys.path.

Because ModuleNotFoundError is a subclass of ImportError, when you explicitly use the latter in a try … except block, you’ll be catching both exceptions. So, if your intention is to catch those situations where the target module isn’t present, then you should be specific and use ModuleNotFoundError.
ImportError
Python raises the ImportError exception for all the import-related issues that aren’t covered by ModuleNotFoundError. This can happen for two main reasons:

An import module statement fails to load a module for a reason not covered by ModuleNotFoundError.
A from module import name statement fails to find name in the target module.

Now go ahead and run the following import statement to see the ImportError exception in action:

In this example, you try to import the non_existing name from the sys module. Because the name doesn’t exist in this module, you get an ImportError. You can quickly fix the problem by providing the correct target name.

In real-world code, you can take advantage of ImportError or ModuleNotFoundError to optionally load different modules or libraries that provide a given functionality depending on the library availability.
For example, say you need to parse a TOML file and read its content. In this case, you can use the standard-library module tomllib if you’re using Python 3.11 or later. Otherwise, you should use the third-party library tomli, which is compatible with tomllib.
Here’s how you can do this:

The import in the try clause targets the standard-library module tomllib. If this import raises an exception because you’re using a Python version lower than 3.11, then the except clause imports the third-party library tomli, which you need to install as an external dependency of your project.
Lookup Error Exceptions
Getting an IndexError when you perform indexing operations on a sequence, or a KeyError when you look up keys in dictionaries, are also common issues in Python. In the following sections, you’ll learn about these two exceptions and when they can happen in your code.
IndexError
The IndexError exception happens when you try to retrieve a value from a sequence using an out-of-range index:

In this example, you use 10 as the index to get a value from your colors list. Because the list only has seven items, the valid indices go from 0 to 6. Your target index is out of range, and Python gives you an IndexError exception.
IndexError can be a frequent exception in Python, especially when you’re using dynamically generated indices. To fix the problem, you need to figure out which index your code is using to retrieve a value from the target sequence and then adjust the index range to correct the issue.

Note: In most situations, you won’t use indices in Python loops. Because of this practice, the index error issue is less common in Python loops than in other programming languages where loops rely on indices.

If you can’t control the range of indices, then you can catch the exception in a try … except block and take the appropriate recovery action.
You can also raise the IndexError exception in your own code. This exception can be appropriate when you’re creating custom sequence-like data structures. For example, say that you need to create a sequence-like stack:

In this example, you define Stack and provide the .push() and .pop() methods. The former method appends a new item to the top of the stack, while the latter removes and returns the item at the top of the stack.
Then, you have the .__len__() special method to support the built-in len() function, which gives you the number of items in the stack.
Finally, you have the .__getitem__() method. This special method takes an index as an argument and returns the item at the input index. The try … except block catches the IndexError exception and reraises it with a more specific error message. You don’t rely on the original message, which makes your code more focused.
KeyError
Similarly, when you try to get a non-existing key from a dictionary, then you get a KeyError exception. This issue is also common in Python:

In this example, you try to get the grape key from the fruits dictionary and get a KeyError exception. In this case, the error message is pretty short. It only displays the name of the missing key.
Again, using dynamically generated keys can be the source of the problem. So, to correct the issue, you’ll need to check the generated and existing keys in your code. Alternatively, you can use the .get() method with an appropriate default value if that’s a suitable solution for your use case.
Finally, you can also raise a KeyError in your code. This could be helpful when you need to create a dictionary-like class and provide a more descriptive error message for your users.
Name Errors: NameError
The NameError exception is also pretty common when you’re starting with Python. This exception happens when you try to use a name that’s not present in your current namespace. So, this exception is closely related to scopes and namespaces.
For example, say that you’re working in a REPL session. You’ve imported the sys module to use it in your current task. For some reason, you restart the interactive session and try to use sys without re-importing it:

Because you restarted the interactive session, all the names and modules that you imported before are gone. Now, when you try to use the sys module, you get a NameError.
Note that if the unknown name is after the dot in a qualified name, you won’t see a NameError. In this case, you’ll get an AttributeError exception instead, which you’ll learn about in the following section.

You’ll find a few exceptions that can occur when working with Python classes, objects, and built-in types. The most common ones are the following:

In the following sections, you’ll learn when these exceptions happen and how to deal with them in your Python code.
TypeError
Python raises a TypeError exception when you apply an operation or function to an object that doesn’t support that operation. For example, consider calling the built-in len() function with a number as an argument:

In this example, the argument to len() is an integer number, which doesn’t support the function. Therefore, you get an error with an appropriate message.
In practice, you can catch the TypeError exception in your own code when you need to prevent a type-related issue. You can also raise the exception in your code to denote a type-related problem. For example, say that you want to write a function that takes an iterable of numbers and returns a list of squared values. You want to make sure that the function only accepts iterable objects.
In this situation, you can have a function like the following:

In this example, your function uses the built-in iter() function to check the input data. This function raises a TypeError exception if the provided input isn’t iterable. You catch this exception and reraise it but with a custom error message that adheres to your code’s purpose. Then, you build the list of squared values using a list comprehension.
ValueError
Similarly, Python raises the ValueError exception when an operation or function gets an argument that has the right type but an inappropriate value:

In this example, you first use the built-in float() function to convert a string to a floating-point number. The input string contains a numeric value, so the operation succeeds.
In the second example, you call the same function using a string that doesn’t represent a valid number and get a ValueError exception. Note that the input argument is a string, which is the correct type of argument. However, the input string isn’t a valid numeric value, so you have the right type but the wrong value.
You can also use ValueError in your Python code. This exception can be appropriate in different situations. For example, say that you need to write a class to represent rainbow colors. This class has seven allowed color names, which you can represent with strings:

In this example, you first define a COLORS constant that holds the rainbow colors and their corresponding Hex and RGB representation.
Then, you define the RainbowColor class. In the class initializer, you use a conditional to make sure that the input color name is one of the rainbow colors. If that’s not the case, then you raise a ValueError because the color name is of the correct type but has the wrong value. It’s a string but not a valid color name.
Here’s how the class works in practice:

In this code snippet, when you try to pass a color name that’s not allowed, then you get a ValueError. If the color name is valid, then the class works fine.
AttributeError
Another common exception that you’ll see when you work with Python classes is AttributeError. This exception occurs when the specified object doesn’t define the attribute or method that you’re trying to access.
Take, for example, your RainbowClass from the previous section. This class has one attribute and two methods, .name, .as_hex(), and .as_rgb(). If you access them, then you’ll have a result according to what they do. However, say that you try to access a method called .as_hsl(). What would happen?
Here’s the answer:

Because RainbowColor doesn’t define the .as_hsl() method, you get an AttributeError that tells you the class doesn’t have an attribute that matches that name.
This error can be common in complex class hierarchies that define similar classes with slightly different interfaces. In this situation, you may think that a given class defines a given method or attribute, but that may not be the case. To fix the issue, you can take a look at the class definition or documentation to make sure that you only use the attributes and methods that the class defines.
You can skip the AttributeError exception by using the built-in hasattr() function:

In this example, you use hasattr() to explicitly check if the color object has an attribute called “as_hsl”.
You can also catch and raise the AttributeError exception in your custom classes. In practice, this exception can be pretty useful because Python encourages you to use a coding style based on handling exceptions called EAFP (easier to ask forgiveness than permission).
In short, using this style means that you decide to handle errors and exceptions when they happen. In contrast, with the LBYL (look before you leap) style, you try to prevent errors and exceptions before they happen. That’s what you did in the example above.
So, instead of relying on hasattr(), you should write the above example as follows:

This coding style is more direct. You go and call the desired method. If that fails with an AttributeError, then you perform alternative actions.
Finally, you’ll also get an AttributeError exception when you try to access an object that’s not defined in an already imported module:

Note that you don’t get an import error in this example. Instead, you get an AttributeError exception. That’s because the objects defined in a module become attributes of that module.
Arithmetic Error Exceptions
The ArithmeticError exception class is the base class for built-in exceptions that are related to three different types of arithmetic errors:

Of these three exceptions, the most common is ZeroDivisionError. The FloatingPointError is defined as a built-in exception, but Python doesn’t use it nowadays. In the following sections, you’ll learn about when the ZeroDivisionError and the OverflowError exceptions may appear in your code.
ZeroDivisionError
Python raises the ZeroDivisionError exception when a division’s divisor or right operand is zero. As a quick example, say that you need to write a function to compute the average grade of a group of students. In this case, you can come up with the following function:

This function works okay when you use appropriate input data. But what would happen if you called the function with an empty list object? Here’s the answer:

Because the input list is empty, the len() function returns 0. This return value produces a ZeroDivisionError exception when the actual division occurs.
To fix the issue, you can catch the exception and present the user with a descriptive error message:

In this updated version of average_grade(), you catch the ZeroDivisionError that an empty input list generates and raise a ValueError with a user-friendly error message.
Finally, the ZeroDivisionError exception also occurs when the right-hand operand on a modulo operation is zero:

The modulo operator returns the remainder of dividing two numbers. Because the division of the numbers is the first step to computing the remainder, you can get a ZeroDivisionError if the right-hand operand is 0.
OverflowError
The OverflowError isn’t that common. Python raises this exception when the result of an arithmetic operation is too large, and there’s no way to represent it:

In this example, the number that results from the power operation is too high, and Python can’t represent it correctly. So, you get an OverflowError exception.

Python has a few built-in exceptions that are closely related to iteration. The RecursionError is related to recursive code and is what you can call a standard exception. Then you have the StopIteration exception, which Python internally uses to control the execution flow of for loops. This exception has an asynchronous counterpart called AsyncStopIteration, which Python uses in async for loops.

Note: The AsyncStopIteration exception has the same semantic meaning as the StopIteration exception but for asynchronous loops. So, this exception won’t be covered in this tutorial.

In the following sections, you’ll learn about the RecursionError and StopIteration exceptions and how Python uses them.
RecursionError
A function that calls itself is known as a recursive function. These types of functions are the base of an iteration technique called recursion.

Consider the following toy function as an example of a recursive function:

This function doesn’t follow the rules of recursion because it doesn’t have a base case that stops the repetition. It only has a recursive case. So, if you call the function, then you get a RecursionError exception:

Python will raise a RecursionError exception when the interpreter detects that the maximum recursion depth is exceeded. This may happen when your base case doesn’t work properly or when the required number of recursions for performing the computation exceeds the recursion limit.

Note: A recursion limit is necessary because every function call occupies memory on your system’s call stack, so it’s a way to control memory usage.

You can use the sys.getrecursionlimit() function to check your current recursion depth:

The result of calling this function is a number representing how many times a recursive function can call itself in Python. If appropriate for your use case, you can also set a different recursion limit using the sys.setrecursionlimit() function.
StopIteration
Python raises a StopIteration exception when you call the built-in next() function on an exhausted iterator. Its purpose is to signal that there are no more items in the iterator. So, Python uses this exception internally to control iteration.
Consider the following toy example:

In this example, you use the built-in iter() function to create an iterator from a short list of numbers. Then, you call next() repeatedly to consume the iterator. The fourth call to this function raises a StopIteration exception to signal that the iterator has no more items to retrieve.
Internally, Python uses this exception to terminate for loops. In other words, when a for loop is iterating over an iterator, the loop stops when it gets the StopIteration exception. Because of this, it’s not common to see this exception in action. Note that Python’s for loop calls next() under the hood to perform the iteration.
In practice, you can use the StopIteration exception in your code as part of an implementation of the iterator protocol. This protocol consists of two special methods:

.__iter__() is called to initialize the iterator. It must return an iterator object.
.__next__() is called to iterate over the iterator. It must return the next value in the data stream.

When you provide these methods in a custom class, then your class supports iteration. To signal when the iteration must stop, you must raise the StopIteration exception in the .__next__() method.

To illustrate how to use StopIteration, say that you need to create a custom class that takes a list of values and creates an iterator over their squares:

In this class, the initializer takes a list of values as an argument. It also defines a reference index, which starts at 0. The .__iter__() method returns self, which is common practice when you’re using the iterator protocol like in this example.
In the .__next__() method, you check if the current index is greater than or equal to the number of values in the input list. If that’s the case, then you raise a StopIteration to signal that the iterator is exhausted. Next, you compute the square of the current value and return it as the method’s result.
Now you can use instances of this class in a for loop:

Your iterator works fine. It generates square values and supports iteration. Note that you don’t see the StopIteration exception happening. However, Python uses the exception internally to terminate the loop when the iterator is done.

When processing files in your Python code, there are several different problems you may face. Your code may try to create a file that already exists, access a non-existing file, run a file operation on a directory rather than a file, or have permission issues. Python has file-related exceptions that flag these situations.
Here are some of the most popular ones:

In the following sections, you’ll learn about these built-in exceptions and when they occur in Python.
FileExistsError
When you try to create a file or directory that already exists, Python raises the built-in FileExistsError exception.
To see this exception in action, say that you need to create a file in your working directory and write some text to it. However, you want to avoid replacing the file if it already exists. In this case, you can take advantage of the FileExistsError and write the following code:

In this example, you use the built-in open() function to open a file called hello.txt. The “x” mode means that you want to open the file for exclusive creation, failing if the file already exists.
Now go ahead and run this hello.py file from your command line:

This command won’t issue any output. However, if you look at your working directory, then you’ll see a new file called hello.txt with the text “Hello, World!” in it.
If you run the command again, then the result will be different:

This time, because the hello.txt file already exists, the file-creation code raises a FileExistsError exception, and the except clause prints an error message on the screen.
FileNotFoundError
Python raises the FileNotFoundError exception when a file or directory is requested but doesn’t exist in the target location. For example, say that you’ve accidentally removed the hello.txt file that you created in the previous section. When you try to read the file’s content, you get an error:

In this code snippet, you try to open the hello.txt file to read its content. Because you removed the file prior to this operation, your code fails with the FileNotFoundError exception.
To handle this issue, you can rewrite your code as in the following example:

Now, you use a try … except block to handle the FileNotFoundError exception and prevent your code from breaking out.
PermissionError
Another common issue when processing files is when you try to access a file or directory without adequate access permissions. In this case, Python raises the PermissionError exception to let you know that you don’t have the required permissions.
For example, say you’re working on a Unix-like system, such as Linux or macOS. You want to update the content of the /etc/hosts file. In Unix-like systems, only the root user can modify this file. So, if you try to do your task using a different user, then you’ll get an error:

This error occurs because the file /etc/hosts is a system file. You need root permissions to modify this file. Your code tries to open the file for appending content without having the necessary permissions, which leads to a PermissionError exception.

Sometimes, you want to create a custom base class with a predefined interface from which your users can derive their own classes. This way, you ensure that the subclasses fulfill the required interface. In Python, you can do this using what’s known as an abstract base class (ABC).
The abc module in the standard library provides ABC class and other related tools that you can use to define custom base classes that define a specific interface. Note that you can’t instantiate ABCs directly. They’re intended to be subclassed.
When you start to create your ABCs, you’ll find that when you define methods, it’s a common practice to raise a NotImplementedError exception for those methods that the subclasses should implement but aren’t a strict requirement.
As an example, say that you want to create a class hierarchy to represent different birds, such as a duck, swan, penguin, and so on. You decide that all the classes should have the .swim() and .fly() methods. In this situation, you can start with the following base class:

In this Bird class, you inherit from abc.ABC, which means you’re building an abstract base class. Then, you define the .swim() and .fly() methods, which raise the NotImplementedError exception with an appropriate error message.

Note: You may also import abstractmethod from abc and decorate .swim() and .fly() with @abstractmethod. Python will raise an error if you instantiate any class with an abstract method.

Here’s an example of how you can derive concrete birds from the above class:

In this example, you create the Duck class with the .swim() and .fly() methods. Then, you create the Penguin class, which only has a .swim() method because penguins can’t fly. You can use the Duck class normally. In contrast, the Penguin class will behave differently when you call its .fly() method:

In this code snippet, the Duck class works as expected. Meanwhile, the Penguin class raises a NotImplementedError when you call the .fly() method on one of its instances.
Assertion Errors: AssertionError
Python has a feature called assertions that you can define using the assert statement. Assertions allow you to set sanity checks during the development of your code. Assertions allow you to test the correctness of your code by checking if some specific conditions remain true. This comes in handy while you’re testing and debugging your code.

The assert statement has the following syntax:

The expression part is a condition that should be true unless you have a bug in your program. If the condition becomes false, then the assertion raises an AssertionError exception and terminates the execution of your program.
Assertions are useful for writing test cases. You can use them to check assumptions like preconditions and postconditions. For example, you can test whether an argument is of a given type, you can test the return value of a function, and so on. These checks can help you catch errors as soon as possible when you’re developing a program.

Note: You shouldn’t rely on assertions to check assumptions in production code because assertions are turned off when your code runs in optimized mode using Python’s -O or -OO command-line options.

As an example, say that you’re in a Python coding interview. You’re asked to implement a function that tackles the FizzBuzz challenge, where you return “fizz” for numbers divisible by three, “buzz” for those divisible by five, and “fizz buzz” for those divisible by both three and five. You write a function like the following:

This function apparently covers all the possible scenarios. However, you decide to write a few basic tests to make sure that the function works correctly. You end up with the following code at the end of your fizzbuzz.py file:

In this code snippet, you’ve added some quick assertions to check whether your function returns the correct string with different input values. Now, you can run the test by executing the file from the command line:

Wow! You’ve gotten an AssertionError exception, which means that some of the tests failed. When you look at the exception traceback, you note the assertion fails when you call the function with 15 as an argument. This number is divisible by 3 and by 5, so the current order of your conditions is incorrect.
You must move the condition number % 15 == 0 to the first position:

The conditions first check whether the input number is divisible by 3 and 5. Now go ahead and run the file again:

Great! All your tests pass. The function does its job correctly. That’s the power of the assert statement.
Finally, note that you shouldn’t explicitly raise the AssertionError exception in your code. Instead, you should let the assert statement raise this exception when the assertion’s condition fails. Additionally, you shouldn’t attempt to handle errors by writing code that catches the AssertionError exception because assertions can be disabled.
Interpreter Exit Exceptions
While writing Python code, you’ll find situations where you’ll need to exit or terminate a running application or program.
For example, if an error occurs while an app is running, you can decide to cleanly exit the app with an appropriate exit status, which can be helpful in command-line apps. Another example is when you’re testing some code that takes too long to run, or it’s somehow hanged. In this case, you’d like a quick way to terminate the code’s execution.
Python has the following exceptions that deal with these situations:

In general, your code shouldn’t be catching or handling these exceptions. Doing that can prevent you from exiting your program, making it impossible to quit from within the code or using the keyboard.

Note: Suddenly terminating a program with a SystemExit or KeyboardInterrupt may cause some undesired messy conditions in your system, such as leftover temporary files and open network or database connections.
You can use the atexit module from the standard library to manage how your code responds to this practice. This module allows you to register and unregister cleanup functions that will automatically execute upon normal termination of the Python interpreter.

In the following sections, you’ll learn when these exceptions can happen in your Python code. You’ll also write a couple of examples that illustrate their use.
SystemExit
Python raises the SystemExit exception when you call the sys.exit() function in your code. When you don’t handle the exception, the Python interpreter exits without printing an exception traceback:

In this example, you call the sys.exit() to terminate the Python interpreter. This call takes you back to your terminal session.
In practice, you can use SystemExit directly when you need to terminate an app. For example, say that you want to create a minimal CLI (command-line interface) app that mimics the basic functionality of the Unix ls command, which lists the content of a given directory.
In this situation, you can write a script like the following:

This program processes the arguments provided at the command line, which are automatically stored in the sys.argv variable. The first item in sys.argv is the program’s name. The second item should be the target directory.
The app should only accept one target directory, so the args_count variable must be 2 at most. If the app gets more than one target directory, then you print an error message and raise a SystemExit exception with an exit status of 2. This indicates that the app exited after a failure.
The elif branch checks whether the user has provided a target directory. If that’s not the case, then you print an error message to the user and exit the app, raising a SystemExit exception.
After checking the content of sys.argv, you create a pathlib.Path object to store the path to your target directory. If this directory doesn’t exist, then you inform the user and exit the app using the SystemExit exception again. This time, the exit status is 1 to signal that the app faced an error during its execution. Finally, the for loop lists the directory content, one entry per line.
On a Unix-like system, such as Linux and macOS, you can run the following command to check how the script works:

When you run the script with no argument, then you get a message telling you that you need to provide a target directory. When you use the echo command to check the exit code, represented by $?, you’ll see that it’s 2. In the second example, you provide two target directories. Again, the app fails with an error message. The echo command also returns 2.
Finally, you run the script with the current working directory as an argument. In this case, you get the list of files that live in that directory. When you run echo, you get an exit status of 0, which signals that the app’s execution was successful.
KeyboardInterrupt
The KeyboardInterupt exception is somewhat different from other exceptions. Python raises this exception when the user intentionally presses the Ctrl+C key combination during the execution of a program. This action interrupts a running program.
For example, say that you’re working on a piece of code that involves a loop. By error, the code falls into an infinite loop. In this situation, you need a quick way to terminate the code’s execution. That’s when the Ctrl+C key combination comes in handy.
Consider the following toy loop:

This loop is intentionally written to be infinite. In practice, you can have real loops that run into infinite iteration because of a logical error. When you encounter this issue, you can press the Ctrl+C keys to terminate the code’s execution. As a result, Python will raise a KeyboardInterrupt exception.
Finally, it’s important to note that you shouldn’t catch this exception in your code because this may prevent the interpreter from exiting.
Exception Groups
In Python 3.11 and greater, you’ll have the ExceptionGroup and BaseExceptionGroup classes. You can use them when you need to raise multiple unrelated exceptions at the same time. The difference between these classes is that BaseExceptionGroup extends BaseException, while ExceptionGroup extends Exception.

You can reach for these exceptions when an asynchronous program has several concurrent tasks that could fail at the same time. But in general, you will raise an ExceptionGroup sparingly.
You can handle and raise exception groups as needed. Here’s how to catch an exception group with the associated except* syntax:

The except* syntax allows you to catch individual exceptions on an exception group. This way, you can handle individual exceptions in specific ways.
Conclusion
You’ve learned about Python’s built-in exceptions, which provide a quick and efficient way to handle errors and exceptional situations in your code. Now, you know the most commonly used built-in exceptions, when they appear, and how to use them in your exception-handling code. You also learned that you can raise these exceptions in your code.
In this tutorial, you’ve:

Learned what errors and exceptions are in Python
Understood how Python organizes the built-in exceptions in a class hierarchy
Explored the most commonly used built-in exceptions
Learned how to handle and raise built-in exceptions in your code

This knowledge will help you efficiently debug your code because each exception has a specific meaning and use case. You’ll also be able to effectively handle and raise built-in exceptions in your code, which is a great skill for a Python developer.

Mark as Completed

Share

Or copy the link:

Copied!

Happy Pythoning!

Related articles

Mortgage Rates Could Fall Another Half Point Just from Market Normalization

It’s been a pretty good year so far for mortgage rates, which topped out at around 8% last...

Farewell: Fintech Nexus is shutting down

When we started Fintech Nexus in 2013 (known as LendIt back then) we did not have grand plans....

Goldman Sachs loses profit after hits from GreenSky, real estate

Second-quarter profit fell 58% to $1.22 billion, or $3.08 a share, due to steep declines in trading and...

Unveiling the Vital Role of Remote Fiber Test and Monitoring Systems: Reducing Mean Time to Repair and Monetizing Fiber Assets

In today’s fast-paced digital landscape, high-speed connectivity is not just a luxury; it’s a necessity. With the increasing...