Skip to Content
Course content

7.2.1 Writing robust error-handling code

Robust error handling is essential to writing resilient and reliable Python programs. It ensures that the application can gracefully handle unexpected situations, provide meaningful feedback to users, and recover from errors without crashing. Below are the best practices for writing robust error-handling code in Python.

1. Use Specific Exceptions

It’s a good practice to catch specific exceptions rather than a generic except Exception block. This approach ensures that only relevant errors are handled and avoids hiding potential issues.

try:
    num1 = int(input("Enter a number: "))
    num2 = int(input("Enter another number: "))
    result = num1 / num2
except ValueError:
    print("Invalid input! Please enter integers.")
except ZeroDivisionError:
    print("Cannot divide by zero!")
  • Why it’s robust: It catches and handles specific errors (e.g., ValueError, ZeroDivisionError), providing relevant messages for each type of error.

2. Log Errors for Debugging

Logging errors helps track issues and simplifies debugging, especially in production environments. Python’s logging module is perfect for logging exceptions and other runtime information.

import logging

# Set up logging configuration
logging.basicConfig(filename='app.log', level=logging.ERROR)

try:
    num1 = int(input("Enter a number: "))
    num2 = int(input("Enter another number: "))
    result = num1 / num2
except ValueError as e:
    logging.error("ValueError occurred: %s", str(e))
    print("Invalid input! Please enter integers.")
except ZeroDivisionError as e:
    logging.error("ZeroDivisionError occurred: %s", str(e))
    print("Cannot divide by zero!")
  • Why it’s robust: The errors are logged with details, making it easier to trace problems in the code. This approach is particularly useful in production environments where users are not always available to provide feedback.

3. Provide Meaningful Error Messages

When an error occurs, ensure that the error message is helpful and clear. Provide users with context about the issue, potential solutions, and where to look for more information.

try:
    file = open("data.txt", "r")
    data = file.read()
except FileNotFoundError:
    print("Error: The file 'data.txt' was not found. Please check the file path.")
except IOError:
    print("Error: There was an issue reading the file. Please check the file permissions.")
  • Why it’s robust: The error messages are informative, guiding the user on what went wrong and how to fix it.

4. Use else and finally Blocks Effectively

The else block runs if no exception is raised, and the finally block always runs, whether or not an exception occurs. Use them to improve the structure of your code.

  • else Block: Use the else block to execute code when no exception occurs in the try block. It’s useful for the code that should only run if everything goes smoothly.
  • finally Block: Use the finally block for code that must run regardless of whether an exception occurred or not (e.g., closing files or releasing resources).
try:
    file = open("data.txt", "r")
    data = file.read()
except FileNotFoundError:
    print("File not found.")
else:
    print("File read successfully!")
finally:
    file.close()
    print("File closed.")
  • Why it’s robust: The else block is used for post-processing, and the finally block ensures resources (e.g., files) are always cleaned up, reducing the risk of resource leaks.

5. Raising Exceptions for Custom Errors

You can raise your own exceptions when specific conditions are met. This helps in creating a more structured error-handling mechanism for your application.

class CustomError(Exception):
    def __init__(self, message):
        self.message = message
        super().__init__(self.message)

def divide(a, b):
    if b == 0:
        raise CustomError("Cannot divide by zero!")
    return a / b

try:
    result = divide(10, 0)
except CustomError as e:
    print(f"Error: {e}")
  • Why it’s robust: By raising custom exceptions, you can define specific error messages and control the flow of error handling based on your application’s needs.

6. Avoid Overuse of try-except for Control Flow

It’s not a good practice to use try-except blocks as a form of regular control flow, especially for expected conditions (e.g., checking if a file exists before opening it). Use conditional statements where applicable.

# Avoid this (misuse of try-except for flow control)
try:
    file = open("data.txt", "r")
except FileNotFoundError:
    file = open("default.txt", "r")

# Instead, use this
if os.path.exists("data.txt"):
    file = open("data.txt", "r")
else:
    file = open("default.txt", "r")
  • Why it’s robust: try-except should be used for exceptional cases, not for predictable conditions. Using if-else statements improves code clarity and efficiency.

7. Graceful Program Exit

When your program encounters an error that it cannot recover from, ensure it exits gracefully. Provide useful error messages or logs, and make sure that any resources (e.g., files, databases) are closed before exiting.

import sys

try:
    # Simulating a critical failure
    x = int(input("Enter a number: "))
    result = 10 / x
except Exception as e:
    print(f"Critical error: {e}")
    sys.exit(1)  # Exit with an error code
  • Why it’s robust: The sys.exit(1) ensures that the program exits with a non-zero status code, indicating an error. It also provides the user with a clear error message.

Summary of Best Practices for Robust Error Handling

  1. Catch specific exceptions to provide meaningful and accurate responses.
  2. Log exceptions for debugging and monitoring purposes.
  3. Provide clear and actionable error messages for users.
  4. Use else and finally blocks to organize code execution and resource management.
  5. Raise custom exceptions for specific error conditions.
  6. Avoid using try-except for regular control flow.
  7. Ensure a graceful program exit, providing feedback when necessary.

By following these best practices, you can write Python programs that are more robust, easier to maintain, and resilient to errors.

Commenting is not enabled on this course.