Skip to main content

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, Parser,
)

Требования к функциям

  1. Декораторы ожидают асинхронные функции, возвращающие HTTP-ответ (любой тип с полями status_code, headers, text/content и т.п.) — например, curl_cffi.AsyncResponse, httpx.Response.

  2. Декораторы ResponseHandler.handlers(...) и ResponseHandler.retry(...) оборачивают результат в WithValid[Response]. Если нужна строгая типизация:

    from typing import Annotated
    from request_decorator_handlers import WithValid
    
    @ResponseHandler.handlers(...)
    async def fetch_user(...) -> WithValid[Response]:
        ...
    
  3. Для интеграции со сторонними клиентов необходимо:

    • использовать их сессию/класс ответа;
    • возвращать исходный объект ответа (его обёртка 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).

JSONPathParser и HTMLParser

Дополнительные декораторы для извлечения данных без ручной работы с ValidationData.

from request_decorator_handlers import JSONPathParser, HTMLParser

@JSONPathParser.parse_field("$.user.email", "email")
@HTMLParser.parse("div.status", save_to="status")
async def call(session):
    return await session.get("https://example.com")

Результат будет обёрнут в WithValid, а извлечённые значения попадут в valid.PARSED.

JSON нормализация

Библиотека включает JSONValidator (используется автоматически):

  • декодирует строки/байты;
  • обрабатывает вложенные JSON-строки, множественные объекты, комментарии, trailing comma;
  • возвращает dict/list или None.

Прямой импорт JSONValidator доступен через from utils.json_validator import JSONValidator.

Работа с результатом

WithValid — универсальная обёртка:

result = await fetch_user()
if result.valid.has_errors():
    ...
print(result.valid.ERRORS)     # список ValidationError
print(result.valid.PARSED)     # извлечённые значений
original_response = result.response

ValidationError содержит key, expected, actual, message.

Управление логами

  • Настройку loguru делайте сами, библиотека лишь добавляет свои сообщения.
  • Не забудьте RequestLogger.enable() (или logger.enable("request_decorator_handlers")).
  • Для полной тишины в рантайме используйте:
    RequestLogger.disable()
    # или вручную
    logger.disable("request_decorator_handlers")
    

Пример комплексной связки

@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")

Дополнительно

  • RetryContext — contextvar для передачи информации о ретраях между вызовами.
  • LOG_ACTION — перечисление действий (удобно для консистентных логов).
  • Каталог examples/ содержит рабочие сценарии.
  • docs/ — текстовые инструкции и подробные примеры.

Если необходима интеграция со своей инфраструктурой логирования или кастомными валидаторами, достаточно реализовать функцию Callable[[Response, ValidationData], None] и передать её в ResponseHandler.handlers(...).

Приятной отладки и чистых логов! 💙

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

request_decorator_handlers-0.1.1.tar.gz (54.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

request_decorator_handlers-0.1.1-py3-none-any.whl (32.3 kB view details)

Uploaded Python 3

File details

Details for the file request_decorator_handlers-0.1.1.tar.gz.

File metadata

File hashes

Hashes for request_decorator_handlers-0.1.1.tar.gz
Algorithm Hash digest
SHA256 c207f5a54dbd7f6cd26cf10e4428073655206a71887996768ed92cb0fd34af49
MD5 837a6aef26d5c8bc5129c4312da3bbe4
BLAKE2b-256 6b82b6b7d30945b44829cd76842c5cf1ffc10d9bc3ec459ad7c8ffc41444b2a8

See more details on using hashes here.

File details

Details for the file request_decorator_handlers-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for request_decorator_handlers-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 00180dfa054616545b3bd216088c02aab32853f61d452cb26251a8c95e0a0f26
MD5 3eeb7efbdc3c56ffe16b1fc67ad418f0
BLAKE2b-256 0d57d6f64d0d95270671ef790c9487005244fa853c02bf280e0db95c49bd39f9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page