Description
When using async_http_client() for async HTTP requests, the rate limiter fails with:
async call made without an async bucket
This occurs because create_rate_limiter() in ratelimiter.py creates an InMemoryBucket, which doesn't support async operations in pyrate-limiter 3.x.
Steps to Reproduce
from httpxthrottlecache import HttpxThrottleCache
import asyncio
mgr = HttpxThrottleCache(request_per_sec_limit=9)
async def test():
async with mgr.async_http_client() as client:
response = await client.get("https://httpbin.org/get")
print(response.status_code)
asyncio.run(test())
Output:
async call made without an async bucket.
Root Cause
In ratelimiter.py:14-24:
def create_rate_limiter(requests_per_second: int, max_delay: Duration | int = Duration.DAY) -> Limiter:
rate = Rate(requests_per_second, Duration.SECOND)
rate_limits = [rate]
base_bucket = InMemoryBucket(rate_limits) # <-- sync-only bucket
bucket = base_bucket
limiter = Limiter(bucket, max_delay=max_delay, raise_when_fail=False, retry_until_max_delay=True)
return limiter
InMemoryBucket from pyrate-limiter 3.x doesn't have async support. When AsyncRateLimitingTransport.handle_async_request() calls limiter.try_acquire_async(), it fails.
Suggested Fix
Use BucketAsyncWrapper to wrap the bucket for async support:
from pyrate_limiter import InMemoryBucket, BucketAsyncWrapper, Limiter, Rate, Duration
def create_rate_limiter(requests_per_second: int, max_delay: Duration | int = Duration.DAY) -> Limiter:
rate = Rate(requests_per_second, Duration.SECOND)
base_bucket = InMemoryBucket([rate])
async_bucket = BucketAsyncWrapper(base_bucket)
return Limiter(async_bucket, max_delay=max_delay, raise_when_fail=False, retry_until_max_delay=True)
Note: BucketAsyncWrapper requires a running event loop when created, so this may need to be lazily initialized or created differently.
Environment
- httpxthrottlecache: 0.3.0
- pyrate-limiter: 3.9.0
- Python: 3.11
Impact
This bug prevents using httpxthrottlecache for any async HTTP operations, which is a significant limitation for libraries that need both sync and async support.
Description
When using
async_http_client()for async HTTP requests, the rate limiter fails with:This occurs because
create_rate_limiter()inratelimiter.pycreates anInMemoryBucket, which doesn't support async operations in pyrate-limiter 3.x.Steps to Reproduce
Output:
Root Cause
In
ratelimiter.py:14-24:InMemoryBucketfrom pyrate-limiter 3.x doesn't have async support. WhenAsyncRateLimitingTransport.handle_async_request()callslimiter.try_acquire_async(), it fails.Suggested Fix
Use
BucketAsyncWrapperto wrap the bucket for async support:Note:
BucketAsyncWrapperrequires a running event loop when created, so this may need to be lazily initialized or created differently.Environment
Impact
This bug prevents using httpxthrottlecache for any async HTTP operations, which is a significant limitation for libraries that need both sync and async support.