Python · API · Просунутий рівень
Обробка помилок
Надійно обробляйте HTTP-помилки, мережеві збої та таймаути в API-скриптах.
Короткий вступ до теми та пояснення перед вправами (вправи нижче):
Практичні паттерни обробки помилок
#Довідник обробки помилок
#Вправи:
Використання raise_for_status()
#Зробіть GET до /api/products/99999/ (неіснуючий продукт). Без raise_for_status() виведіть код статусу. Потім додайте raise_for_status() і обробіть виняток HTTPError, вивівши повідомлення про помилку.
import requests
BASE_URL = 'https://apilearn.tukas.dev'
# Частина 1: без raise_for_status — перевірте код статусу
response = requests.get(f'{BASE_URL}/api/products/99999/')
print(response.status_code)
# Частина 2: з raise_for_status і обробкою HTTPError
Рішення
import requests
BASE_URL = 'https://apilearn.tukas.dev'
# Частина 1: без raise_for_status
response = requests.get(f'{BASE_URL}/api/products/99999/')
print(response.status_code) # 404
# Частина 2: з raise_for_status
try:
response = requests.get(f'{BASE_URL}/api/products/99999/')
response.raise_for_status()
except requests.exceptions.HTTPError as e:
print(f'HTTP помилка: {e.response.status_code}')
Обробка помилки авторизації
#Спробуйте отримати /api/users/profile/ БЕЗ заголовка авторизації. Обробіть HTTPError і виведіть спеціальне повідомлення, якщо статус 401.
import requests
BASE_URL = 'https://apilearn.tukas.dev'
# GET профілю без авторизації — обробіть 401 окремо
try:
response = requests.get(f'{BASE_URL}/api/users/profile/')
response.raise_for_status()
except requests.exceptions.HTTPError as e:
# Виведіть різні повідомлення для 401 і інших кодів
pass
Рішення
import requests
BASE_URL = 'https://apilearn.tukas.dev'
try:
response = requests.get(f'{BASE_URL}/api/users/profile/')
response.raise_for_status()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
print('Помилка авторизації — надайте дійсний токен')
else:
print(f'HTTP помилка: {e.response.status_code}')
Додати timeout до запиту
#Зробіть GET до /api/products/ із timeout=5. Обробіть Timeout і ConnectionError окремо. Виведіть список продуктів при успіху або повідомлення про помилку.
import requests BASE_URL = 'https://apilearn.tukas.dev' # GET з timeout і обробкою мережевих помилок
Рішення
import requests
BASE_URL = 'https://apilearn.tukas.dev'
try:
response = requests.get(f'{BASE_URL}/api/products/', timeout=5)
response.raise_for_status()
products = response.json()['results']
print(f'Отримано {len(products)} продуктів')
except requests.exceptions.Timeout:
print('Помилка: запит перевищив час очікування')
except requests.exceptions.ConnectionError:
print('Помилка: не вдалося підключитися до API')
except requests.exceptions.HTTPError as e:
print(f'HTTP помилка: {e.response.status_code}')
Функція safe_get()
#Напишіть функцію safe_get(url, **kwargs), яка робить GET з timeout=5, викликає raise_for_status() і повертає JSON при успіху або None при будь-якій помилці (виводячи повідомлення). Протестуйте з валідним і неіснуючим URL.
import requests
BASE_URL = 'https://apilearn.tukas.dev'
def safe_get(url, **kwargs):
# Реалізуйте з timeout, raise_for_status і обробкою всіх помилок
pass
# Тест 1: валідний URL
result = safe_get(f'{BASE_URL}/api/products/')
if result:
print(f'Продуктів: {len(result["results"])}')
# Тест 2: неіснуючий ендпоінт
result = safe_get(f'{BASE_URL}/api/products/99999/')
print(result)
Рішення
import requests
BASE_URL = 'https://apilearn.tukas.dev'
def safe_get(url, **kwargs):
try:
response = requests.get(url, timeout=5, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print(f'Помилка: timeout при запиті до {url}')
except requests.exceptions.ConnectionError:
print(f'Помилка: немає з'єднання з {url}')
except requests.exceptions.HTTPError as e:
print(f'HTTP {e.response.status_code} для {url}')
return None
result = safe_get(f'{BASE_URL}/api/products/')
if result:
print(f'Продуктів: {len(result["results"])}')
result = safe_get(f'{BASE_URL}/api/products/99999/')
print(result)
Retry з backoff
#Напишіть функцію retry_get(url, retries=3), яка повторює запит при кодах 429 або 503, очікуючи 2**attempt секунд між спробами. При інших помилках — одразу кидати виняток. Виведіть повідомлення перед кожним очікуванням.
import requests
import time
BASE_URL = 'https://apilearn.tukas.dev'
def retry_get(url, retries=3, **kwargs):
# Повторюйте при 429/503, exponential backoff, кидайте при інших помилках
pass
# Тест із робочим ендпоінтом (не активує retry)
result = retry_get(f'{BASE_URL}/api/products/')
print(result['count'])
Рішення
import requests
import time
BASE_URL = 'https://apilearn.tukas.dev'
def retry_get(url, retries=3, **kwargs):
for attempt in range(retries):
try:
response = requests.get(url, timeout=5, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code in (429, 503) and attempt < retries - 1:
wait = 2 ** attempt
print(f'Спроба {attempt + 1} невдала ({e.response.status_code}), чекаємо {wait}s...')
time.sleep(wait)
else:
raise
return None
result = retry_get(f'{BASE_URL}/api/products/')
print(result['count'])