Files
ss-tools/superset_tool/exceptions.py
Volobuev Andrey 2b35038f73 refactor 1st stage
2025-06-27 17:05:33 +03:00

112 lines
6.9 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 (опционально).
"""
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}
)