Metadata-Version: 2.3
Name: aiofreqlimit
Version: 0.2.2
Summary: Frequency limit for asyncio
Author: Gleb Chipiga
License: MIT License
         
         Copyright (c) 2018-2025 Gleb Chipiga
         
         Permission is hereby granted, free of charge, to any person obtaining a copy
         of this software and associated documentation files (the "Software"), to deal
         in the Software without restriction, including without limitation the rights
         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         copies of the Software, and to permit persons to whom the Software is
         furnished to do so, subject to the following conditions:
         
         The above copyright notice and this permission notice shall be included in all
         copies or substantial portions of the Software.
         
         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: License :: OSI Approved :: MIT License
Classifier: Development Status :: 4 - Beta
Classifier: Operating System :: POSIX
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Internet
Classifier: Framework :: AsyncIO
Requires-Dist: redis>=5.0.0,<6.0.0 ; extra == 'redis'
Requires-Dist: pytest>=9.0.1,<10.0.0 ; extra == 'test'
Requires-Dist: pytest-asyncio>=1.3.0,<2.0.0 ; extra == 'test'
Requires-Dist: hypothesis>=6.148.1,<7.0.0 ; extra == 'test'
Requires-Dist: pytest-cov>=7.0.0,<8.0.0 ; extra == 'test'
Requires-Dist: pytest-mock>=3.14.0,<4.0.0 ; extra == 'test'
Requires-Dist: redis>=5.0.0,<6.0.0 ; extra == 'test'
Requires-Dist: testcontainers[redis]>=4.3.3,<5.0.0 ; extra == 'test'
Requires-Python: >=3.11, <3.15
Project-URL: Homepage, https://github.com/gleb-chipiga/aiofreqlimit
Provides-Extra: redis
Provides-Extra: test
Description-Content-Type: text/markdown

# aiofreqlimit — Async GCRA rate limiter

[![Latest PyPI package version](https://badge.fury.io/py/aiofreqlimit.svg)](https://pypi.org/project/aiofreqlimit)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Downloads](https://img.shields.io/pypi/dm/aiofreqlimit)](https://pypistats.org/packages/aiofreqlimit)

Async rate limiting for Python 3.11+ built on the Generic Cell Rate Algorithm (GCRA) with
type-safe parameters and pluggable backends.

## Installation

```bash
pip install aiofreqlimit
```

## Quickstart

Create a contract (`FreqLimitParams`), choose a backend, and wrap your code with the
async context manager:

```python
import asyncio

from aiofreqlimit import FreqLimit, FreqLimitParams
from aiofreqlimit.backends.memory import InMemoryBackend

params = FreqLimitParams(limit=1, period=1.0, burst=1)  # 1 op / second
limiter = FreqLimit(params, backend=InMemoryBackend())


async def send_message(chat_id: int, text: str) -> None:
    async with limiter.resource(f"chat:{chat_id}"):
        await bot.send_message(chat_id, text)


async def main() -> None:
    await asyncio.gather(*(send_message(42, f"msg {i}") for i in range(5)))


asyncio.run(main())
```

- `key` is any hashable; `None` uses a global bucket.
- `burst` lets you allow short bursts without changing the long-term rate.

## Params are type-safe

You can keep your limits as constants and reuse them across the project:

```python
from aiofreqlimit import FreqLimitParams

TELEGRAM_PER_CHAT = FreqLimitParams(limit=1, period=1.0, burst=1)
TELEGRAM_PER_GROUP = FreqLimitParams(limit=20, period=60.0, burst=3)
```

## Backends

- `InMemoryBackend` (in-process, single event loop) — import from
  `aiofreqlimit.backends.memory`.
  - `idle_ttl: float | None` — drop idle keys after this many seconds (default: None).
  - `sweeper_interval: float | None` — optional background cleanup period; set to
    enable a sweeper task (default: None).
- `RedisBackend` (shared, multi-host) — import from `aiofreqlimit.backends.redis`.
  - Install optional deps: `pip install aiofreqlimit[redis]`.
  - Uses Redis server time and a Lua script for atomic GCRA steps.
  - `prefix: str` — key prefix (default `freqlimit:gcra:`).
  - `extra_ttl: float` — small buffer added to debt horizon; controls how long keys
    stay after backlog is cleared.
- Implement `FreqLimitBackend` to plug in other stores:

```python
from collections.abc import Hashable
from aiofreqlimit import FreqLimitBackend, FreqLimitParams


class RedisBackend(FreqLimitBackend):
    async def reserve(self, key: Hashable, now: float, params: FreqLimitParams) -> float:
        ...
```

`FreqLimit` requires an explicit backend instance; no default is provided.

## Testing

The library ships with pytest + hypothesis tests. To run them with uv:

```bash
uv run pytest tests
```

Integration tests for the Redis backend use Testcontainers; Docker must be available
for those cases.

## License

MIT
