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.
Files changed (96) hide show
  1. {limits-5.0.0rc2 → limits-5.1.0}/HISTORY.rst +18 -17
  2. {limits-5.0.0rc2 → limits-5.1.0}/PKG-INFO +2 -2
  3. {limits-5.0.0rc2 → limits-5.1.0}/limits/_version.py +3 -3
  4. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/__init__.py +22 -6
  5. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/bridge.py +3 -2
  6. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/redis.py +5 -2
  7. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/redis_cluster.py +5 -2
  8. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/redis_sentinel.py +3 -0
  9. {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/PKG-INFO +2 -2
  10. {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/requires.txt +1 -1
  11. {limits-5.0.0rc2 → limits-5.1.0}/requirements/main.txt +1 -1
  12. {limits-5.0.0rc2 → limits-5.1.0}/CLASSIFIERS +0 -0
  13. {limits-5.0.0rc2 → limits-5.1.0}/CONTRIBUTIONS.rst +0 -0
  14. {limits-5.0.0rc2 → limits-5.1.0}/LICENSE.txt +0 -0
  15. {limits-5.0.0rc2 → limits-5.1.0}/MANIFEST.in +0 -0
  16. {limits-5.0.0rc2 → limits-5.1.0}/README.rst +0 -0
  17. {limits-5.0.0rc2 → limits-5.1.0}/doc/Makefile +0 -0
  18. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/_static/custom.css +0 -0
  19. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/api.rst +0 -0
  20. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/async.rst +0 -0
  21. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/changelog.rst +0 -0
  22. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/conf.py +0 -0
  23. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/custom-storage.rst +0 -0
  24. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/benchmark-chart.css +0 -0
  25. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/js/benchmark-chart.js +0 -0
  26. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/js/benchmark-details.js +0 -0
  27. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_static/js/benchmark-loader.js +0 -0
  28. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/_templates/git_info.js +0 -0
  29. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/ext/bench_chart.py +0 -0
  30. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/index.rst +0 -0
  31. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/installation.rst +0 -0
  32. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/performance.rst +0 -0
  33. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/quickstart.rst +0 -0
  34. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/storage.rst +0 -0
  35. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/strategies.rst +0 -0
  36. {limits-5.0.0rc2 → limits-5.1.0}/doc/source/theme_config.py +0 -0
  37. {limits-5.0.0rc2 → limits-5.1.0}/limits/__init__.py +0 -0
  38. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/__init__.py +0 -0
  39. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/__init__.py +0 -0
  40. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/base.py +0 -0
  41. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/__init__.py +0 -0
  42. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/bridge.py +0 -0
  43. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/emcache.py +0 -0
  44. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memcached/memcachio.py +0 -0
  45. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/memory.py +0 -0
  46. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/mongodb.py +0 -0
  47. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/coredis.py +0 -0
  48. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/redispy.py +0 -0
  49. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/storage/redis/valkey.py +0 -0
  50. {limits-5.0.0rc2 → limits-5.1.0}/limits/aio/strategies.py +0 -0
  51. {limits-5.0.0rc2 → limits-5.1.0}/limits/errors.py +0 -0
  52. {limits-5.0.0rc2 → limits-5.1.0}/limits/limits.py +0 -0
  53. {limits-5.0.0rc2 → limits-5.1.0}/limits/py.typed +0 -0
  54. {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/acquire_moving_window.lua +0 -0
  55. {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/acquire_sliding_window.lua +0 -0
  56. {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/clear_keys.lua +0 -0
  57. {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/incr_expire.lua +0 -0
  58. {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/moving_window.lua +0 -0
  59. {limits-5.0.0rc2 → limits-5.1.0}/limits/resources/redis/lua_scripts/sliding_window.lua +0 -0
  60. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/__init__.py +0 -0
  61. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/base.py +0 -0
  62. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/memcached.py +0 -0
  63. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/memory.py +0 -0
  64. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/mongodb.py +0 -0
  65. {limits-5.0.0rc2 → limits-5.1.0}/limits/storage/registry.py +0 -0
  66. {limits-5.0.0rc2 → limits-5.1.0}/limits/strategies.py +0 -0
  67. {limits-5.0.0rc2 → limits-5.1.0}/limits/typing.py +0 -0
  68. {limits-5.0.0rc2 → limits-5.1.0}/limits/util.py +0 -0
  69. {limits-5.0.0rc2 → limits-5.1.0}/limits/version.py +0 -0
  70. {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/SOURCES.txt +0 -0
  71. {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/dependency_links.txt +0 -0
  72. {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/not-zip-safe +0 -0
  73. {limits-5.0.0rc2 → limits-5.1.0}/limits.egg-info/top_level.txt +0 -0
  74. {limits-5.0.0rc2 → limits-5.1.0}/pyproject.toml +0 -0
  75. {limits-5.0.0rc2 → limits-5.1.0}/requirements/ci.txt +0 -0
  76. {limits-5.0.0rc2 → limits-5.1.0}/requirements/dev.txt +0 -0
  77. {limits-5.0.0rc2 → limits-5.1.0}/requirements/docs.txt +0 -0
  78. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-memcached.txt +0 -0
  79. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-mongodb.txt +0 -0
  80. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-redis.txt +0 -0
  81. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/async-valkey.txt +0 -0
  82. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/memcached.txt +0 -0
  83. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/mongodb.txt +0 -0
  84. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/redis.txt +0 -0
  85. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/rediscluster.txt +0 -0
  86. {limits-5.0.0rc2 → limits-5.1.0}/requirements/storage/valkey.txt +0 -0
  87. {limits-5.0.0rc2 → limits-5.1.0}/requirements/test.txt +0 -0
  88. {limits-5.0.0rc2 → limits-5.1.0}/setup.cfg +0 -0
  89. {limits-5.0.0rc2 → limits-5.1.0}/setup.py +0 -0
  90. {limits-5.0.0rc2 → limits-5.1.0}/tests/test_limit_granularities.py +0 -0
  91. {limits-5.0.0rc2 → limits-5.1.0}/tests/test_limits.py +0 -0
  92. {limits-5.0.0rc2 → limits-5.1.0}/tests/test_ratelimit_parser.py +0 -0
  93. {limits-5.0.0rc2 → limits-5.1.0}/tests/test_storage.py +0 -0
  94. {limits-5.0.0rc2 → limits-5.1.0}/tests/test_strategy.py +0 -0
  95. {limits-5.0.0rc2 → limits-5.1.0}/tests/test_utils.py +0 -0
  96. {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.0rc2
7
- ---------
8
- Release Date: 2025-04-15
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
- * Improve presentation of benchmark documentation
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.0rc1
21
- ---------
22
- Release Date: 2025-04-09
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
- * Replaced async support for memached from :pypi:`emcache` to :pypi`:memcachio`
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 in-memory moving window ``test`` and ``get_window_stats`` operations.
33
- * Improved performance of redis moving window ``test`` and ``get_window_stats`` operations.
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.0rc2
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,>=21
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-15T12:47:18-0700",
11
+ "date": "2025-04-23T10:35:38-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "ca0e9ca30c696af1102471218171c07ce8ee7644",
15
- "version": "5.0.0rc2"
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(uri, self.dependencies["valkey"].module)
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(uri, self.dependencies["redis"].module)
109
+ self.bridge = RedispyBridge(
110
+ uri, self.dependencies["redis"].module, key_prefix
111
+ )
104
112
  else:
105
- self.bridge = CoredisBridge(uri, self.dependencies["coredis"].module)
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
- ``self.PREFIX`` in blocks of 5000.
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 ``self.PREFIX`` and calls delete on them,
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.PREFIX}:{key}"
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.PREFIX}:{key}"
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
- ``self.PREFIX`` in blocks of 5000.
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 ``self.PREFIX`` and calls delete on them,
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.0rc2
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,>=21
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"
@@ -1,5 +1,5 @@
1
1
  deprecated>=1.2
2
- packaging<25,>=21
2
+ packaging<26,>=21
3
3
  typing_extensions
4
4
 
5
5
  [all]
@@ -1,3 +1,3 @@
1
1
  deprecated>=1.2
2
- packaging>=21,<25
2
+ packaging>=21,<26
3
3
  typing_extensions
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