Files
ss-tools/superset_tool/exceptions.py
2025-07-11 16:29:20 +03:00

154 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [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 (опционально).
"""
# [CONTRACT]
# Description: Исключение, возникающее при ошибках аутентификации в Superset API.
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).
"""
# [CONTRACT]
# Description: Исключение, возникающее при получении ошибки от Superset API (статус код >= 400).
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.
"""
# [CONTRACT]
# Description: Исключение, специфичное для случая, когда дашборд не найден (статус 404).
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}
)
class DatasetNotFoundError(SupersetAPIError):
"""[API:404] Запрашиваемый набор данных не существует.
@semantic: Соответствует HTTP 404 Not Found.
@context: dataset_id_or_slug, url.
"""
# [CONTRACT]
# Description: Исключение, специфичное для случая, когда набор данных не найден (статус 404).
def __init__(self, dataset_id_or_slug: Union[int, str], message: str = "Dataset not found", **context: Any):
super().__init__(
f"[NOT_FOUND] Dataset '{dataset_id_or_slug}' {message}",
{"subtype": "not_found", "resource_id": dataset_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 (опционально).
"""
# [CONTRACT]
# Description: Исключение, возникающее при сетевых ошибках во время взаимодействия с Superset API.
def __init__(self, message: str = "Network connection failed", **context: Any):
super().__init__(
f"[NETWORK_FAILURE] {message}",
{"type": "network", **context}
)
class FileOperationError(SupersetToolError):
"""
# [CONTRACT]
# Description: Исключение, возникающее при ошибках файловых операций (чтение, запись, архивирование).
"""
pass
class InvalidFileStructureError(FileOperationError):
"""
# [CONTRACT]
# Description: Исключение, возникающее при обнаружении некорректной структуры файлов/директорий.
"""
pass
class ConfigurationError(SupersetToolError):
"""
# [CONTRACT]
# Description: Исключение, возникающее при ошибках в конфигурации инструмента.
"""
pass