limiters


Namelimiters JSON
Version 0.1.2 PyPI version JSON
download
home_pagehttps://github.com/otovo/limiters
SummaryDistributed rate limiters
upload_time2023-05-22 11:11:36
maintainer
docs_urlNone
authorSondre Lillebø Gundersen
requires_python>=3.11,<4.0
licenseBSD-4-Clause
keywords async sync rate limiting limiters
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Limiters

Provides logic for handling two distrinct types of rate limits: concurrency- and time-based.

The data structures are distributed, using Redis, and leverages Lua scripts to save round-trips. A sync and async version is available for
both.

Compatible with redis-clusters. Currently only supports Python 3.11, but adding support for other versions would be simple if desired.

## Installation

```
pip install limiters
```

## Semaphore

The `Semaphore` classes are useful if you're working with concurrency-based
rate limits. Say, you are allowed to have 5 active requests at the time
for a given API token.

On trying to acquire the Semaphore, beware that the client will block the thread until the Semaphore is acquired, or the `max_sleep` limit
is exceeded.

If the `max_sleep` limit is exceeded, a `MaxSleepExceededError` is raised.

Here's how you might use the async version:

```python
from httpx import AsyncClient

from limiters import AsyncSemaphore

limiter = AsyncSemaphore(
    name="foo",  # name of the resource you are limiting traffic for
    capacity=5,  # allow 5 concurrent requests
    max_sleep=30,  # raise an error if it takes longer than 30 seconds to acquire the semaphore
    expiry=30  # set expiry on the semaphore keys in Redis to prevent deadlocks
)


async def get_foo():
    async with AsyncClient() as client:
        async with limiter:
            client.get(...)


async def main():
    await asyncio.gather(
        get_foo() for i in range(100)
    )
```

and here is how you might use the sync version:

```python
import requests

from limiters import SyncSemaphore

limiter = SyncSemaphore(
    name="foo",
    capacity=5,
    max_sleep=30,
    expiry=30
)


def main():
    with limiter:
        requests.get(...)
```

## Token bucket

The `TocketBucket` classes are useful if you're working with time-based
rate limits. Say, you are allowed 100 requests per minute, for a given API token.

If the `max_sleep` limit is exceeded, a `MaxSleepExceededError` is raised.

Here's how you might use the async version:

```python
from httpx import AsyncClient

from limiters import AsyncTokenBucket

limiter = AsyncTokenBucket(
    name="foo",  # name of the resource you are limiting traffic for
    capacity=5,  # hold up to 5 tokens
    refill_frequency=1,  # add tokens every second
    refill_amount=1,  # add 1 token when refilling
    max_sleep=30,  # raise an error there are no free tokens for 30 seconds
)


async def get_foo():
    async with AsyncClient() as client:
        async with limiter:
            client.get(...)


async def main():
    await asyncio.gather(
        get_foo() for i in range(100)
    )
```

and here is how you might use the sync version:

```python
import requests

from limiters import SyncTokenBucket

limiter = SyncTokenBucket(
    name="foo",  # name of the resource you are limiting traffic for
    capacity=5,  # hold up to 5 tokens
    refill_frequency=1,  # add tokens every second
    refill_amount=1,  # add 1 token when refilling
    max_sleep=30,  # raise an error there are no free tokens for 30 seconds
)


def main():
    with limiter:
        requests.get(...)
```

## Contributing

Contributions are very welcome. Here's how to get started:

- Set up a Python 3.11+ venv, and install `poetry`
- Install dependencies with `poetry install`
- Run `pre-commit install` to set up pre-commit
- Run `docker compose up` to run Redis (or run it some other way)
- Make your code changes
- Add tests
- Commit your changes and open a PR

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/otovo/limiters",
    "name": "limiters",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.11,<4.0",
    "maintainer_email": "",
    "keywords": "async,sync,rate,limiting,limiters",
    "author": "Sondre Lilleb\u00f8 Gundersen",
    "author_email": "sondrelg@live.no",
    "download_url": "https://files.pythonhosted.org/packages/6a/54/847903a58ebc7d150295b6557ea89c368c3f62ffdb536cb00204744add3f/limiters-0.1.2.tar.gz",
    "platform": null,
    "description": "# Limiters\n\nProvides logic for handling two distrinct types of rate limits: concurrency- and time-based.\n\nThe data structures are distributed, using Redis, and leverages Lua scripts to save round-trips. A sync and async version is available for\nboth.\n\nCompatible with redis-clusters. Currently only supports Python 3.11, but adding support for other versions would be simple if desired.\n\n## Installation\n\n```\npip install limiters\n```\n\n## Semaphore\n\nThe `Semaphore` classes are useful if you're working with concurrency-based\nrate limits. Say, you are allowed to have 5 active requests at the time\nfor a given API token.\n\nOn trying to acquire the Semaphore, beware that the client will block the thread until the Semaphore is acquired, or the `max_sleep` limit\nis exceeded.\n\nIf the `max_sleep` limit is exceeded, a `MaxSleepExceededError` is raised.\n\nHere's how you might use the async version:\n\n```python\nfrom httpx import AsyncClient\n\nfrom limiters import AsyncSemaphore\n\nlimiter = AsyncSemaphore(\n    name=\"foo\",  # name of the resource you are limiting traffic for\n    capacity=5,  # allow 5 concurrent requests\n    max_sleep=30,  # raise an error if it takes longer than 30 seconds to acquire the semaphore\n    expiry=30  # set expiry on the semaphore keys in Redis to prevent deadlocks\n)\n\n\nasync def get_foo():\n    async with AsyncClient() as client:\n        async with limiter:\n            client.get(...)\n\n\nasync def main():\n    await asyncio.gather(\n        get_foo() for i in range(100)\n    )\n```\n\nand here is how you might use the sync version:\n\n```python\nimport requests\n\nfrom limiters import SyncSemaphore\n\nlimiter = SyncSemaphore(\n    name=\"foo\",\n    capacity=5,\n    max_sleep=30,\n    expiry=30\n)\n\n\ndef main():\n    with limiter:\n        requests.get(...)\n```\n\n## Token bucket\n\nThe `TocketBucket` classes are useful if you're working with time-based\nrate limits. Say, you are allowed 100 requests per minute, for a given API token.\n\nIf the `max_sleep` limit is exceeded, a `MaxSleepExceededError` is raised.\n\nHere's how you might use the async version:\n\n```python\nfrom httpx import AsyncClient\n\nfrom limiters import AsyncTokenBucket\n\nlimiter = AsyncTokenBucket(\n    name=\"foo\",  # name of the resource you are limiting traffic for\n    capacity=5,  # hold up to 5 tokens\n    refill_frequency=1,  # add tokens every second\n    refill_amount=1,  # add 1 token when refilling\n    max_sleep=30,  # raise an error there are no free tokens for 30 seconds\n)\n\n\nasync def get_foo():\n    async with AsyncClient() as client:\n        async with limiter:\n            client.get(...)\n\n\nasync def main():\n    await asyncio.gather(\n        get_foo() for i in range(100)\n    )\n```\n\nand here is how you might use the sync version:\n\n```python\nimport requests\n\nfrom limiters import SyncTokenBucket\n\nlimiter = SyncTokenBucket(\n    name=\"foo\",  # name of the resource you are limiting traffic for\n    capacity=5,  # hold up to 5 tokens\n    refill_frequency=1,  # add tokens every second\n    refill_amount=1,  # add 1 token when refilling\n    max_sleep=30,  # raise an error there are no free tokens for 30 seconds\n)\n\n\ndef main():\n    with limiter:\n        requests.get(...)\n```\n\n## Contributing\n\nContributions are very welcome. Here's how to get started:\n\n- Set up a Python 3.11+ venv, and install `poetry`\n- Install dependencies with `poetry install`\n- Run `pre-commit install` to set up pre-commit\n- Run `docker compose up` to run Redis (or run it some other way)\n- Make your code changes\n- Add tests\n- Commit your changes and open a PR\n",
    "bugtrack_url": null,
    "license": "BSD-4-Clause",
    "summary": "Distributed rate limiters",
    "version": "0.1.2",
    "project_urls": {
        "Homepage": "https://github.com/otovo/limiters",
        "Repository": "https://github.com/otovo/limiters"
    },
    "split_keywords": [
        "async",
        "sync",
        "rate",
        "limiting",
        "limiters"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a70058056ac01edaa8a5188dbd474a12721b272940058e06acf47e8d15d928bf",
                "md5": "7a28e1f7ed7b7bbb7c6f3c10888b6d4f",
                "sha256": "57423433c8246f2f3477ccbf5dfa716387ea710fe1e2ce2cd01e5c4d266a60dc"
            },
            "downloads": -1,
            "filename": "limiters-0.1.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7a28e1f7ed7b7bbb7c6f3c10888b6d4f",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11,<4.0",
            "size": 9503,
            "upload_time": "2023-05-22T11:11:34",
            "upload_time_iso_8601": "2023-05-22T11:11:34.073088Z",
            "url": "https://files.pythonhosted.org/packages/a7/00/58056ac01edaa8a5188dbd474a12721b272940058e06acf47e8d15d928bf/limiters-0.1.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6a54847903a58ebc7d150295b6557ea89c368c3f62ffdb536cb00204744add3f",
                "md5": "f881d15b6ce91b759b375ecfc65c065f",
                "sha256": "21c187ae92aa46dc368129b147bb90aa778ac7a96603d1fdde748a2990bfc901"
            },
            "downloads": -1,
            "filename": "limiters-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "f881d15b6ce91b759b375ecfc65c065f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11,<4.0",
            "size": 8485,
            "upload_time": "2023-05-22T11:11:36",
            "upload_time_iso_8601": "2023-05-22T11:11:36.494703Z",
            "url": "https://files.pythonhosted.org/packages/6a/54/847903a58ebc7d150295b6557ea89c368c3f62ffdb536cb00204744add3f/limiters-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-05-22 11:11:36",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "otovo",
    "github_project": "limiters",
    "github_not_found": true,
    "lcname": "limiters"
}
        
Elapsed time: 0.06822s