limits 4.0.0__tar.gz → 4.0.1__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.0.0 → limits-4.0.1}/HISTORY.rst +9 -0
- {limits-4.0.0 → limits-4.0.1}/PKG-INFO +13 -2
- {limits-4.0.0 → limits-4.0.1}/doc/source/strategies.rst +1 -1
- {limits-4.0.0 → limits-4.0.1}/limits/_version.py +3 -3
- {limits-4.0.0 → limits-4.0.1}/limits/aio/storage/mongodb.py +20 -14
- {limits-4.0.0 → limits-4.0.1}/limits/storage/__init__.py +3 -3
- {limits-4.0.0 → limits-4.0.1}/limits/storage/memcached.py +1 -2
- {limits-4.0.0 → limits-4.0.1}/limits/storage/mongodb.py +20 -13
- {limits-4.0.0 → limits-4.0.1}/limits.egg-info/PKG-INFO +13 -2
- {limits-4.0.0 → limits-4.0.1}/CLASSIFIERS +0 -0
- {limits-4.0.0 → limits-4.0.1}/CONTRIBUTIONS.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/LICENSE.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/MANIFEST.in +0 -0
- {limits-4.0.0 → limits-4.0.1}/README.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/Makefile +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/_static/custom.css +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/api.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/async.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/changelog.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/conf.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/custom-storage.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/index.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/installation.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/quickstart.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/storage.rst +0 -0
- {limits-4.0.0 → limits-4.0.1}/doc/source/theme_config.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/__init__.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/__init__.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/storage/__init__.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/storage/base.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/storage/etcd.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/storage/memcached.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/storage/memory.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/storage/redis.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/aio/strategies.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/errors.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/limits.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/py.typed +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/resources/redis/lua_scripts/acquire_moving_window.lua +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/resources/redis/lua_scripts/clear_keys.lua +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/resources/redis/lua_scripts/incr_expire.lua +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/resources/redis/lua_scripts/moving_window.lua +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/storage/base.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/storage/etcd.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/storage/memory.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/storage/redis.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/storage/redis_cluster.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/storage/redis_sentinel.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/storage/registry.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/strategies.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/typing.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/util.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits/version.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits.egg-info/SOURCES.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits.egg-info/dependency_links.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits.egg-info/not-zip-safe +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits.egg-info/requires.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/limits.egg-info/top_level.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/pyproject.toml +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/ci.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/dev.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/docs.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/main.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/async-etcd.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/async-memcached.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/async-mongodb.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/async-redis.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/etcd.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/memcached.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/mongodb.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/redis.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/storage/rediscluster.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/requirements/test.txt +0 -0
- {limits-4.0.0 → limits-4.0.1}/setup.cfg +0 -0
- {limits-4.0.0 → limits-4.0.1}/setup.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/tests/test_limit_granularities.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/tests/test_limits.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/tests/test_ratelimit_parser.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/tests/test_storage.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/tests/test_strategy.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/tests/test_utils.py +0 -0
- {limits-4.0.0 → limits-4.0.1}/versioneer.py +0 -0
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
Changelog
|
|
4
4
|
=========
|
|
5
5
|
|
|
6
|
+
v4.0.1
|
|
7
|
+
------
|
|
8
|
+
Release Date: 2025-01-16
|
|
9
|
+
|
|
10
|
+
Security
|
|
11
|
+
|
|
12
|
+
* Change pypi release to use trusted publishing
|
|
13
|
+
|
|
6
14
|
v4.0.0
|
|
7
15
|
------
|
|
8
16
|
Release Date: 2025-01-05
|
|
@@ -756,5 +764,6 @@ Release Date: 2015-01-08
|
|
|
756
764
|
|
|
757
765
|
|
|
758
766
|
|
|
767
|
+
|
|
759
768
|
|
|
760
769
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: limits
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.1
|
|
4
4
|
Summary: Rate limiting utilities
|
|
5
5
|
Home-page: https://limits.readthedocs.org
|
|
6
6
|
Author: Ali-Akber Saifee
|
|
@@ -55,6 +55,17 @@ Requires-Dist: emcache>=0.6.1; python_version < "3.11" and extra == "all"
|
|
|
55
55
|
Requires-Dist: emcache>=1; (python_version >= "3.11" and python_version < "3.13.0") and extra == "all"
|
|
56
56
|
Requires-Dist: motor<4,>=3; extra == "all"
|
|
57
57
|
Requires-Dist: aetcd; extra == "all"
|
|
58
|
+
Dynamic: author
|
|
59
|
+
Dynamic: author-email
|
|
60
|
+
Dynamic: classifier
|
|
61
|
+
Dynamic: description
|
|
62
|
+
Dynamic: home-page
|
|
63
|
+
Dynamic: license
|
|
64
|
+
Dynamic: project-url
|
|
65
|
+
Dynamic: provides-extra
|
|
66
|
+
Dynamic: requires-dist
|
|
67
|
+
Dynamic: requires-python
|
|
68
|
+
Dynamic: summary
|
|
58
69
|
|
|
59
70
|
.. |ci| image:: https://github.com/alisaifee/limits/workflows/CI/badge.svg?branch=master
|
|
60
71
|
:target: https://github.com/alisaifee/limits/actions?query=branch%3Amaster+workflow%3ACI
|
|
@@ -35,7 +35,7 @@ Moving Window
|
|
|
35
35
|
=============
|
|
36
36
|
|
|
37
37
|
.. warning:: The moving window strategy is not implemented for the ``memcached``
|
|
38
|
-
storage
|
|
38
|
+
and ``etcd`` storage backends.
|
|
39
39
|
|
|
40
40
|
This strategy is the most effective for preventing bursts from by-passing the
|
|
41
41
|
rate limit as the window for each limit is not fixed at the start and end of each time unit
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-01-
|
|
11
|
+
"date": "2025-01-16T11:38:03-0800",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "4.0.
|
|
14
|
+
"full-revisionid": "074be17ab3008f50de700e996d243ba85580b058",
|
|
15
|
+
"version": "4.0.1"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -3,12 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import datetime
|
|
5
5
|
import time
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import cast
|
|
7
7
|
|
|
8
8
|
from deprecated.sphinx import versionadded, versionchanged
|
|
9
9
|
|
|
10
10
|
from limits.aio.storage.base import MovingWindowSupport, Storage
|
|
11
|
-
from limits.typing import Dict, Optional, ParamSpec, Tuple, Type, TypeVar, Union
|
|
11
|
+
from limits.typing import Dict, List, Optional, ParamSpec, Tuple, Type, TypeVar, Union
|
|
12
12
|
from limits.util import get_dependency
|
|
13
13
|
|
|
14
14
|
P = ParamSpec("P")
|
|
@@ -281,23 +281,29 @@ class MongoDBStorage(Storage, MovingWindowSupport):
|
|
|
281
281
|
|
|
282
282
|
timestamp = time.time()
|
|
283
283
|
try:
|
|
284
|
-
updates: Dict[
|
|
285
|
-
|
|
284
|
+
updates: Dict[
|
|
285
|
+
str,
|
|
286
|
+
Dict[str, Union[datetime.datetime, Dict[str, Union[List[float], int]]]],
|
|
287
|
+
] = {
|
|
288
|
+
"$push": {
|
|
289
|
+
"entries": {
|
|
290
|
+
"$each": [timestamp] * amount,
|
|
291
|
+
"$position": 0,
|
|
292
|
+
"$slice": limit,
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"$set": {
|
|
296
|
+
"expireAt": (
|
|
297
|
+
datetime.datetime.now(datetime.timezone.utc)
|
|
298
|
+
+ datetime.timedelta(seconds=expiry)
|
|
299
|
+
)
|
|
300
|
+
},
|
|
286
301
|
}
|
|
287
302
|
|
|
288
|
-
updates["$set"] = {
|
|
289
|
-
"expireAt": (
|
|
290
|
-
datetime.datetime.now(datetime.timezone.utc)
|
|
291
|
-
+ datetime.timedelta(seconds=expiry)
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
updates["$push"]["entries"]["$each"] = [timestamp] * amount
|
|
295
303
|
await self.database[self.__collection_mapping["windows"]].update_one(
|
|
296
304
|
{
|
|
297
305
|
"_id": key,
|
|
298
|
-
"entries
|
|
299
|
-
"$not": {"$gte": timestamp - expiry}
|
|
300
|
-
},
|
|
306
|
+
f"entries.{limit - amount}": {"$not": {"$gte": timestamp - expiry}},
|
|
301
307
|
},
|
|
302
308
|
updates,
|
|
303
309
|
upsert=True,
|
|
@@ -32,9 +32,9 @@ def storage_from_string(
|
|
|
32
32
|
|
|
33
33
|
from limits.storage import storage_from_string
|
|
34
34
|
|
|
35
|
-
memory =
|
|
36
|
-
memcached =
|
|
37
|
-
redis =
|
|
35
|
+
memory = storage_from_string("memory://")
|
|
36
|
+
memcached = storage_from_string("memcached://localhost:11211")
|
|
37
|
+
redis = storage_from_string("redis://localhost:6379")
|
|
38
38
|
|
|
39
39
|
The same function can be used to construct the :ref:`storage:async storage`
|
|
40
40
|
variants, for example::
|
|
@@ -77,8 +77,7 @@ class MemcachedStorage(Storage):
|
|
|
77
77
|
|
|
78
78
|
if not get_dependency(self.library):
|
|
79
79
|
raise ConfigurationError(
|
|
80
|
-
"memcached prerequisite not available."
|
|
81
|
-
" please install %s" % self.library
|
|
80
|
+
"memcached prerequisite not available. please install %s" % self.library
|
|
82
81
|
) # pragma: no cover
|
|
83
82
|
self.local_storage = threading.local()
|
|
84
83
|
self.local_storage.storage = None
|
|
@@ -3,12 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
import datetime
|
|
4
4
|
import time
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import cast
|
|
7
7
|
|
|
8
8
|
from deprecated.sphinx import versionadded, versionchanged
|
|
9
9
|
|
|
10
10
|
from limits.typing import (
|
|
11
11
|
Dict,
|
|
12
|
+
List,
|
|
12
13
|
MongoClient,
|
|
13
14
|
MongoCollection,
|
|
14
15
|
MongoDatabase,
|
|
@@ -256,23 +257,29 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
|
|
|
256
257
|
|
|
257
258
|
timestamp = time.time()
|
|
258
259
|
try:
|
|
259
|
-
updates: Dict[
|
|
260
|
-
|
|
260
|
+
updates: Dict[
|
|
261
|
+
str,
|
|
262
|
+
Dict[str, Union[datetime.datetime, Dict[str, Union[List[float], int]]]],
|
|
263
|
+
] = {
|
|
264
|
+
"$push": {
|
|
265
|
+
"entries": {
|
|
266
|
+
"$each": [timestamp] * amount,
|
|
267
|
+
"$position": 0,
|
|
268
|
+
"$slice": limit,
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"$set": {
|
|
272
|
+
"expireAt": (
|
|
273
|
+
datetime.datetime.now(datetime.timezone.utc)
|
|
274
|
+
+ datetime.timedelta(seconds=expiry)
|
|
275
|
+
)
|
|
276
|
+
},
|
|
261
277
|
}
|
|
262
278
|
|
|
263
|
-
updates["$set"] = {
|
|
264
|
-
"expireAt": (
|
|
265
|
-
datetime.datetime.now(datetime.timezone.utc)
|
|
266
|
-
+ datetime.timedelta(seconds=expiry)
|
|
267
|
-
)
|
|
268
|
-
}
|
|
269
|
-
updates["$push"]["entries"]["$each"] = [timestamp] * amount
|
|
270
279
|
self.windows.update_one(
|
|
271
280
|
{
|
|
272
281
|
"_id": key,
|
|
273
|
-
"entries
|
|
274
|
-
"$not": {"$gte": timestamp - expiry}
|
|
275
|
-
},
|
|
282
|
+
f"entries.{limit - amount}": {"$not": {"$gte": timestamp - expiry}},
|
|
276
283
|
},
|
|
277
284
|
updates,
|
|
278
285
|
upsert=True,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: limits
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.1
|
|
4
4
|
Summary: Rate limiting utilities
|
|
5
5
|
Home-page: https://limits.readthedocs.org
|
|
6
6
|
Author: Ali-Akber Saifee
|
|
@@ -55,6 +55,17 @@ Requires-Dist: emcache>=0.6.1; python_version < "3.11" and extra == "all"
|
|
|
55
55
|
Requires-Dist: emcache>=1; (python_version >= "3.11" and python_version < "3.13.0") and extra == "all"
|
|
56
56
|
Requires-Dist: motor<4,>=3; extra == "all"
|
|
57
57
|
Requires-Dist: aetcd; extra == "all"
|
|
58
|
+
Dynamic: author
|
|
59
|
+
Dynamic: author-email
|
|
60
|
+
Dynamic: classifier
|
|
61
|
+
Dynamic: description
|
|
62
|
+
Dynamic: home-page
|
|
63
|
+
Dynamic: license
|
|
64
|
+
Dynamic: project-url
|
|
65
|
+
Dynamic: provides-extra
|
|
66
|
+
Dynamic: requires-dist
|
|
67
|
+
Dynamic: requires-python
|
|
68
|
+
Dynamic: summary
|
|
58
69
|
|
|
59
70
|
.. |ci| image:: https://github.com/alisaifee/limits/workflows/CI/badge.svg?branch=master
|
|
60
71
|
:target: https://github.com/alisaifee/limits/actions?query=branch%3Amaster+workflow%3ACI
|
|
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
|
|
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
|