Mercurial > p > roundup > code
comparison roundup/rate_limit.py @ 5996:69a35d164a69
Make rate_limit.py pass flake8.
Fix up some formatting and other issues. I did noqa a line wrap as
breaking the line into multiple parts with proper indenting would make
it unintelligable.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Wed, 25 Dec 2019 20:00:04 -0500 |
| parents | 5d0873a4de4a |
| children | 8f29e4ea05ce |
comparison
equal
deleted
inserted
replaced
| 5995:b4b5b7afacd9 | 5996:69a35d164a69 |
|---|---|
| 3 # with imports, modifications for python 2, implementation of | 3 # with imports, modifications for python 2, implementation of |
| 4 # set/get_tat and marshaling as string, support for testonly | 4 # set/get_tat and marshaling as string, support for testonly |
| 5 # and status method. | 5 # and status method. |
| 6 | 6 |
| 7 from datetime import timedelta, datetime | 7 from datetime import timedelta, datetime |
| 8 | |
| 8 | 9 |
| 9 class RateLimit: # pylint: disable=too-few-public-methods | 10 class RateLimit: # pylint: disable=too-few-public-methods |
| 10 def __init__(self, count, period): | 11 def __init__(self, count, period): |
| 11 self.count = count | 12 self.count = count |
| 12 self.period = period | 13 self.period = period |
| 24 # This should return a previous tat for the key or the current time. | 25 # This should return a previous tat for the key or the current time. |
| 25 if key in self.memory: | 26 if key in self.memory: |
| 26 return self.memory[key] | 27 return self.memory[key] |
| 27 else: | 28 else: |
| 28 return datetime.min | 29 return datetime.min |
| 29 | 30 |
| 30 def set_tat(self, key, tat): | 31 def set_tat(self, key, tat): |
| 31 self.memory[key] = tat | 32 self.memory[key] = tat |
| 32 | 33 |
| 33 def get_tat_as_string(self, key): | 34 def get_tat_as_string(self, key): |
| 34 # get value as string: | 35 # get value as string: |
| 37 if key in self.memory: | 38 if key in self.memory: |
| 38 return self.memory[key].isoformat() | 39 return self.memory[key].isoformat() |
| 39 else: | 40 else: |
| 40 return datetime.min.isoformat() | 41 return datetime.min.isoformat() |
| 41 | 42 |
| 42 | |
| 43 def set_tat_as_string(self, key, tat): | 43 def set_tat_as_string(self, key, tat): |
| 44 # Take value as string and unmarshall: | 44 # Take value as string and unmarshall: |
| 45 # YYYY-MM-DDTHH:MM:SS.mmmmmm | 45 # YYYY-MM-DDTHH:MM:SS.mmmmmm |
| 46 # to datetime | 46 # to datetime |
| 47 self.memory[key] = datetime.strptime(tat,"%Y-%m-%dT%H:%M:%S.%f") | 47 self.memory[key] = datetime.strptime(tat, "%Y-%m-%dT%H:%M:%S.%f") |
| 48 | 48 |
| 49 def update(self, key, limit, testonly=False): | 49 def update(self, key, limit, testonly=False): |
| 50 '''Determine if the item associated with the key should be | 50 '''Determine if the item associated with the key should be |
| 51 rejected given the RateLimit limit. | 51 rejected given the RateLimit limit. |
| 52 ''' | 52 ''' |
| 53 now = datetime.utcnow() | 53 now = datetime.utcnow() |
| 54 tat = max(self.get_tat(key), now) | 54 tat = max(self.get_tat(key), now) |
| 55 separation = (tat - now).total_seconds() | 55 separation = (tat - now).total_seconds() |
| 56 max_interval = limit.period.total_seconds() - limit.inverse | 56 max_interval = limit.period.total_seconds() - limit.inverse |
| 57 if separation > max_interval: | 57 if separation > max_interval: |
| 58 reject = True | 58 reject = True |
| 59 else: | 59 else: |
| 60 reject = False | 60 reject = False |
| 88 ) | 88 ) |
| 89 | 89 |
| 90 # status of current limit as of now | 90 # status of current limit as of now |
| 91 now = datetime.utcnow() | 91 now = datetime.utcnow() |
| 92 | 92 |
| 93 current_count = int((limit.period - (tat - now)).total_seconds()\ | 93 current_count = int((limit.period - (tat - now)).total_seconds() / |
| 94 /limit.inverse) | 94 limit.inverse) |
| 95 ret['X-RateLimit-Remaining'] = str(min(current_count,limit.count)) | 95 ret['X-RateLimit-Remaining'] = str(min(current_count, limit.count)) |
| 96 | 96 |
| 97 # tat_in_epochsec = (tat - datetime(1970, 1, 1)).total_seconds() | 97 # tat_in_epochsec = (tat - datetime(1970, 1, 1)).total_seconds() |
| 98 seconds_to_tat = (tat - now).total_seconds() | 98 seconds_to_tat = (tat - now).total_seconds() |
| 99 ret['X-RateLimit-Reset'] = str(max(seconds_to_tat, 0)) | 99 ret['X-RateLimit-Reset'] = str(max(seconds_to_tat, 0)) |
| 100 ret['X-RateLimit-Reset-date'] = "%s"%tat | 100 ret['X-RateLimit-Reset-date'] = "%s" % tat |
| 101 ret['Now'] = str((now - datetime(1970,1,1)).total_seconds()) | 101 ret['Now'] = str((now - datetime(1970, 1, 1)).total_seconds()) |
| 102 ret['Now-date'] = "%s"%now | 102 ret['Now-date'] = "%s" % now |
| 103 | 103 |
| 104 if self.update(key, limit, testonly=True): | 104 if self.update(key, limit, testonly=True): |
| 105 # A new request would be rejected if it was processes. | 105 # A new request would be rejected if it was processes. |
| 106 # The user has to wait until an item is dequeued. | 106 # The user has to wait until an item is dequeued. |
| 107 # One item is dequeued every limit.inverse seconds. | 107 # One item is dequeued every limit.inverse seconds. |
| 108 ret['Retry-After'] = str(int(limit.inverse)) | 108 ret['Retry-After'] = str(int(limit.inverse)) |
| 109 ret['Retry-After-Timestamp'] = "%s"%(now + timedelta(seconds=limit.inverse)) | 109 ret['Retry-After-Timestamp'] = "%s" % \ |
| 110 (now + timedelta(seconds=limit.inverse)) # noqa: E127 | |
| 110 else: | 111 else: |
| 111 # if we are not rejected, the user can post another | 112 # if we are not rejected, the user can post another |
| 112 # attempt immediately. | 113 # attempt immediately. |
| 113 # Do we even need this header if not rejected? | 114 # Do we even need this header if not rejected? |
| 114 # RFC implies this is used with a 503 (or presumably | 115 # RFC implies this is used with a 503 (or presumably |
