Обработка ошибок

CDEK Python SDK предоставляет несколько типов исключений для обработки различных ошибок, которые могут возникнуть при работе с API.

Типы исключений

CdekException

Базовый класс для всех исключений CDEK SDK.

from cdek.exceptions import CdekException

try:
    # Ваш код
    pass
except CdekException as e:
    print(f"Ошибка CDEK: {e}")

CdekAuthException

Исключение, возникающее при ошибках авторизации.

from cdek.exceptions import CdekAuthException

try:
    client = CdekClient("неверный_логин", "неверный_пароль")
    # Любой запрос вызовет ошибку авторизации
    client.tariff.all()
except CdekAuthException as e:
    print(f"Ошибка авторизации: {e}")
    # Вывод: Ошибка авторизации: Аутентификация не удалась,
    # пожалуйста, проверьте переданные логин и пароль

Причины возникновения:

  • Неверные учетные данные (логин/пароль)

  • Истек срок действия токена

  • Проблемы с сетью при авторизации

CdekRequestException

Исключение, возникающее при ошибках выполнения запросов к API.

from cdek.exceptions import CdekRequestException

try:
    # Попытка получить несуществующий заказ
    client.order.get_by_uuid("00000000-0000-0000-0000-000000000000")
except CdekRequestException as e:
    print(f"Ошибка запроса: {e}")
    print(f"Код ответа HTTP: {e.status_code}")
    print(f"Ответ API: {e.response}")

Свойства исключения:

  • status_code - HTTP код ответа

  • response - полный ответ API в виде словаря

Причины возникновения:

  • Неверные параметры запроса

  • Сущность не найдена (404)

  • Некорректные данные в запросе

  • Ошибки валидации API

  • Проблемы с сетью

Примеры обработки ошибок

Обработка ошибок при создании заказа

from cdek.exceptions import CdekRequestException

try:
    order = OrderRequest(
        number="ORDER-001",
        tariff_code=999,  # Несуществующий тариф
        # ... остальные параметры
    )
    response = client.order.create(order)
except CdekRequestException as e:
    if e.status_code == 400:
        print("Ошибка валидации данных заказа")
        if e.response and "errors" in e.response:
            for error in e.response["errors"]:
                print(f"Код ошибки: {error.get('code')}")
                print(f"Сообщение: {error.get('message')}")
    elif e.status_code == 404:
        print("Ресурс не найден")
    else:
        print(f"Неожиданная ошибка: {e}")

Обработка ошибок при получении заказа

def get_order_safe(uuid: str):
    """Безопасное получение заказа с обработкой ошибок"""
    try:
        return client.order.get_by_uuid(uuid)
    except CdekRequestException as e:
        if e.status_code == 404:
            print(f"Заказ {uuid} не найден")
            return None
        elif e.status_code == 403:
            print(f"Нет доступа к заказу {uuid}")
            return None
        else:
            print(f"Ошибка при получении заказа: {e}")
            raise
    except CdekAuthException as e:
        print(f"Ошибка авторизации: {e}")
        raise
    except Exception as e:
        print(f"Неожиданная ошибка: {e}")
        raise

# Использование
order = get_order_safe("some-uuid")
if order:
    print(f"Заказ найден: {order.entity.uuid}")

Обработка сетевых ошибок

import time
from cdek.exceptions import CdekRequestException

def request_with_retry(func, max_retries=3, delay=1):
    """Выполнение запроса с повторными попытками"""
    for attempt in range(max_retries):
        try:
            return func()
        except CdekRequestException as e:
            # Проверяем, является ли это сетевой ошибкой
            if "Ошибка сети" in str(e) and attempt < max_retries - 1:
                wait_time = delay * (2 ** attempt)  # Экспоненциальная задержка
                print(f"Сетевая ошибка, повтор через {wait_time} сек...")
                time.sleep(wait_time)
            else:
                raise
    return None

# Использование
def get_order():
    return client.order.get_by_uuid("some-uuid")

order = request_with_retry(get_order, max_retries=3)

Коды ошибок API

Библиотека автоматически переводит коды ошибок API на русский язык. Все доступные коды ошибок находятся в constants.ERRORS:

from cdek import constants

# Просмотр всех кодов ошибок
for code, message in constants.ERRORS.items():
    print(f"{code}: {message}")

Основные коды ошибок:

  • v2_entity_not_found - Сущность не найдена

  • v2_bad_request - Некорректный запрос

  • v2_field_is_empty - Не передано обязательное поле

  • v2_invalid_format - Некорректное значение

  • v2_order_not_found - Заказ не найден

  • v2_order_forbidden - Заказ принадлежит другому клиенту

Логирование ошибок

Настройка логирования

import logging

# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger(__name__)

try:
    order = client.order.create(order_request)
    logger.info(f"Заказ создан: {order.entity.uuid}")
except CdekRequestException as e:
    logger.error(f"Ошибка создания заказа: {e}")
    logger.error(f"Код ответа: {e.status_code}")
    logger.error(f"Ответ API: {e.response}")
except CdekAuthException as e:
    logger.error(f"Ошибка авторизации: {e}")
except Exception as e:
    logger.exception("Неожиданная ошибка", exc_info=True)

Валидация данных перед отправкой

Проверка обязательных полей

def validate_order(order: OrderRequest) -> list[str]:
    """Валидация заказа перед отправкой"""
    errors = []

    if not order.number:
        errors.append("Не указан номер заказа")

    if not order.tariff_code:
        errors.append("Не указан код тарифа")

    if not order.from_location:
        errors.append("Не указан адрес отправителя")

    if not order.to_location:
        errors.append("Не указан адрес получателя")

    if not order.packages or len(order.packages) == 0:
        errors.append("Не указаны упаковки")

    return errors

# Использование
order = OrderRequest(...)
errors = validate_order(order)

if errors:
    print("Ошибки валидации:")
    for error in errors:
        print(f"- {error}")
else:
    try:
        response = client.order.create(order)
        print(f"Заказ создан: {response.entity.uuid}")
    except CdekRequestException as e:
        print(f"Ошибка API: {e}")

Обработка частичных ошибок

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

def create_orders_batch(orders: list[OrderRequest]) -> dict:
    """Создание нескольких заказов с обработкой ошибок"""
    results = {
        "success": [],
        "failed": []
    }

    for order in orders:
        try:
            response = client.order.create(order)
            results["success"].append({
                "im_number": order.number,
                "cdek_uuid": response.entity.uuid
            })
        except CdekRequestException as e:
            results["failed"].append({
                "im_number": order.number,
                "error": str(e),
                "status_code": e.status_code,
                "response": e.response
            })
        except Exception as e:
            results["failed"].append({
                "im_number": order.number,
                "error": f"Неожиданная ошибка: {e}"
            })

    return results

# Использование
orders = [order1, order2, order3]
results = create_orders_batch(orders)

print(f"Успешно создано: {len(results['success'])}")
print(f"Ошибок: {len(results['failed'])}")

for failed in results["failed"]:
    print(f"Заказ {failed['im_number']}: {failed['error']}")