fastlimiter


Namefastlimiter JSON
Version 0.1.0 PyPI version JSON
download
home_pagehttps://github.com/anto18671/fastlimiter
SummaryA fast token bucket rate limiter
upload_time2024-09-13 22:06:06
maintainerNone
docs_urlNone
authorAnthony Therrien
requires_python>=3.8
licenseMIT
keywords rate-limiter token-bucket fastapi middleware
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # FastLimiter

[![PyPI version](https://badge.fury.io/py/fastlimiter.svg)](https://badge.fury.io/py/fastlimiter)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A fast and flexible token bucket rate limiter for FastAPI applications.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
  - [Prerequisites](#prerequisites)
  - [Install via Pip](#install-via-pip)
  - [Install from Source](#install-from-source)
- [Quick Start](#quick-start)
- [Usage](#usage)
  - [Basic Usage](#basic-usage)
    - [As Middleware](#as-middleware)
    - [As Decorator](#as-decorator)
  - [Advanced Usage](#advanced-usage)
    - [Custom Key Function](#custom-key-function)
    - [Collecting Statistics](#collecting-statistics)
    - [Updating Parameters at Runtime](#updating-parameters-at-runtime)
- [API Reference](#api-reference)
  - [RateLimiter Class](#ratelimiter-class)
    - [Initialization](#initialization)
    - [Methods](#methods)
- [Configuration](#configuration)
- [Examples](#examples)
  - [Limiting Based on API Key](#limiting-based-on-api-key)
  - [Custom Callback Function](#custom-callback-function)
  - [Resetting Rate Limiter for a Specific Client](#resetting-rate-limiter-for-a-specific-client)
  - [Applying Different Limits to Different Endpoints](#applying-different-limits-to-different-endpoints)
  - [Limiting by User ID](#limiting-by-user-id)
  - [Customizing Rate Limit Exceeded Response](#customizing-rate-limit-exceeded-response)
- [Contributing](#contributing)
  - [Reporting Bugs](#reporting-bugs)
  - [Feature Requests](#feature-requests)
  - [Pull Requests](#pull-requests)
  - [Code Style](#code-style)
  - [Testing](#testing)
- [License](#license)
- [Contact](#contact)

## Features

- **Flexible Rate Limiting**: Configure rate limits based on tokens, capacity, burst size, and time intervals to suit different scenarios.
- **Token Bucket Algorithm**: Implements the efficient token bucket algorithm, which is ideal for handling bursts and smoothing out request rates.
- **FastAPI Integration**: Seamlessly integrates with FastAPI applications through middleware or route decorators.
- **Per-Client Limiting**: Rate limits are applied per client, identified by IP address or custom keys.
- **Statistics Collection**: Optionally collect detailed statistics, including allowed and denied requests, which can be used for monitoring and analytics.
- **Customizable Callbacks**: Add custom functions to execute after each request, allowing for logging, alerting, or other side effects.
- **Dynamic Configuration**: Update rate limiting parameters at runtime without restarting the application.

## Installation

### Prerequisites

- Python 3.8 or higher
- FastAPI 0.65.0 or higher

### Install via Pip

Install FastLimiter from PyPI:

```bash
pip install fastlimiter
```

### Install from Source

Alternatively, you can clone the repository and install it:

```bash
git clone https://github.com/anto18671/fastlimiter.git
cd fastlimiter
pip install .
```

## Quick Start

Here's how you can quickly get started with FastLimiter in your FastAPI application:

```python
from fastapi import FastAPI, Request
from fastlimiter import RateLimiter, setup_rate_limiter

app = FastAPI()

# Initialize the rate limiter
rate_limiter = RateLimiter(rate=10, capacity=100, seconds=60)

# Setup the rate limiter middleware
setup_rate_limiter(app, rate_limiter)

@app.get("/items")
async def read_items(request: Request):
    return {"message": "Hello, World!"}
```

In this example, the rate limiter allows up to 100 requests per client per 60 seconds, refilling at a rate of 10 tokens per 60 seconds.

## Usage

### Basic Usage

#### As Middleware

To apply rate limiting to all incoming requests, use the middleware:

```python
from fastapi import FastAPI
from fastlimiter import RateLimiter, setup_rate_limiter

app = FastAPI()

rate_limiter = RateLimiter(rate=5, capacity=10, seconds=60)
setup_rate_limiter(app, rate_limiter)
```

#### As Decorator

To apply rate limiting to specific routes, use the `limit` decorator:

```python
from fastapi import FastAPI, Request
from fastlimiter import RateLimiter

app = FastAPI()

rate_limiter = RateLimiter(rate=5, capacity=10, seconds=60)

@app.get("/limited")
@rate_limiter.limit()
async def limited_endpoint(request: Request):
    return {"message": "This endpoint is rate-limited."}
```

### Advanced Usage

#### Custom Key Function

You can customize how the rate limiter identifies clients by overriding `get_key_from_request`:

```python
def custom_key_function(self, request: Request) -> str:
    return request.headers.get("X-API-Key", "default")

rate_limiter.get_key_from_request = custom_key_function.__get__(rate_limiter, RateLimiter)
```

#### Collecting Statistics

Enable statistics collection to monitor rate limiting:

```python
rate_limiter.enable_stats_collection()

stats = rate_limiter.get_stats("client_key")
print(stats)
```

#### Updating Parameters at Runtime

You can dynamically update the rate limiter's parameters at runtime:

```python
rate_limiter.update_rate(new_rate=20)
rate_limiter.update_capacity(new_capacity=50)
rate_limiter.update_burst(new_burst=10)
rate_limiter.update_time(seconds=30)
rate_limiter.update_stats_window(new_stats_window=120)
```

## API Reference

### RateLimiter Class

#### Initialization

```python
RateLimiter(
    rate: int,
    capacity: int = 1024,
    burst: int = None,
    stats_window: int = 60,
    enable_stats: bool = True,
    seconds: int = 0,
    minutes: int = 0,
    hours: int = 0
)
```

- **rate** _(int)_: The rate at which tokens are added to the bucket.
- **capacity** _(int)_: Maximum number of tokens in the bucket.
- **burst** _(int, optional)_: Extra tokens allowed during bursts. Defaults to 0 if not provided.
- **stats_window** _(int, optional)_: Time window for collecting statistics in seconds.
- **enable_stats** _(bool, optional)_: Enable or disable statistics collection.
- **seconds**, **minutes**, **hours**: Define the time interval over which tokens are refilled.

#### Methods

##### allow_request(key: str) -> bool

Determines if a request identified by `key` is allowed based on the rate limit.

- **Parameters**:
  - `key` _(str)_: The unique identifier for the client/request.
- **Returns**:
  - `bool`: `True` if the request is allowed, `False` otherwise.
- **Raises**:
  - `HTTPException`: Raises an HTTP 429 exception if the request is denied.

##### get_wait_time(key: str) -> float

Returns the time in seconds a client identified by `key` needs to wait before making a new request.

- **Parameters**:
  - `key` _(str)_: The unique identifier for the client/request.
- **Returns**:
  - `float`: Time in seconds to wait before the next allowed request.

##### update_stats(key: str, allowed: bool, timestamp: float)

Updates the statistics for a given `key`.

- **Parameters**:
  - `key` _(str)_: The unique identifier for the client/request.
  - `allowed` _(bool)_: Whether the request was allowed.
  - `timestamp` _(float)_: The time when the request was processed.

##### get_stats(key: str) -> Dict

Retrieves the statistics for a given `key`.

- **Parameters**:
  - `key` _(str)_: The unique identifier for the client/request.
- **Returns**:
  - `Dict`: A dictionary containing statistical data such as total allowed, total denied, etc.

##### reset(key: str = None)

Resets the rate limiter state for a specific `key` or all keys if `key` is `None`.

- **Parameters**:
  - `key` _(str, optional)_: The unique identifier for the client/request. If `None`, resets all keys.

##### update_capacity(new_capacity: int)

Updates the capacity of the token bucket.

- **Parameters**:
  - `new_capacity` _(int)_: The new maximum number of tokens.

##### update_burst(new_burst: int)

Updates the burst size.

- **Parameters**:
  - `new_burst` _(int)_: The new burst size.

##### update_stats_window(new_stats_window: int)

Updates the time window for statistics collection.

- **Parameters**:
  - `new_stats_window` _(int)_: The new statistics window in seconds.

##### update_time(seconds: int = 0, minutes: int = 0, hours: int = 0)

Updates the time interval over which tokens are refilled.

- **Parameters**:
  - `seconds`, `minutes`, `hours`: Time components for the new interval.

##### update_rate(new_rate: int)

Updates the rate at which tokens are added to the bucket.

- **Parameters**:
  - `new_rate` _(int)_: The new token addition rate.

##### get_key_from_request(request: Request) -> str

Retrieves a unique key from the incoming request. By default, uses the client's IP address.

- **Parameters**:
  - `request` _(Request)_: The incoming FastAPI request.
- **Returns**:
  - `str`: The unique key for rate limiting.

##### enable_stats_collection()

Enables statistics collection.

##### disable_stats_collection()

Disables statistics collection.

##### add_callback(callback: Callable)

Adds a callback function to be executed after each request is processed.

- **Parameters**:
  - `callback` _(Callable)_: The function to call after processing a request.

##### limit()

Creates a rate-limiting decorator for FastAPI routes.

- **Returns**:
  - `Callable`: A decorator that can be applied to FastAPI route handlers.

##### fastapi_middleware(request: Request, call_next: Callable)

Middleware function to apply rate limiting to incoming FastAPI requests.

- **Parameters**:
  - `request` _(Request)_: The incoming FastAPI request.
  - `call_next` _(Callable)_: The next middleware or route handler.

## Configuration

Customize the rate limiter by adjusting the parameters:

- **Rate**: The number of tokens added to the bucket over the specified time interval.
- **Capacity**: The maximum number of tokens the bucket can hold.
- **Burst**: Additional tokens allowed for handling bursts of requests.
- **Time Interval**: The period over which tokens are refilled (specified in seconds, minutes, and/or hours).

Example:

```python
rate_limiter = RateLimiter(
    rate=100,
    capacity=200,
    burst=50,
    seconds=60,
    enable_stats=True,
    stats_window=300
)
```

## Examples

### Limiting Based on API Key

```python
def api_key_key_function(self, request: Request) -> str:
    return request.headers.get("X-API-Key", "anonymous")

rate_limiter.get_key_from_request = api_key_key_function.__get__(rate_limiter, RateLimiter)
```

### Custom Callback Function

```python
def log_request(allowed: bool, key: str):
    status = "allowed" if allowed else "denied"
    emoji = "😊" if allowed else "😞"
    print(f"Request from {key} was {status}. {emoji}")

rate_limiter.add_callback(log_request)
```

### Resetting Rate Limiter for a Specific Client

```python
rate_limiter.reset(key="client_ip")
```

### Applying Different Limits to Different Endpoints

```python
from fastapi import FastAPI, Request
from fastlimiter import RateLimiter

app = FastAPI()

# General rate limiter for most endpoints
general_limiter = RateLimiter(rate=100, capacity=200, seconds=60)

# Specific rate limiter for a heavy endpoint
heavy_limiter = RateLimiter(rate=10, capacity=20, seconds=60)

@app.get("/general")
@general_limiter.limit()
async def general_endpoint(request: Request):
    return {"message": "This is a general endpoint."}

@app.get("/heavy")
@heavy_limiter.limit()
async def heavy_endpoint(request: Request):
    return {"message": "This endpoint has stricter rate limiting."}
```

### Limiting by User ID

If your application uses authentication, you might want to rate limit based on user ID:

```python
def user_id_key_function(self, request: Request) -> str:
    user = request.state.user  # Assuming user is stored in request.state
    return str(user.id) if user else "anonymous"

rate_limiter.get_key_from_request = user_id_key_function.__get__(rate_limiter, RateLimiter)
```

### Customizing Rate Limit Exceeded Response

Customize the response when the rate limit is exceeded:

```python
from fastapi.responses import PlainTextResponse

async def rate_limit_middleware(request: Request, call_next: Callable):
    key = rate_limiter.get_key_from_request(request)
    try:
        await rate_limiter.allow_request(key)
        response = await call_next(request)
        return response
    except HTTPException as exc:
        return PlainTextResponse(
            "You have exceeded your request rate limit. Please try again later.",
            status_code=exc.status_code
        )

app.middleware("http")(rate_limit_middleware)
```

## Contributing

We welcome contributions to improve FastLimiter! Here's how you can contribute:

### Reporting Bugs

If you find a bug, please open an issue on [GitHub](https://github.com/anto18671/fastlimiter/issues) with detailed steps to reproduce the problem.

### Feature Requests

Have an idea for a new feature? Open an issue with the tag "enhancement" to discuss it.

### Pull Requests

1. Fork the repository on GitHub.
2. Clone your forked repository to your local machine.
3. Create a new branch for your changes: `git checkout -b feature/your-feature-name`.
4. Make your changes and commit them with clear messages.
5. Push your changes to your fork: `git push origin feature/your-feature-name`.
6. Open a pull request on the main repository.

### Code Style

Please follow the PEP 8 style guide for Python code.

### Testing

Ensure that all existing tests pass and write new tests for your code.

- Run tests with `pytest`.
- For asynchronous code, use `pytest_asyncio`.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

## Contact

For questions or suggestions, please open an issue on [GitHub](https://github.com/anto18671/fastlimiter/issues):

- **Author**: Anthony Therrien

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/anto18671/fastlimiter",
    "name": "fastlimiter",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "rate-limiter, token-bucket, fastapi, middleware",
    "author": "Anthony Therrien",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/4c/a6/e206ba08ed55f3faed77c6fbff5e2cfd85ef1e93156c0f2f42c2fcc35e8a/fastlimiter-0.1.0.tar.gz",
    "platform": null,
    "description": "# FastLimiter\r\n\r\n[![PyPI version](https://badge.fury.io/py/fastlimiter.svg)](https://badge.fury.io/py/fastlimiter)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\nA fast and flexible token bucket rate limiter for FastAPI applications.\r\n\r\n## Table of Contents\r\n\r\n- [Features](#features)\r\n- [Installation](#installation)\r\n  - [Prerequisites](#prerequisites)\r\n  - [Install via Pip](#install-via-pip)\r\n  - [Install from Source](#install-from-source)\r\n- [Quick Start](#quick-start)\r\n- [Usage](#usage)\r\n  - [Basic Usage](#basic-usage)\r\n    - [As Middleware](#as-middleware)\r\n    - [As Decorator](#as-decorator)\r\n  - [Advanced Usage](#advanced-usage)\r\n    - [Custom Key Function](#custom-key-function)\r\n    - [Collecting Statistics](#collecting-statistics)\r\n    - [Updating Parameters at Runtime](#updating-parameters-at-runtime)\r\n- [API Reference](#api-reference)\r\n  - [RateLimiter Class](#ratelimiter-class)\r\n    - [Initialization](#initialization)\r\n    - [Methods](#methods)\r\n- [Configuration](#configuration)\r\n- [Examples](#examples)\r\n  - [Limiting Based on API Key](#limiting-based-on-api-key)\r\n  - [Custom Callback Function](#custom-callback-function)\r\n  - [Resetting Rate Limiter for a Specific Client](#resetting-rate-limiter-for-a-specific-client)\r\n  - [Applying Different Limits to Different Endpoints](#applying-different-limits-to-different-endpoints)\r\n  - [Limiting by User ID](#limiting-by-user-id)\r\n  - [Customizing Rate Limit Exceeded Response](#customizing-rate-limit-exceeded-response)\r\n- [Contributing](#contributing)\r\n  - [Reporting Bugs](#reporting-bugs)\r\n  - [Feature Requests](#feature-requests)\r\n  - [Pull Requests](#pull-requests)\r\n  - [Code Style](#code-style)\r\n  - [Testing](#testing)\r\n- [License](#license)\r\n- [Contact](#contact)\r\n\r\n## Features\r\n\r\n- **Flexible Rate Limiting**: Configure rate limits based on tokens, capacity, burst size, and time intervals to suit different scenarios.\r\n- **Token Bucket Algorithm**: Implements the efficient token bucket algorithm, which is ideal for handling bursts and smoothing out request rates.\r\n- **FastAPI Integration**: Seamlessly integrates with FastAPI applications through middleware or route decorators.\r\n- **Per-Client Limiting**: Rate limits are applied per client, identified by IP address or custom keys.\r\n- **Statistics Collection**: Optionally collect detailed statistics, including allowed and denied requests, which can be used for monitoring and analytics.\r\n- **Customizable Callbacks**: Add custom functions to execute after each request, allowing for logging, alerting, or other side effects.\r\n- **Dynamic Configuration**: Update rate limiting parameters at runtime without restarting the application.\r\n\r\n## Installation\r\n\r\n### Prerequisites\r\n\r\n- Python 3.8 or higher\r\n- FastAPI 0.65.0 or higher\r\n\r\n### Install via Pip\r\n\r\nInstall FastLimiter from PyPI:\r\n\r\n```bash\r\npip install fastlimiter\r\n```\r\n\r\n### Install from Source\r\n\r\nAlternatively, you can clone the repository and install it:\r\n\r\n```bash\r\ngit clone https://github.com/anto18671/fastlimiter.git\r\ncd fastlimiter\r\npip install .\r\n```\r\n\r\n## Quick Start\r\n\r\nHere's how you can quickly get started with FastLimiter in your FastAPI application:\r\n\r\n```python\r\nfrom fastapi import FastAPI, Request\r\nfrom fastlimiter import RateLimiter, setup_rate_limiter\r\n\r\napp = FastAPI()\r\n\r\n# Initialize the rate limiter\r\nrate_limiter = RateLimiter(rate=10, capacity=100, seconds=60)\r\n\r\n# Setup the rate limiter middleware\r\nsetup_rate_limiter(app, rate_limiter)\r\n\r\n@app.get(\"/items\")\r\nasync def read_items(request: Request):\r\n    return {\"message\": \"Hello, World!\"}\r\n```\r\n\r\nIn this example, the rate limiter allows up to 100 requests per client per 60 seconds, refilling at a rate of 10 tokens per 60 seconds.\r\n\r\n## Usage\r\n\r\n### Basic Usage\r\n\r\n#### As Middleware\r\n\r\nTo apply rate limiting to all incoming requests, use the middleware:\r\n\r\n```python\r\nfrom fastapi import FastAPI\r\nfrom fastlimiter import RateLimiter, setup_rate_limiter\r\n\r\napp = FastAPI()\r\n\r\nrate_limiter = RateLimiter(rate=5, capacity=10, seconds=60)\r\nsetup_rate_limiter(app, rate_limiter)\r\n```\r\n\r\n#### As Decorator\r\n\r\nTo apply rate limiting to specific routes, use the `limit` decorator:\r\n\r\n```python\r\nfrom fastapi import FastAPI, Request\r\nfrom fastlimiter import RateLimiter\r\n\r\napp = FastAPI()\r\n\r\nrate_limiter = RateLimiter(rate=5, capacity=10, seconds=60)\r\n\r\n@app.get(\"/limited\")\r\n@rate_limiter.limit()\r\nasync def limited_endpoint(request: Request):\r\n    return {\"message\": \"This endpoint is rate-limited.\"}\r\n```\r\n\r\n### Advanced Usage\r\n\r\n#### Custom Key Function\r\n\r\nYou can customize how the rate limiter identifies clients by overriding `get_key_from_request`:\r\n\r\n```python\r\ndef custom_key_function(self, request: Request) -> str:\r\n    return request.headers.get(\"X-API-Key\", \"default\")\r\n\r\nrate_limiter.get_key_from_request = custom_key_function.__get__(rate_limiter, RateLimiter)\r\n```\r\n\r\n#### Collecting Statistics\r\n\r\nEnable statistics collection to monitor rate limiting:\r\n\r\n```python\r\nrate_limiter.enable_stats_collection()\r\n\r\nstats = rate_limiter.get_stats(\"client_key\")\r\nprint(stats)\r\n```\r\n\r\n#### Updating Parameters at Runtime\r\n\r\nYou can dynamically update the rate limiter's parameters at runtime:\r\n\r\n```python\r\nrate_limiter.update_rate(new_rate=20)\r\nrate_limiter.update_capacity(new_capacity=50)\r\nrate_limiter.update_burst(new_burst=10)\r\nrate_limiter.update_time(seconds=30)\r\nrate_limiter.update_stats_window(new_stats_window=120)\r\n```\r\n\r\n## API Reference\r\n\r\n### RateLimiter Class\r\n\r\n#### Initialization\r\n\r\n```python\r\nRateLimiter(\r\n    rate: int,\r\n    capacity: int = 1024,\r\n    burst: int = None,\r\n    stats_window: int = 60,\r\n    enable_stats: bool = True,\r\n    seconds: int = 0,\r\n    minutes: int = 0,\r\n    hours: int = 0\r\n)\r\n```\r\n\r\n- **rate** _(int)_: The rate at which tokens are added to the bucket.\r\n- **capacity** _(int)_: Maximum number of tokens in the bucket.\r\n- **burst** _(int, optional)_: Extra tokens allowed during bursts. Defaults to 0 if not provided.\r\n- **stats_window** _(int, optional)_: Time window for collecting statistics in seconds.\r\n- **enable_stats** _(bool, optional)_: Enable or disable statistics collection.\r\n- **seconds**, **minutes**, **hours**: Define the time interval over which tokens are refilled.\r\n\r\n#### Methods\r\n\r\n##### allow_request(key: str) -> bool\r\n\r\nDetermines if a request identified by `key` is allowed based on the rate limit.\r\n\r\n- **Parameters**:\r\n  - `key` _(str)_: The unique identifier for the client/request.\r\n- **Returns**:\r\n  - `bool`: `True` if the request is allowed, `False` otherwise.\r\n- **Raises**:\r\n  - `HTTPException`: Raises an HTTP 429 exception if the request is denied.\r\n\r\n##### get_wait_time(key: str) -> float\r\n\r\nReturns the time in seconds a client identified by `key` needs to wait before making a new request.\r\n\r\n- **Parameters**:\r\n  - `key` _(str)_: The unique identifier for the client/request.\r\n- **Returns**:\r\n  - `float`: Time in seconds to wait before the next allowed request.\r\n\r\n##### update_stats(key: str, allowed: bool, timestamp: float)\r\n\r\nUpdates the statistics for a given `key`.\r\n\r\n- **Parameters**:\r\n  - `key` _(str)_: The unique identifier for the client/request.\r\n  - `allowed` _(bool)_: Whether the request was allowed.\r\n  - `timestamp` _(float)_: The time when the request was processed.\r\n\r\n##### get_stats(key: str) -> Dict\r\n\r\nRetrieves the statistics for a given `key`.\r\n\r\n- **Parameters**:\r\n  - `key` _(str)_: The unique identifier for the client/request.\r\n- **Returns**:\r\n  - `Dict`: A dictionary containing statistical data such as total allowed, total denied, etc.\r\n\r\n##### reset(key: str = None)\r\n\r\nResets the rate limiter state for a specific `key` or all keys if `key` is `None`.\r\n\r\n- **Parameters**:\r\n  - `key` _(str, optional)_: The unique identifier for the client/request. If `None`, resets all keys.\r\n\r\n##### update_capacity(new_capacity: int)\r\n\r\nUpdates the capacity of the token bucket.\r\n\r\n- **Parameters**:\r\n  - `new_capacity` _(int)_: The new maximum number of tokens.\r\n\r\n##### update_burst(new_burst: int)\r\n\r\nUpdates the burst size.\r\n\r\n- **Parameters**:\r\n  - `new_burst` _(int)_: The new burst size.\r\n\r\n##### update_stats_window(new_stats_window: int)\r\n\r\nUpdates the time window for statistics collection.\r\n\r\n- **Parameters**:\r\n  - `new_stats_window` _(int)_: The new statistics window in seconds.\r\n\r\n##### update_time(seconds: int = 0, minutes: int = 0, hours: int = 0)\r\n\r\nUpdates the time interval over which tokens are refilled.\r\n\r\n- **Parameters**:\r\n  - `seconds`, `minutes`, `hours`: Time components for the new interval.\r\n\r\n##### update_rate(new_rate: int)\r\n\r\nUpdates the rate at which tokens are added to the bucket.\r\n\r\n- **Parameters**:\r\n  - `new_rate` _(int)_: The new token addition rate.\r\n\r\n##### get_key_from_request(request: Request) -> str\r\n\r\nRetrieves a unique key from the incoming request. By default, uses the client's IP address.\r\n\r\n- **Parameters**:\r\n  - `request` _(Request)_: The incoming FastAPI request.\r\n- **Returns**:\r\n  - `str`: The unique key for rate limiting.\r\n\r\n##### enable_stats_collection()\r\n\r\nEnables statistics collection.\r\n\r\n##### disable_stats_collection()\r\n\r\nDisables statistics collection.\r\n\r\n##### add_callback(callback: Callable)\r\n\r\nAdds a callback function to be executed after each request is processed.\r\n\r\n- **Parameters**:\r\n  - `callback` _(Callable)_: The function to call after processing a request.\r\n\r\n##### limit()\r\n\r\nCreates a rate-limiting decorator for FastAPI routes.\r\n\r\n- **Returns**:\r\n  - `Callable`: A decorator that can be applied to FastAPI route handlers.\r\n\r\n##### fastapi_middleware(request: Request, call_next: Callable)\r\n\r\nMiddleware function to apply rate limiting to incoming FastAPI requests.\r\n\r\n- **Parameters**:\r\n  - `request` _(Request)_: The incoming FastAPI request.\r\n  - `call_next` _(Callable)_: The next middleware or route handler.\r\n\r\n## Configuration\r\n\r\nCustomize the rate limiter by adjusting the parameters:\r\n\r\n- **Rate**: The number of tokens added to the bucket over the specified time interval.\r\n- **Capacity**: The maximum number of tokens the bucket can hold.\r\n- **Burst**: Additional tokens allowed for handling bursts of requests.\r\n- **Time Interval**: The period over which tokens are refilled (specified in seconds, minutes, and/or hours).\r\n\r\nExample:\r\n\r\n```python\r\nrate_limiter = RateLimiter(\r\n    rate=100,\r\n    capacity=200,\r\n    burst=50,\r\n    seconds=60,\r\n    enable_stats=True,\r\n    stats_window=300\r\n)\r\n```\r\n\r\n## Examples\r\n\r\n### Limiting Based on API Key\r\n\r\n```python\r\ndef api_key_key_function(self, request: Request) -> str:\r\n    return request.headers.get(\"X-API-Key\", \"anonymous\")\r\n\r\nrate_limiter.get_key_from_request = api_key_key_function.__get__(rate_limiter, RateLimiter)\r\n```\r\n\r\n### Custom Callback Function\r\n\r\n```python\r\ndef log_request(allowed: bool, key: str):\r\n    status = \"allowed\" if allowed else \"denied\"\r\n    emoji = \"\u00f0\u0178\u02dc\u0160\" if allowed else \"\u00f0\u0178\u02dc\u017e\"\r\n    print(f\"Request from {key} was {status}. {emoji}\")\r\n\r\nrate_limiter.add_callback(log_request)\r\n```\r\n\r\n### Resetting Rate Limiter for a Specific Client\r\n\r\n```python\r\nrate_limiter.reset(key=\"client_ip\")\r\n```\r\n\r\n### Applying Different Limits to Different Endpoints\r\n\r\n```python\r\nfrom fastapi import FastAPI, Request\r\nfrom fastlimiter import RateLimiter\r\n\r\napp = FastAPI()\r\n\r\n# General rate limiter for most endpoints\r\ngeneral_limiter = RateLimiter(rate=100, capacity=200, seconds=60)\r\n\r\n# Specific rate limiter for a heavy endpoint\r\nheavy_limiter = RateLimiter(rate=10, capacity=20, seconds=60)\r\n\r\n@app.get(\"/general\")\r\n@general_limiter.limit()\r\nasync def general_endpoint(request: Request):\r\n    return {\"message\": \"This is a general endpoint.\"}\r\n\r\n@app.get(\"/heavy\")\r\n@heavy_limiter.limit()\r\nasync def heavy_endpoint(request: Request):\r\n    return {\"message\": \"This endpoint has stricter rate limiting.\"}\r\n```\r\n\r\n### Limiting by User ID\r\n\r\nIf your application uses authentication, you might want to rate limit based on user ID:\r\n\r\n```python\r\ndef user_id_key_function(self, request: Request) -> str:\r\n    user = request.state.user  # Assuming user is stored in request.state\r\n    return str(user.id) if user else \"anonymous\"\r\n\r\nrate_limiter.get_key_from_request = user_id_key_function.__get__(rate_limiter, RateLimiter)\r\n```\r\n\r\n### Customizing Rate Limit Exceeded Response\r\n\r\nCustomize the response when the rate limit is exceeded:\r\n\r\n```python\r\nfrom fastapi.responses import PlainTextResponse\r\n\r\nasync def rate_limit_middleware(request: Request, call_next: Callable):\r\n    key = rate_limiter.get_key_from_request(request)\r\n    try:\r\n        await rate_limiter.allow_request(key)\r\n        response = await call_next(request)\r\n        return response\r\n    except HTTPException as exc:\r\n        return PlainTextResponse(\r\n            \"You have exceeded your request rate limit. Please try again later.\",\r\n            status_code=exc.status_code\r\n        )\r\n\r\napp.middleware(\"http\")(rate_limit_middleware)\r\n```\r\n\r\n## Contributing\r\n\r\nWe welcome contributions to improve FastLimiter! Here's how you can contribute:\r\n\r\n### Reporting Bugs\r\n\r\nIf you find a bug, please open an issue on [GitHub](https://github.com/anto18671/fastlimiter/issues) with detailed steps to reproduce the problem.\r\n\r\n### Feature Requests\r\n\r\nHave an idea for a new feature? Open an issue with the tag \"enhancement\" to discuss it.\r\n\r\n### Pull Requests\r\n\r\n1. Fork the repository on GitHub.\r\n2. Clone your forked repository to your local machine.\r\n3. Create a new branch for your changes: `git checkout -b feature/your-feature-name`.\r\n4. Make your changes and commit them with clear messages.\r\n5. Push your changes to your fork: `git push origin feature/your-feature-name`.\r\n6. Open a pull request on the main repository.\r\n\r\n### Code Style\r\n\r\nPlease follow the PEP 8 style guide for Python code.\r\n\r\n### Testing\r\n\r\nEnsure that all existing tests pass and write new tests for your code.\r\n\r\n- Run tests with `pytest`.\r\n- For asynchronous code, use `pytest_asyncio`.\r\n\r\n## License\r\n\r\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\r\n\r\n## Contact\r\n\r\nFor questions or suggestions, please open an issue on [GitHub](https://github.com/anto18671/fastlimiter/issues):\r\n\r\n- **Author**: Anthony Therrien\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A fast token bucket rate limiter",
    "version": "0.1.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/anto18671/fastlimiter/issues",
        "Homepage": "https://github.com/anto18671/fastlimiter",
        "Source Code": "https://github.com/anto18671/fastlimiter"
    },
    "split_keywords": [
        "rate-limiter",
        " token-bucket",
        " fastapi",
        " middleware"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "579464e8af8f7ff4bcf0b69923a8fc808e28816d37f7e9c5f32989b95e132261",
                "md5": "cd74f6684ab17746daaf287df2c682fa",
                "sha256": "dd4b3c9b0bba5bf8cb1365e356d5fb72bc2d8f2293210f10c7d31c82a05ee323"
            },
            "downloads": -1,
            "filename": "fastlimiter-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cd74f6684ab17746daaf287df2c682fa",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 11078,
            "upload_time": "2024-09-13T22:06:04",
            "upload_time_iso_8601": "2024-09-13T22:06:04.774690Z",
            "url": "https://files.pythonhosted.org/packages/57/94/64e8af8f7ff4bcf0b69923a8fc808e28816d37f7e9c5f32989b95e132261/fastlimiter-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4ca6e206ba08ed55f3faed77c6fbff5e2cfd85ef1e93156c0f2f42c2fcc35e8a",
                "md5": "63813738a2270a215ced50aab887c825",
                "sha256": "2e7907e10e5119745234a8a308d944fae119e474391b2e3383831367ac0823b9"
            },
            "downloads": -1,
            "filename": "fastlimiter-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "63813738a2270a215ced50aab887c825",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 20134,
            "upload_time": "2024-09-13T22:06:06",
            "upload_time_iso_8601": "2024-09-13T22:06:06.267841Z",
            "url": "https://files.pythonhosted.org/packages/4c/a6/e206ba08ed55f3faed77c6fbff5e2cfd85ef1e93156c0f2f42c2fcc35e8a/fastlimiter-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-13 22:06:06",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "anto18671",
    "github_project": "fastlimiter",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "fastlimiter"
}
        
Elapsed time: 3.26609s