How to raise Exceptions in Python

Posted in Python by Dirk - last update: Feb 08, 2024

Raising Exceptions in Python

Syntax: To raise an exception in Python, you use the raise keyword followed by the exception you want to raise. The general syntax is as follows:

raise ExceptionType("Optional error message")

with

  • ExceptionType: This can be any built-in or user-defined exception class.
  • "Optional error message": A human-readable message providing additional information about the exception.

Example

def divide(x, y):
    if y == 0:
        raise ValueError("Cannot divide by zero")
    return x / y

# Example usage:
try:
    result = divide(10, 0)
except ValueError as e:
    print(f"Error: {e}")
else:
    print(f"Result: {result}")

In this example, the divide function raises a ValueError when attempting to divide by zero. The try-except block catches the exception and handles it gracefully.

Error Handling

In Python, exceptions are a way of handling errors and unexpected situations in your code. Exception handling allows you to gracefully respond to issues that may arise during the execution of your program. One crucial aspect of exception handling is the ability to raise exceptions when certain conditions are met. This tutorial will guide you through the process of raising exceptions in Python, covering the basics, common use cases, and best practices.

What is an Exception?

An exception is an event that disrupts the normal flow of a program’s execution. When an exceptional situation occurs, an exception object is created, and the normal program flow is interrupted. To handle these situations, Python provides a mechanism to raise, catch, and handle exceptions.

Common use cases

Input Validation:

Raise exceptions when validating user input to handle incorrect or unexpected data.

Example:

def get_positive_integer():
    try:
        user_input = int(input("Enter a positive integer: "))
        if user_input <= 0:
            raise ValueError("Input must be a positive integer")
        return user_input
    except ValueError as e:
        print(f"Error: {e}")
        return None

result = get_positive_integer()
if result is not None:
    print(f"You entered a valid positive integer: {result}")
else:
    print("Invalid input. Please try again.")

In this example, the function get_positive_integer raises a ValueError if the user enters a non-positive integer, providing a clear error message.

File Operations:

Raise exceptions when working with files, like FileNotFoundError when a file is not found.

Example:

try:
    with open("nonexistent_file.txt", "r") as file:
        content = file.read()
except FileNotFoundError as e:
    print(f"Error: {e}")

This code attempts to read the content of a file, but if the file does not exist, it raises a FileNotFoundError with an informative error message.

Network Operations:

Raise exceptions for network-related errors, such as ConnectionError or TimeoutError.

import requests

try:
    response = requests.get("https://example.com/nonexistent")
    response.raise_for_status()  # Raises an HTTPError for bad responses (4xx or 5xx)
except requests.HTTPError as e:
    print(f"HTTP Error: {e}")
except requests.RequestException as e:
    print(f"Request Error: {e}")

Here, the code uses the requests library to make an HTTP request. If the server responds with an error (4xx or 5xx), it raises an HTTPError. Other network-related errors are caught using requests.RequestException.

Custom Exceptions:

Create and raise custom exceptions to represent specific error conditions in your application.

class CustomError(Exception):
    pass

def example_function(value):
    if value < 0:
        raise CustomError("Value must be non-negative")

try:
    example_function(-5)
except CustomError as e:
    print(f"Custom Error: {e}")

In this case, a custom exception CustomError is raised when a function receives an invalid argument. Custom exceptions allow you to handle specific error conditions in your application.

AssertionError:

Use the assert statement to raise AssertionError when conditions for correctness are not met.

Example:

def divide_positive_numbers(x, y):
    assert x > 0 and y > 0, "Both numbers must be positive"
    return x / y

try:
    result = divide_positive_numbers(-10, 2)
except AssertionError as e:
    print(f"Assertion Error: {e}")

Best Practices for Raising Exceptions

  1. Use Built-in Exceptions: whenever possible, use built-in exception classes that accurately represent the error condition.
  2. Provide Descriptive Messages: include informative error messages when raising exceptions to aid in debugging.
  3. Use Custom Exceptions Sparingly: reserve custom exceptions for cases where a specific error condition in your application needs unique handling.
  4. Avoid Broad Exception Handling: be specific in catching exceptions. Avoid catching generic Exception unless necessary, as it may hide unexpected issues.
  5. Handle Exceptions Appropriately: use try-except blocks to catch and handle exceptions gracefully. Log or display relevant information.
  6. Clean-Up with finally: use the finally block for cleanup code that should execute regardless of whether an exception occurred.
  7. Consider Context Managers: leverage context managers (with statement) for resource management, as they automatically handle exceptions during setup and teardown.

Handling exceptions

In general, it is good practice to handle exceptions appropriately, especially for those that can be anticipated and managed during program execution. This ensures that your program responds gracefully to unexpected situations, provides informative error messages, and allows for proper cleanup before termination when necessary.

Not all built-in exceptions in Python necessarily lead to a program crash if they are not explicitly handled. The impact of an unhandled exception depends on the type of exception and where it occurs in the code. Here are some categories of exceptions based on their impact:

Fatal Errors:

Some exceptions are considered fatal errors, and if not handled, they lead to a program termination. Examples include SyntaxError, NameError, and TypeError. These errors indicate fundamental issues in the code structure or logic and usually cannot be recovered during runtime.

Non-Fatal Errors:

Certain exceptions are considered non-fatal, and unhandled instances of these exceptions may not crash the program but can lead to unexpected behavior. Examples include ValueError, KeyError, and IndexError. While these errors indicate issues with input or data manipulation, the program can often continue executing if they are appropriately handled.

Runtime Errors:

Some exceptions, such as ZeroDivisionError, FileNotFoundError, and IOError, occur during runtime and can be anticipated and handled to prevent program termination. If not handled, they can lead to unintended consequences, like corrupted data or incomplete operations.

User-Interruptible Exceptions:

Exceptions like KeyboardInterrupt and SystemExit can be triggered by user actions. While these can lead to program termination, they are often designed to allow users to gracefully exit the program and might not be considered critical errors.

Custom Exceptions:

When you define custom exceptions in your code, their impact depends on how they are raised and handled. Custom exceptions can be designed to either terminate the program or provide a way to recover, depending on your application’s requirements.

Common built-in Exceptions:

  • SyntaxError: Raised when there is a syntax error in the code.
  • IndentationError: Raised when indentation is incorrect.
  • NameError: Raised when a local or global name is not found.
  • TypeError: Raised when an operation or function is applied to an object of an inappropriate type.
  • ValueError: Raised when a function receives an argument of the correct type but an inappropriate value.
  • ZeroDivisionError: Raised when division or modulo by zero occurs.
  • FileNotFoundError: Raised when attempting to open a file that cannot be found.
  • IOError: Raised when an I/O operation (e.g., reading or writing a file) fails.
  • IndexError: Raised when trying to access an index that does not exist in a sequence (e.g., list, tuple).
  • KeyError: Raised when a dictionary key is not found.
  • AttributeError: Raised when trying to access an attribute that does not exist.
  • ImportError: Raised when an import statement fails to import a module.
  • TypeError: Raised when an operation or function is applied to an object of an inappropriate type.
  • OverflowError: Raised when a numerical operation exceeds the limits of the data type.
  • MemoryError: Raised when an operation runs out of memory.
  • RuntimeError: Raised when an error is detected that doesn’t fall into any of the other categories.
  • ConnectionError: Raised for network-related errors, like ConnectionRefusedError, TimeoutError, etc.
  • OSError: Base class for various operating system-related errors.
  • KeyboardInterrupt: Raised when the user interrupts the program execution, typically by pressing Ctrl+C.
  • SystemExit: Raised when the interpreter is requested to exit.

Other articles