Explain Codes LogoExplain Codes Logo

One try block with multiple excepts

python
exception-handling
best-practices
logging
Nikita BarsukovbyNikita Barsukov·Mar 1, 2025
TLDR

Deal with different exceptions in Python, each try block can house multiple except blocks, each one being responsible for a specific set of exceptions. Group exceptions if they share the same handling logic. Utilize Exception as a generic fallback with extra caution as it can potentially shadow unrecognized errors.

Example:

try: # Here be dragons...and risky code pass except (TypeError, ValueError) as e: # Joint handling because misery loves company print(f"A one-two punch from {e}!") except Exception as e: # Net wider than a goalkeeper's arm span print(f"Caught a wild {e}!")

Key points to bear in mind:

  • Allocate individual except blocks for different exception types for optimal clarity
  • Tuples unite error types that share similar handling procedures
  • General Exception clause is most effective as a last line of defense
  • Prioritize explicit handling to keep error transparency throughout the program

Breaking down the Exception Hierarchy

To master handling multiple exceptions, it's essential to understand the exception hierarchy in Python. From commonplace exceptions like KeyboardInterrupt, extending to IOErrors, right up to the generic Exception class, this knowledge hierarchy allows for effective tailoring of try-except blocks.

try: # Code here, fingers crossed! except KeyboardInterrupt: # Specific handling; say "No Interruptions!" except IOError as e: # Handle I/O errors, because I/O, I/O, it's off to code we go... handle_io_error(e) except Exception as general_error: # Catch-all for those sneaky, unexpected exceptions log_exception(general_error)

In the example above, you can see that we handle more specific exceptions before the more encompassing Exception.

Best practices and nuanced handling

Utilize the exception hierarchy

Comprehend the inheritance structure of exceptions, allowing you to catch the most specific errors and work down the hierarchy. More defined handlers equate to granular error recovery and precise end-user feedback.

Harness the power of else and finally

Add more depth to your except clauses by implementing else for code that should run if the try block does not raise any error, and finally which always executes, no matter what happens in the previous blocks.

Avoid generic catch-all excepts

Instead of resorting to a wildcard Exception block, always aim to be more specific with your exceptions. This approach not only boosts debugging efficiency but also ensures your program's robustness.

Log before you lose it all

Upon catching exceptions, logging them immediately is key. Contextual information is often critical for effective debugging. Use Python's logging library to differentiate between various error levels as well as record stack traces.

Joke-centric logging payload:

import logging try: # Code logic, and maybe a chicken crossing a road... except SpecificError as e: # Specific issue, like if the chicken didn't make it :( logging.error("Chicken crossed to the ‘except’ side", exc_info=True) except AnotherError as e: # Warning level message, maybe chicken's taking its time crossing logging.warning("Chicken is taking a detour", exc_info=True) except Exception as e: logging.exception("Chicken encountered an unexpected from fox")

Define and use custom exceptions for specialized situations

Custom exceptions are a great way to take control of your code's flow management. Being explicit with defined use-cases means that the relevant exceptions can be caught and handled effectively.

Advanced patterns and pro tips

Operate with reraised exceptions

If you need to, you can handle an exception and then reraise it up the chain by using a raise without any arguments in the exception block.

try: # Let's see what happens here... except SomeError as e: handle_error(e) # I'm throwing this back at you! raise

Create a chain of exceptions using the from keyword. Doing so nests the traceback of the original error within the new exception, providing a comprehensive insight into the culprit behind the secondary issue.

try: # Code here might cause an initial exception except FirstError as e: raise SecondError("A second error. Surprise, surprise") from e