Exceptions
Quality Score
Overall Score: 8.6/10 ✅ Excellent
- Technical Accuracy: 28/35
- Code Quality: 24/25
- Educational Value: 21/25
- Documentation: 13/15
Last reviewed: June 25, 2026
Exceptions are a mechanism for handling errors in Python (and most programming
languages). When an error occurs when running a piece of code, Python raises an
exception. There are multiple types of exceptions (built-in and custom).
BaseException is the base class for all built-in exceptions, but commonly used
exceptions inherit from Exception. You can also create your own custom
exceptions by inheriting from Exception or any of its subclasses.
If the exception is not caught, the program will terminate immediately, raising the corresponding exception and traceback.
src.intermediate.exceptions.exception_uncontrolled()
Example of an unhandled exception that propagates to the caller.
This function intentionally performs an invalid operation (adding an integer and a string) without catching the exception. This shows what happens when exceptions are not handled - they propagate up the call stack until caught or the program crashes.
Raises:
| Type | Description |
|---|---|
TypeError
|
When attempting to add incompatible types (int + str). |
Source code in src/intermediate/exceptions/exceptions.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
Always raise proper exception when building your python code. It will help you and your team to understand the error and how to fix it. A proper module/library, should implement a consistent and robust mechanism to raise exceptions, including in the docstrings sections of the module.
Controlling exceptions
Exceptions can be caught and handled using a try block. You can catch the
exception, do something (like logging, metrics...), and continue running the
program.
src.intermediate.exceptions.exception_controlled()
Example of proper exception handling by catching and logging.
This function shows how to catch an exception, log it, and continue execution gracefully. This is appropriate when the error is expected and recoverable, and you want the program to continue running.
Source code in src/intermediate/exceptions/exceptions.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
You can catch the exception, logging and raise the same exception to terminate the execution.
src.intermediate.exceptions.exception_controlled_raise_exception()
Example of catching and re-raising the same exception.
This function shows how to catch an exception, log relevant information, and then re-raise the same exception. This pattern is useful when you want to add logging or perform cleanup while still propagating the error to the caller.
Raises:
| Type | Description |
|---|---|
TypeError
|
When attempting to add incompatible types (int + str). |
Source code in src/intermediate/exceptions/exceptions.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
Similar way, you can catch the exception, logging and raise another your custom
exception (always use from to preserve the original exception context). You
can terminate the execution of a running program by raising an exception at any
time.
src.intermediate.exceptions.exception_controlled_raise_custom_exception(number=1, char='a')
Example of catching and raising a custom exception.
This function shows how to catch a built-in exception and raise a custom exception instead. This pattern is useful for wrapping low-level exceptions with domain-specific errors that provide better context to callers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
number
|
int
|
An integer to be added. Default is 1. |
1
|
char
|
str
|
A string to be added. Default is "a". |
'a'
|
Raises:
| Type | Description |
|---|---|
CustomError
|
A custom exception wrapping the original TypeError. |
Source code in src/intermediate/exceptions/exceptions.py
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | |
With the finally block, you can run code that will always run, regardless if the code in the try block raises an exception. It will be always executed.
src.intermediate.exceptions.exception_with_finally(raise_exception)
Example of using finally clause for cleanup code.
This function shows how the finally block executes regardless of whether an exception is raised or not. The finally block is useful for cleanup operations that must always run, such as closing files or releasing resources.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raise_exception
|
bool
|
If True, raises a TypeError. If False, executes successfully without raising an exception. |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
When raise_exception is True and attempting to add incompatible types (int + str). |
Source code in src/intermediate/exceptions/exceptions.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
Adding the else block, you can run code that will only run if the code in the
try block does not raise an exception. It will be executed only if the try block
succeeds.
src.intermediate.exceptions.exception_with_else(raise_exception)
Example of using else clause for code that runs if no exception occurs.
This function shows how the else block executes only if no exception is raised in the try block. The else block is useful for code that should run only when the try block succeeds, such as processing results or performing follow-up actions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raise_exception
|
bool
|
If True, raises a TypeError. If False, executes successfully without raising an exception. |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
When raise_exception is True and attempting to add incompatible types (int + str). |
Source code in src/intermediate/exceptions/exceptions.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
Multiple exceptions can be caught in a single except block by specifying a
tuple of exception types. This is useful when you want to handle different
exceptions in the same way.
src.intermediate.exceptions.multiple_exceptions_controlled(type_error)
Example of multiple exception handling with specific except blocks.
This function shows how to handle multiple exceptions in a single try block by using multiple except clauses. Each except clause can handle a specific type of exception, allowing for more granular error handling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
type_error
|
bool
|
If True, raises a TypeError. If False, raises a ValueError. |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
When attempting to add incompatible types (int + str). |
ValueError
|
When attempting to convert a non-numeric string to int. |
Source code in src/intermediate/exceptions/exceptions.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |
You can also create your own custom exceptions, by inheriting from Exception
or any of its subclasses. This allows you to create exceptions that are specific
to your application or library. You can also add custom attributes and methods
to your custom exceptions, enriching the information that can be provided when
the exception is raised.
src.intermediate.exceptions.CustomError
Bases: Exception
Custom exception class for demonstrating exception handling patterns.
This class extends the built-in Exception class to create a custom exception that can carry additional context about the error, including the original exception and a custom message.
Attributes:
| Name | Type | Description |
|---|---|---|
message |
str
|
A descriptive message explaining the error context. |
exception |
Exception | None
|
The original exception that triggered this custom error. |
Source code in src/intermediate/exceptions/custom_exceptions.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
Common pitfalls
Do not catch Exception or BaseException unless you have a very good reason
and you have great observability of your code. Catching these exceptions can
hide bugs and make it difficult to debug your code. The best approach is to
catch the exceptions that you expect to occur and handle them appropriately, and
let the unexpected exceptions propagate all the way up.