Обработка исключений¶
SchoolMosPy предоставляет иерархию исключений для обработки различных типов ошибок при работе с API.
Иерархия исключений¶
Все исключения находятся в модуле schoolmospy.utils.exceptions.
APIError¶
Базовое исключение для всех ошибок API.
Определение¶
class APIError(Exception):
def __init__(
self,
message: str,
status_code: int | None = None,
response: Any | None = None,
) -> None:
...
Атрибуты¶
| Атрибут | Тип | Описание |
|---|---|---|
message |
str |
Сообщение об ошибке |
status_code |
int \| None |
HTTP статус код |
response |
Any \| None |
Тело ответа от сервера |
Использование¶
from schoolmospy.utils.exceptions import APIError
try:
marks = await client.marks.get(from_date, to_date)
except APIError as e:
print(f"Ошибка API: {e}")
print(f"Статус код: {e.status_code}")
print(f"Ответ: {e.response}")
AuthError¶
Исключение для ошибок аутентификации (HTTP 401).
Когда возникает¶
- Неверный или истекший токен
- Отсутствие прав доступа
- Невалидный profile_id
Использование¶
from schoolmospy.utils.exceptions import AuthError
try:
profile = await client.get_me()
except AuthError:
print("Ошибка аутентификации. Проверьте токен и profile_id.")
# Здесь можно реализовать логику обновления токена
Пример обработки¶
async def safe_request_with_retry():
max_retries = 3
for attempt in range(max_retries):
try:
return await client.get_me()
except AuthError:
if attempt == max_retries - 1:
print("Не удалось аутентифицироваться после нескольких попыток")
raise
print(f"Попытка {attempt + 1} не удалась, повторяем...")
# Здесь можно обновить токен
await asyncio.sleep(1)
NotFoundError¶
Исключение для ошибок "не найдено" (HTTP 404).
Когда возникает¶
- Запрашиваемый ресурс не существует
- Неверный ID или GUID
- Ресурс был удален
Использование¶
from schoolmospy.utils.exceptions import NotFoundError
try:
events = await client.events.get(
from_date, to_date, "invalid-guid"
)
except NotFoundError:
print("Ресурс не найден. Проверьте корректность GUID.")
ServerError¶
Исключение для ошибок сервера (HTTP 5xx).
Когда возникает¶
- Внутренняя ошибка сервера (500)
- Сервис недоступен (503)
- Тайм-аут шлюза (504)
Использование¶
from schoolmospy.utils.exceptions import ServerError
import asyncio
async def request_with_retry():
max_retries = 3
retry_delay = 2
for attempt in range(max_retries):
try:
return await client.marks.get(from_date, to_date)
except ServerError as e:
if attempt == max_retries - 1:
print("Сервер недоступен после нескольких попыток")
raise
print(f"Ошибка сервера: {e}. Повтор через {retry_delay}с...")
await asyncio.sleep(retry_delay)
retry_delay *= 2 # Экспоненциальная задержка
HTTPError¶
Исключение для неожиданных HTTP ошибок.
Когда возникает¶
- Любой HTTP статус код, не покрытый другими исключениями
- Неожиданные ошибки клиента (4xx, кроме 401 и 404)
Использование¶
from schoolmospy.utils.exceptions import HTTPError
try:
homeworks = await client.homeworks.get(from_date, to_date)
except HTTPError as e:
print(f"Неожиданная HTTP ошибка: {e}")
print(f"Статус: {e.status_code}")
Комплексная обработка ошибок¶
Обработка всех типов ошибок¶
from schoolmospy.utils.exceptions import (
AuthError,
NotFoundError,
ServerError,
HTTPError,
APIError
)
async def safe_api_call():
try:
marks = await client.marks.get(from_date, to_date)
return marks
except AuthError:
print("❌ Ошибка аутентификации")
print("Проверьте токен и profile_id")
except NotFoundError:
print("❌ Ресурс не найден")
print("Проверьте корректность запроса")
except ServerError as e:
print(f"❌ Ошибка сервера: {e.status_code}")
print("Попробуйте позже")
except HTTPError as e:
print(f"❌ HTTP ошибка: {e.status_code}")
print(f"Детали: {e.response}")
except APIError as e:
print(f"❌ Общая ошибка API: {e}")
return None
Обработка с логированием¶
import logging
logger = logging.getLogger(__name__)
async def request_with_logging():
try:
return await client.get_me()
except AuthError as e:
logger.error(f"Auth failed: {e}")
raise
except ServerError as e:
logger.warning(f"Server error: {e.status_code}")
raise
except APIError as e:
logger.error(f"API error: {e}")
raise
Retry-декоратор¶
import asyncio
from functools import wraps
from schoolmospy.utils.exceptions import ServerError, APIError
def retry_on_error(max_retries=3, delay=1):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_retries):
try:
return await func(*args, **kwargs)
except (ServerError, APIError) as e:
last_exception = e
if attempt < max_retries - 1:
await asyncio.sleep(delay * (attempt + 1))
continue
raise last_exception
return wrapper
return decorator
@retry_on_error(max_retries=3, delay=2)
async def get_marks_safe():
return await client.marks.get(from_date, to_date)
Контекстный менеджер для обработки ошибок¶
from contextlib import asynccontextmanager
@asynccontextmanager
async def handle_api_errors():
try:
yield
except AuthError:
print("Требуется повторная аутентификация")
except NotFoundError:
print("Ресурс не найден")
except ServerError:
print("Сервер временно недоступен")
except APIError as e:
print(f"Ошибка API: {e}")
# Использование
async with handle_api_errors():
marks = await client.marks.get(from_date, to_date)
Лучшие практики¶
1. Специфичная обработка¶
Обрабатывайте специфичные исключения перед общими:
try:
result = await client.get_me()
except AuthError:
# Специфичная обработка
handle_auth_error()
except APIError:
# Общая обработка
handle_generic_error()
2. Логирование ошибок¶
Всегда логируйте ошибки для отладки:
import logging
try:
marks = await client.marks.get(from_date, to_date)
except APIError as e:
logging.error(f"Failed to get marks: {e}", exc_info=True)
raise
3. Graceful degradation¶
Предоставляйте fallback при ошибках:
async def get_marks_or_empty():
try:
return await client.marks.get(from_date, to_date)
except APIError:
logging.warning("Failed to get marks, returning empty list")
return []
4. Timeout handling¶
Используйте таймауты для избежания зависания:
import asyncio
try:
marks = await asyncio.wait_for(
client.marks.get(from_date, to_date),
timeout=10.0
)
except asyncio.TimeoutError:
print("Запрос превысил таймаут")
except APIError as e:
print(f"Ошибка API: {e}")
См. также¶
- StudentClient - Основной клиент
- Quick Start - Руководство по началу работы