|  | @@ -50,6 +50,7 @@ class TaskBucket(object):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  | +    min_wait = 0.05
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def __init__(self, task_registry):
 | 
	
		
			
				|  |  |          self.task_registry = task_registry
 | 
	
	
		
			
				|  | @@ -60,19 +61,29 @@ class TaskBucket(object):
 | 
	
		
			
				|  |  |          """Put a task into the appropiate bucket."""
 | 
	
		
			
				|  |  |          self.buckets[task_name].put_nowait(task)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def get(self):
 | 
	
		
			
				|  |  | +    def get(self, timeout=None):
 | 
	
		
			
				|  |  |          """Retrive the task from the first available bucket.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          Available as in, there is an item in the queue and you can
 | 
	
		
			
				|  |  |          consume tokens from it.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  | +        time_spent = 0
 | 
	
		
			
				|  |  |          for bucket in self.buckets.values():
 | 
	
		
			
				|  |  | +            remaining_times = []
 | 
	
		
			
				|  |  |              try:
 | 
	
		
			
				|  |  |                  return bucket.get_nowait()
 | 
	
		
			
				|  |  | -            except (BucketRateExceeded, QueueEmpty):
 | 
	
		
			
				|  |  | +            except BucketRateExceeded:
 | 
	
		
			
				|  |  | +                remaining_times.append(bucket.expected_time())
 | 
	
		
			
				|  |  | +            except QueueEmpty:
 | 
	
		
			
				|  |  |                  pass
 | 
	
		
			
				|  |  | -            time.sleep(0.01)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if timeout and time_spent >= timeout:
 | 
	
		
			
				|  |  | +                raise QueueEmpty()
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                shortest_wait = min(remaining_times or [self.min_wait])
 | 
	
		
			
				|  |  | +                time_spent += shortest_wait
 | 
	
		
			
				|  |  | +                time.sleep(shortest_wait)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def init_with_registry(self):
 | 
	
		
			
				|  |  |          """Initialize with buckets for all the task types in the registry."""
 | 
	
	
		
			
				|  | @@ -94,12 +105,13 @@ class TaskBucket(object):
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  |          task_type = self.task_registry[task_name]
 | 
	
		
			
				|  |  |          task_queue = Queue()
 | 
	
		
			
				|  |  | -        rate_limit = parse_ratelimit_string(task_type.rate_limit)
 | 
	
		
			
				|  |  | +        rate_limit = getattr(task_type, "rate_limit", None)
 | 
	
		
			
				|  |  | +        rate_limit = parse_ratelimit_string(rate_limit)
 | 
	
		
			
				|  |  |          if rate_limit:
 | 
	
		
			
				|  |  |              task_queue = TokenBucketQueue(rate_limit, queue=task_queue)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          self.buckets[task_name] = task_queue
 | 
	
		
			
				|  |  | -        return bucket
 | 
	
		
			
				|  |  | +        return task_queue
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class TokenBucketQueue(object):
 | 
	
	
		
			
				|  | @@ -134,11 +146,11 @@ class TokenBucketQueue(object):
 | 
	
		
			
				|  |  |          self.fill_rate = float(fill_rate)
 | 
	
		
			
				|  |  |          self.timestamp = time.time()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def put(self, item, nb=True):
 | 
	
		
			
				|  |  | +    def put(self, item, nb=False):
 | 
	
		
			
				|  |  |          put = self.queue.put_nowait if nb else self.queue.put
 | 
	
		
			
				|  |  |          put(item)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def get(self, nb=True):
 | 
	
		
			
				|  |  | +    def get(self, nb=False):
 | 
	
		
			
				|  |  |          get = self.queue.get_nowait if nb else self.queue.get
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if not self.can_consume(1):
 | 
	
	
		
			
				|  | @@ -149,8 +161,8 @@ class TokenBucketQueue(object):
 | 
	
		
			
				|  |  |      def get_nowait(self):
 | 
	
		
			
				|  |  |          return self.get(nb=True)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def put_nowait(self):
 | 
	
		
			
				|  |  | -        return self.put(nb=True)
 | 
	
		
			
				|  |  | +    def put_nowait(self, item):
 | 
	
		
			
				|  |  | +        return self.put(item, nb=True)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def qsize(self):
 | 
	
		
			
				|  |  |          return self.queue.qsize()
 | 
	
	
		
			
				|  | @@ -160,9 +172,14 @@ class TokenBucketQueue(object):
 | 
	
		
			
				|  |  |          sufficient tokens otherwise False."""
 | 
	
		
			
				|  |  |          if tokens <= self._get_tokens():
 | 
	
		
			
				|  |  |              self._tokens -= tokens
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            return False
 | 
	
		
			
				|  |  | -        return True
 | 
	
		
			
				|  |  | +            return True
 | 
	
		
			
				|  |  | +        return False
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def expected_time(self, tokens=1):
 | 
	
		
			
				|  |  | +        """Returns the expected time in seconds when a new token should be
 | 
	
		
			
				|  |  | +        available."""
 | 
	
		
			
				|  |  | +        tokens = max(tokens, self._get_tokens())
 | 
	
		
			
				|  |  | +        return (tokens - self._get_tokens()) / self.fill_rate
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def _get_tokens(self):
 | 
	
		
			
				|  |  |          if self._tokens < self.capacity:
 |