PYGRL - Python General Rate Limiter
Another Python package aim to offer "Rate Limiting" functionality for general use cases.
Features
- 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)
Dependencies
- Python 3.10
Installation
pip3 install pygrl
Example - BasicStorage
Imports
from pygrl import BasicStorage, GeneralRateLimiter as grl, ExceededRateLimitError
Check limit with BasicStorage
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}")
Apply rate limiter decorator with BasicStorage
@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}")
Apply rate limiter decorator with BasicStorage (Keyed function)
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}")
Apply rate limiter decorator with BasicStorage (key_builder with arguments)
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}")
Apply rate limiter decorator with BasicStorage (key_builder with keyword argument)
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}")
Example - SQLite3_Storage
Imports
from pygrl import SQLite3_Storage, GeneralRateLimiter as grl, ExceededRateLimitError
Check limit with SQLite3_Storage
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}")
Apply rate limiter decorator with SQLite3_Storage
@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}")
Apply rate limiter decorator with SQLite3_Storage (Keyed function)
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}")
Apply rate limiter decorator with SQLite3_Storage (key_builder with arguments)
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}")
Apply rate limiter decorator with SQLite3_Storage (key_builder with keyword argument)
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}")
Source Code
Metadata
Release files for pygrl 0.0.6.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pygrl-0.0.6.5.tar.gz | 8.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pygrl-0.0.6.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.4 kB
Release files / pygrl-0.0.6.5.tar.gz
| Download URL | pygrl-0.0.6.5.tar.gz |
|---|---|
| Size | 8.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1f392330ab6856880c0fe782a9c27f6c9bc47e6eeea61eb5c4127c79e43372f7
|
|
BLAKE2b-256 checksum How to use checksums |
2088e37483419c6b3c67da3b1686a5322d6b8e05a92c5ee4f58630589ce8bd7f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.0 CPython/3.10.12
|
Release files / pygrl-0.0.6.5-py3-none-any.whl
| Download URL | pygrl-0.0.6.5-py3-none-any.whl |
|---|---|
| Size | 10.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1e85d0421d9f95d306e927955354d105e39dc87369aee41f4e1738aaa912f083
|
|
BLAKE2b-256 checksum How to use checksums |
7a688c1df71ce01703deaddbf305ac3bd447d39911c4beed230a0052c97c4aea
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.0 CPython/3.10.12
|