Codebase list flask-limiter / e523477
Update upstream source from tag 'upstream/3.3.0' Update to upstream version '3.3.0' with Debian dir dd6f5364357fd5b42c0f3e62cee07b34c0b18a17 Carsten Schoenert 1 year, 1 month ago
5 changed file(s) with 73 addition(s) and 8 deletion(s). Raw diff Collapse all Expand all
11
22 Changelog
33 =========
4
5 v3.3.0
6 ------
7 Release Date: 2023-02-26
8
9 * Bug Fix
10
11 * Ensure per route limits are preferred (over application limits)
12 when populating rate limiting headers in the case where no rate limit has been
13 breached in the request.
414
515 v3.2.0
616 ------
4454
4555 * Allow scoping regular limit decorators / context managers
4656
57 v3.3.0
58 ------
59 Release Date: 2023-02-26
60
4761 v3.2.0
4862 ------
4963 Release Date: 2023-02-15
6680
6781 * Simplify registration of decorated function & blueprint limits
6882
83
84 v3.3.0
85 ------
86 Release Date: 2023-02-26
6987
7088 v3.2.0
7189 ------
925943
926944
927945
946
2323 # setup.py/versioneer.py will grep for the variable names, so they must
2424 # each be defined on a line of their own. _version.py will just call
2525 # get_keywords().
26 git_refnames = " (HEAD -> master, tag: 3.2.0, stable)"
27 git_full = "b764e15950729baf12820086f9713c47414990d1"
28 git_date = "2023-02-15 16:29:02 -0800"
26 git_refnames = " (tag: 3.3.0, stable)"
27 git_full = "5d4174a6584c369b3a233dd58edcc392f7cbab9b"
28 git_date = "2023-02-26 18:07:51 -0800"
2929 keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
3030 return keywords
3131
971971 method = self.limiter.hit
972972 kwargs["cost"] = lim.cost
973973
974 if not limit_for_header or lim.limit < limit_for_header.limit:
975 limit_for_header = RequestLimit(self, lim.limit, args, False)
976
977 view_limits.append(RequestLimit(self, lim.limit, args, False))
974 request_limit = RequestLimit(self, lim.limit, args, False, lim.shared)
975 view_limits.append(request_limit)
978976
979977 if not method(lim.limit, *args, **kwargs):
980978 self.logger.info(
989987 if self._fail_on_first_breach:
990988 break
991989
990 if not limit_for_header and view_limits:
991 # Pick a non shared limit over a shared one if possible
992 # when no rate limit has been hit. This should be the best hint
993 # for the client.
994 explicit = [limit for limit in view_limits if not limit.shared]
995 limit_for_header = explicit[0] if explicit else view_limits[0]
996
992997 self.context.view_rate_limit = limit_for_header or None
993998 self.context.view_rate_limits = view_limits
994999
9951000 on_breach_response = None
9961001 for limit in failed_limits:
997 request_limit = RequestLimit(self, limit[0].limit, limit[1], True)
1002 request_limit = RequestLimit(
1003 self, limit[0].limit, limit[1], True, limit[0].shared
1004 )
9981005 for cb in dict.fromkeys([self._on_breach, limit[0].on_breach]):
9991006 if cb:
10001007 try:
2727 #: Whether the limit was breached within the context of this request
2828 breached: bool
2929
30 #: Whether the limit is a shared limit
31 shared: bool
32
3033 def __init__(
3134 self,
3235 extension: Limiter,
3336 limit: RateLimitItem,
3437 request_args: List[str],
3538 breached: bool,
39 shared: bool,
3640 ) -> None:
3741 self.extension: weakref.ProxyType[Limiter] = weakref.proxy(extension)
3842 self.limit = limit
3943 self.request_args = request_args
4044 self.key = limit.key_for(*request_args)
4145 self.breached = breached
46 self.shared = shared
4247 self._window: Optional[Tuple[int, int]] = None
4348
4449 @property
325325 limiter = Limiter(
326326 get_remote_address,
327327 app=app,
328 application_limits=["60/minute"],
328329 default_limits=["10/minute"],
329330 headers_enabled=True,
330331 )
343344 resp = cli.get("/t1")
344345 assert resp.headers.get("X-RateLimit-Limit") == "10"
345346 assert resp.headers.get("X-RateLimit-Remaining") == "9"
347 assert resp.headers.get("X-RateLimit-Reset") == str(int(time.time() + 61))
348 assert resp.headers.get("Retry-After") == str(60)
349 resp = cli.get("/t2")
350 assert resp.headers.get("X-RateLimit-Limit") == "2"
351 assert resp.headers.get("X-RateLimit-Remaining") == "1"
352 assert resp.headers.get("X-RateLimit-Reset") == str(int(time.time() + 2))
353
354 assert resp.headers.get("Retry-After") == str(1)
355
356
357 def test_headers_application_limits():
358 app = Flask(__name__)
359 limiter = Limiter(
360 get_remote_address,
361 app=app,
362 application_limits=["60/minute"],
363 headers_enabled=True,
364 )
365
366 @app.route("/t1")
367 def t1():
368 return "test"
369
370 @app.route("/t2")
371 @limiter.limit("2/second; 5 per minute; 10/hour")
372 def t2():
373 return "test"
374
375 with hiro.Timeline().freeze():
376 with app.test_client() as cli:
377 resp = cli.get("/t1")
378 assert resp.headers.get("X-RateLimit-Limit") == "60"
379 assert resp.headers.get("X-RateLimit-Remaining") == "59"
346380 assert resp.headers.get("X-RateLimit-Reset") == str(int(time.time() + 61))
347381 assert resp.headers.get("Retry-After") == str(60)
348382 resp = cli.get("/t2")