Another Python package aim to offer "Rate Limiting" functionality for general use cases.
- Flexible storage strategy (Memory | File | Database)
- MemoryStorage
BasicStorage
- FileStorage
SQLite3_Storage
- MemoryStorage
- Cleanup expired rate limiters
- Use as a decorator
- Use as a variable
- Compatible with fastapi (TO BE TESTED)
- Support asynchronous DB operations (TODO)
- Python 3.10
pip3 install pygrl
from pygrl import BasicStorage, GeneralRateLimiter as grl, ExceededRateLimitError
storage = BasicStorage()
rate_limiter = grl(storage, 10, 1)
try:
for i in range(12):
allowed_to_pass = rate_limiter.check_limit("client-key")
if allowed_to_pass:
print(f"Request {i + 1}: Allowed")
else:
print(f"Request {i + 1}: Exceeded rate limit")
except Exception as e:
print(f"Rate limit exceeded: {e}")
@grl.general_rate_limiter(storage=BasicStorage(), max_requests=10, time_window=1)
def fn(a, b):
return a + b
try:
for i in range(12):
result = fn(i, i + 1)
print(f"Result {i + 1}: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")
import random
@grl.general_rate_limiter(storage=BasicStorage(), max_requests=2, time_window=1)
def connect(key: str, host: str, port: int):
return f"{key} connected to {host}:{port}"
users = ["Alice", "Bob", "Charlie", "David", "Eve"]
try:
for i in range(12):
user = random.choice(users)
result = connect(key=user, host="localhost", port=3306)
print(f"Result: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")
import random
# Take all arguments to construct the key!
key_builder = lambda f, *args, **kwargs: ",".join(list(map(lambda x: str(x), args)))
@grl.general_rate_limiter(
storage=BasicStorage(),
max_requests=2,
time_window=1,
key_builder=key_builder
)
def connect(username: str, host: str, port: int, *args):
return f"{username} connected to {host}:{port}"
users = ["Alice", "Bob", "Charlie", "David", "Eve"]
try:
for i in range(12):
user = random.choice(users)
result = connect(user, "localhost", 3306)
# Modify the function call like below would make every call unique,
# thus never trigger the `ExceedRateLimitError`.
# result = connect(user, "localhost", 3306, i) # `i` is unique
print(f"Result: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")
import random
@grl.general_rate_limiter(
storage=BasicStorage(),
max_requests=2,
time_window=1,
key_builder=lambda f, *args, **kwargs: kwargs["username"]
)
def connect(username: str, host: str, port: int):
return f"{username} connected to {host}:{port}"
users = ["Alice", "Bob", "Charlie", "David", "Eve"]
try:
for i in range(12):
user = random.choice(users)
result = connect(username=user, host="localhost", port=3306)
print(f"Result: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")
from pygrl import SQLite3_Storage, GeneralRateLimiter as grl, ExceededRateLimitError
storage = SQLite3_Storage("storage1.db", overwrite=True)
rate_limiter = grl(storage, 10, 1)
try:
for i in range(12):
allowed_to_pass = rate_limiter.check_limit("client-key")
if allowed_to_pass:
print(f"Request {i + 1}: Allowed")
else:
print(f"Request {i + 1}: Exceeded rate limit")
except Exception as e:
print(f"Rate limit exceeded: {e}")
@grl.general_rate_limiter(storage=SQLite3_Storage("storage2.db", overwrite=True), max_requests=10, time_window=1)
def fn(a, b):
return a + b
try:
for i in range(12):
result = fn(i, i + 1)
print(f"Result {i + 1}: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")
import random
@grl.general_rate_limiter(storage=SQLite3_Storage("storage3.db", overwrite=True), max_requests=2, time_window=1)
def connect(key: str, host: str, port: int):
return f"{key} connected to {host}:{port}"
users = ["Alice", "Bob", "Charlie", "David", "Eve"]
try:
for i in range(12):
user = random.choice(users)
result = connect(key=user, host="localhost", port=3306)
print(f"Result: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")
import random
# Take all arguments to construct the key!
key_builder = lambda f, *args, **kwargs: ",".join(list(map(lambda x: str(x), args)))
@grl.general_rate_limiter(
storage=storage=SQLite3_Storage("storage3.db", overwrite=True),
max_requests=2,
time_window=1,
key_builder=key_builder
)
def connect(username: str, host: str, port: int, *args):
return f"{username} connected to {host}:{port}"
users = ["Alice", "Bob", "Charlie", "David", "Eve"]
try:
for i in range(12):
user = random.choice(users)
result = connect(user, "localhost", 3306)
# Modify the function call like below would make every call unique,
# thus never trigger the `ExceedRateLimitError`.
# result = connect(user, "localhost", 3306, i) # `i` is unique
print(f"Result: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")
import random
@grl.general_rate_limiter(
storage=storage=SQLite3_Storage("storage3.db", overwrite=True),
max_requests=2,
time_window=1,
key_builder=lambda f, *args, **kwargs: kwargs["username"]
)
def connect(username: str, host: str, port: int):
return f"{username} connected to {host}:{port}"
users = ["Alice", "Bob", "Charlie", "David", "Eve"]
try:
for i in range(12):
user = random.choice(users)
result = connect(username=user, host="localhost", port=3306)
print(f"Result: {result}")
except ExceededRateLimitError as e:
print(f"Rate limit exceeded: {e}")