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

Roundup Issue Tracker: http://roundup-tracker.org/