else/Ratelimiter/ratelimiter.py

67 lines
2 KiB
Python
Raw Normal View History

2016-01-24 20:48:39 +00:00
import time
class Ratelimiter:
2016-09-05 23:37:07 +00:00
def __init__(self, allowance, period=1, operation_cost=1, mode='sleep'):
2016-01-24 20:48:39 +00:00
'''
2016-09-05 23:37:07 +00:00
allowance:
2016-05-21 19:51:36 +00:00
Our spending balance per `period` seconds.
2016-01-24 20:48:39 +00:00
period:
2016-09-05 23:37:07 +00:00
The number of seconds over which we can perform `allowance` operations.
2016-01-24 20:48:39 +00:00
operation_cost:
The default amount to remove from our balance after each operation.
Pass a `cost` parameter to `self.limit` to use a nondefault value.
mode:
2016-05-21 19:51:36 +00:00
'sleep':
If we do not have the balance for an operation, sleep until we do.
Return True every time.
'reject':
If we do not have the balance for an operation, return False.
The cost is not subtracted, so hopefully we have enough next time.
2016-01-24 20:48:39 +00:00
'''
if mode not in ('sleep', 'reject'):
raise ValueError('Invalid mode %s' % repr(mode))
2016-05-21 19:51:36 +00:00
2016-09-05 23:37:07 +00:00
self.allowance = allowance
2016-01-24 20:48:39 +00:00
self.period = period
self.operation_cost = operation_cost
self.mode = mode
self.last_operation = time.time()
self.balance = 0
2016-05-21 19:51:36 +00:00
@property
def gain_rate(self):
2016-09-05 23:37:07 +00:00
return self.allowance / self.period
2016-01-24 20:48:39 +00:00
def limit(self, cost=None):
2016-05-21 19:51:36 +00:00
'''
See the main class docstring for info about cost and mode behavior.
'''
2016-01-24 20:48:39 +00:00
if cost is None:
cost = self.operation_cost
2016-05-21 19:51:36 +00:00
time_diff = time.time() - self.last_operation
self.balance += time_diff * self.gain_rate
2016-09-05 23:37:07 +00:00
self.balance = min(self.balance, self.allowance)
2016-01-24 20:48:39 +00:00
if self.balance >= cost:
self.balance -= cost
2016-05-21 19:51:36 +00:00
succesful = True
else:
if self.mode == 'reject':
succesful = False
else:
deficit = cost - self.balance
time_needed = deficit / self.gain_rate
time.sleep(time_needed)
self.balance = 0
succesful = True
2016-01-24 20:48:39 +00:00
self.last_operation = time.time()
2016-05-21 19:51:36 +00:00
return succesful