Metadata-Version: 2.4
Name: python-rate-limiter
Version: 0.1.0
Summary: A Python rate limiter library for FastAPI and Flask applications
Home-page: https://github.com/yourusername/python-rate-limiter
Author: Your Name
Author-email: your.email@example.com
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.68.0; extra == "fastapi"
Provides-Extra: flask
Requires-Dist: flask>=2.0.0; extra == "flask"
Provides-Extra: all
Requires-Dist: fastapi>=0.68.0; extra == "all"
Requires-Dist: flask>=2.0.0; extra == "all"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: provides-extra
Dynamic: requires-python
Dynamic: summary

# Rate Limiter

A Python rate limiter library for FastAPI and Flask applications. This library provides a simple, thread-safe rate limiting implementation using a sliding window algorithm.

## Features

- 🚀 **Easy to use** - Simple API for both FastAPI and Flask
- 🔒 **Thread-safe** - Safe for concurrent requests
- ⚡ **Lightweight** - No external dependencies for core functionality
- 🎯 **Flexible** - Support for custom key functions and exempt paths
- 📊 **Headers** - Automatic rate limit headers in responses

## Installation

**Current Status:** This package is not yet published to PyPI. Use one of the installation methods below.

### Option 1: Install from Local Source (Development/Use)

If you have the source code locally:

```bash
# Install in development mode (editable)
pip install -e .

# Or install normally
pip install .
```

For framework-specific integrations, install the dependencies:

```bash
# FastAPI
pip install -e ".[fastapi]"
# or
pip install fastapi

# Flask
pip install -e ".[flask]"
# or
pip install flask

# Both
pip install -e ".[all]"
```

### Option 2: Install from Git Repository

```bash
pip install git+https://github.com/yourusername/python-rate-limiter.git
```

### Option 3: Publish to PyPI (For Public Distribution)

**Note:** The package name `python-rate-limiter` may already be taken on PyPI. Check availability first and consider alternative names if needed.

To make the package available via `pip install python-rate-limiter`, you need to publish it to PyPI:

1. **Create accounts:**
   - Create an account on [PyPI](https://pypi.org/account/register/)
   - Create an account on [TestPyPI](https://test.pypi.org/account/register/) (for testing)

2. **Build the package:**
   ```bash
   pip install build twine
   python -m build
   ```

3. **Test on TestPyPI:**
   ```bash
   python -m twine upload --repository testpypi dist/*
   ```

4. **Publish to PyPI:**
   ```bash
   python -m twine upload dist/*
   ```

5. **Update setup.py:**
   - Update the `url` field with your actual repository URL
   - Update `author` and `author_email` with your information
   - If the package name is taken, update the `name` field

After publishing, users can install with:
```bash
pip install python-rate-limiter
```

For detailed publishing instructions, see [PUBLISHING.md](PUBLISHING.md).

## Quick Start

### FastAPI

#### Using Middleware (Recommended)

```python
from fastapi import FastAPI
from python_rate_limiter import FastAPIRateLimiter

app = FastAPI()

# Initialize rate limiter
limiter = FastAPIRateLimiter(
    max_requests=100,
    time_window=60.0,  # 60 seconds
    exempt_paths=["/health", "/docs"]  # Optional: exempt certain paths
)

# Add middleware
app.middleware("http")(limiter.middleware)

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/api/data")
async def get_data():
    return {"data": "some data"}
```

#### Using Decorator

```python
from fastapi import FastAPI, Request
from python_rate_limiter import rate_limit

app = FastAPI()

@app.get("/api/endpoint")
@rate_limit(max_requests=10, time_window=60)
async def endpoint(request: Request):
    return {"message": "Hello"}
```

#### Custom Key Function

```python
from fastapi import FastAPI, Request
from python_rate_limiter import rate_limit

app = FastAPI()

def get_user_id(request: Request) -> str:
    # Extract user ID from request (e.g., from JWT token)
    return request.headers.get("X-User-ID", "anonymous")

@app.get("/api/user-data")
@rate_limit(max_requests=50, time_window=60, key_func=get_user_id)
async def user_data(request: Request):
    return {"user_data": "..."}
```

### Flask

#### Using Extension (Recommended)

```python
from flask import Flask
from python_rate_limiter import FlaskRateLimiter

app = Flask(__name__)

# Initialize rate limiter
limiter = FlaskRateLimiter(
    max_requests=100,
    time_window=60.0,
    exempt_paths=["/health", "/static"]  # Optional: exempt certain paths
)

# Initialize with app
limiter.init_app(app)

@app.route("/")
def root():
    return {"message": "Hello World"}

@app.route("/api/data")
def get_data():
    return {"data": "some data"}
```

#### Using Decorator

```python
from flask import Flask
from python_rate_limiter import rate_limit

app = Flask(__name__)

@app.route("/api/endpoint")
@rate_limit(max_requests=10, time_window=60)
def endpoint():
    return {"message": "Hello"}
```

#### Custom Key Function

```python
from flask import Flask, request
from python_rate_limiter import rate_limit

app = Flask(__name__)

def get_user_id() -> str:
    # Extract user ID from request (e.g., from session or JWT)
    return request.headers.get("X-User-ID", "anonymous")

@app.route("/api/user-data")
@rate_limit(max_requests=50, time_window=60, key_func=get_user_id)
def user_data():
    return {"user_data": "..."}
```

## Core API

### RateLimiter

The core rate limiter class can be used independently:

```python
from python_rate_limiter import RateLimiter

limiter = RateLimiter(max_requests=100, time_window=60.0)

# Check if request is allowed
is_allowed, retry_after = limiter.is_allowed("user_ip_address")
if not is_allowed:
    print(f"Rate limit exceeded. Retry after {retry_after} seconds")

# Or raise exception
try:
    limiter.check_rate_limit("user_ip_address")
except RateLimitExceeded as e:
    print(f"Rate limit exceeded. Retry after {e.retry_after} seconds")

# Get remaining requests
remaining = limiter.get_remaining("user_ip_address")

# Reset rate limit
limiter.reset("user_ip_address")  # Reset specific key
limiter.reset()  # Reset all keys
```

## Response Headers

The library automatically adds rate limit headers to responses:

- `X-RateLimit-Limit`: Maximum number of requests allowed
- `X-RateLimit-Remaining`: Number of remaining requests
- `X-RateLimit-Reset`: Unix timestamp when the rate limit resets
- `Retry-After`: Seconds to wait before retrying (when rate limit exceeded)

## Error Responses

When rate limit is exceeded, the library returns:

- **Status Code**: `429 Too Many Requests`
- **Response Body**:
  ```json
  {
    "error": "Rate limit exceeded",
    "retry_after": 45.2,
    "message": "Too many requests. Please try again later."
  }
  ```

## Configuration Options

### FastAPIRateLimiter / FlaskRateLimiter

- `max_requests` (int): Maximum number of requests allowed (default: 100)
- `time_window` (float): Time window in seconds (default: 60.0)
- `key_func` (Callable): Function to extract key from request (default: uses IP address)
- `exempt_paths` (list[str]): List of path patterns to exempt from rate limiting

### Rate Limiter Algorithm

The library uses a **sliding window** algorithm, which provides accurate rate limiting by tracking individual request timestamps within the time window.

## Examples

See the `examples/` directory for complete working examples.

## License

MIT License

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

