limits 3.14.0__py3-none-any.whl → 4.0.0__py3-none-any.whl
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/_version.py +3 -3
- limits/aio/storage/base.py +2 -2
- limits/aio/storage/etcd.py +3 -3
- limits/aio/storage/memcached.py +2 -2
- limits/aio/storage/memory.py +5 -5
- limits/aio/storage/mongodb.py +10 -16
- limits/aio/storage/redis.py +8 -8
- limits/resources/redis/lua_scripts/moving_window.lua +3 -1
- limits/storage/base.py +2 -2
- limits/storage/etcd.py +3 -4
- limits/storage/memcached.py +2 -2
- limits/storage/memory.py +5 -5
- limits/storage/mongodb.py +8 -11
- limits/storage/redis.py +7 -6
- limits/storage/redis_sentinel.py +1 -1
- limits/util.py +1 -1
- {limits-3.14.0.dist-info → limits-4.0.0.dist-info}/METADATA +13 -1
- limits-4.0.0.dist-info/RECORD +37 -0
- limits-3.14.0.dist-info/RECORD +0 -37
- {limits-3.14.0.dist-info → limits-4.0.0.dist-info}/LICENSE.txt +0 -0
- {limits-3.14.0.dist-info → limits-4.0.0.dist-info}/WHEEL +0 -0
- {limits-3.14.0.dist-info → limits-4.0.0.dist-info}/top_level.txt +0 -0
limits/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "
|
|
11
|
+
"date": "2025-01-05T13:27:13-0800",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "
|
|
14
|
+
"full-revisionid": "61f7d58d4f8588486cfc3b567210604f415878f2",
|
|
15
|
+
"version": "4.0.0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
limits/aio/storage/base.py
CHANGED
|
@@ -103,7 +103,7 @@ class Storage(LazyDependency, metaclass=StorageRegistry):
|
|
|
103
103
|
raise NotImplementedError
|
|
104
104
|
|
|
105
105
|
@abstractmethod
|
|
106
|
-
async def get_expiry(self, key: str) ->
|
|
106
|
+
async def get_expiry(self, key: str) -> float:
|
|
107
107
|
"""
|
|
108
108
|
:param key: the key to get the expiry for
|
|
109
109
|
"""
|
|
@@ -169,7 +169,7 @@ class MovingWindowSupport(ABC):
|
|
|
169
169
|
@abstractmethod
|
|
170
170
|
async def get_moving_window(
|
|
171
171
|
self, key: str, limit: int, expiry: int
|
|
172
|
-
) -> Tuple[
|
|
172
|
+
) -> Tuple[float, int]:
|
|
173
173
|
"""
|
|
174
174
|
returns the starting point and the number of entries in the moving
|
|
175
175
|
window
|
limits/aio/storage/etcd.py
CHANGED
|
@@ -116,12 +116,12 @@ class EtcdStorage(Storage):
|
|
|
116
116
|
return int(amount)
|
|
117
117
|
return 0
|
|
118
118
|
|
|
119
|
-
async def get_expiry(self, key: str) ->
|
|
119
|
+
async def get_expiry(self, key: str) -> float:
|
|
120
120
|
cur = await self.storage.get(self.prefixed_key(key))
|
|
121
121
|
if cur:
|
|
122
122
|
window_end = float(cur.value.split(b":")[1])
|
|
123
|
-
return
|
|
124
|
-
return
|
|
123
|
+
return window_end
|
|
124
|
+
return time.time()
|
|
125
125
|
|
|
126
126
|
async def check(self) -> bool:
|
|
127
127
|
try:
|
limits/aio/storage/memcached.py
CHANGED
|
@@ -126,14 +126,14 @@ class MemcachedStorage(Storage):
|
|
|
126
126
|
|
|
127
127
|
return amount
|
|
128
128
|
|
|
129
|
-
async def get_expiry(self, key: str) ->
|
|
129
|
+
async def get_expiry(self, key: str) -> float:
|
|
130
130
|
"""
|
|
131
131
|
:param key: the key to get the expiry for
|
|
132
132
|
"""
|
|
133
133
|
storage = await self.get_storage()
|
|
134
134
|
item = await storage.get(f"{key}/expires".encode())
|
|
135
135
|
|
|
136
|
-
return
|
|
136
|
+
return item and float(item.value) or time.time()
|
|
137
137
|
|
|
138
138
|
async def check(self) -> bool:
|
|
139
139
|
"""
|
limits/aio/storage/memory.py
CHANGED
|
@@ -128,12 +128,12 @@ class MemoryStorage(Storage, MovingWindowSupport):
|
|
|
128
128
|
|
|
129
129
|
return True
|
|
130
130
|
|
|
131
|
-
async def get_expiry(self, key: str) ->
|
|
131
|
+
async def get_expiry(self, key: str) -> float:
|
|
132
132
|
"""
|
|
133
133
|
:param key: the key to get the expiry for
|
|
134
134
|
"""
|
|
135
135
|
|
|
136
|
-
return
|
|
136
|
+
return self.expirations.get(key, time.time())
|
|
137
137
|
|
|
138
138
|
async def get_num_acquired(self, key: str, expiry: int) -> int:
|
|
139
139
|
"""
|
|
@@ -153,7 +153,7 @@ class MemoryStorage(Storage, MovingWindowSupport):
|
|
|
153
153
|
# FIXME: arg limit is not used
|
|
154
154
|
async def get_moving_window(
|
|
155
155
|
self, key: str, limit: int, expiry: int
|
|
156
|
-
) -> Tuple[
|
|
156
|
+
) -> Tuple[float, int]:
|
|
157
157
|
"""
|
|
158
158
|
returns the starting point and the number of entries in the moving
|
|
159
159
|
window
|
|
@@ -167,9 +167,9 @@ class MemoryStorage(Storage, MovingWindowSupport):
|
|
|
167
167
|
|
|
168
168
|
for item in self.events.get(key, [])[::-1]:
|
|
169
169
|
if item.atime >= timestamp - expiry:
|
|
170
|
-
return
|
|
170
|
+
return item.atime, acquired
|
|
171
171
|
|
|
172
|
-
return
|
|
172
|
+
return timestamp, acquired
|
|
173
173
|
|
|
174
174
|
async def check(self) -> bool:
|
|
175
175
|
"""
|
limits/aio/storage/mongodb.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import calendar
|
|
5
4
|
import datetime
|
|
6
5
|
import time
|
|
7
6
|
from typing import Any, cast
|
|
@@ -135,21 +134,19 @@ class MongoDBStorage(Storage, MovingWindowSupport):
|
|
|
135
134
|
),
|
|
136
135
|
)
|
|
137
136
|
|
|
138
|
-
async def get_expiry(self, key: str) ->
|
|
137
|
+
async def get_expiry(self, key: str) -> float:
|
|
139
138
|
"""
|
|
140
139
|
:param key: the key to get the expiry for
|
|
141
140
|
"""
|
|
142
141
|
counter = await self.database[self.__collection_mapping["counters"]].find_one(
|
|
143
142
|
{"_id": key}
|
|
144
143
|
)
|
|
145
|
-
|
|
146
|
-
counter["expireAt"]
|
|
147
|
-
|
|
148
|
-
|
|
144
|
+
return (
|
|
145
|
+
(counter["expireAt"] if counter else datetime.datetime.now())
|
|
146
|
+
.replace(tzinfo=datetime.timezone.utc)
|
|
147
|
+
.timestamp()
|
|
149
148
|
)
|
|
150
149
|
|
|
151
|
-
return calendar.timegm(expiry.timetuple())
|
|
152
|
-
|
|
153
150
|
async def get(self, key: str) -> int:
|
|
154
151
|
"""
|
|
155
152
|
:param key: the key to get the counter value for
|
|
@@ -227,7 +224,7 @@ class MongoDBStorage(Storage, MovingWindowSupport):
|
|
|
227
224
|
|
|
228
225
|
async def get_moving_window(
|
|
229
226
|
self, key: str, limit: int, expiry: int
|
|
230
|
-
) -> Tuple[
|
|
227
|
+
) -> Tuple[float, int]:
|
|
231
228
|
"""
|
|
232
229
|
returns the starting point and the number of entries in the moving
|
|
233
230
|
window
|
|
@@ -237,7 +234,7 @@ class MongoDBStorage(Storage, MovingWindowSupport):
|
|
|
237
234
|
:return: (start of window, number of acquired entries)
|
|
238
235
|
"""
|
|
239
236
|
timestamp = time.time()
|
|
240
|
-
result
|
|
237
|
+
if result := (
|
|
241
238
|
await self.database[self.__collection_mapping["windows"]]
|
|
242
239
|
.aggregate(
|
|
243
240
|
[
|
|
@@ -264,12 +261,9 @@ class MongoDBStorage(Storage, MovingWindowSupport):
|
|
|
264
261
|
]
|
|
265
262
|
)
|
|
266
263
|
.to_list(length=1)
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return (int(result[0]["min"]), result[0]["count"])
|
|
271
|
-
|
|
272
|
-
return (int(timestamp), 0)
|
|
264
|
+
):
|
|
265
|
+
return result[0]["min"], result[0]["count"]
|
|
266
|
+
return timestamp, 0
|
|
273
267
|
|
|
274
268
|
async def acquire_entry(
|
|
275
269
|
self, key: str, limit: int, expiry: int, amount: int = 1
|
limits/aio/storage/redis.py
CHANGED
|
@@ -78,7 +78,7 @@ class RedisInteractor:
|
|
|
78
78
|
|
|
79
79
|
async def get_moving_window(
|
|
80
80
|
self, key: str, limit: int, expiry: int
|
|
81
|
-
) -> Tuple[
|
|
81
|
+
) -> Tuple[float, int]:
|
|
82
82
|
"""
|
|
83
83
|
returns the starting point and the number of entries in the moving
|
|
84
84
|
window
|
|
@@ -88,12 +88,12 @@ class RedisInteractor:
|
|
|
88
88
|
:return: (start of window, number of acquired entries)
|
|
89
89
|
"""
|
|
90
90
|
key = self.prefixed_key(key)
|
|
91
|
-
timestamp =
|
|
91
|
+
timestamp = time.time()
|
|
92
92
|
window = await self.lua_moving_window.execute(
|
|
93
|
-
[key], [
|
|
93
|
+
[key], [timestamp - expiry, limit]
|
|
94
94
|
)
|
|
95
95
|
if window:
|
|
96
|
-
return
|
|
96
|
+
return float(window[0]), window[1] # type: ignore
|
|
97
97
|
return timestamp, 0
|
|
98
98
|
|
|
99
99
|
async def _acquire_entry(
|
|
@@ -118,14 +118,14 @@ class RedisInteractor:
|
|
|
118
118
|
|
|
119
119
|
return bool(acquired)
|
|
120
120
|
|
|
121
|
-
async def _get_expiry(self, key: str, connection: AsyncRedisClient) ->
|
|
121
|
+
async def _get_expiry(self, key: str, connection: AsyncRedisClient) -> float:
|
|
122
122
|
"""
|
|
123
123
|
:param key: the key to get the expiry for
|
|
124
124
|
:param connection: Redis connection
|
|
125
125
|
"""
|
|
126
126
|
|
|
127
127
|
key = self.prefixed_key(key)
|
|
128
|
-
return
|
|
128
|
+
return max(await connection.ttl(key), 0) + time.time()
|
|
129
129
|
|
|
130
130
|
async def _check(self, connection: AsyncRedisClient) -> bool:
|
|
131
131
|
"""
|
|
@@ -261,7 +261,7 @@ class RedisStorage(RedisInteractor, Storage, MovingWindowSupport):
|
|
|
261
261
|
|
|
262
262
|
return await super()._acquire_entry(key, limit, expiry, self.storage, amount)
|
|
263
263
|
|
|
264
|
-
async def get_expiry(self, key: str) ->
|
|
264
|
+
async def get_expiry(self, key: str) -> float:
|
|
265
265
|
"""
|
|
266
266
|
:param key: the key to get the expiry for
|
|
267
267
|
"""
|
|
@@ -450,7 +450,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
450
450
|
key, self.storage_replica if self.use_replicas else self.storage
|
|
451
451
|
)
|
|
452
452
|
|
|
453
|
-
async def get_expiry(self, key: str) ->
|
|
453
|
+
async def get_expiry(self, key: str) -> float:
|
|
454
454
|
"""
|
|
455
455
|
:param key: the key to get the expiry for
|
|
456
456
|
"""
|
limits/storage/base.py
CHANGED
|
@@ -99,7 +99,7 @@ class Storage(LazyDependency, metaclass=StorageRegistry):
|
|
|
99
99
|
raise NotImplementedError
|
|
100
100
|
|
|
101
101
|
@abstractmethod
|
|
102
|
-
def get_expiry(self, key: str) ->
|
|
102
|
+
def get_expiry(self, key: str) -> float:
|
|
103
103
|
"""
|
|
104
104
|
:param key: the key to get the expiry for
|
|
105
105
|
"""
|
|
@@ -161,7 +161,7 @@ class MovingWindowSupport(ABC):
|
|
|
161
161
|
raise NotImplementedError
|
|
162
162
|
|
|
163
163
|
@abstractmethod
|
|
164
|
-
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[
|
|
164
|
+
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[float, int]:
|
|
165
165
|
"""
|
|
166
166
|
returns the starting point and the number of entries in the moving
|
|
167
167
|
window
|
limits/storage/etcd.py
CHANGED
|
@@ -110,12 +110,11 @@ class EtcdStorage(Storage):
|
|
|
110
110
|
return int(amount)
|
|
111
111
|
return 0
|
|
112
112
|
|
|
113
|
-
def get_expiry(self, key: str) ->
|
|
113
|
+
def get_expiry(self, key: str) -> float:
|
|
114
114
|
value, _ = self.storage.get(self.prefixed_key(key))
|
|
115
115
|
if value:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return int(time.time())
|
|
116
|
+
return float(value.split(b":")[1])
|
|
117
|
+
return time.time()
|
|
119
118
|
|
|
120
119
|
def check(self) -> bool:
|
|
121
120
|
try:
|
limits/storage/memcached.py
CHANGED
|
@@ -192,12 +192,12 @@ class MemcachedStorage(Storage):
|
|
|
192
192
|
|
|
193
193
|
return amount
|
|
194
194
|
|
|
195
|
-
def get_expiry(self, key: str) ->
|
|
195
|
+
def get_expiry(self, key: str) -> float:
|
|
196
196
|
"""
|
|
197
197
|
:param key: the key to get the expiry for
|
|
198
198
|
"""
|
|
199
199
|
|
|
200
|
-
return
|
|
200
|
+
return float(self.storage.get(key + "/expires") or time.time())
|
|
201
201
|
|
|
202
202
|
def check(self) -> bool:
|
|
203
203
|
"""
|
limits/storage/memory.py
CHANGED
|
@@ -121,12 +121,12 @@ class MemoryStorage(Storage, MovingWindowSupport):
|
|
|
121
121
|
self.events[key][:0] = [LockableEntry(expiry) for _ in range(amount)]
|
|
122
122
|
return True
|
|
123
123
|
|
|
124
|
-
def get_expiry(self, key: str) ->
|
|
124
|
+
def get_expiry(self, key: str) -> float:
|
|
125
125
|
"""
|
|
126
126
|
:param key: the key to get the expiry for
|
|
127
127
|
"""
|
|
128
128
|
|
|
129
|
-
return
|
|
129
|
+
return self.expirations.get(key, time.time())
|
|
130
130
|
|
|
131
131
|
def get_num_acquired(self, key: str, expiry: int) -> int:
|
|
132
132
|
"""
|
|
@@ -143,7 +143,7 @@ class MemoryStorage(Storage, MovingWindowSupport):
|
|
|
143
143
|
else 0
|
|
144
144
|
)
|
|
145
145
|
|
|
146
|
-
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[
|
|
146
|
+
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[float, int]:
|
|
147
147
|
"""
|
|
148
148
|
returns the starting point and the number of entries in the moving
|
|
149
149
|
window
|
|
@@ -157,9 +157,9 @@ class MemoryStorage(Storage, MovingWindowSupport):
|
|
|
157
157
|
|
|
158
158
|
for item in self.events.get(key, [])[::-1]:
|
|
159
159
|
if item.atime >= timestamp - expiry:
|
|
160
|
-
return
|
|
160
|
+
return item.atime, acquired
|
|
161
161
|
|
|
162
|
-
return
|
|
162
|
+
return timestamp, acquired
|
|
163
163
|
|
|
164
164
|
def check(self) -> bool:
|
|
165
165
|
"""
|
limits/storage/mongodb.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import calendar
|
|
4
3
|
import datetime
|
|
5
4
|
import time
|
|
6
5
|
from abc import ABC, abstractmethod
|
|
@@ -122,19 +121,17 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
|
|
|
122
121
|
self.counters.find_one_and_delete({"_id": key})
|
|
123
122
|
self.windows.find_one_and_delete({"_id": key})
|
|
124
123
|
|
|
125
|
-
def get_expiry(self, key: str) ->
|
|
124
|
+
def get_expiry(self, key: str) -> float:
|
|
126
125
|
"""
|
|
127
126
|
:param key: the key to get the expiry for
|
|
128
127
|
"""
|
|
129
128
|
counter = self.counters.find_one({"_id": key})
|
|
130
|
-
|
|
131
|
-
counter["expireAt"]
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
return (
|
|
130
|
+
(counter["expireAt"] if counter else datetime.datetime.now())
|
|
131
|
+
.replace(tzinfo=datetime.timezone.utc)
|
|
132
|
+
.timestamp()
|
|
134
133
|
)
|
|
135
134
|
|
|
136
|
-
return calendar.timegm(expiry.timetuple())
|
|
137
|
-
|
|
138
135
|
def get(self, key: str) -> int:
|
|
139
136
|
"""
|
|
140
137
|
:param key: the key to get the counter value for
|
|
@@ -205,7 +202,7 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
|
|
|
205
202
|
except: # noqa: E722
|
|
206
203
|
return False
|
|
207
204
|
|
|
208
|
-
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[
|
|
205
|
+
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[float, int]:
|
|
209
206
|
"""
|
|
210
207
|
returns the starting point and the number of entries in the moving
|
|
211
208
|
window
|
|
@@ -243,9 +240,9 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
|
|
|
243
240
|
)
|
|
244
241
|
|
|
245
242
|
if result:
|
|
246
|
-
return
|
|
243
|
+
return result[0]["min"], result[0]["count"]
|
|
247
244
|
|
|
248
|
-
return
|
|
245
|
+
return timestamp, 0
|
|
249
246
|
|
|
250
247
|
def acquire_entry(self, key: str, limit: int, expiry: int, amount: int = 1) -> bool:
|
|
251
248
|
"""
|
limits/storage/redis.py
CHANGED
|
@@ -32,7 +32,7 @@ class RedisInteractor:
|
|
|
32
32
|
def prefixed_key(self, key: str) -> str:
|
|
33
33
|
return f"{self.PREFIX}:{key}"
|
|
34
34
|
|
|
35
|
-
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[
|
|
35
|
+
def get_moving_window(self, key: str, limit: int, expiry: int) -> Tuple[float, int]:
|
|
36
36
|
"""
|
|
37
37
|
returns the starting point and the number of entries in the moving
|
|
38
38
|
window
|
|
@@ -43,9 +43,10 @@ class RedisInteractor:
|
|
|
43
43
|
"""
|
|
44
44
|
key = self.prefixed_key(key)
|
|
45
45
|
timestamp = time.time()
|
|
46
|
-
window
|
|
46
|
+
if window := self.lua_moving_window([key], [timestamp - expiry, limit]):
|
|
47
|
+
return float(window[0]), window[1]
|
|
47
48
|
|
|
48
|
-
return
|
|
49
|
+
return timestamp, 0
|
|
49
50
|
|
|
50
51
|
def _incr(
|
|
51
52
|
self,
|
|
@@ -109,14 +110,14 @@ class RedisInteractor:
|
|
|
109
110
|
|
|
110
111
|
return bool(acquired)
|
|
111
112
|
|
|
112
|
-
def _get_expiry(self, key: str, connection: RedisClient) ->
|
|
113
|
+
def _get_expiry(self, key: str, connection: RedisClient) -> float:
|
|
113
114
|
"""
|
|
114
115
|
:param key: the key to get the expiry for
|
|
115
116
|
:param connection: Redis connection
|
|
116
117
|
"""
|
|
117
118
|
|
|
118
119
|
key = self.prefixed_key(key)
|
|
119
|
-
return
|
|
120
|
+
return max(connection.ttl(key), 0) + time.time()
|
|
120
121
|
|
|
121
122
|
def _check(self, connection: RedisClient) -> bool:
|
|
122
123
|
"""
|
|
@@ -232,7 +233,7 @@ class RedisStorage(RedisInteractor, Storage, MovingWindowSupport):
|
|
|
232
233
|
|
|
233
234
|
return super()._acquire_entry(key, limit, expiry, self.storage, amount)
|
|
234
235
|
|
|
235
|
-
def get_expiry(self, key: str) ->
|
|
236
|
+
def get_expiry(self, key: str) -> float:
|
|
236
237
|
"""
|
|
237
238
|
:param key: the key to get the expiry for
|
|
238
239
|
"""
|
limits/storage/redis_sentinel.py
CHANGED
|
@@ -101,7 +101,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
101
101
|
key, self.storage_slave if self.use_replicas else self.storage
|
|
102
102
|
)
|
|
103
103
|
|
|
104
|
-
def get_expiry(self, key: str) ->
|
|
104
|
+
def get_expiry(self, key: str) -> float:
|
|
105
105
|
"""
|
|
106
106
|
:param key: the key to get the expiry for
|
|
107
107
|
"""
|
limits/util.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: limits
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: Rate limiting utilities
|
|
5
5
|
Home-page: https://limits.readthedocs.org
|
|
6
6
|
Author: Ali-Akber Saifee
|
|
@@ -172,6 +172,18 @@ Check specific limits without hitting them
|
|
|
172
172
|
time.sleep(0.01)
|
|
173
173
|
assert True == moving_window.hit(one_per_second, "test_namespace", "foo")
|
|
174
174
|
|
|
175
|
+
Query available capacity and reset time for a limit
|
|
176
|
+
|
|
177
|
+
.. code-block:: python
|
|
178
|
+
|
|
179
|
+
assert True == moving_window.hit(one_per_minute, "test_namespace", "foo")
|
|
180
|
+
window = moving_window.get_window_stats(one_per_minute, "test_namespace", "foo")
|
|
181
|
+
assert window.remaining == 0
|
|
182
|
+
assert False == moving_window.hit(one_per_minute, "test_namespace", "foo")
|
|
183
|
+
time.sleep(window.reset_time - time.time())
|
|
184
|
+
assert True == moving_window.hit(one_per_minute, "test_namespace", "foo")
|
|
185
|
+
|
|
186
|
+
|
|
175
187
|
Links
|
|
176
188
|
=====
|
|
177
189
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
limits/__init__.py,sha256=j_yVhgN9pdz8o5rQjVwdJTBSq8F-CTzof9kkiYgjRbw,728
|
|
2
|
+
limits/_version.py,sha256=bGcuGXkHFvjcc-8fCFZ7Jl28gop0tHquuV_Enu505jw,497
|
|
3
|
+
limits/errors.py,sha256=xCKGOVJiD-g8FlsQQb17AW2pTUvalYSuizPpvEVoYJE,626
|
|
4
|
+
limits/limits.py,sha256=ZsXESq2e1ji7c2ZKjSkIAasCjiLdjVLPUa9oah_I8U4,4943
|
|
5
|
+
limits/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
limits/strategies.py,sha256=Zy6PIhkysPbxnMzFjyXEsxMM6jhRoQ5XT5WskTNruK0,6949
|
|
7
|
+
limits/typing.py,sha256=4yitf6iwDK-QEfSxv3EbTdGLOrqowLFffHAqYRUqiYY,3275
|
|
8
|
+
limits/util.py,sha256=fTx0JQBT6ZY3fxGefjT07CLimUVCFjS1jLqskXmb7Eo,5726
|
|
9
|
+
limits/version.py,sha256=YwkF3dtq1KGzvmL3iVGctA8NNtGlK_0arrzZkZGVjUs,47
|
|
10
|
+
limits/aio/__init__.py,sha256=IOetunwQy1c5GefzitK8lewbTzHGiE-kmE9NlqSdr3U,82
|
|
11
|
+
limits/aio/strategies.py,sha256=SHjmJnmy7Nh4tBydkA-0qPaULYcLOAM91T4RPybq0Sg,6768
|
|
12
|
+
limits/aio/storage/__init__.py,sha256=CbtuSlVl1jPyN_vsEI_ApWblDblVaL46xcZ2M_oM0V8,595
|
|
13
|
+
limits/aio/storage/base.py,sha256=V1Ur9Cu29_vP5IYBIsWHTgrc4riW8FEyz5Dcvv6fPoc,4821
|
|
14
|
+
limits/aio/storage/etcd.py,sha256=krqjWujvybuaFa2g_FkPr2ZtX9Ac1-oJzErfGW3h27o,4783
|
|
15
|
+
limits/aio/storage/memcached.py,sha256=n8b9GVtXMWdc-w4-xP1_MPJ9dgVcgoJ5j53mTdU6E3E,4799
|
|
16
|
+
limits/aio/storage/memory.py,sha256=4ah9RpE5r7Q2yOLT-OndhP4ZHmvcwV3rKvCicnK2CJc,5845
|
|
17
|
+
limits/aio/storage/mongodb.py,sha256=pXS2JFqMPkfPLYhQatG2ImAeYas8CelTwiOGo-3eFpM,10701
|
|
18
|
+
limits/aio/storage/redis.py,sha256=JQm4pkwynSo1k6wFVB7SyRsh7yav0_61Px1FqUwuGl4,15658
|
|
19
|
+
limits/resources/redis/lua_scripts/acquire_moving_window.lua,sha256=5CFJX7D6T6RG5SFr6eVZ6zepmI1EkGWmKeVEO4QNrWo,483
|
|
20
|
+
limits/resources/redis/lua_scripts/clear_keys.lua,sha256=zU0cVfLGmapRQF9x9u0GclapM_IB2pJLszNzVQ1QRK4,184
|
|
21
|
+
limits/resources/redis/lua_scripts/incr_expire.lua,sha256=Uq9NcrrcDI-F87TDAJexoSJn2SDgeXIUEYozCp9S3oA,195
|
|
22
|
+
limits/resources/redis/lua_scripts/moving_window.lua,sha256=5hUZghISDh8Cbg8HJediM_OKjjNMF-0CBywWmsc93vA,430
|
|
23
|
+
limits/storage/__init__.py,sha256=XAW1jVDMLFkPr_Tl1SXpg_p4Y3nhEatTSYq1MlnYJcA,2564
|
|
24
|
+
limits/storage/base.py,sha256=E7ZInoGZqoM1QIpd1f8lvytlic4sMOQdl_eTzD-mlWk,4631
|
|
25
|
+
limits/storage/etcd.py,sha256=Q1tndCAAJp0jnir-b-ZBN3-7Kf3v_uwNAqQJLmqB96Q,4440
|
|
26
|
+
limits/storage/memcached.py,sha256=1maTeD7EbSWq0NgSZcukn-4QdVGcU7-sxZIpUDuh3kw,6637
|
|
27
|
+
limits/storage/memory.py,sha256=ButyS6v7o7DB55bwM3CltFK4Fc5mwKuFEBPgat51CXU,5550
|
|
28
|
+
limits/storage/mongodb.py,sha256=7z5I2kkPaGDKKkQqHD9vp1z1G6842SbtHFaHfVRQUF0,9830
|
|
29
|
+
limits/storage/redis.py,sha256=R7UbE5ng1NjIHEO17gu-vIZ4qgy91JctbPYGEkZ2iM0,8483
|
|
30
|
+
limits/storage/redis_cluster.py,sha256=MsiEpwHphQd0P88AwGw1NVSi3UwVrhsg-pvzkHxU2kw,3739
|
|
31
|
+
limits/storage/redis_sentinel.py,sha256=665CvL3UZYB2sB_vVkZ4CCaPKcbIXvQUWuDWnBoSOLU,4124
|
|
32
|
+
limits/storage/registry.py,sha256=xcBcxuu6srqmoS4WqDpkCXnRLB19ctH98v21P8S9kS8,708
|
|
33
|
+
limits-4.0.0.dist-info/LICENSE.txt,sha256=T6i7kq7F5gIPfcno9FCxU5Hcwm22Bjq0uHZV3ElcjsQ,1061
|
|
34
|
+
limits-4.0.0.dist-info/METADATA,sha256=KraoprBoY3V_JScEnVSxHI5KRbmc3rkwsMZiPhwfgeM,7662
|
|
35
|
+
limits-4.0.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
36
|
+
limits-4.0.0.dist-info/top_level.txt,sha256=C7g5ahldPoU2s6iWTaJayUrbGmPK1d6e9t5Nn0vQ2jM,7
|
|
37
|
+
limits-4.0.0.dist-info/RECORD,,
|
limits-3.14.0.dist-info/RECORD
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
limits/__init__.py,sha256=j_yVhgN9pdz8o5rQjVwdJTBSq8F-CTzof9kkiYgjRbw,728
|
|
2
|
-
limits/_version.py,sha256=xQZScpdLAtlbMB-VTWYGmvTmdMnGvXsZY1qG4iWHZKo,498
|
|
3
|
-
limits/errors.py,sha256=xCKGOVJiD-g8FlsQQb17AW2pTUvalYSuizPpvEVoYJE,626
|
|
4
|
-
limits/limits.py,sha256=ZsXESq2e1ji7c2ZKjSkIAasCjiLdjVLPUa9oah_I8U4,4943
|
|
5
|
-
limits/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
limits/strategies.py,sha256=Zy6PIhkysPbxnMzFjyXEsxMM6jhRoQ5XT5WskTNruK0,6949
|
|
7
|
-
limits/typing.py,sha256=4yitf6iwDK-QEfSxv3EbTdGLOrqowLFffHAqYRUqiYY,3275
|
|
8
|
-
limits/util.py,sha256=fi2XoUBoEk0T-le41VQn3x-BJ3QqrLeIcP52Bj-qwpg,5724
|
|
9
|
-
limits/version.py,sha256=YwkF3dtq1KGzvmL3iVGctA8NNtGlK_0arrzZkZGVjUs,47
|
|
10
|
-
limits/aio/__init__.py,sha256=IOetunwQy1c5GefzitK8lewbTzHGiE-kmE9NlqSdr3U,82
|
|
11
|
-
limits/aio/strategies.py,sha256=SHjmJnmy7Nh4tBydkA-0qPaULYcLOAM91T4RPybq0Sg,6768
|
|
12
|
-
limits/aio/storage/__init__.py,sha256=CbtuSlVl1jPyN_vsEI_ApWblDblVaL46xcZ2M_oM0V8,595
|
|
13
|
-
limits/aio/storage/base.py,sha256=xdYpBBonyMjxE9iT-2oZjm6x29aDU6Xd09MeBYbZcMo,4817
|
|
14
|
-
limits/aio/storage/etcd.py,sha256=Rjb_EYKFRr4F2Z6zvAPP9vQOyXJQHaju3VjxxUs75_c,4791
|
|
15
|
-
limits/aio/storage/memcached.py,sha256=6aTlACfCtchdcZqoisnei0MOlCH7yLV9A1yCjOE5f9g,4802
|
|
16
|
-
limits/aio/storage/memory.py,sha256=DlmWluqUwBUWQIQ6XZi-mPrb15vfzBhA4iAKhBELDnE,5856
|
|
17
|
-
limits/aio/storage/mongodb.py,sha256=gZq9Ky3J7j0xfqIqB3ULBbQ5VjHzIyT1c7aNFp01VKk,10764
|
|
18
|
-
limits/aio/storage/redis.py,sha256=jkqtdIwTpfXTXwgTWTA1Jlc3Lpc-vnu4XRy6CIptiZA,15651
|
|
19
|
-
limits/resources/redis/lua_scripts/acquire_moving_window.lua,sha256=5CFJX7D6T6RG5SFr6eVZ6zepmI1EkGWmKeVEO4QNrWo,483
|
|
20
|
-
limits/resources/redis/lua_scripts/clear_keys.lua,sha256=zU0cVfLGmapRQF9x9u0GclapM_IB2pJLszNzVQ1QRK4,184
|
|
21
|
-
limits/resources/redis/lua_scripts/incr_expire.lua,sha256=Uq9NcrrcDI-F87TDAJexoSJn2SDgeXIUEYozCp9S3oA,195
|
|
22
|
-
limits/resources/redis/lua_scripts/moving_window.lua,sha256=iAInenlVd_fFDi15APpRWbOuPUz_G3nFnVAqb7wOedA,398
|
|
23
|
-
limits/storage/__init__.py,sha256=XAW1jVDMLFkPr_Tl1SXpg_p4Y3nhEatTSYq1MlnYJcA,2564
|
|
24
|
-
limits/storage/base.py,sha256=fDdYLa-RrnjhBTO1hE5aTTM8q8n3M5HD-65KyWWXBtg,4627
|
|
25
|
-
limits/storage/etcd.py,sha256=wkC_mj4Tsf2nwUKByMiHiGzA40N3mDepEwdLmvH8wmw,4484
|
|
26
|
-
limits/storage/memcached.py,sha256=bMzfZgYa_EWcZAjSZLcygpk3hpeOAErBpRE8dVwyXQs,6640
|
|
27
|
-
limits/storage/memory.py,sha256=R16E-Ccnmn1-LlolkFf-kB1-QHh8eiwFFLYVv0PuFD0,5561
|
|
28
|
-
limits/storage/mongodb.py,sha256=t8ey5-gYrJcmyvwJkqje0-TR-UMYvBF900a_zEXAYPI,9873
|
|
29
|
-
limits/storage/redis.py,sha256=3zJ1gDMDepT_pGN9d2aAN7Pea7tMBI49VK60IHv-Ooc,8452
|
|
30
|
-
limits/storage/redis_cluster.py,sha256=MsiEpwHphQd0P88AwGw1NVSi3UwVrhsg-pvzkHxU2kw,3739
|
|
31
|
-
limits/storage/redis_sentinel.py,sha256=lI7y7x9VGDCGq_-WRb6jR6cDgnzsOC1KGaRbwE69DNk,4122
|
|
32
|
-
limits/storage/registry.py,sha256=xcBcxuu6srqmoS4WqDpkCXnRLB19ctH98v21P8S9kS8,708
|
|
33
|
-
limits-3.14.0.dist-info/LICENSE.txt,sha256=T6i7kq7F5gIPfcno9FCxU5Hcwm22Bjq0uHZV3ElcjsQ,1061
|
|
34
|
-
limits-3.14.0.dist-info/METADATA,sha256=52wQlpKMn5JwhkgyJN-FDcGgUnImn7Ce2j5yz2BqGKg,7189
|
|
35
|
-
limits-3.14.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
36
|
-
limits-3.14.0.dist-info/top_level.txt,sha256=C7g5ahldPoU2s6iWTaJayUrbGmPK1d6e9t5Nn0vQ2jM,7
|
|
37
|
-
limits-3.14.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|