limits 3.12.0__tar.gz → 3.13.0__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-3.12.0 → limits-3.13.0}/HISTORY.rst +9 -0
- {limits-3.12.0 → limits-3.13.0}/PKG-INFO +1 -1
- {limits-3.12.0 → limits-3.13.0}/limits/_version.py +3 -3
- {limits-3.12.0 → limits-3.13.0}/limits/aio/strategies.py +10 -5
- {limits-3.12.0 → limits-3.13.0}/limits/strategies.py +8 -5
- {limits-3.12.0 → limits-3.13.0}/limits.egg-info/PKG-INFO +1 -1
- {limits-3.12.0 → limits-3.13.0}/tests/test_strategy.py +5 -3
- {limits-3.12.0 → limits-3.13.0}/CLASSIFIERS +0 -0
- {limits-3.12.0 → limits-3.13.0}/CONTRIBUTIONS.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/LICENSE.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/MANIFEST.in +0 -0
- {limits-3.12.0 → limits-3.13.0}/README.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/Makefile +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/_static/custom.css +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/api.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/async.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/changelog.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/conf.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/custom-storage.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/index.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/installation.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/quickstart.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/storage.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/strategies.rst +0 -0
- {limits-3.12.0 → limits-3.13.0}/doc/source/theme_config.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/__init__.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/__init__.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/storage/__init__.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/storage/base.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/storage/etcd.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/storage/memcached.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/storage/memory.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/storage/mongodb.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/aio/storage/redis.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/errors.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/limits.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/py.typed +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/resources/redis/lua_scripts/acquire_moving_window.lua +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/resources/redis/lua_scripts/clear_keys.lua +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/resources/redis/lua_scripts/incr_expire.lua +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/resources/redis/lua_scripts/moving_window.lua +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/__init__.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/base.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/etcd.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/memcached.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/memory.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/mongodb.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/redis.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/redis_cluster.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/redis_sentinel.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/storage/registry.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/typing.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/util.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits/version.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits.egg-info/SOURCES.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits.egg-info/dependency_links.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits.egg-info/not-zip-safe +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits.egg-info/requires.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/limits.egg-info/top_level.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/pyproject.toml +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/ci.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/dev.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/docs.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/main.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/async-etcd.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/async-memcached.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/async-mongodb.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/async-redis.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/etcd.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/memcached.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/mongodb.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/redis.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/storage/rediscluster.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/requirements/test.txt +0 -0
- {limits-3.12.0 → limits-3.13.0}/setup.cfg +0 -0
- {limits-3.12.0 → limits-3.13.0}/setup.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/tests/test_limit_granularities.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/tests/test_limits.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/tests/test_ratelimit_parser.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/tests/test_storage.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/tests/test_utils.py +0 -0
- {limits-3.12.0 → limits-3.13.0}/versioneer.py +0 -0
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
Changelog
|
|
4
4
|
=========
|
|
5
5
|
|
|
6
|
+
v3.13.0
|
|
7
|
+
-------
|
|
8
|
+
Release Date: 2024-06-22
|
|
9
|
+
|
|
10
|
+
* Feature
|
|
11
|
+
|
|
12
|
+
* Add ``cost`` parameter to ``test`` methods in strategies.
|
|
13
|
+
|
|
6
14
|
v3.12.0
|
|
7
15
|
-------
|
|
8
16
|
Release Date: 2024-05-12
|
|
@@ -707,5 +715,6 @@ Release Date: 2015-01-08
|
|
|
707
715
|
|
|
708
716
|
|
|
709
717
|
|
|
718
|
+
|
|
710
719
|
|
|
711
720
|
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2024-
|
|
11
|
+
"date": "2024-06-22T18:39:54-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "3.
|
|
14
|
+
"full-revisionid": "7b87c4d37659ae5fe0a8bf7216bfff789facd5f3",
|
|
15
|
+
"version": "3.13.0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -29,13 +29,14 @@ class RateLimiter(ABC):
|
|
|
29
29
|
raise NotImplementedError
|
|
30
30
|
|
|
31
31
|
@abstractmethod
|
|
32
|
-
async def test(self, item: RateLimitItem, *identifiers: str) -> bool:
|
|
32
|
+
async def test(self, item: RateLimitItem, *identifiers: str, cost: int = 1) -> bool:
|
|
33
33
|
"""
|
|
34
34
|
Check if the rate limit can be consumed
|
|
35
35
|
|
|
36
36
|
:param item: the rate limit item
|
|
37
37
|
:param identifiers: variable list of strings to uniquely identify the
|
|
38
38
|
limit
|
|
39
|
+
:param cost: The expected cost to be consumed, default 1
|
|
39
40
|
"""
|
|
40
41
|
raise NotImplementedError
|
|
41
42
|
|
|
@@ -86,13 +87,14 @@ class MovingWindowRateLimiter(RateLimiter):
|
|
|
86
87
|
item.key_for(*identifiers), item.amount, item.get_expiry(), amount=cost
|
|
87
88
|
)
|
|
88
89
|
|
|
89
|
-
async def test(self, item: RateLimitItem, *identifiers: str) -> bool:
|
|
90
|
+
async def test(self, item: RateLimitItem, *identifiers: str, cost: int = 1) -> bool:
|
|
90
91
|
"""
|
|
91
92
|
Check if the rate limit can be consumed
|
|
92
93
|
|
|
93
94
|
:param item: the rate limit item
|
|
94
95
|
:param identifiers: variable list of strings to uniquely identify the
|
|
95
96
|
limit
|
|
97
|
+
:param cost: The expected cost to be consumed, default 1
|
|
96
98
|
"""
|
|
97
99
|
res = await cast(MovingWindowSupport, self.storage).get_moving_window(
|
|
98
100
|
item.key_for(*identifiers),
|
|
@@ -101,7 +103,7 @@ class MovingWindowRateLimiter(RateLimiter):
|
|
|
101
103
|
)
|
|
102
104
|
amount = res[1]
|
|
103
105
|
|
|
104
|
-
return amount
|
|
106
|
+
return amount <= item.amount - cost
|
|
105
107
|
|
|
106
108
|
async def get_window_stats(
|
|
107
109
|
self, item: RateLimitItem, *identifiers: str
|
|
@@ -147,16 +149,19 @@ class FixedWindowRateLimiter(RateLimiter):
|
|
|
147
149
|
<= item.amount
|
|
148
150
|
)
|
|
149
151
|
|
|
150
|
-
async def test(self, item: RateLimitItem, *identifiers: str) -> bool:
|
|
152
|
+
async def test(self, item: RateLimitItem, *identifiers: str, cost: int = 1) -> bool:
|
|
151
153
|
"""
|
|
152
154
|
Check if the rate limit can be consumed
|
|
153
155
|
|
|
154
156
|
:param item: the rate limit item
|
|
155
157
|
:param identifiers: variable list of strings to uniquely identify the
|
|
156
158
|
limit
|
|
159
|
+
:param cost: The expected cost to be consumed, default 1
|
|
157
160
|
"""
|
|
158
161
|
|
|
159
|
-
return
|
|
162
|
+
return (
|
|
163
|
+
await self.storage.get(item.key_for(*identifiers)) < item.amount - cost + 1
|
|
164
|
+
)
|
|
160
165
|
|
|
161
166
|
async def get_window_stats(
|
|
162
167
|
self, item: RateLimitItem, *identifiers: str
|
|
@@ -28,13 +28,14 @@ class RateLimiter(metaclass=ABCMeta):
|
|
|
28
28
|
raise NotImplementedError
|
|
29
29
|
|
|
30
30
|
@abstractmethod
|
|
31
|
-
def test(self, item: RateLimitItem, *identifiers: str) -> bool:
|
|
31
|
+
def test(self, item: RateLimitItem, *identifiers: str, cost: int = 1) -> bool:
|
|
32
32
|
"""
|
|
33
33
|
Check the rate limit without consuming from it.
|
|
34
34
|
|
|
35
35
|
:param item: The rate limit item
|
|
36
36
|
:param identifiers: variable list of strings to uniquely identify this
|
|
37
37
|
instance of the limit
|
|
38
|
+
:param cost: The expected cost to be consumed, default 1
|
|
38
39
|
"""
|
|
39
40
|
raise NotImplementedError
|
|
40
41
|
|
|
@@ -84,13 +85,14 @@ class MovingWindowRateLimiter(RateLimiter):
|
|
|
84
85
|
item.key_for(*identifiers), item.amount, item.get_expiry(), amount=cost
|
|
85
86
|
)
|
|
86
87
|
|
|
87
|
-
def test(self, item: RateLimitItem, *identifiers: str) -> bool:
|
|
88
|
+
def test(self, item: RateLimitItem, *identifiers: str, cost: int = 1) -> bool:
|
|
88
89
|
"""
|
|
89
90
|
Check if the rate limit can be consumed
|
|
90
91
|
|
|
91
92
|
:param item: The rate limit item
|
|
92
93
|
:param identifiers: variable list of strings to uniquely identify this
|
|
93
94
|
instance of the limit
|
|
95
|
+
:param cost: The expected cost to be consumed, default 1
|
|
94
96
|
"""
|
|
95
97
|
|
|
96
98
|
return (
|
|
@@ -99,7 +101,7 @@ class MovingWindowRateLimiter(RateLimiter):
|
|
|
99
101
|
item.amount,
|
|
100
102
|
item.get_expiry(),
|
|
101
103
|
)[1]
|
|
102
|
-
|
|
104
|
+
<= item.amount - cost
|
|
103
105
|
)
|
|
104
106
|
|
|
105
107
|
def get_window_stats(self, item: RateLimitItem, *identifiers: str) -> WindowStats:
|
|
@@ -144,16 +146,17 @@ class FixedWindowRateLimiter(RateLimiter):
|
|
|
144
146
|
<= item.amount
|
|
145
147
|
)
|
|
146
148
|
|
|
147
|
-
def test(self, item: RateLimitItem, *identifiers: str) -> bool:
|
|
149
|
+
def test(self, item: RateLimitItem, *identifiers: str, cost: int = 1) -> bool:
|
|
148
150
|
"""
|
|
149
151
|
Check if the rate limit can be consumed
|
|
150
152
|
|
|
151
153
|
:param item: The rate limit item
|
|
152
154
|
:param identifiers: variable list of strings to uniquely identify this
|
|
153
155
|
instance of the limit
|
|
156
|
+
:param cost: The expected cost to be consumed, default 1
|
|
154
157
|
"""
|
|
155
158
|
|
|
156
|
-
return self.storage.get(item.key_for(*identifiers)) < item.amount
|
|
159
|
+
return self.storage.get(item.key_for(*identifiers)) < item.amount - cost + 1
|
|
157
160
|
|
|
158
161
|
def get_window_stats(self, item: RateLimitItem, *identifiers: str) -> WindowStats:
|
|
159
162
|
"""
|
|
@@ -48,6 +48,7 @@ class TestWindow:
|
|
|
48
48
|
assert not limiter.hit(limit, "k1", cost=11)
|
|
49
49
|
assert limiter.hit(limit, "k2", cost=5)
|
|
50
50
|
assert limiter.get_window_stats(limit, "k2").remaining == 5
|
|
51
|
+
assert not limiter.test(limit, "k2", cost=6)
|
|
51
52
|
assert not limiter.hit(limit, "k2", cost=6)
|
|
52
53
|
|
|
53
54
|
@all_storage
|
|
@@ -130,9 +131,10 @@ class TestWindow:
|
|
|
130
131
|
limiter.hit(limit, "k2", cost=5)
|
|
131
132
|
# 5 hits in the last 100ms
|
|
132
133
|
with window(2, delay=1.8):
|
|
133
|
-
assert all(limiter.hit(limit, "k2") for i in range(
|
|
134
|
-
|
|
135
|
-
assert not limiter.hit(limit, "k2")
|
|
134
|
+
assert all(limiter.hit(limit, "k2") for i in range(4))
|
|
135
|
+
assert not limiter.test(limit, "k2", cost=2)
|
|
136
|
+
assert not limiter.hit(limit, "k2", cost=2)
|
|
137
|
+
assert limiter.hit(limit, "k2")
|
|
136
138
|
|
|
137
139
|
# 5 more succeed since there were only 5 in the last 2 seconds
|
|
138
140
|
assert all([limiter.hit(limit, "k2") for i in range(5)])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{limits-3.12.0 → limits-3.13.0}/limits/resources/redis/lua_scripts/acquire_moving_window.lua
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|