Skip to content

Commit

Permalink
Reorganize throttle decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
sigmavirus24 committed Mar 31, 2021
1 parent 1ad3b14 commit 734573c
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 38 deletions.
21 changes: 12 additions & 9 deletions doc/source/decorators.rst → doc/source/contrib.rst
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
==========================
Rush's Throttle Decorator
User Contributed Modules
==========================

:class:`~rush.decorator.ThrottleDecorator` is an inferace which allows
Rush's Throttle Decorator
=========================

:class:`~rush.contrib.decorator.ThrottleDecorator` is an inferace which allows
Rush's users to limit calls to a function using a decorator. Both
synchronous and asynchronous functions are supported.


.. autoclass:: rush.decorator.ThrottleDecorator
.. autoclass:: rush.contrib.decorator.ThrottleDecorator
:members:


Example
=======
-------

.. code-block:: python
from rush import decorator
from rush import exceptions
from rush import quota
from rush import throttle
from rush.contrib import decorator
from rush.limiters import periodic
from rush.stores import dictionary
t = throttle.Throttle(
limiter=periodic.PeriodicLimiter(
store=dictionary.DictionaryStore()
limiter=periodic.PeriodicLimiter(
store=dictionary.DictionaryStore()
),
rate=quota.Quota.per_second(
count=1,
Expand All @@ -36,10 +38,11 @@ Example
@decorator.ThrottleDecorator(throttle=t)
def ratelimited_func():
return True
try:
for _ in range(2):
ratelimited_func()
except exceptions.ThrottleExceeded as e:
except decorator.ThrottleExceeded as e:
limit_result = e.result
print(limit_result.limited) # => True
print(limit_result.remaining) # => 0
Expand Down
2 changes: 1 addition & 1 deletion doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ Table of Contents
:maxdepth: 2

throttles
decorators
limiters
storage
contrib
examples
releases/index
Empty file added src/rush/contrib/__init__.py
Empty file.
37 changes: 23 additions & 14 deletions src/rush/decorator.py → src/rush/contrib/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import attr

from rush import exceptions as rexc
from rush import result as res
from rush import throttle as thr
from rush import exceptions
from rush import result
from rush import throttle as _throttle


@attr.s
Expand All @@ -25,12 +25,12 @@ class ThrottleDecorator:
decorated functions.
"""

throttle: thr.Throttle = attr.ib()
throttle: _throttle.Throttle = attr.ib()

def _check(self, key: str, thr: thr.Throttle) -> res.RateLimitResult:
def _check(self, key: str) -> result.RateLimitResult:
result = self.throttle.check(key=key, quantity=1)
if result.limited:
raise rexc.ThrottleExceeded("Rate-limit exceeded", result=result)
raise ThrottleExceeded("Rate-limit exceeded", result=result)
return result

def __call__(self, func: typing.Callable) -> typing.Callable:
Expand Down Expand Up @@ -60,9 +60,9 @@ async def wrapper(*args, **kwargs) -> typing.Callable:
:param kwargs:
keyworded arguments to pass to the decorated function.
:raises:
`~rush.exceptions.ThrottleExceeded`
`~rush.contrib.decorator.ThrottleExceeded`
"""
self._check(key=key, thr=self.throttle)
self._check(key=key)
return await func(*args, **kwargs)

else:
Expand All @@ -81,9 +81,9 @@ def wrapper(*args, **kwargs) -> typing.Callable:
:param kwargs:
keyworded arguments to pass to the decorated function.
:raises:
`~rush.exceptions.ThrottleExceeded`
`~rush.contrib.decorator.ThrottleExceeded`
"""
self._check(key=key, thr=self.throttle)
self._check(key=key)
return func(*args, **kwargs)

return wrapper
Expand All @@ -106,7 +106,7 @@ async def wrapper(*args, **kwargs) -> typing.Callable:
"""Perform naive sleep and retry strategy.
Call the throttled function. If the function raises a
`ThrottleExceeded` exception sleep for the recommended
``ThrottleExceeded`` exception sleep for the recommended
time and retry.
:param args:
Expand All @@ -117,7 +117,7 @@ async def wrapper(*args, **kwargs) -> typing.Callable:
while True:
try:
return await throttled_func(*args, **kwargs)
except rexc.ThrottleExceeded as e:
except ThrottleExceeded as e:
await asyncio.sleep(
e.result.retry_after.total_seconds()
)
Expand All @@ -129,7 +129,7 @@ def wrapper(*args, **kwargs) -> typing.Callable:
"""Perform naive sleep and retry strategy.
Call the throttled function. If the function raises a
`ThrottleExceeded` exception sleep for the recommended
``ThrottleExceeded`` exception sleep for the recommended
time and retry.
:param args:
Expand All @@ -140,7 +140,16 @@ def wrapper(*args, **kwargs) -> typing.Callable:
while True:
try:
return throttled_func(*args, **kwargs)
except rexc.ThrottleExceeded as e:
except ThrottleExceeded as e:
time.sleep(e.result.retry_after.total_seconds())

return wrapper


class ThrottleExceeded(exceptions.RushError):
"""The rate-limit has been exceeded."""

def __init__(self, message, *, result: result.RateLimitResult) -> None:
"""Handle extra arguments for easier access by users."""
super().__init__(message)
self.result = result
10 changes: 0 additions & 10 deletions src/rush/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Exceptions for the rush library."""
from rush import result


class RushError(Exception):
Expand Down Expand Up @@ -41,12 +40,3 @@ def __init__(self, message, *, url, error):
super().__init__(message)
self.url = url
self.error = error


class ThrottleExceeded(Exception):
"""The rate-limit has been exceeded."""

def __init__(self, message, *, result: result.RateLimitResult) -> None:
"""Handle extra arguments for easier access by users."""
super().__init__(message)
self.result = result
7 changes: 3 additions & 4 deletions test/unit/test_throttle_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
import mock
import pytest

from rush import decorator
from rush import exceptions
from rush.contrib import decorator


class TestThrottleDecorator:
Expand Down Expand Up @@ -36,7 +35,7 @@ def test_call_sync_limited(self):
def test_func():
return True

with pytest.raises(exceptions.ThrottleExceeded):
with pytest.raises(decorator.ThrottleExceeded):
test_func()

def test_call_async_non_limited(self):
Expand Down Expand Up @@ -65,7 +64,7 @@ async def test_func():
return True

loop = asyncio.get_event_loop()
with pytest.raises(exceptions.ThrottleExceeded):
with pytest.raises(decorator.ThrottleExceeded):
loop.run_until_complete(test_func())

def test_sleep_and_retry_sync(self):
Expand Down

0 comments on commit 734573c

Please sign in to comment.