Skip to main content

A modern and fast library for developing API clients.

Project description

unihttp

codecov PyPI version PyPI - Python Version PyPI - Downloads GitHub License GitHub Repo stars Telegram

unihttp is a modern and fast library for creating declarative API clients.

Table of Contents

Features

  • Declarative: Define API methods using standard Python type hints.
  • Type-Safe: Full support for static type checking.
  • Backend Agnostic: Works with httpx, aiohttp, and requests.
  • Extensible: Powerful middleware and error handling systems.

Installation

pip install unihttp

To include a specific HTTP backend (recommended):

pip install "unihttp[httpx]"    # For HTTPX (Sync/Async) support
# OR
pip install "unihttp[requests]" # For Requests (Sync) support
# OR
pip install "unihttp[aiohttp]"  # For Aiohttp (Async) support

Quick Start

1. Define Methods

unihttp uses markers to map method arguments to HTTP request components.

from dataclasses import dataclass
from unihttp import BaseMethod, Path, Query, Body, Header, Form, File


@dataclass
class User:
    id: int
    name: str
    email: str

@dataclass
class GetUser(BaseMethod[User]):
    __url__ = "/users/{id}"
    __method__ = "GET"

    id: Path[int]
    compact: Query[bool] = False

@dataclass
class CreateUser(BaseMethod[User]):
    __url__ = "/users"
    __method__ = "POST"
    
    name: Body[str]
    email: Body[str]

2. Client Implementation Strategies

You can choose between a purely declarative style using bind_method or a more imperative style using call_method.

Option A: Declarative Client (via bind_method)

This is the most concise way to define your client. You simply bind the methods to the client class.

[!NOTE] PyCharm Users: There is currently a known issue with displaying type hints for descriptors like bind_method (see PY-51768). This is expected to be fixed in the 2026.1 version.

from unihttp import bind_method
from unihttp.clients.httpx import HTTPXSyncClient
from unihttp.serializers.adaptix import DEFAULT_RETORT

class UserClient(HTTPXSyncClient):
    get_user = bind_method(GetUser)
    create_user = bind_method(CreateUser)

client = UserClient(
    base_url="https://api.example.com",
    request_dumper=DEFAULT_RETORT,
    response_loader=DEFAULT_RETORT
)
user = client.get_user(id=123)

Option B: Imperative Client (via call_method)

If you need more control, need to preprocess arguments, or simply prefer explicit method definitions, you can define methods in the client and use call_method.

class UserClient(HTTPXSyncClient):
    def get_user(self, user_id: int) -> User:
        # You can add custom logic here before the call
        return self.call_method(GetUser(id=user_id))
    
    def create_user(self, name: str, email: str) -> User:
        return self.call_method(CreateUser(name=name, email=email))

Markers Reference

unihttp provides several markers to define how arguments are serialized:

  • Path: Substitutes placeholders in the __url__ (e.g., /users/{id}).
  • Query: Adds parameters to the URL query string.
  • Body: Sends data as the JSON request body.
  • Header: Adds HTTP headers to the request.
  • Form: Sends data as form-encoded (application/x-www-form-urlencoded).
  • File: Used for multipart file uploads.
    • UploadFile: A wrapper for file uploads that allows specifying a filename and content type (e.g., UploadFile(b"content", filename="test.txt")).

Middleware

Middleware allows you to intercept requests and responses globally. This is useful for logging, authentication, or modifying requests on the fly.

from unihttp.middlewares.base import Middleware
from unihttp.http.request import HTTPRequest
from unihttp.http.response import HTTPResponse

class LoggingMiddleware(Middleware):
    def handle(self, request: HTTPRequest, next_handler) -> HTTPResponse:
        print(f"Requesting {request.url}")
        
        # Call the next handler in the chain
        response = next_handler(request)
        
        print(f"Status: {response.status_code}")
        return response

client = HTTPXSyncClient(
    # ...
    middleware=[LoggingMiddleware()]
)

Error Handling

unihttp offers a layered approach to error handling, giving you control at multiple levels.

1. Method-Level Handling

Override on_error in your Method class to handle specific status codes for that endpoint.

@dataclass
class GetUser(BaseMethod[User]):
    # ...
    def on_error(self, response):
        if response.status_code == 404:
            return None  # Return None (or a default object) instead of raising
        return super().on_error(response)

2. Client-Level Handling

Override handle_error in your Client class to catch errors that weren't handled by the method. This is great for global concerns like token expiration.

class MyClient(HTTPXSyncClient):
    def handle_error(self, response: HTTPResponse, method):
        if response.status_code == 401:
            raise MyAuthException("Session expired, please log in again.")

3. Middleware-Level Handling

You can wrap the execution in a try/except block or inspect the response within a middleware. This is useful for logging exceptions or global error reporting.

class ErrorReportingMiddleware(Middleware):
    def handle(self, request: HTTPRequest, next_handler):
        try:
            return next_handler(request)
        except Exception as e:
            # Report exception to external service
            sentry_sdk.capture_exception(e)
            raise

4. Response Body Validation

Sometimes APIs return 200 OK but the body contains an error message. You can override validate_response to handle this.

# In your Method or Client
def validate_response(self, response: HTTPResponse):
    if "error" in response.data:
        raise ApiError(response.data["error"])

Custom JSON Serialization

You can use high-performance JSON libraries like orjson or ujson by passing custom json_dumps and json_loads to the client.

import orjson
from unihttp.clients.httpx import HTTPXSyncClient

client = HTTPXSyncClient(
    # ...
    json_dumps=lambda x: orjson.dumps(x).decode(),
    json_loads=orjson.loads
)

Powered by Adaptix

unihttp leverages adaptix for all data serialization and validation tasks. adaptix is a powerful and extremely fast library that allows you to:

  • Validate data strictly against your type hints.
  • Serialize/Deserialize complex data structures (dataclasses, TypedDicts, etc.) with high performance.
  • Customize serialization logic (field renaming, value transformation) using Retort.

Crucially, you can customize serialization down to individual fields in each method, giving you granular control over how your data is processed.

from adaptix import Retort, name_mapping, P
from unihttp.serializers.adaptix import AdaptixDumper, AdaptixLoader, DEFAULT_RETORT

# Create a Retort that renames specific fields (e.g., camelCase for external API)
retort = Retort(
    recipe=[
        name_mapping(map={"user_name": "userName"}),
        dumper(P[CreateUser].email, lambda x: x.lower()),
    ]
)
retort.extend(DEFAULT_RETORT)

client = UserClient(
    # ...
    request_dumper=AdaptixDumper(retort),
    response_loader=AdaptixLoader(retort),
)

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

unihttp-0.2.0.tar.gz (32.8 kB view details)

Uploaded Source

Built Distribution

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

unihttp-0.2.0-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file unihttp-0.2.0.tar.gz.

File metadata

  • Download URL: unihttp-0.2.0.tar.gz
  • Upload date:
  • Size: 32.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for unihttp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 5570b12b80bcc7953c7aed51da1e124d882399b63a3e1f753d0386df9fb55516
MD5 205f8cc5d438f6096f05c87f3b68d0e4
BLAKE2b-256 3449b7ff4c5bcb9c13588cb1d9bd7e68632edf69ebe0d5d55118543f96e41e77

See more details on using hashes here.

File details

Details for the file unihttp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: unihttp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 22.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for unihttp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 40921ce7141a04d3bc3f508f8a2c1ec25f27812c80ef9314f5cdb48dde0aa9ae
MD5 5ce38601cb320f61b98357015e2b280d
BLAKE2b-256 9db954cbff21534da6523cdc78c0eaf2f498abf250e7e35aef92e4c6c8cb177a

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