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 viabackend=or usebackend="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 asyncacquire_async(). - Parallel requests: thread pool (
ThreadPoolExecutor) for sync, andasyncio.gather+Semaphorefor 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
itemsmay be adict({"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 toFalseto raise immediately.
Rate limiting
RateLimitConfig(max_rate, burst)
max_rate: maximum requests per second (<= 0disables limiting).burst: how many requests may be sent back-to-back before smoothing kicks in.
Backend constraints
requestssupports sync APIs only (callingrequest_asyncraisesAsyncRequiredError).aiohttpsupports async APIs only (callingrequestraisesAsyncRequiredError).httpxsupports both.
Development & CI
- Tests run on
masteranddevbranches (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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2b25beeaf4675ce10624eabe76442c985ab6ea7e8ff108addd66eede5d03956
|
|
| MD5 |
a07b4eb0706cc1a36630cc6e53e917c5
|
|
| BLAKE2b-256 |
3038391ba34d27eea067c2e90cb3e877d8820cf1720b424e7f0e482326d3146a
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aba069bb93a59ba6f614c90dde27e64019815c5d6c066cad171d8b14b860e2a4
|
|
| MD5 |
dfed2a3b5a18185e234f2f22938e5d42
|
|
| BLAKE2b-256 |
0ea369594b4cbdfb57e0eb9ef2e44d921a3662fe1fb129d1d26d7e319e7eb309
|