Metadata-Version: 2.1
Name: limits
Version: 3.13.0
Summary: Rate limiting utilities
Home-page: https://limits.readthedocs.org
Author: Ali-Akber Saifee
Author-email: ali@indydevs.org
License: MIT
Project-URL: Source, https://github.com/alisaifee/limits
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
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
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.8
License-File: LICENSE.txt
Requires-Dist: deprecated>=1.2
Requires-Dist: importlib_resources>=1.3
Requires-Dist: packaging<25,>=21
Requires-Dist: typing_extensions
Provides-Extra: redis
Requires-Dist: redis!=4.5.2,!=4.5.3,<6.0.0,>3; extra == "redis"
Provides-Extra: rediscluster
Requires-Dist: redis!=4.5.2,!=4.5.3,>=4.2.0; extra == "rediscluster"
Provides-Extra: memcached
Requires-Dist: pymemcache<5.0.0,>3; extra == "memcached"
Provides-Extra: mongodb
Requires-Dist: pymongo<5,>4.1; extra == "mongodb"
Provides-Extra: etcd
Requires-Dist: etcd3; extra == "etcd"
Provides-Extra: async-redis
Requires-Dist: coredis<5,>=3.4.0; extra == "async-redis"
Provides-Extra: async-memcached
Requires-Dist: emcache>=0.6.1; python_version < "3.11" and extra == "async-memcached"
Requires-Dist: emcache>=1; python_version >= "3.11" and extra == "async-memcached"
Provides-Extra: async-mongodb
Requires-Dist: motor<4,>=3; extra == "async-mongodb"
Provides-Extra: async-etcd
Requires-Dist: aetcd; extra == "async-etcd"
Provides-Extra: all
Requires-Dist: redis!=4.5.2,!=4.5.3,<6.0.0,>3; extra == "all"
Requires-Dist: redis!=4.5.2,!=4.5.3,>=4.2.0; extra == "all"
Requires-Dist: pymemcache<5.0.0,>3; extra == "all"
Requires-Dist: pymongo<5,>4.1; extra == "all"
Requires-Dist: etcd3; extra == "all"
Requires-Dist: coredis<5,>=3.4.0; extra == "all"
Requires-Dist: emcache>=0.6.1; python_version < "3.11" and extra == "all"
Requires-Dist: emcache>=1; python_version >= "3.11" and extra == "all"
Requires-Dist: motor<4,>=3; extra == "all"
Requires-Dist: aetcd; extra == "all"

.. |ci| image:: https://github.com/alisaifee/limits/workflows/CI/badge.svg?branch=master
    :target: https://github.com/alisaifee/limits/actions?query=branch%3Amaster+workflow%3ACI
.. |codecov| image:: https://codecov.io/gh/alisaifee/limits/branch/master/graph/badge.svg
   :target: https://codecov.io/gh/alisaifee/limits
.. |pypi| image:: https://img.shields.io/pypi/v/limits.svg?style=flat-square
    :target: https://pypi.python.org/pypi/limits
.. |pypi-versions| image:: https://img.shields.io/pypi/pyversions/limits?style=flat-square
    :target: https://pypi.python.org/pypi/limits
.. |license| image:: https://img.shields.io/pypi/l/limits.svg?style=flat-square
    :target: https://pypi.python.org/pypi/limits
.. |docs| image:: https://readthedocs.org/projects/limits/badge/?version=latest
   :target: https://limits.readthedocs.org

limits
------
|docs| |ci| |codecov| |pypi| |pypi-versions| |license|


**limits** is a python library to perform rate limiting with commonly used storage backends (Redis, Memcached, MongoDB & Etcd).


Supported Strategies
====================
`Fixed Window <https://limits.readthedocs.io/en/latest/strategies.html#fixed-window>`_
   This strategy resets at a fixed interval (start of minute, hour, day etc).
   For example, given a rate limit of ``10/minute`` the strategy will:

   - Allow 10 requests between ``00:01:00`` and ``00:02:00``
   - Allow 10 requests at ``00:00:59`` and 10 more requests at ``00:01:00``


`Fixed Window (Elastic) <https://limits.readthedocs.io/en/latest/strategies.html#fixed-window-with-elastic-expiry>`_
   Identical to Fixed window, except every breach of rate limit results in an extension
   to the time out. For example a rate limit of `1/minute` hit twice within a minute will
   result in a lock-out for two minutes.

`Moving Window <https://limits.readthedocs.io/en/latest/strategies.html#moving-window>`_
   Sliding window strategy enforces a rate limit of N/(m time units)
   on the **last m** time units at the second granularity.

   For example, with a rate limit of ``10/minute``:

   - Allow 9 requests that arrive at ``00:00:59``
   - Allow another request that arrives at ``00:01:00``
   - Reject the request that arrives at ``00:01:01``

Storage backends
================

- `Redis <https://limits.readthedocs.io/en/latest/storage.html#redis-storage>`_
- `Memcached <https://limits.readthedocs.io/en/latest/storage.html#memcached-storage>`_
- `MongoDB <https://limits.readthedocs.io/en/latest/storage.html#mongodb-storage>`_
- `Etcd <https://limits.readthedocs.io/en/latest/storage.html#etcd-storage>`_
- `In-Memory <https://limits.readthedocs.io/en/latest/storage.html#in-memory-storage>`_

Dive right in
=============

Initialize the storage backend

.. code-block:: python

   from limits import storage
   memory_storage = storage.MemoryStorage()
   # or memcached
   memcached_storage = storage.MemcachedStorage("memcached://localhost:11211")
   # or redis
   redis_storage = storage.RedisStorage("redis://localhost:6379")
   # or use the factory
   storage_uri = "memcached://localhost:11211"
   some_storage = storage.storage_from_string(storage_uri)

Initialize a rate limiter with the Moving Window Strategy

.. code-block:: python

   from limits import strategies
   moving_window = strategies.MovingWindowRateLimiter(memory_storage)


Initialize a rate limit

.. code-block:: python

    from limits import parse
    one_per_minute = parse("1/minute")

Initialize a rate limit explicitly

.. code-block:: python

    from limits import RateLimitItemPerSecond
    one_per_second = RateLimitItemPerSecond(1, 1)

Test the limits

.. code-block:: python

    assert True == moving_window.hit(one_per_minute, "test_namespace", "foo")
    assert False == moving_window.hit(one_per_minute, "test_namespace", "foo")
    assert True == moving_window.hit(one_per_minute, "test_namespace", "bar")

    assert True == moving_window.hit(one_per_second, "test_namespace", "foo")
    assert False == moving_window.hit(one_per_second, "test_namespace", "foo")
    time.sleep(1)
    assert True == moving_window.hit(one_per_second, "test_namespace", "foo")

Check specific limits without hitting them

.. code-block:: python

    assert True == moving_window.hit(one_per_second, "test_namespace", "foo")
    while not moving_window.test(one_per_second, "test_namespace", "foo"):
        time.sleep(0.01)
    assert True == moving_window.hit(one_per_second, "test_namespace", "foo")

Links
=====

* `Documentation <http://limits.readthedocs.org/en/latest>`_
* `Changelog <http://limits.readthedocs.org/en/stable/changelog.html>`_

