limits 3.13.0__py3-none-any.whl → 3.14.1__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 CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-06-22T18:39:54-0700",
11
+ "date": "2024-11-30T11:04:11-0800",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "7b87c4d37659ae5fe0a8bf7216bfff789facd5f3",
15
- "version": "3.13.0"
14
+ "full-revisionid": "0671723f54aed5692d4c9d9b47cf0326d5263de5",
15
+ "version": "3.14.1"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -6,7 +6,7 @@ import datetime
6
6
  import time
7
7
  from typing import Any, cast
8
8
 
9
- from deprecated.sphinx import versionadded
9
+ from deprecated.sphinx import versionadded, versionchanged
10
10
 
11
11
  from limits.aio.storage.base import MovingWindowSupport, Storage
12
12
  from limits.typing import Dict, Optional, ParamSpec, Tuple, Type, TypeVar, Union
@@ -17,6 +17,10 @@ R = TypeVar("R")
17
17
 
18
18
 
19
19
  @versionadded(version="2.1")
20
+ @versionchanged(
21
+ version="3.14.0",
22
+ reason="Added option to select custom collection names for windows & counters",
23
+ )
20
24
  class MongoDBStorage(Storage, MovingWindowSupport):
21
25
  """
22
26
  Rate limit storage with MongoDB as backend.
@@ -35,6 +39,8 @@ class MongoDBStorage(Storage, MovingWindowSupport):
35
39
  self,
36
40
  uri: str,
37
41
  database_name: str = "limits",
42
+ counter_collection_name: str = "counters",
43
+ window_collection_name: str = "windows",
38
44
  wrap_exceptions: bool = False,
39
45
  **options: Union[float, str, bool],
40
46
  ) -> None:
@@ -43,6 +49,9 @@ class MongoDBStorage(Storage, MovingWindowSupport):
43
49
  This uri is passed directly to :class:`~motor.motor_asyncio.AsyncIOMotorClient`
44
50
  :param database_name: The database to use for storing the rate limit
45
51
  collections.
52
+ :param counter_collection_name: The collection name to use for individual counters
53
+ used in fixed window strategies
54
+ :param window_collection_name: The collection name to use for moving window storage
46
55
  :param wrap_exceptions: Whether to wrap storage exceptions in
47
56
  :exc:`limits.errors.StorageError` before raising it.
48
57
  :param options: all remaining keyword arguments are passed
@@ -66,6 +75,10 @@ class MongoDBStorage(Storage, MovingWindowSupport):
66
75
  self.storage.get_io_loop = asyncio.get_running_loop
67
76
 
68
77
  self.__database_name = database_name
78
+ self.__collection_mapping = {
79
+ "counters": counter_collection_name,
80
+ "windows": window_collection_name,
81
+ }
69
82
  self.__indices_created = False
70
83
 
71
84
  @property
@@ -81,8 +94,12 @@ class MongoDBStorage(Storage, MovingWindowSupport):
81
94
  async def create_indices(self) -> None:
82
95
  if not self.__indices_created:
83
96
  await asyncio.gather(
84
- self.database.counters.create_index("expireAt", expireAfterSeconds=0),
85
- self.database.windows.create_index("expireAt", expireAfterSeconds=0),
97
+ self.database[self.__collection_mapping["counters"]].create_index(
98
+ "expireAt", expireAfterSeconds=0
99
+ ),
100
+ self.database[self.__collection_mapping["windows"]].create_index(
101
+ "expireAt", expireAfterSeconds=0
102
+ ),
86
103
  )
87
104
  self.__indices_created = True
88
105
 
@@ -92,12 +109,15 @@ class MongoDBStorage(Storage, MovingWindowSupport):
92
109
  """
93
110
  num_keys = sum(
94
111
  await asyncio.gather(
95
- self.database.counters.count_documents({}),
96
- self.database.windows.count_documents({}),
112
+ self.database[self.__collection_mapping["counters"]].count_documents(
113
+ {}
114
+ ),
115
+ self.database[self.__collection_mapping["windows"]].count_documents({}),
97
116
  )
98
117
  )
99
118
  await asyncio.gather(
100
- self.database.counters.drop(), self.database.windows.drop()
119
+ self.database[self.__collection_mapping["counters"]].drop(),
120
+ self.database[self.__collection_mapping["windows"]].drop(),
101
121
  )
102
122
 
103
123
  return cast(int, num_keys)
@@ -107,15 +127,21 @@ class MongoDBStorage(Storage, MovingWindowSupport):
107
127
  :param key: the key to clear rate limits for
108
128
  """
109
129
  await asyncio.gather(
110
- self.database.counters.find_one_and_delete({"_id": key}),
111
- self.database.windows.find_one_and_delete({"_id": key}),
130
+ self.database[self.__collection_mapping["counters"]].find_one_and_delete(
131
+ {"_id": key}
132
+ ),
133
+ self.database[self.__collection_mapping["windows"]].find_one_and_delete(
134
+ {"_id": key}
135
+ ),
112
136
  )
113
137
 
114
138
  async def get_expiry(self, key: str) -> int:
115
139
  """
116
140
  :param key: the key to get the expiry for
117
141
  """
118
- counter = await self.database.counters.find_one({"_id": key})
142
+ counter = await self.database[self.__collection_mapping["counters"]].find_one(
143
+ {"_id": key}
144
+ )
119
145
  expiry = (
120
146
  counter["expireAt"]
121
147
  if counter
@@ -128,7 +154,7 @@ class MongoDBStorage(Storage, MovingWindowSupport):
128
154
  """
129
155
  :param key: the key to get the counter value for
130
156
  """
131
- counter = await self.database.counters.find_one(
157
+ counter = await self.database[self.__collection_mapping["counters"]].find_one(
132
158
  {
133
159
  "_id": key,
134
160
  "expireAt": {"$gte": datetime.datetime.now(datetime.timezone.utc)},
@@ -156,7 +182,9 @@ class MongoDBStorage(Storage, MovingWindowSupport):
156
182
  seconds=expiry
157
183
  )
158
184
 
159
- response = await self.database.counters.find_one_and_update(
185
+ response = await self.database[
186
+ self.__collection_mapping["counters"]
187
+ ].find_one_and_update(
160
188
  {"_id": key},
161
189
  [
162
190
  {
@@ -209,30 +237,34 @@ class MongoDBStorage(Storage, MovingWindowSupport):
209
237
  :return: (start of window, number of acquired entries)
210
238
  """
211
239
  timestamp = time.time()
212
- result = await self.database.windows.aggregate(
213
- [
214
- {"$match": {"_id": key}},
215
- {
216
- "$project": {
217
- "entries": {
218
- "$filter": {
219
- "input": "$entries",
220
- "as": "entry",
221
- "cond": {"$gte": ["$$entry", timestamp - expiry]},
240
+ result = (
241
+ await self.database[self.__collection_mapping["windows"]]
242
+ .aggregate(
243
+ [
244
+ {"$match": {"_id": key}},
245
+ {
246
+ "$project": {
247
+ "entries": {
248
+ "$filter": {
249
+ "input": "$entries",
250
+ "as": "entry",
251
+ "cond": {"$gte": ["$$entry", timestamp - expiry]},
252
+ }
222
253
  }
223
254
  }
224
- }
225
- },
226
- {"$unwind": "$entries"},
227
- {
228
- "$group": {
229
- "_id": "$_id",
230
- "min": {"$min": "$entries"},
231
- "count": {"$sum": 1},
232
- }
233
- },
234
- ]
235
- ).to_list(length=1)
255
+ },
256
+ {"$unwind": "$entries"},
257
+ {
258
+ "$group": {
259
+ "_id": "$_id",
260
+ "min": {"$min": "$entries"},
261
+ "count": {"$sum": 1},
262
+ }
263
+ },
264
+ ]
265
+ )
266
+ .to_list(length=1)
267
+ )
236
268
 
237
269
  if result:
238
270
  return (int(result[0]["min"]), result[0]["count"])
@@ -266,7 +298,7 @@ class MongoDBStorage(Storage, MovingWindowSupport):
266
298
  )
267
299
  }
268
300
  updates["$push"]["entries"]["$each"] = [timestamp] * amount
269
- await self.database.windows.update_one(
301
+ await self.database[self.__collection_mapping["windows"]].update_one(
270
302
  {
271
303
  "_id": key,
272
304
  "entries.%d" % (limit - amount): {
limits/storage/mongodb.py CHANGED
@@ -6,7 +6,7 @@ import time
6
6
  from abc import ABC, abstractmethod
7
7
  from typing import Any, cast
8
8
 
9
- from deprecated.sphinx import versionadded
9
+ from deprecated.sphinx import versionadded, versionchanged
10
10
 
11
11
  from limits.typing import (
12
12
  Dict,
@@ -36,6 +36,8 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
36
36
  self,
37
37
  uri: str,
38
38
  database_name: str = "limits",
39
+ counter_collection_name: str = "counters",
40
+ window_collection_name: str = "windows",
39
41
  wrap_exceptions: bool = False,
40
42
  **options: Union[int, str, bool],
41
43
  ) -> None:
@@ -44,6 +46,9 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
44
46
  This uri is passed directly to :class:`~pymongo.mongo_client.MongoClient`
45
47
  :param database_name: The database to use for storing the rate limit
46
48
  collections.
49
+ :param counter_collection_name: The collection name to use for individual counters
50
+ used in fixed window strategies
51
+ :param window_collection_name: The collection name to use for moving window storage
47
52
  :param wrap_exceptions: Whether to wrap storage exceptions in
48
53
  :exc:`limits.errors.StorageError` before raising it.
49
54
  :param options: all remaining keyword arguments are passed to the
@@ -53,6 +58,10 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
53
58
 
54
59
  super().__init__(uri, wrap_exceptions=wrap_exceptions, **options)
55
60
  self._database_name = database_name
61
+ self._collection_mapping = {
62
+ "counters": counter_collection_name,
63
+ "windows": window_collection_name,
64
+ }
56
65
  self.lib = self.dependencies["pymongo"].module
57
66
  self.lib_errors, _ = get_dependency("pymongo.errors")
58
67
  self._storage_uri = uri
@@ -74,11 +83,11 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
74
83
 
75
84
  @property
76
85
  def counters(self) -> MongoCollection:
77
- return self._database["counters"]
86
+ return self._database[self._collection_mapping["counters"]]
78
87
 
79
88
  @property
80
89
  def windows(self) -> MongoCollection:
81
- return self._database["windows"]
90
+ return self._database[self._collection_mapping["windows"]]
82
91
 
83
92
  @abstractmethod
84
93
  def _init_mongo_client(
@@ -278,6 +287,10 @@ class MongoDBStorageBase(Storage, MovingWindowSupport, ABC):
278
287
 
279
288
 
280
289
  @versionadded(version="2.1")
290
+ @versionchanged(
291
+ version="3.14.0",
292
+ reason="Added option to select custom collection names for windows & counters",
293
+ )
281
294
  class MongoDBStorage(MongoDBStorageBase):
282
295
  STORAGE_SCHEME = ["mongodb", "mongodb+srv"]
283
296
 
@@ -1,15 +1,19 @@
1
1
  import urllib
2
- import warnings
3
- from typing import cast
4
2
 
5
3
  from deprecated.sphinx import versionchanged
6
4
  from packaging.version import Version
7
5
 
8
- from limits.errors import ConfigurationError
9
6
  from limits.storage.redis import RedisStorage
10
- from limits.typing import Dict, List, Optional, Tuple, Union
7
+ from limits.typing import Dict, Optional, Union
11
8
 
12
9
 
10
+ @versionchanged(
11
+ version="3.14.0",
12
+ reason="""
13
+ Dropped support for the :pypi:`redis-py-cluster` library
14
+ which has been abandoned/deprecated.
15
+ """,
16
+ )
13
17
  @versionchanged(
14
18
  version="2.5.0",
15
19
  reason="""
@@ -37,13 +41,19 @@ class RedisClusterStorage(RedisStorage):
37
41
 
38
42
  DEPENDENCIES = {
39
43
  "redis": Version("4.2.0"),
40
- "rediscluster": Version("2.0.0"), # Deprecated since 2.6.0
41
44
  }
42
45
 
43
- def __init__(self, uri: str, **options: Union[float, str, bool]) -> None:
46
+ def __init__(
47
+ self,
48
+ uri: str,
49
+ wrap_exceptions: bool = False,
50
+ **options: Union[float, str, bool],
51
+ ) -> None:
44
52
  """
45
53
  :param uri: url of the form
46
54
  ``redis+cluster://[:password]@host:port,host:port``
55
+ :param wrap_exceptions: Whether to wrap storage exceptions in
56
+ :exc:`limits.errors.StorageError` before raising it.
47
57
  :param options: all remaining keyword arguments are passed
48
58
  directly to the constructor of :class:`redis.cluster.RedisCluster`
49
59
  :raise ConfigurationError: when the :pypi:`redis` library is not
@@ -64,53 +74,15 @@ class RedisClusterStorage(RedisStorage):
64
74
  cluster_hosts.append((host, int(port)))
65
75
 
66
76
  self.storage = None
67
- self.using_redis_py = False
68
- self.__pick_storage(
69
- cluster_hosts, **{**self.DEFAULT_OPTIONS, **parsed_auth, **options}
77
+ merged_options = {**self.DEFAULT_OPTIONS, **parsed_auth, **options}
78
+ self.dependency = self.dependencies["redis"].module
79
+ startup_nodes = [self.dependency.cluster.ClusterNode(*c) for c in cluster_hosts]
80
+ self.storage = self.dependency.cluster.RedisCluster(
81
+ startup_nodes=startup_nodes, **merged_options
70
82
  )
71
83
  assert self.storage
72
84
  self.initialize_storage(uri)
73
- super(RedisStorage, self).__init__(uri, **options)
74
-
75
- def __pick_storage(
76
- self, cluster_hosts: List[Tuple[str, int]], **options: Union[float, str, bool]
77
- ) -> None:
78
- try:
79
- redis_py = self.dependencies["redis"].module
80
- startup_nodes = [redis_py.cluster.ClusterNode(*c) for c in cluster_hosts]
81
- self.storage = redis_py.cluster.RedisCluster(
82
- startup_nodes=startup_nodes, **options
83
- )
84
- self.using_redis_py = True
85
- return
86
- except ConfigurationError: # pragma: no cover
87
- self.__use_legacy_cluster_implementation(cluster_hosts, **options)
88
- if not self.storage:
89
- raise ConfigurationError(
90
- (
91
- "Unable to find an implementation for redis cluster"
92
- " Cluster support requires either redis-py>=4.2 or"
93
- " redis-py-cluster"
94
- )
95
- )
96
-
97
- def __use_legacy_cluster_implementation(
98
- self, cluster_hosts: List[Tuple[str, int]], **options: Union[float, str, bool]
99
- ) -> None: # pragma: no cover
100
- redis_cluster = self.dependencies["rediscluster"].module
101
- warnings.warn(
102
- (
103
- "Using redis-py-cluster is deprecated as the library has been"
104
- " absorbed by redis-py (>=4.2). The support will be eventually "
105
- " removed from the limits library and is no longer tested "
106
- " against since version: 2.6. To get rid of this warning, "
107
- " uninstall redis-py-cluster and ensure redis-py>=4.2.0 is installed"
108
- )
109
- )
110
- self.storage = redis_cluster.RedisCluster(
111
- startup_nodes=[{"host": c[0], "port": c[1]} for c in cluster_hosts],
112
- **options,
113
- )
85
+ super(RedisStorage, self).__init__(uri, wrap_exceptions, **options)
114
86
 
115
87
  def reset(self) -> Optional[int]:
116
88
  """
@@ -125,15 +97,9 @@ class RedisClusterStorage(RedisStorage):
125
97
  usage as it could be slow on very large data sets"""
126
98
 
127
99
  prefix = self.prefixed_key("*")
128
- if self.using_redis_py:
129
- count = 0
130
- for primary in self.storage.get_primaries():
131
- node = self.storage.get_redis_connection(primary)
132
- keys = node.keys(prefix)
133
- count += sum([node.delete(k.decode("utf-8")) for k in keys])
134
- return count
135
- else: # pragma: no cover
136
- keys = self.storage.keys(prefix)
137
- return cast(
138
- int, sum([self.storage.delete(k.decode("utf-8")) for k in keys])
139
- )
100
+ count = 0
101
+ for primary in self.storage.get_primaries():
102
+ node = self.storage.get_redis_connection(primary)
103
+ keys = node.keys(prefix)
104
+ count += sum([node.delete(k.decode("utf-8")) for k in keys])
105
+ return count
@@ -5,7 +5,7 @@ from packaging.version import Version
5
5
 
6
6
  from limits.errors import ConfigurationError
7
7
  from limits.storage.redis import RedisStorage
8
- from limits.typing import Dict, Optional, Union
8
+ from limits.typing import Dict, Optional, Tuple, Type, Union
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  import redis.sentinel
@@ -86,6 +86,12 @@ class RedisSentinelStorage(RedisStorage):
86
86
  self.use_replicas = use_replicas
87
87
  self.initialize_storage(uri)
88
88
 
89
+ @property
90
+ def base_exceptions(
91
+ self,
92
+ ) -> Union[Type[Exception], Tuple[Type[Exception], ...]]: # pragma: no cover
93
+ return self.dependencies["redis"].RedisError # type: ignore[no-any-return, attr-defined]
94
+
89
95
  def get(self, key: str) -> int:
90
96
  """
91
97
  :param key: the key to get the counter value for
limits/typing.py CHANGED
@@ -110,8 +110,8 @@ class ScriptP(Protocol[R_co]):
110
110
 
111
111
 
112
112
  MongoClient: TypeAlias = "pymongo.MongoClient[Dict[str, Any]]" # type:ignore[misc]
113
- MongoDatabase: TypeAlias = "pymongo.database.Database[Dict[str, Any]]" # type:ignore[misc]
114
- MongoCollection: TypeAlias = "pymongo.collection.Collection[Dict[str, Any]]" # type:ignore[misc]
113
+ MongoDatabase: TypeAlias = "pymongo.database.Database[Dict[str, Any]]" # type:ignore
114
+ MongoCollection: TypeAlias = "pymongo.collection.Collection[Dict[str, Any]]" # type:ignore
115
115
 
116
116
  __all__ = [
117
117
  "AsyncRedisClient",
limits/util.py CHANGED
@@ -1,13 +1,13 @@
1
1
  """ """
2
2
 
3
3
  import dataclasses
4
+ import importlib.resources
4
5
  import re
5
6
  import sys
6
7
  from collections import UserDict
7
8
  from types import ModuleType
8
- from typing import TYPE_CHECKING, cast
9
+ from typing import TYPE_CHECKING
9
10
 
10
- import importlib_resources
11
11
  from packaging.version import Version
12
12
 
13
13
  from limits.typing import Dict, List, NamedTuple, Optional, Tuple, Type, Union
@@ -142,7 +142,7 @@ def get_dependency(module_path: str) -> Tuple[Optional[ModuleType], Optional[Ver
142
142
 
143
143
 
144
144
  def get_package_data(path: str) -> bytes:
145
- return cast(bytes, importlib_resources.files("limits").joinpath(path).read_bytes())
145
+ return importlib.resources.files("limits").joinpath(path).read_bytes()
146
146
 
147
147
 
148
148
  def parse_many(limit_string: str) -> List[RateLimitItem]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: limits
3
- Version: 3.13.0
3
+ Version: 3.14.1
4
4
  Summary: Rate limiting utilities
5
5
  Home-page: https://limits.readthedocs.org
6
6
  Author: Ali-Akber Saifee
@@ -20,10 +20,9 @@ Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: Implementation :: PyPy
23
- Requires-Python: >=3.8
23
+ Requires-Python: >=3.9
24
24
  License-File: LICENSE.txt
25
25
  Requires-Dist: deprecated >=1.2
26
- Requires-Dist: importlib-resources >=1.3
27
26
  Requires-Dist: packaging <25,>=21
28
27
  Requires-Dist: typing-extensions
29
28
  Provides-Extra: all
@@ -36,12 +35,12 @@ Requires-Dist: coredis <5,>=3.4.0 ; extra == 'all'
36
35
  Requires-Dist: motor <4,>=3 ; extra == 'all'
37
36
  Requires-Dist: aetcd ; extra == 'all'
38
37
  Requires-Dist: emcache >=0.6.1 ; (python_version < "3.11") and extra == 'all'
39
- Requires-Dist: emcache >=1 ; (python_version >= "3.11") and extra == 'all'
38
+ Requires-Dist: emcache >=1 ; (python_version >= "3.11" and python_version < "3.13.0") and extra == 'all'
40
39
  Provides-Extra: async-etcd
41
40
  Requires-Dist: aetcd ; extra == 'async-etcd'
42
41
  Provides-Extra: async-memcached
43
42
  Requires-Dist: emcache >=0.6.1 ; (python_version < "3.11") and extra == 'async-memcached'
44
- Requires-Dist: emcache >=1 ; (python_version >= "3.11") and extra == 'async-memcached'
43
+ Requires-Dist: emcache >=1 ; (python_version >= "3.11" and python_version < "3.13.0") and extra == 'async-memcached'
45
44
  Provides-Extra: async-mongodb
46
45
  Requires-Dist: motor <4,>=3 ; extra == 'async-mongodb'
47
46
  Provides-Extra: async-redis
@@ -1,11 +1,11 @@
1
1
  limits/__init__.py,sha256=j_yVhgN9pdz8o5rQjVwdJTBSq8F-CTzof9kkiYgjRbw,728
2
- limits/_version.py,sha256=WegVz4YKhtYN102E8QvX81l0oVnN2UGchIOm2eVCePg,498
2
+ limits/_version.py,sha256=WbNixYn5JfqVMNZ6ag1B3zh5evGgXTIqinJPk2vcWH0,498
3
3
  limits/errors.py,sha256=xCKGOVJiD-g8FlsQQb17AW2pTUvalYSuizPpvEVoYJE,626
4
4
  limits/limits.py,sha256=ZsXESq2e1ji7c2ZKjSkIAasCjiLdjVLPUa9oah_I8U4,4943
5
5
  limits/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  limits/strategies.py,sha256=Zy6PIhkysPbxnMzFjyXEsxMM6jhRoQ5XT5WskTNruK0,6949
7
- limits/typing.py,sha256=nwJLek44QIg3869AbOSvPwotfp6JR7vEHz_UgZBiqgg,3287
8
- limits/util.py,sha256=xMRR5bKksYcnzY0H0xORDGvRFF5btiBognY2sSd38NE,5743
7
+ limits/typing.py,sha256=4yitf6iwDK-QEfSxv3EbTdGLOrqowLFffHAqYRUqiYY,3275
8
+ limits/util.py,sha256=fi2XoUBoEk0T-le41VQn3x-BJ3QqrLeIcP52Bj-qwpg,5724
9
9
  limits/version.py,sha256=YwkF3dtq1KGzvmL3iVGctA8NNtGlK_0arrzZkZGVjUs,47
10
10
  limits/aio/__init__.py,sha256=IOetunwQy1c5GefzitK8lewbTzHGiE-kmE9NlqSdr3U,82
11
11
  limits/aio/strategies.py,sha256=SHjmJnmy7Nh4tBydkA-0qPaULYcLOAM91T4RPybq0Sg,6768
@@ -14,7 +14,7 @@ limits/aio/storage/base.py,sha256=xdYpBBonyMjxE9iT-2oZjm6x29aDU6Xd09MeBYbZcMo,48
14
14
  limits/aio/storage/etcd.py,sha256=Rjb_EYKFRr4F2Z6zvAPP9vQOyXJQHaju3VjxxUs75_c,4791
15
15
  limits/aio/storage/memcached.py,sha256=6aTlACfCtchdcZqoisnei0MOlCH7yLV9A1yCjOE5f9g,4802
16
16
  limits/aio/storage/memory.py,sha256=DlmWluqUwBUWQIQ6XZi-mPrb15vfzBhA4iAKhBELDnE,5856
17
- limits/aio/storage/mongodb.py,sha256=sFQQK-JuInGEXC5Upjk_9NBe9hoNhgxQYimdRraXxQ8,9392
17
+ limits/aio/storage/mongodb.py,sha256=gZq9Ky3J7j0xfqIqB3ULBbQ5VjHzIyT1c7aNFp01VKk,10764
18
18
  limits/aio/storage/redis.py,sha256=jkqtdIwTpfXTXwgTWTA1Jlc3Lpc-vnu4XRy6CIptiZA,15651
19
19
  limits/resources/redis/lua_scripts/acquire_moving_window.lua,sha256=5CFJX7D6T6RG5SFr6eVZ6zepmI1EkGWmKeVEO4QNrWo,483
20
20
  limits/resources/redis/lua_scripts/clear_keys.lua,sha256=zU0cVfLGmapRQF9x9u0GclapM_IB2pJLszNzVQ1QRK4,184
@@ -25,13 +25,13 @@ limits/storage/base.py,sha256=fDdYLa-RrnjhBTO1hE5aTTM8q8n3M5HD-65KyWWXBtg,4627
25
25
  limits/storage/etcd.py,sha256=wkC_mj4Tsf2nwUKByMiHiGzA40N3mDepEwdLmvH8wmw,4484
26
26
  limits/storage/memcached.py,sha256=bMzfZgYa_EWcZAjSZLcygpk3hpeOAErBpRE8dVwyXQs,6640
27
27
  limits/storage/memory.py,sha256=R16E-Ccnmn1-LlolkFf-kB1-QHh8eiwFFLYVv0PuFD0,5561
28
- limits/storage/mongodb.py,sha256=QWd_SW--P86SMPDrDrBQS-2xKbWY3cw9x71KsI506zY,9213
28
+ limits/storage/mongodb.py,sha256=t8ey5-gYrJcmyvwJkqje0-TR-UMYvBF900a_zEXAYPI,9873
29
29
  limits/storage/redis.py,sha256=3zJ1gDMDepT_pGN9d2aAN7Pea7tMBI49VK60IHv-Ooc,8452
30
- limits/storage/redis_cluster.py,sha256=KwhWV0v3_TliRyS3OU15IlpeC8gRQr29U4FkcME01fo,5380
31
- limits/storage/redis_sentinel.py,sha256=7PVB0hBl0I_enhN_h9QSJTE7zGuYtjkebotTqxm2iZo,3875
30
+ limits/storage/redis_cluster.py,sha256=MsiEpwHphQd0P88AwGw1NVSi3UwVrhsg-pvzkHxU2kw,3739
31
+ limits/storage/redis_sentinel.py,sha256=lI7y7x9VGDCGq_-WRb6jR6cDgnzsOC1KGaRbwE69DNk,4122
32
32
  limits/storage/registry.py,sha256=xcBcxuu6srqmoS4WqDpkCXnRLB19ctH98v21P8S9kS8,708
33
- limits-3.13.0.dist-info/LICENSE.txt,sha256=T6i7kq7F5gIPfcno9FCxU5Hcwm22Bjq0uHZV3ElcjsQ,1061
34
- limits-3.13.0.dist-info/METADATA,sha256=vQsvBw6YR-jK6cCIusRrIjc-UG-aSL4PoHqFfYKzvjE,7170
35
- limits-3.13.0.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
36
- limits-3.13.0.dist-info/top_level.txt,sha256=C7g5ahldPoU2s6iWTaJayUrbGmPK1d6e9t5Nn0vQ2jM,7
37
- limits-3.13.0.dist-info/RECORD,,
33
+ limits-3.14.1.dist-info/LICENSE.txt,sha256=T6i7kq7F5gIPfcno9FCxU5Hcwm22Bjq0uHZV3ElcjsQ,1061
34
+ limits-3.14.1.dist-info/METADATA,sha256=iaF5tB4JgLvdzKk1-BNDZ1jkNo_3j-QidH4Ar7h218o,7189
35
+ limits-3.14.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
36
+ limits-3.14.1.dist-info/top_level.txt,sha256=C7g5ahldPoU2s6iWTaJayUrbGmPK1d6e9t5Nn0vQ2jM,7
37
+ limits-3.14.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5