Как создаются исключения для работы с внешними системами, например, с базами данных или веб-сервисами?

При работе с внешними системами, такими как базы данных или веб-сервисы, обычно создают кастомные исключения для более точной обработки ошибок. Это делается путем наследования от базового класса Exception или его подклассов (например, IOError, ValueError).
Например:

  class DatabaseConnectionError(Exception):
      """Исключение при ошибке подключения к БД."""
      pass

  class APIRequestError(Exception):
      """Исключение при ошибке запроса к API."""
      def __init__(self, status_code, message="API request failed"):
          self.status_code = status_code
          self.message = message
          super().__init__(self.message)

  try:
      # Код, работающий с внешней системой
      raise DatabaseConnectionError("Не удалось подключиться к базе данных.")
  except DatabaseConnectionError as e:
      print(f"Ошибка базы данных: {e}")

  try:
      # код, вызывающий API
      raise APIRequestError(status_code=500, message="Internal Server Error")
  except APIRequestError as e:
      print(f"API Error: Status Code - {e.status_code}, Message - {e.message}")
  
Использование таких исключений позволяет:
  • Различать ошибки внешней системы от других типов ошибок.
  • Обрабатывать исключения более конкретно (например, retry при временной ошибке соединения).
  • Предоставлять дополнительную информацию об ошибке (например, код ошибки API).

При работе с внешними системами, такими как базы данных или веб-сервисы, важно создавать собственные исключения для более точной обработки ошибок и предоставления полезной информации разработчикам. Вот как обычно создаются и используются исключения для таких случаев:

  1. Определение базового класса исключения: Обычно начинают с создания базового класса исключения, специфичного для вашего модуля или интеграции с внешней системой. Этот класс наследуется от встроенного класса Exception или от его подкласса, например IOError или ValueError. Этот базовый класс служит корнем для всех остальных исключений, связанных с этой внешней системой.
    
    class ExternalServiceError(Exception):
        """Базовое исключение для всех ошибок, связанных с внешней системой."""
        pass
          
  2. Создание специализированных исключений: Затем создаются более конкретные исключения, которые наследуются от базового класса ExternalServiceError. Эти исключения представляют различные типы ошибок, которые могут возникнуть при взаимодействии с внешней системой (например, ошибка подключения, ошибка авторизации, таймаут запроса, ошибка в данных и т.д.).
    
    class DatabaseConnectionError(ExternalServiceError):
        """Ошибка подключения к базе данных."""
        pass
    
    class AuthenticationError(ExternalServiceError):
        """Ошибка аутентификации при обращении к веб-сервису."""
        pass
    
    class RequestTimeoutError(ExternalServiceError):
        """Превышено время ожидания ответа от веб-сервиса."""
        pass
    
    class InvalidDataError(ExternalServiceError):
        """Некорректные данные, полученные от внешней системы."""
        def __init__(self, message, data=None):
            super().__init__(message)
            self.data = data
          

    Обратите внимание на пример InvalidDataError. Этот класс принимает дополнительные аргументы, такие как data, которые могут быть полезны для отладки и понимания причины ошибки.

  3. Использование исключений: В коде, который взаимодействует с внешней системой, необходимо обрабатывать потенциальные ошибки и возбуждать соответствующие исключения.
    
    import requests
    
    def get_data_from_api(url):
        try:
            response = requests.get(url, timeout=5)
            response.raise_for_status()  # Raises HTTPError for bad responses (4xx, 5xx)
            return response.json()
        except requests.exceptions.ConnectionError as e:
            raise DatabaseConnectionError(f"Ошибка подключения к API: {e}")
        except requests.exceptions.Timeout:
            raise RequestTimeoutError("Превышено время ожидания ответа от API.")
        except requests.exceptions.HTTPError as e:
            raise AuthenticationError(f"Ошибка аутентификации или доступа к API: {e}")
        except ValueError as e:
            raise InvalidDataError(f"Некорректный формат данных от API: {e}")
        except Exception as e:
            raise ExternalServiceError(f"Неизвестная ошибка при работе с API: {e}")
          

    В этом примере используется библиотека requests для взаимодействия с веб-сервисом. В блоке try...except обрабатываются различные типы ошибок, которые могут возникнуть при выполнении запроса. Если возникает ошибка, возбуждается соответствующее пользовательское исключение с более информативным сообщением. Исключения requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.HTTPError и ValueError перехватываются и преобразуются в более конкретные исключения, специфичные для нашего приложения.

    Важно! Всегда добавляйте блок except Exception as e: в конце, чтобы перехватить любые непредвиденные исключения и преобразовать их в базовое исключение ExternalServiceError. Это позволит избежать падения программы из-за необработанных ошибок.

  4. Обработка исключений на верхнем уровне: На верхнем уровне приложения, где вызываются функции, взаимодействующие с внешней системой, необходимо обрабатывать пользовательские исключения и принимать соответствующие меры, например, повторить попытку, записать ошибку в лог или отобразить сообщение об ошибке пользователю.
    
    try:
        data = get_data_from_api("https://example.com/api/data")
        print(data)
    except AuthenticationError as e:
        print(f"Ошибка аутентификации: {e}")
    except RequestTimeoutError as e:
        print(f"Превышено время ожидания: {e}")
    except ExternalServiceError as e:
        print(f"Произошла ошибка при работе с внешним сервисом: {e}")
          

Преимущества использования пользовательских исключений:

  • Улучшенная читаемость кода: Исключения с понятными именами делают код более читаемым и понятным.
  • Более точная обработка ошибок: Позволяют обрабатывать различные типы ошибок по-разному, обеспечивая более гибкую и эффективную обработку.
  • Упрощенная отладка: Предоставляют более информативные сообщения об ошибках, что упрощает отладку и поиск неисправностей.
  • Абстракция: Скрывают детали реализации взаимодействия с внешней системой от вызывающего кода, что делает код более модульным и переносимым.

Дополнительные рекомендации:

  • Включайте в сообщения об ошибках достаточно информации для отладки, например, URL, параметры запроса, код ответа и т.д.
  • Используйте логирование для записи информации об ошибках, чтобы можно было анализировать их в дальнейшем.
  • Рассмотрите возможность использования библиотек, которые предоставляют готовые исключения для работы с конкретными внешними системами (например, psycopg2 для PostgreSQL, pymongo для MongoDB).
0