Handling Exceptions with Python Requests

When working with web APIs or making HTTP requests using Python’s requests library, it’s essential to handle potential exceptions that may arise. This tutorial will cover how to properly use try-except blocks when making requests, ensuring your code is robust and reliable.

Understanding Exceptions in Requests

The requests library raises specific exception classes for different types of errors. These include:

  • ConnectionError: Raised when a network problem occurs (e.g., DNS failure, refused connection).
  • HTTPError: Raised when the server returns an HTTP error (4xx or 5xx status code). To raise this exception, you must call Response.raise_for_status().
  • Timeout: Raised when the request times out.
  • TooManyRedirects: Raised when the number of redirects exceeds the configured maximum.

All exceptions that requests explicitly raises inherit from RequestException. Understanding these exception types is crucial for handling them appropriately in your code.

Basic Exception Handling

To catch exceptions, you use a try-except block. For example, to catch all exceptions related to requests:

import requests

try:
    r = requests.get('http://www.example.com')
except requests.exceptions.RequestException as e:
    print(f"An error occurred: {e}")

This code will catch any exception that is an instance of RequestException or its subclasses, providing a broad safety net for your request.

Handling Specific Exceptions

While catching the base RequestException can be useful for general error handling, you might want to handle specific exceptions differently. For example, you could retry a request that timed out but abort if there’s a connection error:

import requests

url = 'http://www.example.com'

try:
    r = requests.get(url, timeout=3)
    r.raise_for_status()  # This can raise an HTTPError
except requests.exceptions.Timeout:
    print("Timeout Error: The request timed out.")
except requests.exceptions.HTTPError as errh:
    print(f"HTTP Error: {errh}")
except requests.exceptions.ConnectionError as errc:
    print(f"Error Connecting: {errc}")
except requests.exceptions.RequestException as err:
    print(f"Something Else Happened: {err}")

In this example, the code attempts to fetch a URL with a timeout of 3 seconds. If an HTTPError occurs (because raise_for_status() was called), it’s caught and handled specifically. The same goes for Timeout, ConnectionError, and any other request-related exceptions.

Ordering Exceptions

When catching multiple specific exceptions, it’s crucial to list them from most specific to least specific. This is because Python will stop at the first except block that matches the exception type. So, if you put a general exception before more specific ones, those specific exceptions will never be caught:

# Incorrect ordering: RequestException is too broad and catches everything
try:
    # request code here
except requests.exceptions.RequestException as err:
    print("Something Else Happened:", err)
except requests.exceptions.HTTPError as errh:
    print("Http Error:", errh)  # This will never be reached

# Correct ordering: Specific exceptions first, then the general one
try:
    # request code here
except requests.exceptions.HTTPError as errh:
    print("Http Error:", errh)
except requests.exceptions.RequestException as err:
    print("Something Else Happened:", err)

Accessing Exception Details

Sometimes, you might need more information about what went wrong. The exception object itself can provide useful details:

try:
    r = requests.post('somerestapi.com/post-here', data={'birthday': '9/9/3999'})
    r.raise_for_status()
except requests.exceptions.HTTPError as e:
    print(e.response.text)  # Prints the response body of the HTTP error

This can be particularly helpful for debugging API interactions where the server’s error message is crucial for understanding what went wrong.

Conclusion

Proper exception handling is vital when working with the requests library in Python. By catching specific exceptions and ordering them correctly, you can write more robust code that gracefully handles potential errors. Remember to leverage the details provided by the exception objects themselves for better debugging and error handling.

Leave a Reply

Your email address will not be published. Required fields are marked *