Skip to main content

snowland-http

A rate-limited, parallel HTTP client with pluggable requests / httpx / aiohttp backends.

Features

  • Pluggable transport: requests (sync only), httpx (sync + async), aiohttp (async only). Select via backend= or use backend="auto" to auto-detect (preference: httpx > aiohttp > requests).
  • Global rate limiting: a token-bucket limiter shared by all parallel workers, so the aggregate request rate never exceeds the configured ceiling. Provides both a blocking acquire() and an async acquire_async().
  • Parallel requests: thread pool (ThreadPoolExecutor) for sync, and asyncio.gather + Semaphore for async.
  • Connection lifecycle: explicit open() / close() (and async counterparts), with context-manager support that opens on enter and closes on exit.

Installation

All three transports (requests / httpx / aiohttp) are optional dependencies, independent of each other — none is required for the package to import (backends are imported lazily). Install at least one to use the corresponding backend:

# Option A: install a transport library directly
pip install requests          # or httpx / aiohttp — install at least one

# Option B: install via extras (recommended)
pip install ".[requests]"     # sync backend only
pip install ".[httpx]"        # sync + async backend (recommended)
pip install ".[aiohttp]"      # async backend only
pip install ".[all]"          # everything

With backend="auto", the client detects installed libraries in the order httpx > aiohttp > requests.

Quick start

Sync + rate limiting + parallel

from snowland_http import HttpClient, RateLimitConfig

client = HttpClient(
    backend="requests",
    rate_limit=RateLimitConfig(max_rate=5, burst=2),  # <=5 req/s, burst of 2
)

resp = client.get("https://example.com")
print(resp.status_code, resp.json())

# parallel GET
results = client.get_many(["https://example.com/1", "https://example.com/2"])
for r in results:
    print(r if isinstance(r, Exception) else r.status_code)

Async + rate limiting + parallel

import asyncio
from snowland_http import HttpClient, RateLimitConfig

async def main():
    client = HttpClient(
        backend="httpx",
        rate_limit=RateLimitConfig(max_rate=10, burst=5),
    )
    async with client:  # open_async on enter, close_async on exit
        results = await client.get_many_async(["https://example.com/1", "https://example.com/2"])
        for r in results:
            print(r.status_code)

asyncio.run(main())

API

HttpClient(backend="auto", rate_limit=None, max_workers=10, max_concurrency=10)

Method Description
request(method, url, **kwargs) Single sync request
get/post/put/delete/head/patch(url, **kwargs) Sync convenience methods
request_many(items, max_workers, return_exceptions) Sync parallel (thread pool)
get_many(urls, method="GET", ...) Sync parallel GET
request_async(method, url, **kwargs) Single async request
get_async/... Async convenience methods
request_many_async(items, max_concurrency, return_exceptions) Async parallel
get_many_async(urls, ...) Async parallel GET
open() / open_async() Open / establish connection resources
close() / close_async() Close connection resources
  • Each element of items may be a dict ({"method": ..., "url": ..., ...}) or a (method, url, kwargs_dict) tuple.
  • Parallel methods default to return_exceptions=True: a single failure is returned as an exception object in the result list rather than aborting the rest. Set it to False to raise immediately.

Rate limiting

RateLimitConfig(max_rate, burst)

  • max_rate: maximum requests per second (<= 0 disables limiting).
  • burst: how many requests may be sent back-to-back before smoothing kicks in.

Backend constraints

  • requests supports sync APIs only (calling request_async raises AsyncRequiredError).
  • aiohttp supports async APIs only (calling request raises AsyncRequiredError).
  • httpx supports both.

Development & CI

  • Tests run on master and dev branches (see .github/workflows/test.yml), across Python 3.8–3.12, installing .[all] so functional/parallel tests execute.
  • Publishing to PyPI happens on GitHub Release (release: published) via .github/workflows/release.yml, using PyPI Trusted Publishing (OIDC) by default.

Run the test suite locally:

pip install -e ".[all]"
python -m unittest discover -s tests -v

License

BSD 3-Clause. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

snowland_http-0.1.0.tar.gz (13.2 kB view details)

Uploaded Source

Built Distribution

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

snowland_http-0.1.0-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file snowland_http-0.1.0.tar.gz.

File metadata

  • Download URL: snowland_http-0.1.0.tar.gz
  • Upload date:
  • Size: 13.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for snowland_http-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c2b25beeaf4675ce10624eabe76442c985ab6ea7e8ff108addd66eede5d03956
MD5 a07b4eb0706cc1a36630cc6e53e917c5
BLAKE2b-256 3038391ba34d27eea067c2e90cb3e877d8820cf1720b424e7f0e482326d3146a

See more details on using hashes here.

File details

Details for the file snowland_http-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: snowland_http-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for snowland_http-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 aba069bb93a59ba6f614c90dde27e64019815c5d6c066cad171d8b14b860e2a4
MD5 dfed2a3b5a18185e234f2f22938e5d42
BLAKE2b-256 0ea369594b4cbdfb57e0eb9ef2e44d921a3662fe1fb129d1d26d7e319e7eb309

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 Sentry Error logging StatusPage Status page