Fix RateLimiter family debounce stuff

This commit is contained in:
Kristóf Tóth 2018-07-31 11:48:41 +02:00
parent 8a0928beca
commit 3dff144b91

View File

@ -21,31 +21,28 @@ class RateLimiter:
so it is only acceptable for stuff running on a separate thread. so it is only acceptable for stuff running on a separate thread.
If this is no good for you please refer to AsyncRateLimiter in this module, If this is no good for you please refer to AsyncRateLimiter in this module,
which is designed not to block and use the IOLoop it is being called from, which is designed not to block and use the IOLoop it is being called from.
or redefine the action argument of __init__ (which defaults to time.sleep).
""" """
def __init__(self, rate_per_second, action=sleep): def __init__(self, rate_per_second):
""" """
:param rate_per_second: max frequency the decorated method should be :param rate_per_second: max frequency the decorated method should be
invoked with invoked with
:param action: what to do when rate limiting. defaults to time.sleep,
receives the number of seconds until the next invocation
should occour as an argument
""" """
self.min_interval = 1 / float(rate_per_second) self.min_interval = 1 / float(rate_per_second)
self.action = action
self.fun = None self.fun = None
self.last_call = time() self.last_call = time()
def action(self, seconds_to_next_call):
if seconds_to_next_call:
sleep(seconds_to_next_call)
self.fun()
def __call__(self, fun): def __call__(self, fun):
@wraps(fun) @wraps(fun)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
self.fun = partial(fun, *args, **kwargs) self.fun = partial(fun, *args, **kwargs)
limit_seconds = self._limit_rate() limit_seconds = self._limit_rate()
if limit_seconds: self.action(limit_seconds)
self.action(limit_seconds)
return
self.fun()
return wrapper return wrapper
def _limit_rate(self): def _limit_rate(self):
@ -62,10 +59,11 @@ class AsyncRateLimiter(RateLimiter):
""" """
Decorator class for rate limiting, non-blocking. Decorator class for rate limiting, non-blocking.
The semantics of the rate limiting are similar to that of RateLimiter, The semantics of the rate limiting:
but this decorator never blocks, instead it adds an async callback version - unlike RateLimiter this decorator never blocks, instead it adds an async
of the decorated function to the IOLoop to be executed after the rate limiting callback version of the decorated function to the IOLoop
has expired. (to be executed after the rate limiting has expired).
- the timing works similarly to RateLimiter
""" """
def __init__(self, rate_per_second, ioloop_factory): def __init__(self, rate_per_second, ioloop_factory):
""" """
@ -79,24 +77,24 @@ class AsyncRateLimiter(RateLimiter):
self._last_callback = None self._last_callback = None
self._make_action_thread_safe() self._make_action_thread_safe()
super().__init__(rate_per_second=rate_per_second)
super().__init__(
rate_per_second=rate_per_second,
action=self.async_action
)
def _make_action_thread_safe(self): def _make_action_thread_safe(self):
self.async_action = partial(self.ioloop.add_callback, self.async_action) self.action = partial(self.ioloop.add_callback, self.action)
@lazy_property @lazy_property
def ioloop(self): def ioloop(self):
return self._ioloop_factory() return self._ioloop_factory()
def async_action(self, seconds_to_next_call): def action(self, seconds_to_next_call):
if self._last_callback: if self._last_callback:
self.ioloop.remove_timeout(self._last_callback) self.ioloop.remove_timeout(self._last_callback)
self._last_callback = self.ioloop.call_later( self._last_callback = self.ioloop.call_later(
seconds_to_next_call, seconds_to_next_call,
self.fun self.fun_with_debounce
) )
def fun_with_debounce(self):
self.last_call = time()
self.fun()