limits 4.7.2__tar.gz → 5.0.0rc1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {limits-4.7.2 → limits-5.0.0rc1}/HISTORY.rst +16 -82
- {limits-4.7.2 → limits-5.0.0rc1}/PKG-INFO +7 -14
- {limits-4.7.2 → limits-5.0.0rc1}/README.rst +4 -3
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/_static/custom.css +1 -1
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/api.rst +0 -12
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/async.rst +3 -6
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/conf.py +12 -14
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/index.rst +2 -1
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/installation.rst +0 -20
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/quickstart.rst +42 -27
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/storage.rst +3 -36
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/strategies.rst +0 -11
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/theme_config.py +2 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/_version.py +3 -3
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/__init__.py +0 -2
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/base.py +1 -5
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/memcached.py +34 -58
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/memory.py +20 -38
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/mongodb.py +26 -31
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/redis/__init__.py +2 -4
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/redis/bridge.py +0 -1
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/redis/coredis.py +2 -6
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/redis/redispy.py +1 -8
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/strategies.py +1 -28
- {limits-4.7.2 → limits-5.0.0rc1}/limits/resources/redis/lua_scripts/acquire_moving_window.lua +5 -2
- limits-5.0.0rc1/limits/resources/redis/lua_scripts/moving_window.lua +30 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/__init__.py +0 -2
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/base.py +1 -5
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/memcached.py +6 -29
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/memory.py +16 -35
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/mongodb.py +25 -34
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/redis.py +1 -7
- {limits-4.7.2 → limits-5.0.0rc1}/limits/strategies.py +1 -31
- {limits-4.7.2 → limits-5.0.0rc1}/limits/typing.py +0 -50
- {limits-4.7.2 → limits-5.0.0rc1}/limits.egg-info/PKG-INFO +7 -14
- {limits-4.7.2 → limits-5.0.0rc1}/limits.egg-info/SOURCES.txt +0 -4
- {limits-4.7.2 → limits-5.0.0rc1}/limits.egg-info/requires.txt +2 -20
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/docs.txt +1 -1
- limits-5.0.0rc1/requirements/storage/async-memcached.txt +1 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/test.txt +1 -6
- {limits-4.7.2 → limits-5.0.0rc1}/setup.cfg +0 -6
- {limits-4.7.2 → limits-5.0.0rc1}/setup.py +0 -2
- {limits-4.7.2 → limits-5.0.0rc1}/tests/test_storage.py +0 -9
- {limits-4.7.2 → limits-5.0.0rc1}/tests/test_strategy.py +8 -43
- limits-4.7.2/limits/aio/storage/etcd.py +0 -146
- limits-4.7.2/limits/resources/redis/lua_scripts/moving_window.lua +0 -21
- limits-4.7.2/limits/storage/etcd.py +0 -139
- limits-4.7.2/requirements/storage/async-etcd.txt +0 -1
- limits-4.7.2/requirements/storage/async-memcached.txt +0 -2
- limits-4.7.2/requirements/storage/etcd.txt +0 -1
- {limits-4.7.2 → limits-5.0.0rc1}/CLASSIFIERS +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/CONTRIBUTIONS.rst +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/LICENSE.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/MANIFEST.in +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/Makefile +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/changelog.rst +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/custom-storage.rst +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/ext/_static/benchmark-chart.css +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/ext/_static/js/benchmark-chart.js +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/ext/_static/js/benchmark-details.js +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/ext/_static/js/benchmark-loader.js +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/ext/_templates/git_info.js +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/ext/bench_chart.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/doc/source/performance.rst +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/__init__.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/__init__.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/aio/storage/redis/valkey.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/errors.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/limits.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/py.typed +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/resources/redis/lua_scripts/acquire_sliding_window.lua +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/resources/redis/lua_scripts/clear_keys.lua +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/resources/redis/lua_scripts/incr_expire.lua +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/resources/redis/lua_scripts/sliding_window.lua +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/redis_cluster.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/redis_sentinel.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/storage/registry.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/util.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits/version.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits.egg-info/dependency_links.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits.egg-info/not-zip-safe +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/limits.egg-info/top_level.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/pyproject.toml +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/ci.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/dev.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/main.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/async-mongodb.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/async-redis.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/async-valkey.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/memcached.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/mongodb.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/redis.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/rediscluster.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/requirements/storage/valkey.txt +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/tests/test_limit_granularities.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/tests/test_limits.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/tests/test_ratelimit_parser.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/tests/test_utils.py +0 -0
- {limits-4.7.2 → limits-5.0.0rc1}/versioneer.py +0 -0
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
Changelog
|
|
4
4
|
=========
|
|
5
5
|
|
|
6
|
+
v5.0.0rc1
|
|
7
|
+
---------
|
|
8
|
+
Release Date: 2025-04-09
|
|
9
|
+
|
|
10
|
+
* Backward incompatible changes
|
|
11
|
+
|
|
12
|
+
* Dropped support for Fixed Window with Elastic Expiry strategy
|
|
13
|
+
* Dropped support for etcd
|
|
14
|
+
* Replaced async support for memached from :pypi:`emcache` to :pypi`:memcachio`
|
|
15
|
+
|
|
16
|
+
* Performance
|
|
17
|
+
|
|
18
|
+
* Improved performance of in-memory moving window ``test`` and ``get_window_stats`` operations.
|
|
19
|
+
* Improved performance of redis moving window ``test`` and ``get_window_stats`` operations.
|
|
20
|
+
* Improved performance of mongodb moving window ``test`` and ``get_window_stats`` operations.
|
|
21
|
+
|
|
6
22
|
v4.7.2
|
|
7
23
|
------
|
|
8
24
|
Release Date: 2025-04-09
|
|
@@ -793,85 +809,3 @@ Release Date: 2015-01-08
|
|
|
793
809
|
|
|
794
810
|
* Initial import of common rate limiting code from `Flask-Limiter <https://github.com/alisaifee/flask-limiter>`_
|
|
795
811
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: limits
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0rc1
|
|
4
4
|
Summary: Rate limiting utilities
|
|
5
5
|
Home-page: https://limits.readthedocs.org
|
|
6
6
|
Author: Ali-Akber Saifee
|
|
@@ -32,19 +32,14 @@ Provides-Extra: memcached
|
|
|
32
32
|
Requires-Dist: pymemcache<5.0.0,>3; extra == "memcached"
|
|
33
33
|
Provides-Extra: mongodb
|
|
34
34
|
Requires-Dist: pymongo<5,>4.1; extra == "mongodb"
|
|
35
|
-
Provides-Extra: etcd
|
|
36
|
-
Requires-Dist: etcd3; extra == "etcd"
|
|
37
35
|
Provides-Extra: valkey
|
|
38
36
|
Requires-Dist: valkey>=6; extra == "valkey"
|
|
39
37
|
Provides-Extra: async-redis
|
|
40
38
|
Requires-Dist: coredis<5,>=3.4.0; extra == "async-redis"
|
|
41
39
|
Provides-Extra: async-memcached
|
|
42
|
-
Requires-Dist:
|
|
43
|
-
Requires-Dist: emcache>=1; (python_version >= "3.11" and python_version < "3.13.0") and extra == "async-memcached"
|
|
40
|
+
Requires-Dist: memcachio>=0.3; extra == "async-memcached"
|
|
44
41
|
Provides-Extra: async-mongodb
|
|
45
42
|
Requires-Dist: motor<4,>=3; extra == "async-mongodb"
|
|
46
|
-
Provides-Extra: async-etcd
|
|
47
|
-
Requires-Dist: aetcd; extra == "async-etcd"
|
|
48
43
|
Provides-Extra: async-valkey
|
|
49
44
|
Requires-Dist: valkey>=6; extra == "async-valkey"
|
|
50
45
|
Provides-Extra: all
|
|
@@ -52,13 +47,10 @@ Requires-Dist: redis!=4.5.2,!=4.5.3,<6.0.0,>3; extra == "all"
|
|
|
52
47
|
Requires-Dist: redis!=4.5.2,!=4.5.3,>=4.2.0; extra == "all"
|
|
53
48
|
Requires-Dist: pymemcache<5.0.0,>3; extra == "all"
|
|
54
49
|
Requires-Dist: pymongo<5,>4.1; extra == "all"
|
|
55
|
-
Requires-Dist: etcd3; extra == "all"
|
|
56
50
|
Requires-Dist: valkey>=6; extra == "all"
|
|
57
51
|
Requires-Dist: coredis<5,>=3.4.0; extra == "all"
|
|
58
|
-
Requires-Dist:
|
|
59
|
-
Requires-Dist: emcache>=1; (python_version >= "3.11" and python_version < "3.13.0") and extra == "all"
|
|
52
|
+
Requires-Dist: memcachio>=0.3; extra == "all"
|
|
60
53
|
Requires-Dist: motor<4,>=3; extra == "all"
|
|
61
|
-
Requires-Dist: aetcd; extra == "all"
|
|
62
54
|
Requires-Dist: valkey>=6; extra == "all"
|
|
63
55
|
Dynamic: author
|
|
64
56
|
Dynamic: author-email
|
|
@@ -86,13 +78,14 @@ Dynamic: summary
|
|
|
86
78
|
.. |docs| image:: https://readthedocs.org/projects/limits/badge/?version=latest
|
|
87
79
|
:target: https://limits.readthedocs.org
|
|
88
80
|
|
|
81
|
+
######
|
|
89
82
|
limits
|
|
90
|
-
|
|
83
|
+
######
|
|
91
84
|
|docs| |ci| |codecov| |pypi| |pypi-versions| |license|
|
|
92
85
|
|
|
93
86
|
|
|
94
87
|
**limits** is a python library for rate limiting via multiple strategies
|
|
95
|
-
with commonly used storage backends (Redis, Memcached
|
|
88
|
+
with commonly used storage backends (Redis, Memcached & MongoDB).
|
|
96
89
|
|
|
97
90
|
The library provides identical APIs for use in sync and
|
|
98
91
|
`async <https://limits.readthedocs.io/en/stable/async.html>`_ codebases.
|
|
@@ -188,13 +181,13 @@ Scenario 2:
|
|
|
188
181
|
- ``weighted_count = floor(8 + (4 * 0.33)) = floor(8 + 1.32) = 9``.
|
|
189
182
|
- Since the weighted count is below the limit, the request is allowed.
|
|
190
183
|
|
|
184
|
+
|
|
191
185
|
Storage backends
|
|
192
186
|
================
|
|
193
187
|
|
|
194
188
|
- `Redis <https://limits.readthedocs.io/en/latest/storage.html#redis-storage>`_
|
|
195
189
|
- `Memcached <https://limits.readthedocs.io/en/latest/storage.html#memcached-storage>`_
|
|
196
190
|
- `MongoDB <https://limits.readthedocs.io/en/latest/storage.html#mongodb-storage>`_
|
|
197
|
-
- `Etcd <https://limits.readthedocs.io/en/latest/storage.html#etcd-storage>`_
|
|
198
191
|
- `In-Memory <https://limits.readthedocs.io/en/latest/storage.html#in-memory-storage>`_
|
|
199
192
|
|
|
200
193
|
Dive right in
|
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
.. |docs| image:: https://readthedocs.org/projects/limits/badge/?version=latest
|
|
12
12
|
:target: https://limits.readthedocs.org
|
|
13
13
|
|
|
14
|
+
######
|
|
14
15
|
limits
|
|
15
|
-
|
|
16
|
+
######
|
|
16
17
|
|docs| |ci| |codecov| |pypi| |pypi-versions| |license|
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
**limits** is a python library for rate limiting via multiple strategies
|
|
20
|
-
with commonly used storage backends (Redis, Memcached
|
|
21
|
+
with commonly used storage backends (Redis, Memcached & MongoDB).
|
|
21
22
|
|
|
22
23
|
The library provides identical APIs for use in sync and
|
|
23
24
|
`async <https://limits.readthedocs.io/en/stable/async.html>`_ codebases.
|
|
@@ -113,13 +114,13 @@ Scenario 2:
|
|
|
113
114
|
- ``weighted_count = floor(8 + (4 * 0.33)) = floor(8 + 1.32) = 9``.
|
|
114
115
|
- Since the weighted count is below the limit, the request is allowed.
|
|
115
116
|
|
|
117
|
+
|
|
116
118
|
Storage backends
|
|
117
119
|
================
|
|
118
120
|
|
|
119
121
|
- `Redis <https://limits.readthedocs.io/en/latest/storage.html#redis-storage>`_
|
|
120
122
|
- `Memcached <https://limits.readthedocs.io/en/latest/storage.html#memcached-storage>`_
|
|
121
123
|
- `MongoDB <https://limits.readthedocs.io/en/latest/storage.html#mongodb-storage>`_
|
|
122
|
-
- `Etcd <https://limits.readthedocs.io/en/latest/storage.html#etcd-storage>`_
|
|
123
124
|
- `In-Memory <https://limits.readthedocs.io/en/latest/storage.html#in-memory-storage>`_
|
|
124
125
|
|
|
125
126
|
Dive right in
|
|
@@ -28,7 +28,6 @@ a single parameter: a subclass of :class:`~limits.storage.Storage`.
|
|
|
28
28
|
Provided by :mod:`limits.strategies`
|
|
29
29
|
|
|
30
30
|
.. autoclass:: FixedWindowRateLimiter
|
|
31
|
-
.. autoclass:: FixedWindowElasticExpiryRateLimiter
|
|
32
31
|
.. autoclass:: MovingWindowRateLimiter
|
|
33
32
|
.. autoclass:: SlidingWindowCounterRateLimiter
|
|
34
33
|
|
|
@@ -47,7 +46,6 @@ expose async variants and expect a subclass of :class:`limits.aio.storage.Storag
|
|
|
47
46
|
Provided by :mod:`limits.aio.strategies`
|
|
48
47
|
|
|
49
48
|
.. autoclass:: FixedWindowRateLimiter
|
|
50
|
-
.. autoclass:: FixedWindowElasticExpiryRateLimiter
|
|
51
49
|
.. autoclass:: MovingWindowRateLimiter
|
|
52
50
|
.. autoclass:: SlidingWindowCounterRateLimiter
|
|
53
51
|
|
|
@@ -101,11 +99,6 @@ MongoDB Storage
|
|
|
101
99
|
|
|
102
100
|
.. autoclass:: MongoDBStorage
|
|
103
101
|
|
|
104
|
-
Etcd Storage
|
|
105
|
-
^^^^^^^^^^^^
|
|
106
|
-
|
|
107
|
-
.. autoclass:: EtcdStorage
|
|
108
|
-
|
|
109
102
|
|
|
110
103
|
Async Storage
|
|
111
104
|
-------------
|
|
@@ -144,11 +137,6 @@ Async MongoDB Storage
|
|
|
144
137
|
|
|
145
138
|
.. autoclass:: MongoDBStorage
|
|
146
139
|
|
|
147
|
-
Async Etcd Storage
|
|
148
|
-
^^^^^^^^^^^^^^^^^^
|
|
149
|
-
|
|
150
|
-
.. autoclass:: EtcdStorage
|
|
151
|
-
|
|
152
140
|
Abstract storage classes
|
|
153
141
|
------------------------
|
|
154
142
|
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
Async Support
|
|
3
3
|
=============
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
A new namespace ``limits.aio`` is available which mirrors the original
|
|
8
|
-
``limits.storage`` and ``limits.strategies`` packages.
|
|
5
|
+
The namespace ``limits.aio`` mirrors ``limits.storage`` and ``limits.strategies``
|
|
6
|
+
with async variants.
|
|
9
7
|
|
|
10
8
|
The following async storage backends are implemented:
|
|
11
9
|
|
|
@@ -14,9 +12,8 @@ The following async storage backends are implemented:
|
|
|
14
12
|
or `redis-py <https://redis-py.readthedocs.io>`_. Refer to
|
|
15
13
|
:paramref:`limits.aio.storage.RedisStorage.implementation` for
|
|
16
14
|
details on selecting the dependency)
|
|
17
|
-
- Memcached (via `
|
|
15
|
+
- Memcached (via `memcachio <https://memcachio.readthedocs.org>`_)
|
|
18
16
|
- MongoDB (via `motor <https://motor.readthedocs.org>`_)
|
|
19
|
-
- Etcd (via `aetcd <https://aetcd.readthedocs.org>`_)
|
|
20
17
|
|
|
21
18
|
Quick start
|
|
22
19
|
===========
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
#
|
|
2
|
+
from __future__ import annotations
|
|
2
3
|
|
|
3
4
|
import os
|
|
4
5
|
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
|
-
from docutils import nodes
|
|
8
|
-
from sphinx.application import Sphinx
|
|
9
|
-
from sphinx.util.docutils import SphinxDirective
|
|
10
|
-
|
|
11
8
|
sys.path.insert(0, os.path.abspath("../../"))
|
|
12
9
|
sys.path.insert(0, os.path.abspath("./"))
|
|
13
10
|
|
|
14
|
-
from theme_config import *
|
|
11
|
+
from theme_config import * # noqa
|
|
15
12
|
|
|
16
13
|
import limits
|
|
17
14
|
|
|
@@ -28,16 +25,18 @@ release = version
|
|
|
28
25
|
|
|
29
26
|
|
|
30
27
|
if branch_from_env := os.environ.get("READTHEDOCS_VERSION", None):
|
|
28
|
+
branch_from_env = "master" if branch_from_env == "latest" else branch_from_env
|
|
31
29
|
benchmark_git_context = {
|
|
32
30
|
"branch": branch_from_env,
|
|
33
|
-
"sha": os.environ.get("READTHEDOCS_GIT_COMMIT_HASH", "")
|
|
31
|
+
"sha": os.environ.get("READTHEDOCS_GIT_COMMIT_HASH", ""),
|
|
34
32
|
}
|
|
35
33
|
else:
|
|
36
34
|
import limits._version
|
|
35
|
+
|
|
37
36
|
git_info = limits._version.git_pieces_from_vcs("", os.path.abspath("../../"), False)
|
|
38
37
|
benchmark_git_context = {
|
|
39
38
|
"branch": git_info.get("branch", ""),
|
|
40
|
-
"sha": git_info.get("long", None)
|
|
39
|
+
"sha": git_info.get("long", None),
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
html_static_path = ["_static"]
|
|
@@ -48,18 +47,19 @@ html_css_files = [
|
|
|
48
47
|
]
|
|
49
48
|
|
|
50
49
|
html_title = f"{project} <small><b style='color: var(--color-brand-primary)'>{{{release}}}</b></small>"
|
|
50
|
+
|
|
51
51
|
try:
|
|
52
52
|
ahead = int(ahead)
|
|
53
53
|
if ahead > 0:
|
|
54
|
-
html_theme_options[
|
|
54
|
+
html_theme_options[ # noqa
|
|
55
55
|
"announcement"
|
|
56
56
|
] = f"""
|
|
57
57
|
This is a development version. The documentation for the latest version: <b>{release}</b> can be found <a href="/en/stable">here</a>
|
|
58
58
|
"""
|
|
59
59
|
html_title = f"{project} <small><b style='color: var(--color-brand-primary)'>{{dev}}</b></small>"
|
|
60
|
-
except:
|
|
60
|
+
except ValueError:
|
|
61
61
|
pass
|
|
62
|
-
sys.path.append(str(Path(
|
|
62
|
+
sys.path.append(str(Path("ext").resolve()))
|
|
63
63
|
|
|
64
64
|
extensions = [
|
|
65
65
|
"sphinx.ext.autodoc",
|
|
@@ -93,18 +93,16 @@ autosectionlabel_prefix_document = True
|
|
|
93
93
|
|
|
94
94
|
extlinks = {"pypi": ("https://pypi.org/project/%s", "%s")}
|
|
95
95
|
|
|
96
|
-
copybutton_exclude =
|
|
96
|
+
copybutton_exclude = ".gp, .go"
|
|
97
97
|
|
|
98
98
|
intersphinx_mapping = {
|
|
99
99
|
"python": ("http://docs.python.org/", None),
|
|
100
100
|
"coredis": ("https://coredis.readthedocs.io/en/latest/", None),
|
|
101
|
-
"
|
|
101
|
+
"memcachio": ("https://memcachio.readthedocs.io/en/latest/", None),
|
|
102
102
|
"motor": ("https://motor.readthedocs.io/en/stable/", None),
|
|
103
103
|
"redis-py-cluster": ("https://redis-py-cluster.readthedocs.io/en/latest/", None),
|
|
104
104
|
"redis-py": ("https://redis-py.readthedocs.io/en/latest/", None),
|
|
105
105
|
"pymemcache": ("https://pymemcache.readthedocs.io/en/latest/", None),
|
|
106
106
|
"pymongo": ("https://pymongo.readthedocs.io/en/stable/", None),
|
|
107
|
-
"python-etcd3": ("https://python-etcd3.readthedocs.io/en/latest/", None),
|
|
108
|
-
"aetcd": ("https://aetcd.readthedocs.io/en/latest/", None),
|
|
109
107
|
"valkey-py": ("https://valkey-py.readthedocs.io/en/latest/", None),
|
|
110
108
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
----
|
|
19
19
|
|
|
20
20
|
**limits** is a python library for rate limiting via multiple strategies
|
|
21
|
-
with commonly used storage backends (Redis, Memcached
|
|
21
|
+
with commonly used storage backends (Redis, Memcached & MongoDB).
|
|
22
22
|
|
|
23
23
|
The library provides identical APIs for use in sync and
|
|
24
24
|
:ref:`async <async:async support>` codebases.
|
|
@@ -92,4 +92,5 @@ References
|
|
|
92
92
|
- `Redis: rate limiting pattern #2 <http://redis.io/commands/INCR>`_
|
|
93
93
|
- `DomainTools: redis rate limiter <https://github.com/DomainTools/rate-limit>`_
|
|
94
94
|
- `Cloudflare: How we built rate limiting capable of scaling to millions of domains <https://blog.cloudflare.com/counting-things-a-lot-of-different-things/>`_
|
|
95
|
+
|
|
95
96
|
.. include:: ../../CONTRIBUTIONS.rst
|
|
@@ -48,16 +48,6 @@ Install the package with pip:
|
|
|
48
48
|
|
|
49
49
|
.. literalinclude:: ../../requirements/storage/mongodb.txt
|
|
50
50
|
|
|
51
|
-
.. tab:: Etcd
|
|
52
|
-
|
|
53
|
-
.. code:: console
|
|
54
|
-
|
|
55
|
-
$ pip install limits[etcd]
|
|
56
|
-
|
|
57
|
-
Includes:
|
|
58
|
-
|
|
59
|
-
.. literalinclude:: ../../requirements/storage/etcd.txt
|
|
60
|
-
|
|
61
51
|
.. tab:: Valkey
|
|
62
52
|
|
|
63
53
|
.. code:: console
|
|
@@ -115,16 +105,6 @@ along with the package using the following extras:
|
|
|
115
105
|
|
|
116
106
|
.. literalinclude:: ../../requirements/storage/async-mongodb.txt
|
|
117
107
|
|
|
118
|
-
.. tab:: Etcd
|
|
119
|
-
|
|
120
|
-
.. code:: console
|
|
121
|
-
|
|
122
|
-
$ pip install limits[async-etcd]
|
|
123
|
-
|
|
124
|
-
Includes:
|
|
125
|
-
|
|
126
|
-
.. literalinclude:: ../../requirements/storage/async-etcd.txt
|
|
127
|
-
|
|
128
108
|
.. tab:: Valkey
|
|
129
109
|
|
|
130
110
|
.. code:: console
|
|
@@ -92,49 +92,64 @@ Test the limits
|
|
|
92
92
|
Consume the limits
|
|
93
93
|
------------------
|
|
94
94
|
|
|
95
|
-
.. code::
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
.. code-block:: python-console
|
|
96
|
+
|
|
97
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
98
|
+
True
|
|
99
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
100
|
+
False
|
|
101
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "bar")
|
|
102
|
+
True
|
|
103
|
+
|
|
104
|
+
>>> limiter.hit(one_per_second, "test_namespace", "foo")
|
|
105
|
+
True
|
|
106
|
+
>>> limiter.hit(one_per_second, "test_namespace", "foo")
|
|
107
|
+
False
|
|
108
|
+
>>> time.sleep(1)
|
|
109
|
+
>>> limiter.hit(one_per_second, "test_namespace", "foo")
|
|
110
|
+
True
|
|
105
111
|
|
|
106
112
|
Check without consuming
|
|
107
113
|
-----------------------
|
|
108
114
|
|
|
109
|
-
.. code::
|
|
115
|
+
.. code-block:: python-console
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
>>> limiter.hit(one_per_second, "test_namespace", "foo")
|
|
118
|
+
True
|
|
119
|
+
>>> while not limiter.test(one_per_second, "test_namespace", "foo"):
|
|
120
|
+
... time.sleep(0.01)
|
|
121
|
+
>>> limiter.hit(one_per_second, "test_namespace", "foo")
|
|
122
|
+
True
|
|
115
123
|
|
|
116
124
|
Query available capacity and reset time
|
|
117
125
|
-----------------------------------------
|
|
118
126
|
|
|
119
|
-
.. code::
|
|
127
|
+
.. code-block:: python-console
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
130
|
+
True
|
|
131
|
+
>>> window = limiter.get_window_stats(one_per_minute, "test_namespace", "foo")
|
|
132
|
+
>>> window.remaining
|
|
133
|
+
0
|
|
134
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
135
|
+
False
|
|
136
|
+
>>> time.sleep(window.reset_time - time.time())
|
|
137
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
138
|
+
True
|
|
127
139
|
|
|
128
140
|
|
|
129
141
|
Clear a limit
|
|
130
142
|
=============
|
|
131
143
|
|
|
132
|
-
.. code::
|
|
144
|
+
.. code-block:: python-console
|
|
133
145
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
limiter.
|
|
137
|
-
|
|
146
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
147
|
+
True
|
|
148
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
149
|
+
False
|
|
150
|
+
>>> limiter.clear(one_per_minute, "test_namespace", "foo")
|
|
151
|
+
>>> limiter.hit(one_per_minute, "test_namespace", "foo")
|
|
152
|
+
True
|
|
138
153
|
|
|
139
154
|
|
|
140
155
|
|
|
@@ -36,18 +36,10 @@ the results in `github <https://github.com/alisaifee/limits/actions/workflows/co
|
|
|
36
36
|
:pypi:`redis` can be used instead of :pypi:`coredis` by setting
|
|
37
37
|
:paramref:`limits.aio.storage.RedisStorage.implementation` to ``redispy``
|
|
38
38
|
|
|
39
|
-
`Redis <https://redis.io>`_
|
|
39
|
+
`Redis Server <https://redis.io>`_
|
|
40
40
|
|
|
41
41
|
.. program-output:: bash -c "cat ../../.github/workflows/compatibility.yml | grep -o -P 'LIMITS_REDIS_SERVER_VERSION=[\d\.]+' | cut -d = -f 2 | sort --version-sort | uniq"
|
|
42
42
|
|
|
43
|
-
Redis with SSL
|
|
44
|
-
|
|
45
|
-
.. program-output:: bash -c "cat ../../.github/workflows/compatibility.yml | grep -o -P 'LIMITS_REDIS_SERVER_SSL_VERSION=[\d\.]+' | cut -d = -f 2 | sort --version-sort | uniq"
|
|
46
|
-
|
|
47
|
-
`Redis Sentinel <https://redis.io/topics/sentinel>`_
|
|
48
|
-
|
|
49
|
-
.. program-output:: bash -c "cat ../../.github/workflows/compatibility.yml | grep -o -P 'LIMITS_REDIS_SENTINEL_SERVER_VERSION=[\d\.]+' | cut -d = -f 2 | sort --version-sort | uniq"
|
|
50
|
-
|
|
51
43
|
.. tab:: Redis Cluster
|
|
52
44
|
|
|
53
45
|
Dependency versions:
|
|
@@ -63,7 +55,7 @@ the results in `github <https://github.com/alisaifee/limits/actions/workflows/co
|
|
|
63
55
|
:pypi:`redis` can be used instead of :pypi:`coredis` by setting
|
|
64
56
|
:paramref:`limits.aio.storage.RedisClusterStorage.implementation` to ``redispy``
|
|
65
57
|
|
|
66
|
-
`Redis
|
|
58
|
+
`Redis Cluster <https://redis.io/topics/cluster-tutorial>`_
|
|
67
59
|
|
|
68
60
|
.. program-output:: bash -c "cat ../../.github/workflows/compatibility.yml | grep -o -P 'LIMITS_REDIS_SERVER_VERSION=[\d\.]+' | cut -d = -f 2 | sort --version-sort | uniq"
|
|
69
61
|
|
|
@@ -95,20 +87,6 @@ the results in `github <https://github.com/alisaifee/limits/actions/workflows/co
|
|
|
95
87
|
|
|
96
88
|
.. program-output:: bash -c "cat ../../.github/workflows/compatibility.yml | grep -o -P 'LIMITS_MONGODB_SERVER_VERSION=[\d\.]+' | cut -d = -f 2 | sort --version-sort | uniq"
|
|
97
89
|
|
|
98
|
-
.. tab:: Etcd
|
|
99
|
-
|
|
100
|
-
Dependency versions:
|
|
101
|
-
|
|
102
|
-
.. literalinclude:: ../../requirements/storage/etcd.txt
|
|
103
|
-
|
|
104
|
-
Dependency versions (async):
|
|
105
|
-
|
|
106
|
-
.. literalinclude:: ../../requirements/storage/async-etcd.txt
|
|
107
|
-
|
|
108
|
-
`Etcd <https://www.etcd.io/>`_
|
|
109
|
-
|
|
110
|
-
.. program-output:: bash -c "cat ../../.github/workflows/compatibility.yml | grep -o -P 'LIMITS_ETCD_SERVER_VERSION=[\d\.]+' | cut -d = -f 2 | sort --version-sort | uniq"
|
|
111
|
-
|
|
112
90
|
.. tab:: Valkey
|
|
113
91
|
|
|
114
92
|
Dependency versions:
|
|
@@ -246,20 +224,9 @@ Examples:
|
|
|
246
224
|
|
|
247
225
|
Depends on: :pypi:`pymongo`
|
|
248
226
|
|
|
249
|
-
Etcd Storage
|
|
250
|
-
------------
|
|
251
|
-
|
|
252
|
-
Requires the location of an etcd node
|
|
253
|
-
|
|
254
|
-
Example: ``etcd://localhost:2379``
|
|
255
|
-
|
|
256
|
-
Depends on: :pypi:`etcd3`
|
|
257
|
-
|
|
258
227
|
Async Storage
|
|
259
228
|
=============
|
|
260
229
|
|
|
261
|
-
.. versionadded:: 2.1
|
|
262
|
-
|
|
263
230
|
When using limits in an async code base the same uri schema can be used
|
|
264
231
|
to query for an async implementation of the storage by prefixing the
|
|
265
232
|
scheme with ``async+``.
|
|
@@ -271,7 +238,7 @@ For example:
|
|
|
271
238
|
- ``async+redis+cluster://localhost:7000,localhost:7001``
|
|
272
239
|
- ``async+redis+sentinel://:sekret@localhost:26379/my-redis-service``
|
|
273
240
|
- ``async+memcached://localhost:11211``
|
|
274
|
-
- ``async+
|
|
241
|
+
- ``async+mongodb://localhost:27017``
|
|
275
242
|
- ``async+memory://``
|
|
276
243
|
|
|
277
244
|
For implementation details of currently supported async backends refer to :ref:`api:async storage`
|
|
@@ -41,17 +41,6 @@ For example, with a rate limit of 10 requests per minute:
|
|
|
41
41
|
(e.g., combine a 2 requests per second limit with a 10 requests per minute limit).
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
Fixed Window with Elastic Expiry
|
|
46
|
-
==================================
|
|
47
|
-
.. deprecated:: 4.1
|
|
48
|
-
|
|
49
|
-
This variant extends the window’s expiry with each hit by resetting the timer on
|
|
50
|
-
every request. Although designed to impose larger penalties for breaches, it is now
|
|
51
|
-
deprecated and should not be used.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
44
|
Moving Window
|
|
56
45
|
=============
|
|
57
46
|
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-04-
|
|
11
|
+
"date": "2025-04-09T18:20:47-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "
|
|
14
|
+
"full-revisionid": "4a01f1090a5accfb05b7db6dc6469f7c51d4fa67",
|
|
15
|
+
"version": "5.0.0rc1"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -6,14 +6,12 @@ Implementations of storage backends to be used with
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
from .base import MovingWindowSupport, SlidingWindowCounterSupport, Storage
|
|
9
|
-
from .etcd import EtcdStorage
|
|
10
9
|
from .memcached import MemcachedStorage
|
|
11
10
|
from .memory import MemoryStorage
|
|
12
11
|
from .mongodb import MongoDBStorage
|
|
13
12
|
from .redis import RedisClusterStorage, RedisSentinelStorage, RedisStorage
|
|
14
13
|
|
|
15
14
|
__all__ = [
|
|
16
|
-
"EtcdStorage",
|
|
17
15
|
"MemcachedStorage",
|
|
18
16
|
"MemoryStorage",
|
|
19
17
|
"MongoDBStorage",
|
|
@@ -75,16 +75,12 @@ class Storage(LazyDependency, metaclass=StorageRegistry):
|
|
|
75
75
|
raise NotImplementedError
|
|
76
76
|
|
|
77
77
|
@abstractmethod
|
|
78
|
-
async def incr(
|
|
79
|
-
self, key: str, expiry: int, elastic_expiry: bool = False, amount: int = 1
|
|
80
|
-
) -> int:
|
|
78
|
+
async def incr(self, key: str, expiry: int, amount: int = 1) -> int:
|
|
81
79
|
"""
|
|
82
80
|
increments the counter for a given rate limit key
|
|
83
81
|
|
|
84
82
|
:param key: the key to increment
|
|
85
83
|
:param expiry: amount in seconds for the key to expire in
|
|
86
|
-
:param elastic_expiry: whether to keep extending the rate limit
|
|
87
|
-
window every hit.
|
|
88
84
|
:param amount: the number to increment by
|
|
89
85
|
"""
|
|
90
86
|
raise NotImplementedError
|