# [MODULE] Иерархия исключений # @contract: Все ошибки наследуют `SupersetToolError` для единой точки обработки. # @semantic: Каждый тип исключения соответствует конкретной проблемной области в инструменте Superset. # @coherence: # - Полное покрытие всех сценариев ошибок клиента и утилит. # - Четкая классификация по уровню серьезности (от общей до специфичной). # - Дополнительный `context` для каждой ошибки, помогающий в диагностике. # [IMPORTS] Standard library from pathlib import Path # [IMPORTS] Typing from typing import Optional, Dict, Any,Union class SupersetToolError(Exception): """[BASE] Базовый класс для всех ошибок инструмента Superset. @semantic: Обеспечивает стандартизированный формат сообщений об ошибках с контекстом. @invariant: - `message` всегда присутствует. - `context` всегда является словарем, даже если пустой. """ def __init__(self, message: str, context: Optional[Dict[str, Any]] = None): # [PRECONDITION] Проверка типа контекста if not isinstance(context, (dict, type(None))): # [COHERENCE_CHECK_FAILED] Ошибка в передаче контекста raise TypeError("Контекст ошибки должен быть словарем или None") self.context = context or {} super().__init__(f"{message} | Context: {self.context}") # [POSTCONDITION] Логирование создания ошибки # Можно добавить здесь логирование, но обычно ошибки логируются в месте их перехвата/подъема, # чтобы избежать дублирования и получить полный стек вызовов. # [ERROR-GROUP] Проблемы аутентификации и авторизации class AuthenticationError(SupersetToolError): """[AUTH] Ошибки аутентификации (неверные учетные данные) или авторизации (проблемы с сессией). @context: url, username, error_detail (опционально). """ def __init__(self, message: str = "Authentication failed", **context: Any): super().__init__( f"[AUTH_FAILURE] {message}", {"type": "authentication", **context} ) class PermissionDeniedError(AuthenticationError): """[AUTH] Ошибка отказа в доступе из-за недостаточных прав пользователя. @semantic: Указывает на то, что операция не разрешена. @context: required_permission (опционально), user_roles (опционально), endpoint (опционально). @invariant: Наследует от `AuthenticationError`, так как это разновидность проблемы доступа. """ def __init__(self, message: str = "Permission denied", required_permission: Optional[str] = None, **context: Any): full_message = f"Permission denied: {required_permission}" if required_permission else message super().__init__( full_message, {"type": "authorization", "required_permission": required_permission, **context} ) # [ERROR-GROUP] Проблемы API-вызовов class SupersetAPIError(SupersetToolError): """[API] Общие ошибки взаимодействия с Superset API. @semantic: Для ошибок, возвращаемых Superset API, или проблем с парсингом ответа. @context: endpoint, method, status_code, response_body (опционально), error_message (из API). """ def __init__(self, message: str = "Superset API error", **context: Any): super().__init__( f"[API_FAILURE] {message}", {"type": "api_call", **context} ) # [ERROR-SUBCLASS] Детализированные ошибки API class ExportError(SupersetAPIError): """[API:EXPORT] Проблемы, специфичные для операций экспорта дашбордов. @semantic: Может быть вызвано невалидным форматом ответа, ошибками Superset при экспорте. @context: dashboard_id (опционально), details (опционально). """ def __init__(self, message: str = "Dashboard export failed", **context: Any): super().__init__(f"[EXPORT_FAILURE] {message}", {"subtype": "export", **context}) class DashboardNotFoundError(SupersetAPIError): """[API:404] Запрошенный дашборд или ресурс не существует. @semantic: Соответствует HTTP 404 Not Found. @context: dashboard_id_or_slug, url. """ def __init__(self, dashboard_id_or_slug: Union[int, str], message: str = "Dashboard not found", **context: Any): super().__init__( f"[NOT_FOUND] Dashboard '{dashboard_id_or_slug}' {message}", {"subtype": "not_found", "resource_id": dashboard_id_or_slug, **context} ) # [ERROR-SUBCLASS] Детализированные ошибки обработки файлов class InvalidZipFormatError(SupersetToolError): """[FILE:ZIP] Некорректный формат ZIP-архива или содержимого для импорта/экспорта. @semantic: Указывает на проблемы с целостностью или структурой ZIP-файла. @context: file_path, expected_content (например, metadata.yaml), error_detail. """ def __init__(self, message: str = "Invalid ZIP format or content", file_path: Optional[Union[str, Path]] = None, **context: Any): super().__init__( f"[FILE_ERROR] {message}", {"type": "file_validation", "file_path": str(file_path) if file_path else "N/A", **context} ) # [ERROR-GROUP] Системные и network-ошибки class NetworkError(SupersetToolError): """[NETWORK] Проблемы соединения, таймауты, DNS-ошибки и т.п. @semantic: Ошибки, связанные с невозможностью установить или поддерживать сетевое соединение. @context: url, original_exception (опционально), timeout (опционально). """ def __init__(self, message: str = "Network connection failed", **context: Any): super().__init__( f"[NETWORK_FAILURE] {message}", {"type": "network", **context} )