limits 5.0.0rc2__tar.gz → 5.1.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-5.0.0rc2 → limits-5.1.0}/HISTORY.rst +18 -17
- {limits-5.0.0rc2 → limits-5.1.0}/PKG-INFO +2 -2
- {limits-5.0.0rc2 → limits-5.1.0}/limits/_version.py +3 -3
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/__init__.py +22 -6
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/bridge.py +3 -2
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/redis.py +5 -2
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/redis_cluster.py +5 -2
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/redis_sentinel.py +3 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/PKG-INFO +2 -2
- {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/requires.txt +1 -1
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/main.txt +1 -1
- {limits-5.0.0rc2 → limits-5.1.0}/CLASSIFIERS +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/CONTRIBUTIONS.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/LICENSE.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/MANIFEST.in +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/README.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/Makefile +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/_static/custom.css +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/api.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/async.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/changelog.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/conf.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/custom-storage.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/benchmark-chart.css +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/js/benchmark-chart.js +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/js/benchmark-details.js +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/js/benchmark-loader.js +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_templates/git_info.js +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/bench_chart.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/index.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/installation.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/performance.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/quickstart.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/storage.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/strategies.rst +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/doc/source/theme_config.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/__init__.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/__init__.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/__init__.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/base.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/__init__.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/bridge.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/emcache.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/memcachio.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memory.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/mongodb.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/coredis.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/redispy.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/valkey.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/strategies.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/errors.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/limits.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/py.typed +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/acquire_moving_window.lua +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/acquire_sliding_window.lua +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/clear_keys.lua +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/incr_expire.lua +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/moving_window.lua +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/sliding_window.lua +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/__init__.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/base.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/memcached.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/memory.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/mongodb.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/registry.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/strategies.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/typing.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/util.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits/version.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/SOURCES.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/dependency_links.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/not-zip-safe +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/top_level.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/pyproject.toml +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/ci.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/dev.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/docs.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-memcached.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-mongodb.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-redis.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-valkey.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/memcached.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/mongodb.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/redis.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/rediscluster.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/valkey.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/requirements/test.txt +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/setup.cfg +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/setup.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/tests/test_limit_granularities.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/tests/test_limits.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/tests/test_ratelimit_parser.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/tests/test_storage.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/tests/test_strategy.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/tests/test_utils.py +0 -0
- {limits-5.0.0rc2 → limits-5.1.0}/versioneer.py +0 -0
|
@@ -3,35 +3,34 @@
|
|
|
3
3
|
Changelog
|
|
4
4
|
=========
|
|
5
5
|
|
|
6
|
-
v5.0
|
|
7
|
-
|
|
8
|
-
Release Date: 2025-04-
|
|
9
|
-
|
|
10
|
-
* Compatibility
|
|
11
|
-
|
|
12
|
-
* Add back emcache as a non default implementation for memcached + asyncio
|
|
13
|
-
* Remove support for memcached < 1.5
|
|
14
|
-
|
|
15
|
-
* Documentation
|
|
6
|
+
v5.1.0
|
|
7
|
+
------
|
|
8
|
+
Release Date: 2025-04-23
|
|
16
9
|
|
|
17
|
-
|
|
10
|
+
* Features
|
|
18
11
|
|
|
12
|
+
* Expose ``key_prefix`` constructor argument for all redis storage
|
|
13
|
+
implementations to simplify customizing the prefix used for all
|
|
14
|
+
keys created in redis.
|
|
19
15
|
|
|
20
|
-
v5.0.
|
|
21
|
-
|
|
22
|
-
Release Date: 2025-04-
|
|
16
|
+
v5.0.0
|
|
17
|
+
------
|
|
18
|
+
Release Date: 2025-04-15
|
|
23
19
|
|
|
24
20
|
* Backward incompatible changes
|
|
25
21
|
|
|
26
22
|
* Dropped support for Fixed Window with Elastic Expiry strategy
|
|
27
23
|
* Dropped support for etcd
|
|
28
|
-
*
|
|
24
|
+
* Changed the default implementation for async+memached from :pypi:`emcache`
|
|
25
|
+
to :pypi`:memcachio`
|
|
29
26
|
|
|
30
27
|
* Performance
|
|
31
28
|
|
|
32
|
-
* Improved performance of
|
|
33
|
-
|
|
29
|
+
* Improved performance of redis moving window ``test`` and ``get_window_stats`` operations
|
|
30
|
+
especially when dealing with large rate limits.
|
|
34
31
|
* Improved performance of mongodb moving window ``test`` and ``get_window_stats`` operations.
|
|
32
|
+
* Improved performance of in-memory moving window ``test`` and ``get_window_stats`` operations.
|
|
33
|
+
* Reduced load on event loop when expiring limits with async in-memory implementations
|
|
35
34
|
|
|
36
35
|
v4.7.3
|
|
37
36
|
------
|
|
@@ -835,3 +834,5 @@ Release Date: 2015-01-08
|
|
|
835
834
|
|
|
836
835
|
* Initial import of common rate limiting code from `Flask-Limiter <https://github.com/alisaifee/flask-limiter>`_
|
|
837
836
|
|
|
837
|
+
|
|
838
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: limits
|
|
3
|
-
Version: 5.0
|
|
3
|
+
Version: 5.1.0
|
|
4
4
|
Summary: Rate limiting utilities
|
|
5
5
|
Home-page: https://limits.readthedocs.org
|
|
6
6
|
Author: Ali-Akber Saifee
|
|
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
24
|
Requires-Dist: deprecated>=1.2
|
|
25
|
-
Requires-Dist: packaging<
|
|
25
|
+
Requires-Dist: packaging<26,>=21
|
|
26
26
|
Requires-Dist: typing_extensions
|
|
27
27
|
Provides-Extra: redis
|
|
28
28
|
Requires-Dist: redis!=4.5.2,!=4.5.3,<6.0.0,>3; extra == "redis"
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-04-
|
|
11
|
+
"date": "2025-04-23T10:35:38-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "5.0
|
|
14
|
+
"full-revisionid": "2b76ea0a8aa2a37b2069ab5990e9ae180de6491f",
|
|
15
|
+
"version": "5.1.0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -51,6 +51,8 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
51
51
|
"valkey": Version("6.0"),
|
|
52
52
|
}
|
|
53
53
|
MODE: Literal["BASIC", "CLUSTER", "SENTINEL"] = "BASIC"
|
|
54
|
+
PREFIX = "LIMITS"
|
|
55
|
+
|
|
54
56
|
bridge: RedisBridge
|
|
55
57
|
storage_exceptions: tuple[Exception, ...]
|
|
56
58
|
target_server: Literal["redis", "valkey"]
|
|
@@ -60,6 +62,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
60
62
|
uri: str,
|
|
61
63
|
wrap_exceptions: bool = False,
|
|
62
64
|
implementation: Literal["redispy", "coredis", "valkey"] = "coredis",
|
|
65
|
+
key_prefix: str = PREFIX,
|
|
63
66
|
**options: float | str | bool,
|
|
64
67
|
) -> None:
|
|
65
68
|
"""
|
|
@@ -86,6 +89,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
86
89
|
- ``redispy``: :class:`redis.asyncio.client.Redis`
|
|
87
90
|
- ``valkey``: :class:`valkey.asyncio.client.Valkey`
|
|
88
91
|
|
|
92
|
+
:param key_prefix: the prefix for each key created in redis
|
|
89
93
|
:param options: all remaining keyword arguments are passed
|
|
90
94
|
directly to the constructor of :class:`coredis.Redis` or :class:`redis.asyncio.client.Redis`
|
|
91
95
|
:raise ConfigurationError: when the redis library is not available
|
|
@@ -97,12 +101,18 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
97
101
|
super().__init__(uri, wrap_exceptions=wrap_exceptions)
|
|
98
102
|
self.options = options
|
|
99
103
|
if self.target_server == "valkey" or implementation == "valkey":
|
|
100
|
-
self.bridge = ValkeyBridge(
|
|
104
|
+
self.bridge = ValkeyBridge(
|
|
105
|
+
uri, self.dependencies["valkey"].module, key_prefix
|
|
106
|
+
)
|
|
101
107
|
else:
|
|
102
108
|
if implementation == "redispy":
|
|
103
|
-
self.bridge = RedispyBridge(
|
|
109
|
+
self.bridge = RedispyBridge(
|
|
110
|
+
uri, self.dependencies["redis"].module, key_prefix
|
|
111
|
+
)
|
|
104
112
|
else:
|
|
105
|
-
self.bridge = CoredisBridge(
|
|
113
|
+
self.bridge = CoredisBridge(
|
|
114
|
+
uri, self.dependencies["coredis"].module, key_prefix
|
|
115
|
+
)
|
|
106
116
|
self.configure_bridge()
|
|
107
117
|
self.bridge.register_scripts()
|
|
108
118
|
|
|
@@ -226,7 +236,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
226
236
|
async def reset(self) -> int | None:
|
|
227
237
|
"""
|
|
228
238
|
This function calls a Lua Script to delete keys prefixed with
|
|
229
|
-
|
|
239
|
+
:paramref:`RedisStorage.key_prefix` in blocks of 5000.
|
|
230
240
|
|
|
231
241
|
.. warning:: This operation was designed to be fast, but was not tested
|
|
232
242
|
on a large production based system. Be careful with its usage as it
|
|
@@ -268,6 +278,7 @@ class RedisClusterStorage(RedisStorage):
|
|
|
268
278
|
uri: str,
|
|
269
279
|
wrap_exceptions: bool = False,
|
|
270
280
|
implementation: Literal["redispy", "coredis", "valkey"] = "coredis",
|
|
281
|
+
key_prefix: str = RedisStorage.PREFIX,
|
|
271
282
|
**options: float | str | bool,
|
|
272
283
|
) -> None:
|
|
273
284
|
"""
|
|
@@ -283,6 +294,7 @@ class RedisClusterStorage(RedisStorage):
|
|
|
283
294
|
- ``coredis``: :class:`coredis.RedisCluster`
|
|
284
295
|
- ``redispy``: :class:`redis.asyncio.cluster.RedisCluster`
|
|
285
296
|
- ``valkey``: :class:`valkey.asyncio.cluster.ValkeyCluster`
|
|
297
|
+
:param key_prefix: the prefix for each key created in redis
|
|
286
298
|
:param options: all remaining keyword arguments are passed
|
|
287
299
|
directly to the constructor of :class:`coredis.RedisCluster` or
|
|
288
300
|
:class:`redis.asyncio.RedisCluster`
|
|
@@ -293,6 +305,7 @@ class RedisClusterStorage(RedisStorage):
|
|
|
293
305
|
uri,
|
|
294
306
|
wrap_exceptions=wrap_exceptions,
|
|
295
307
|
implementation=implementation,
|
|
308
|
+
key_prefix=key_prefix,
|
|
296
309
|
**options,
|
|
297
310
|
)
|
|
298
311
|
|
|
@@ -303,8 +316,8 @@ class RedisClusterStorage(RedisStorage):
|
|
|
303
316
|
"""
|
|
304
317
|
Redis Clusters are sharded and deleting across shards
|
|
305
318
|
can't be done atomically. Because of this, this reset loops over all
|
|
306
|
-
keys that are prefixed with
|
|
307
|
-
one at a time.
|
|
319
|
+
keys that are prefixed with :paramref:`RedisClusterStorage.key_prefix`
|
|
320
|
+
and calls delete on them one at a time.
|
|
308
321
|
|
|
309
322
|
.. warning:: This operation was not tested with extremely large data sets.
|
|
310
323
|
On a large production based system, care should be taken with its
|
|
@@ -354,6 +367,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
354
367
|
uri: str,
|
|
355
368
|
wrap_exceptions: bool = False,
|
|
356
369
|
implementation: Literal["redispy", "coredis", "valkey"] = "coredis",
|
|
370
|
+
key_prefix: str = RedisStorage.PREFIX,
|
|
357
371
|
service_name: str | None = None,
|
|
358
372
|
use_replicas: bool = True,
|
|
359
373
|
sentinel_kwargs: dict[str, float | str | bool] | None = None,
|
|
@@ -372,6 +386,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
372
386
|
- ``coredis``: :class:`coredis.sentinel.Sentinel`
|
|
373
387
|
- ``redispy``: :class:`redis.asyncio.sentinel.Sentinel`
|
|
374
388
|
- ``valkey``: :class:`valkey.asyncio.sentinel.Sentinel`
|
|
389
|
+
:param key_prefix: the prefix for each key created in redis
|
|
375
390
|
:param service_name: sentinel service name (if not provided in `uri`)
|
|
376
391
|
:param use_replicas: Whether to use replicas for read only operations
|
|
377
392
|
:param sentinel_kwargs: optional arguments to pass as
|
|
@@ -391,6 +406,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
391
406
|
uri,
|
|
392
407
|
wrap_exceptions=wrap_exceptions,
|
|
393
408
|
implementation=implementation,
|
|
409
|
+
key_prefix=key_prefix,
|
|
394
410
|
**options,
|
|
395
411
|
)
|
|
396
412
|
|
|
@@ -8,7 +8,6 @@ from limits.util import get_package_data
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class RedisBridge(ABC):
|
|
11
|
-
PREFIX = "LIMITS"
|
|
12
11
|
RES_DIR = "resources/redis/lua_scripts"
|
|
13
12
|
|
|
14
13
|
SCRIPT_MOVING_WINDOW = get_package_data(f"{RES_DIR}/moving_window.lua")
|
|
@@ -26,18 +25,20 @@ class RedisBridge(ABC):
|
|
|
26
25
|
self,
|
|
27
26
|
uri: str,
|
|
28
27
|
dependency: ModuleType,
|
|
28
|
+
key_prefix: str,
|
|
29
29
|
) -> None:
|
|
30
30
|
self.uri = uri
|
|
31
31
|
self.parsed_uri = urllib.parse.urlparse(self.uri)
|
|
32
32
|
self.dependency = dependency
|
|
33
33
|
self.parsed_auth = {}
|
|
34
|
+
self.key_prefix = key_prefix
|
|
34
35
|
if self.parsed_uri.username:
|
|
35
36
|
self.parsed_auth["username"] = self.parsed_uri.username
|
|
36
37
|
if self.parsed_uri.password:
|
|
37
38
|
self.parsed_auth["password"] = self.parsed_uri.password
|
|
38
39
|
|
|
39
40
|
def prefixed_key(self, key: str) -> str:
|
|
40
|
-
return f"{self.
|
|
41
|
+
return f"{self.key_prefix}:{key}"
|
|
41
42
|
|
|
42
43
|
@abstractmethod
|
|
43
44
|
def register_scripts(self) -> None: ...
|
|
@@ -68,6 +68,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
68
68
|
self,
|
|
69
69
|
uri: str,
|
|
70
70
|
connection_pool: redis.connection.ConnectionPool | None = None,
|
|
71
|
+
key_prefix: str = PREFIX,
|
|
71
72
|
wrap_exceptions: bool = False,
|
|
72
73
|
**options: float | str | bool,
|
|
73
74
|
) -> None:
|
|
@@ -82,6 +83,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
82
83
|
:pypi:`valkey`.
|
|
83
84
|
:param connection_pool: if provided, the redis client is initialized with
|
|
84
85
|
the connection pool and any other params passed as :paramref:`options`
|
|
86
|
+
:param key_prefix: the prefix for each key created in redis
|
|
85
87
|
:param wrap_exceptions: Whether to wrap storage exceptions in
|
|
86
88
|
:exc:`limits.errors.StorageError` before raising it.
|
|
87
89
|
:param options: all remaining keyword arguments are passed
|
|
@@ -89,6 +91,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
89
91
|
:raise ConfigurationError: when the :pypi:`redis` library is not available
|
|
90
92
|
"""
|
|
91
93
|
super().__init__(uri, wrap_exceptions=wrap_exceptions, **options)
|
|
94
|
+
self.key_prefix = key_prefix
|
|
92
95
|
self.target_server = "valkey" if uri.startswith("valkey") else "redis"
|
|
93
96
|
self.dependency = self.dependencies[self.target_server].module
|
|
94
97
|
|
|
@@ -165,7 +168,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
165
168
|
return f"{self._current_window_key(key)}/-1"
|
|
166
169
|
|
|
167
170
|
def prefixed_key(self, key: str) -> str:
|
|
168
|
-
return f"{self.
|
|
171
|
+
return f"{self.key_prefix}:{key}"
|
|
169
172
|
|
|
170
173
|
def get_moving_window(self, key: str, limit: int, expiry: int) -> tuple[float, int]:
|
|
171
174
|
"""
|
|
@@ -295,7 +298,7 @@ class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
|
|
|
295
298
|
def reset(self) -> int | None:
|
|
296
299
|
"""
|
|
297
300
|
This function calls a Lua Script to delete keys prefixed with
|
|
298
|
-
|
|
301
|
+
:paramref:`RedisStorage.key_prefix` in blocks of 5000.
|
|
299
302
|
|
|
300
303
|
.. warning::
|
|
301
304
|
This operation was designed to be fast, but was not tested
|
|
@@ -56,6 +56,7 @@ class RedisClusterStorage(RedisStorage):
|
|
|
56
56
|
def __init__(
|
|
57
57
|
self,
|
|
58
58
|
uri: str,
|
|
59
|
+
key_prefix: str = RedisStorage.PREFIX,
|
|
59
60
|
wrap_exceptions: bool = False,
|
|
60
61
|
**options: float | str | bool,
|
|
61
62
|
) -> None:
|
|
@@ -65,6 +66,7 @@ class RedisClusterStorage(RedisStorage):
|
|
|
65
66
|
|
|
66
67
|
If the uri scheme is ``valkey+cluster`` the implementation used will be from
|
|
67
68
|
:pypi:`valkey`.
|
|
69
|
+
:param key_prefix: the prefix for each key created in redis
|
|
68
70
|
:param wrap_exceptions: Whether to wrap storage exceptions in
|
|
69
71
|
:exc:`limits.errors.StorageError` before raising it.
|
|
70
72
|
:param options: all remaining keyword arguments are passed
|
|
@@ -86,6 +88,7 @@ class RedisClusterStorage(RedisStorage):
|
|
|
86
88
|
host, port = loc.split(":")
|
|
87
89
|
cluster_hosts.append((host, int(port)))
|
|
88
90
|
|
|
91
|
+
self.key_prefix = key_prefix
|
|
89
92
|
self.storage = None
|
|
90
93
|
self.target_server = "valkey" if uri.startswith("valkey") else "redis"
|
|
91
94
|
merged_options = {**self.DEFAULT_OPTIONS, **parsed_auth, **options}
|
|
@@ -108,8 +111,8 @@ class RedisClusterStorage(RedisStorage):
|
|
|
108
111
|
"""
|
|
109
112
|
Redis Clusters are sharded and deleting across shards
|
|
110
113
|
can't be done atomically. Because of this, this reset loops over all
|
|
111
|
-
keys that are prefixed with
|
|
112
|
-
one at a time.
|
|
114
|
+
keys that are prefixed with :paramref:`RedisClusterStorage.prefix` and
|
|
115
|
+
calls delete on them one at a time.
|
|
113
116
|
|
|
114
117
|
.. warning::
|
|
115
118
|
This operation was not tested with extremely large data sets.
|
|
@@ -45,6 +45,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
45
45
|
service_name: str | None = None,
|
|
46
46
|
use_replicas: bool = True,
|
|
47
47
|
sentinel_kwargs: dict[str, float | str | bool] | None = None,
|
|
48
|
+
key_prefix: str = RedisStorage.PREFIX,
|
|
48
49
|
wrap_exceptions: bool = False,
|
|
49
50
|
**options: float | str | bool,
|
|
50
51
|
) -> None:
|
|
@@ -59,6 +60,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
59
60
|
:param use_replicas: Whether to use replicas for read only operations
|
|
60
61
|
:param sentinel_kwargs: kwargs to pass as
|
|
61
62
|
:attr:`sentinel_kwargs` to :class:`redis.sentinel.Sentinel`
|
|
63
|
+
:param key_prefix: the prefix for each key created in redis
|
|
62
64
|
:param wrap_exceptions: Whether to wrap storage exceptions in
|
|
63
65
|
:exc:`limits.errors.StorageError` before raising it.
|
|
64
66
|
:param options: all remaining keyword arguments are passed
|
|
@@ -87,6 +89,7 @@ class RedisSentinelStorage(RedisStorage):
|
|
|
87
89
|
for loc in parsed.netloc[sep:].split(","):
|
|
88
90
|
host, port = loc.split(":")
|
|
89
91
|
sentinel_configuration.append((host, int(port)))
|
|
92
|
+
self.key_prefix = key_prefix
|
|
90
93
|
self.service_name = (
|
|
91
94
|
parsed.path.replace("/", "") if parsed.path else service_name
|
|
92
95
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: limits
|
|
3
|
-
Version: 5.0
|
|
3
|
+
Version: 5.1.0
|
|
4
4
|
Summary: Rate limiting utilities
|
|
5
5
|
Home-page: https://limits.readthedocs.org
|
|
6
6
|
Author: Ali-Akber Saifee
|
|
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
24
|
Requires-Dist: deprecated>=1.2
|
|
25
|
-
Requires-Dist: packaging<
|
|
25
|
+
Requires-Dist: packaging<26,>=21
|
|
26
26
|
Requires-Dist: typing_extensions
|
|
27
27
|
Provides-Extra: redis
|
|
28
28
|
Requires-Dist: redis!=4.5.2,!=4.5.3,<6.0.0,>3; extra == "redis"
|
|
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
|
{limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/acquire_moving_window.lua
RENAMED
|
File without changes
|
{limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/acquire_sliding_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
|