Fundamentals 11 min read

20 Essential Python Exception Handling Techniques

This article presents twenty practical Python exception‑handling techniques—from basic try‑except structures and custom exceptions to advanced concepts like exception chaining, dynamic exception classes, and generator cleanup—providing developers with clear examples and explanations to write more robust, readable, and Pythonic code.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
20 Essential Python Exception Handling Techniques

Exception handling is a crucial aspect of writing robust code. While many developers are familiar with basic try‑except blocks, there are deeper techniques that can make exception handling more efficient, readable, and Pythonic. This article presents 20 Python exception handling tricks that can significantly improve your code.

1. Simplest Exception Handling

We all know the simplest exception handling pattern:

try:
    # Your code here
except IOError:
    # Handle I/O errors
except Exception as e:
    # Handle other exceptions
finally:
    # Cleanup, runs no matter what

Exceptions are organized in a hierarchy; if an IOError occurs, its except block runs, while other exceptions fall to the generic Exception handler. Understanding this hierarchy lets you catch errors more broadly or specifically as needed.

Using the finally clause ensures cleanup operations run regardless of whether an exception occurs, which is ideal for closing files or releasing resources.

2. Custom Exceptions

Creating custom exceptions can make code more readable and maintainable by clearly indicating specific error conditions.

class MyCustomError(Exception):
    pass

try:
    raise MyCustomError("A specific error occurred")
except MyCustomError as e:
    print(e)

3. Else in Try‑Except

If no exception is raised, the else clause of a try‑except block executes. This construct is not available in many other languages.

try:
    # Attempt operation
except Exception:
    # Handle error
else:
    # Executes if no exceptions

4. Using the as Keyword

The as keyword assigns the caught exception to a variable, allowing detailed inspection and easier debugging.

try:
    # Some operation
except Exception as e:
    print(f"Error: {e}")

5. Capturing Multiple Exceptions

A tuple can be used to catch several exception types in a single except clause, simplifying error‑handling code.

try:
    # Risky operation
except (TypeError, ValueError) as e:
    # Handle both exceptions

6. Raising a New Exception from Another

Python allows raising a new exception while preserving the original traceback using the from clause, which aids debugging complex scenarios.

try:
    # Some operation
except Exception as original_error:
    raise RuntimeError("Something bad happened") from original_error

This technique can be powerful but should be used with care.

7. Ignoring Exceptions

The contextlib.suppress() function can gracefully ignore specific exceptions, making code clearer and more readable.

from contextlib import suppress

with suppress(FileNotFoundError):
    # Operation that might not find a file

8. Using Assertions

Assertions raise an exception when a condition is not met. Use them cautiously, as they can be disabled with optimization flags.

assert condition, "Condition was not met"

AssertionError can be caught directly in an except block.

9. Formatting Exception Information

The traceback module can print detailed exception information, helping with debugging.

import traceback

try:
    raise ValueError("An error occurred")
except:
    traceback.print_exc()  # Print exception information to stderr

10. Emitting Non‑Fatal Warnings

The warnings module issues warnings instead of exceptions, useful for alerting users or developers without stopping execution.

import warnings

warnings.warn("This is a warning message", UserWarning)

11. Suppressing Exceptions (repeat)

The suppress function from contextlib can ignore specific exceptions while ensuring resources are cleaned up.

from contextlib import suppress

with suppress(FileNotFoundError):
    open('non_existent_file.txt', 'r')

12. Creating an Exception‑Handling Wrapper

The functools module can be used to build a decorator that centralises exception handling across multiple functions.

from functools import wraps

def exception_handler(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print(f"Handled exception: {e}")
            return None
    return wrapper

@exception_handler
def risky_function():
    raise ValueError("Something went wrong")

risky_function()

13. Accessing Exception Attributes

sys.exc_info() provides detailed information about the current exception, useful for logging or further processing.

import sys

try:
    raise TypeError("An error occurred")
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print(exc_type, exc_value)

14. Analysing the Current Exception Context

The inspect module can examine the current exception context, which is valuable in complex error‑handling scenarios.

import inspect

def current_exception():
    for frame in inspect.trace():
        if frame[3] == 'risky_function':
            return frame[0].f_locals.get('e')

try:
    risky_function()
except Exception as e:
    print(current_exception())

15. Creating Dynamic Exception Classes

The types module can dynamically create exception classes at runtime.

import types

DynamicException = types.new_class('DynamicException', (Exception,))
raise DynamicException("A dynamically created exception")

16. Listing All Built‑in Exceptions

The builtins module can enumerate all built‑in exception types, helping you understand the hierarchy.

import builtins

for name in dir(builtins):
    obj = getattr(builtins, name)
    if isinstance(obj, type) and issubclass(obj, BaseException):
        print(name)

17. Customising Exception String Representation

Override __str__ and __repr__ in a custom exception to provide richer error messages.

class MyException(Exception):
    def __str__(self):
        return "This is a custom message for MyException"
    def __repr__(self):
        return "MyException()"

raise MyException

18. Exceptions Not Caught by except Exception

Deriving from BaseException creates exceptions that bypass a generic except Exception block, useful for critical errors.

class MyCriticalError(BaseException):
    pass

try:
    raise MyCriticalError("A critical error")
except Exception as e:
    print("This will not catch MyCriticalError")

19. Gracefully Handling User and System Interrupts

Catching KeyboardInterrupt and SystemExit allows graceful shutdown of scripts.

import sys

try:
    while True:
        continue
except KeyboardInterrupt:
    print("User interrupted the process")
    sys.exit(0)

20. Generator Resource Cleanup

GeneratorExit is raised when a generator is closed; catching it enables cleanup actions.

def my_generator():
    try:
        yield "Hello"
    except GeneratorExit:
        print("Generator closing")
        raise

gen = my_generator()
next(gen)
gen.close()

Conclusion

Python exceptions can greatly enhance code robustness and clarity. The 20 examples compiled in this article help you leverage Python's error‑handling capabilities and markedly improve your exception management.

pythonException HandlingprogrammingBest PracticesError Management
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.