Token throttler is an extendable rate-limiting library somewhat based on a token bucket algorithm
Project description
Token throttler
Token throttler is an extendable rate-limiting library somewhat based on a token bucket algorithm.
Table of contents
1. Installation
Token throttler is available on PyPI:
$ python -m pip install token-throttler
Token throttler officially supports Python >= 3.8.
NOTE: Depending on the storage engine you pick, you can install token throttler with extras:
$ python -m pip install token-throttler[redis]
2. Features
- Blocking (TokenThrottler) and non-blocking (TokenThrottlerAsync)
- Global throttler(s) configuration
- Configurable token throttler cost and identifier
- Multiple buckets per throttler per identifier
- Buckets can be added/removed manually or by a
dict
configuration - Manual usage or usage via decorator
- Decorator usage supports async code too
- Custom decorator can be written
- Extendable storage engine (eg. Redis)
3. Usage
Token throttler supports both manual usage and via decorator.
Decorator usage supports both async and sync.
1) Manual usage example:
from token_throttler import TokenBucket, TokenThrottler
from token_throttler.storage import RuntimeStorage
throttler: TokenThrottler = TokenThrottler(1, RuntimeStorage())
throttler.add_bucket("hello_world", TokenBucket(10, 10))
throttler.add_bucket("hello_world", TokenBucket(30, 20))
def hello_world() -> None:
print("Hello World")
for i in range(10):
throttler.consume("hello_world")
hello_world()
if throttler.consume("hello_world"):
hello_world()
else:
print(
f"bucket_one ran out of tokens, retry in: {throttler.wait_time('hello_world')}"
)
2) Decorator usage example:
from token_throttler import TokenBucket, TokenThrottler, TokenThrottlerException
from token_throttler.storage import RuntimeStorage
throttler: TokenThrottler = TokenThrottler(1, RuntimeStorage())
throttler.add_bucket("hello_world", TokenBucket(10, 10))
throttler.add_bucket("hello_world", TokenBucket(30, 20))
@throttler.enable("hello_world")
def hello_world() -> None:
print("Hello World")
for i in range(10):
hello_world()
try:
hello_world()
except TokenThrottlerException:
print(
f"bucket_one ran out of tokens, retry in: {throttler.wait_time('hello_world')}"
)
3) FastAPI usage example:
from fastapi import Depends, FastAPI, Request
from pydantic import BaseModel
from token_throttler import TokenThrottler, TokenBucket
from token_throttler.storage import RuntimeStorage
from token_throttler.ext.fastapi import FastAPIThrottler
app: FastAPI = FastAPI()
ban_hammer: TokenThrottler = TokenThrottler(cost=1, storage=RuntimeStorage())
class User(BaseModel):
id: int
name: str
u1: User = User(id=1, name="Test")
users: list[User] = [u1]
def create_buckets() -> None:
for user in users:
ban_hammer.add_bucket(
str(user.id), TokenBucket(replenish_time=10, max_tokens=10)
)
# For example purposes only - feel free to load your users with the bucket(s) anywhere else
app.add_event_handler("startup", create_buckets)
# Add your auth logic here
def get_auth_user() -> User:
return u1
# Since this is a FastAPI dependency, you can have the `Request` object here too if needed (e.g. storing the JWT token in
# a request lifecycle).
# You can also use sub-dependencies like shown below - `auth_user`
async def get_user_id(
request: Request, auth_user: User = Depends(get_auth_user)
) -> str:
return str(auth_user.id)
# You can configure the dependency per route, on a router level or on global application level.
# Pass your unique user identifier (e.g. JWT subject, apikey or user's ID) as a callback parameter
# of the `enable` method.
@app.get(
"/throttle", dependencies=[Depends(FastAPIThrottler(ban_hammer).enable(get_user_id))]
)
async def read_users():
return {"detail": "This is throttled URL"}
For other examples see examples directory.
4. Storage
TokenThrottler
supports RuntimeStorage
and RedisStorage
.
TokenThrottlerAsync
supports RedisStorageAsync
If you want your own storage engine, feel free to extend the token_throttler.storage.BucketStorage
or token_throttler.storage.BucketStorageAsync
classes.
For storage examples see examples directory.
5. Configuration
Token throttler supports global and per instance configurations.
Configuration params:
IDENTIFIER_FAIL_SAFE
(defaultFalse
) - if invalid identifier is given as a param for theconsume
method andIDENTIFIER_FAIL_SAFE
is set toTrue
, noKeyError
exception will be raised andconsume
will act like a limitless bucket is being consumed.ENABLE_THREAD_LOCK
(defaultFalse
) - if set toTrue
, throttler will acquire a thread lock upon callingconsume
method and release the lock once theconsume
is finished. This avoids various race conditions at a slight performance cost.
Global configuration usage
Global configuration means that all the throttler instances will use the same configuration from
the ThrottlerConfigGlobal
class singleton (!) instance - default_config
.
from token_throttler import TokenBucket, TokenThrottler, default_config
from token_throttler.storage import RuntimeStorage
default_config.set({
"ENABLE_THREAD_LOCK": False,
"IDENTIFIER_FAIL_SAFE": True,
})
throttler: TokenThrottler = TokenThrottler(1, RuntimeStorage())
throttler.add_bucket("hello_world", TokenBucket(10, 10))
throttler.add_bucket("hello_world", TokenBucket(30, 20))
...
Whenever you change the options inside default_config
object, all of the TokenThrottler
instances, that are not
using the instance specific configuration, will get updated with the new settings.
NOTE: If any modifications are attempted on the default_config
object during runtime, a RuntimeWarning
will be emitted. The default_config
object is designed to be a singleton instance of the ThrottlerConfigGlobal
class, responsible for storing and managing configuration settings. To ensure the proper functioning of the throttling mechanism and to maintain the integrity of the configuration, it is recommended to avoid making runtime changes to the default_config
object. Any such modifications may result in unexpected behavior or inconsistencies in the throttling behavior. Instead, consider utilizing instance-specific configuration.
Instance configuration usage
Instance configuration is to be used when TokenThrottler
instance(s) needs a different configuration that the global one.
If no instance configuration is passed, TokenThrottler
object will use the global configuration.
from token_throttler import ThrottlerConfig, TokenBucket, TokenThrottler
from token_throttler.storage import RuntimeStorage
throttler: TokenThrottler = TokenThrottler(
1,
RuntimeStorage(),
ThrottlerConfig(
{
"ENABLE_THREAD_LOCK": True,
"IDENTIFIER_FAIL_SAFE": True,
}
),
)
throttler.add_bucket("hello_world", TokenBucket(10, 10))
throttler.add_bucket("hello_world", TokenBucket(30, 20))
...
Project details
Release history Release notifications | RSS feed
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
File details
Details for the file token_throttler-1.5.1.tar.gz
.
File metadata
- Download URL: token_throttler-1.5.1.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.2 CPython/3.12.2 Linux/6.8.4-1-MANJARO
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0ef3557e10710c88329ae1c79da6bb44eb40e282f8bc22a568de624ebf5e2a7a |
|
MD5 | 077a873904ef557692eaa8db658d12aa |
|
BLAKE2b-256 | 6552925c07f16cc4825225800f848f7a72a5ba0b039b2e5bb7850c27110879a0 |
File details
Details for the file token_throttler-1.5.1-py3-none-any.whl
.
File metadata
- Download URL: token_throttler-1.5.1-py3-none-any.whl
- Upload date:
- Size: 18.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.2 CPython/3.12.2 Linux/6.8.4-1-MANJARO
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 420acd6c18c93741a508909bc2efc71c0f7d0e98acf925aae22184f0ef557185 |
|
MD5 | 751d02944642e6a7efcd48b4c60e8b34 |
|
BLAKE2b-256 | 3c0f92c8e4b9912e61be8ddae45007fb88d4b22da049c1234ebbf008bc414591 |