Decorators, validators, logging and debugging helpers for async HTTP requests
Project description
request-decorator-handlers
Набор декораторов и утилит для асинхронных HTTP-запросов. Библиотека помогает логировать запросы, повторять их при ошибках, валидировать ответы и извлекать нужные данные, не усложняя приложение бизнес-логикой.
- 🧪 Валидаторы с наглядным журналом ошибок (
ValidatorиResponseHandler) - 📦 Парсеры (
Parser.JSON,Parser.HTML, JSONPath/HTML helpers) - 🪵 Цветное логирование (
RequestLogger) с управлением черезloguru - 🧾 Отладчик (
RequestDebugger) — коллекция невалидных ответов и уникальных JSON - 🔁 Повторы запросов (
ResponseHandler.retry)
Установка
pip install request-decorator-handlers
Основные импорты
from request_decorator_handlers import (
RequestLogger, RequestDebugger,
ResponseHandler, Validator, ParserUtil,
)
Быстрый старт
import asyncio
from request_decorator_handlers import (
RequestLogger, RequestDebugger,
ResponseHandler, Validator, ParserUtil,
)
from curl_cffi import AsyncSession
# @RequestDebugger.debug(save_errors=True)
@ResponseHandler.retry(error_key="STATUS_CODE", quantity=3, interval=2.0)
@RequestLogger.log(action="GET_IP", log_level="success_error", show_body=True)
@ResponseHandler.handlers(
Validator.status([312]),
Validator.content_type("json"),
Validator.JSON("$.query", exists=True, error_key="QUERY_EXISTS"),
)
async def fetch_ip(client: AsyncSession):
return await client.get("http://ip-api.com/json")
async def main():
async with AsyncSession() as client:
result = await fetch_ip(client)
print(result.valid.ERRORS, result.valid.PARSED)
asyncio.run(main())
Требования к функциям
-
Декораторы ожидают асинхронные функции, возвращающие HTTP-ответ (любой тип с полями
status_code,headers,text/contentи т.п.) — например,curl_cffi.AsyncResponse,httpx.Response. -
Декораторы
ResponseHandler.handlers(...)иResponseHandler.retry(...)оборачивают результат вWithValid[Response]. Если нужна строгая типизация:from typing import Annotated from request_decorator_handlers import WithValid @ResponseHandler.handlers(...) async def fetch_user(...) -> WithValid[Response]: ...
-
Для интеграции со сторонними клиентов необходимо:
- использовать их сессию/класс ответа;
- возвращать исходный объект ответа (его обёртка
WithValidсоздаётся сама).
Быстрый пример
import asyncio
from curl_cffi import AsyncSession
from request_decorator_handlers import (
RequestLogger, RequestDebugger,
ResponseHandler, Validator,
)
# Настраиваем loguru, потом включаем вывод библиотеки
from loguru import logger
logger.add(sys.stderr, format="{time} {level} {message}", colorize=True)
RequestLogger.enable()
@RequestDebugger.debug(save_errors=True)
@RequestLogger.log(action="GET_IP", log_level="success_error", show_body=True)
@ResponseHandler.handlers(
Validator.status([200]),
Validator.content_type("json"),
Validator.JSON("$.query", exists=True, error_key="QUERY_EXISTS"),
)
@ResponseHandler.retry(error_key="STATUS_CODE", quantity=3, interval=2.0)
async def fetch_ip(client: AsyncSession):
return await client.get("http://ip-api.com/json")
async def main():
async with AsyncSession() as client:
result = await fetch_ip(client)
print(result.valid.ERRORS, result.valid.PARSED)
asyncio.run(main())
RequestLogger — цветное логирование
Логгер работает через loguru. По умолчанию его вывод выключен, чтобы библиотека
не засоряла консоль. Включите его один раз после своей настройки loguru:
from request_decorator_handlers import RequestLogger
RequestLogger.enable()
Декоратор RequestLogger.log
@RequestLogger.log(
action: str,
log_level: Literal["full", "success_error", "error_only"] = "full",
show_body: bool = False,
len_text: int | None = None,
show_response_headers: bool = False,
show_request_body: bool = False,
show_request_headers: bool = False,
enabled: bool = True,
)
action— подпись (отображается в логе).log_level— объём сообщений:full— старт, успехи, ошибки;success_error— только финальные успехи + ошибки;error_only— только ошибки.
show_body— добавить блок с телом ответа (JSON/текст/байты).show_request_body/show_request_headers— показать исходные данные.show_response_headers— вывести заголовки из ответа.len_text— ограничение длины строковых значений.enabled— быстро отключить логирование для конкретной функции.
Дополнительно:
RequestLogger.disable()— навсегда убрать вывод.RequestLogger.muted()— контекстный менеджер для временной тишины.
ResponseHandler — валидация и ретраи
Декоратор ResponseHandler.handlers
@ResponseHandler.handlers(
Validator.status([200]),
Validator.headers(["content-type"]),
...
)
Каждый валидатор получает оригинальный ответ и объект ValidationData. Если
валидатор находит несоответствие, он добавляет запись об ошибке.
Функция, обёрнутая в ResponseHandler.handlers, возвращает WithValid[Response]:
with_valid.valid.has_errors() # bool
with_valid.valid.ERRORS # список ValidationError
with_valid.valid.PARSED # словарь с распарсенными значениями
with_valid.response # оригинальный Response
Доступные валидаторы
Все валидаторы находятся в Validator (из request_decorator_handlers):
| Валидатор | Назначение | Основные аргументы |
|---|---|---|
Validator.status(allowed) |
Проверить HTTP-код | allowed: Iterable[int] |
Validator.headers(required) |
Обязательные заголовки | список имён |
Validator.cookies(session, domain, required) |
Проверка cookie | сессия + домен + имена cookie |
Validator.content_type(expected) |
Формат ответа | expected: "json", "html", "text", "image", "bytes" |
Validator.REGEX(pattern, target="text") |
Совпадение по regex | target: "text", "json", "content"; expect_match; save_to |
Validator.JSON(path, ...) |
JSONPath-проверка | exists, value, is_type, range_value, regex, words, error_key |
Validator.HTML(selector, ...) |
HTML/XPath-проверка | selector_type, extract, attr_name, exists, фильтры |
Validator.cloudflare_blocked() |
Детектор Cloudflare-страниц | keywords (опционально), error_key |
Фильтры для JSON/HTML валидаторов
words: список слов/фраз (хватает совпадения одного).regex,regex_flags: дополнительное регулярное выражение.value: точное совпадение.is_type: проверка типа (int,str,boolи т.п.).range_value=(min, max): числовой диапазон.
Хранение парсенных значений
У многих валидаторов есть save_to, чтобы сохранить найденные значения в
valid.PARSED. Это касается Validator.REGEX и методов из Parser (см. ниже).
JSON / HTML / REGEX парсеры
Parser (из request_decorator_handlers.validation.response) удобен, если
нужно извлечь данные и использовать их дальше:
@ResponseHandler.handlers(
Validator.status([200]),
Parser.JSON("$.user.email", save_to="email"),
Parser.HTML("span.username", words=["admin"], save_to="role"),
Validator.REGEX(r'"token"\\s*:\\s*"([^"]+)"', target="json", save_to="token"),
)
ResponseHandler.retry
@ResponseHandler.retry(
error_key: str | Iterable[str],
quantity: int = 3,
interval: float = 0.5,
enabled: bool = True,
)
error_key— одно имя или список имён ошибок изValidationData.ERRORS.quantity— максимум попыток (включая первую).interval— задержка между повторами в секундах.enabled— общий переключатель.
Контекст ретрая сохраняется в ValidationData.RETRY (экземпляр RetryInfo).
RequestDebugger — запись проблемных ответов
@RequestDebugger.debug(
enabled: bool = True,
save_errors: bool = True,
save_unique_structures: bool = False,
debug_dir: str = "debug_request",
)
save_errors— сохраняет JSON с ошибками и метаданными запроса.save_unique_structures— сохраняет структуру успешного ответа (для документов по API).debug_dir— корневая папка для записей.
Файлы структурируются по домену, пути и типу (errors_json, unique_json).
Результат будет обёрнут в WithValid, а извлечённые значения попадут в
valid.PARSED.
Пример комплексной связки
@RequestDebugger.debug(save_errors=True, debug_dir="debug")
@RequestLogger.log("FETCH_PROFILE", log_level="success_error", show_body=True)
@ResponseHandler.retry(error_key=["STATUS_CODE", "API_STATUS"], quantity=3, interval=2)
@ResponseHandler.handlers(
Validator.status([200]),
Validator.content_type("json"),
Parser.JSON("$.profile.id", save_to="profile_id"),
Validator.JSON("$.profile.is_active", value=True, error_key="USER_INACTIVE"),
Validator.REGEX(r"\"token\":\"([^\"]+)\"", target="json", save_to="token"),
)
async def fetch_profile(session):
return await session.get("https://api.example.com/profile")
Приятной отладки и чистых логов! 💙
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file request_decorator_handlers-0.1.6.tar.gz.
File metadata
- Download URL: request_decorator_handlers-0.1.6.tar.gz
- Upload date:
- Size: 48.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca088a50dd875843cae2cc44148e8a80074a2449e233cbe4686227bcebff1ba8
|
|
| MD5 |
34b51150a2808cb36dcb6789d1c5f0d4
|
|
| BLAKE2b-256 |
9d97e29656dde33b6e1496702b5ff5213163ff9c722a022ce1a30afd0fc1ff3c
|
File details
Details for the file request_decorator_handlers-0.1.6-py3-none-any.whl.
File metadata
- Download URL: request_decorator_handlers-0.1.6-py3-none-any.whl
- Upload date:
- Size: 31.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2efb5b4695d2cf336eb702bdd7da79b59b6f172adf9e03e0ea4f38dcd00841d
|
|
| MD5 |
c5374b9b4a4e63624f72adc29bd6ca32
|
|
| BLAKE2b-256 |
ac6dea81d3dcb2726da72dcf5e0b52bfb1806c0201025d2d229281b112253759
|