Ошибки
Любой сбой выбрасывает исключение. Два класса и идентификатор запроса в каждой ошибке API.
Как его поймать
from openemail import OpenEmailApiError, OpenEmailNetworkError, openemail try: openemail.emails.send({'from': sender, 'to': recipient, 'subject': subject, 'text': text})except OpenEmailApiError as error: if error.is_validation: print(error.code, error.param, error.message) if error.is_permission: print(openemail.addresses.list()) if error.is_rate_limited: print('try again in', error.retry_after_seconds, 'seconds') print(error.status, error.request_id) raiseexcept OpenEmailNetworkError as error: if error.is_timeout: print('no answer in time') raisepermission_error при отправке обычно касается области отправки самого КЛЮЧА (домена или адреса, которые ему не выданы), а не рабочего пространства, поэтому пример печатает то, что addresses.list() сообщает о том, от чьего имени этот ключ может отправлять.
Классы
| Класс | Когда |
|---|---|
| OpenEmailApiError | API ответил, и ответ не был успешным. Несёт message, status, type, code, param, doc_url, request_id, retry_after_seconds, fields и body. |
| OpenEmailNetworkError | Ответ не пришёл: DNS, TLS, оборванное соединение или таймаут. Несёт cause, исходное исключение httpx, а is_timeout равно True, когда причиной был таймаут. |
| OpenEmailError | Базовый класс для обоих, так что один except ловит любой сбой, вызванный API или сетью. WebhookVerificationError, который выбрасывает verify_webhook_signature, тоже наследует от него. |
| ValueError | Выбрасывается до того, как что-либо отправлено: отсутствующий или некорректный ключ, непригодный base_url, учётные данные, которые ушли бы по обычному http, пустой идентификатор. Неподходящий http_client или тело, которое JSON не может передать, вместо этого выбрасывают TypeError. |
fields перечисляет каждый ответ, который отклонила форма подписки, в виде key и error и равно None при любой другой ошибке. body хранит JSON, который прислал API, или None, если тело не было JSON.
| Свойство | True, когда |
|---|---|
| is_auth | type равен authentication_error, 401: ключа нет, учётные данные не того типа или ключ, который выдавали не мы. |
| is_permission | permission_error, 403: настоящий ключ без нужной области или без нужного адреса From. |
| is_scope_missing | code равен insufficient_scope: это тот самый 403, который называет недостающую область. |
| is_invalid_request | invalid_request_error, 400: запрос, который не удалось разобрать. Письмо сверх предельного размера возвращается как 422 message_too_large, поэтому ловит его свойство is_validation. |
| is_validation | validation_error, 422: схема отклонила запрос, а param называет поле. |
| is_not_found | not_found_error, 404: такого ресурса нет. |
| is_conflict | conflict_error, 409: ресурс уже прошёл ту точку, в которой с ним можно было это сделать. |
| is_rate_limited | rate_limit_error, 429. retry_after_seconds содержит время ожидания, если сервер его назвал. |
| is_server_error | status равен 500 или выше. При обращении в поддержку укажите request_id. |
| is_retryable | status равен 408, 429, 500, 502, 503 или 504. |
| is_step_up_required | code равен step_up_required: это 403, который токен доступа OAuth получает перед чувствительным изменением, пока человек не подтвердит код. |
Большинство свойств читают type, замороженную половину конверта. code остаётся str, потому что API гарантирует, что набор кодов открыт и только пополняется, так что незнакомый код трактуйте по его type. Закрытый Literal сделал бы обновление SDK платой за чтение нового вида отказа.
Тело, которое не является конвертом ошибки API, всё равно становится OpenEmailApiError: type выводится из статуса, а code устанавливается в unrecognised_response. Успешный ответ, тело которого не JSON, тоже приводит к такому исключению.
Отмена вызова AsyncOpenEmail не выбрасывает OpenEmailError. Распространяется сама отмена, произошла ли она во время запроса или во время ожидания перед повтором, и после неё ничего не повторяется.
request_id
Каждый OpenEmailApiError несёт идентификатор запроса, присланный сервером, из тела ошибки или из заголовка x-request-id, и это единственное, что связывает ваш сбой со строкой в логе сервера. Успешный вызов возвращает только разобранное тело, поэтому идентификатора запроса у него нет.
str(error) заканчивается статусом, кодом и идентификатором запроса, так что строка лога, которая печатает исключение, сохраняет все три. OpenEmailApiError к тому же переживает сериализацию через pickle со всеми полями, так что исключение, выброшенное в рабочем процессе, доходит до родительского процесса в целости.