from Queue import Queue, Full, Empty
Full=Full
Empty=Empty

class Producer:
	"""A producer is a process which produces elements and appends them to an internal queue.  Producer processes (generally the run() method in threads - see below) should append elements to their queues by using self.produce(element,max_wait=0).
	  
	The queue has a finite length, and thus appending can block indefinitely (if max_wait is unspecified) or fail with a Full exception after max_wait seconds.  The producer's queue can be emptied by calling empty_queue().  Clients of the Producer class (generally Consumers) can get the next element in the list with get_next_element(max_wait=0) which can fail with an Empty exception.
	
	Producers are designed to be mixed-in with Thread classes.  You need to initialize Producers specifying the queue's maximum length.
	"""
	
	def __init__(self,max_length):
		self.__queue = Queue(max_length)
		
	def produce(self,element,max_wait=0):
		if max_wait == 0: self.__queue.put_nowait(element)
		else: self.__queue.put(element,True,max_wait)
	
	def empty_queue(self):
		try:
			while True: dummy = self.queue.get_nowait()
		except Empty: pass

	def get_next_element(self,max_wait): # for use of the Consumer class
		if max_wait == 0: return self.__queue.get_nowait()
		else: return self.__queue.get(True,max_wait)
		
	
# def abort_consumption() using events instead of locking queues?
class Consumer:
	"""A Consumer is a process which consumes Producer queue elements, one at a time.  Consumer processes can be initialized with or without a producer, but need to be told sooner or later which Producer to consume, with consumer_instance.consume_from(producer_instance).  To consume an element in the process (generally the run() method in threads - see below), use the self.consume(max_wait) method, which can raise the Empty exception if there is no element to consume, or no producer to consume from.
	
	Consumers are designed to be mixed-in with Thread classes.  You can either initialize Consumers specifying the producer instance you want to consume from, or initialize them without a producer instance, and later tell consumers which producer to consume from, with consumer_instance.consume_from().
	"""
	
	__producer = None
	
	def __init__(self,producer=None):
		if producer is None: pass
		else: self.consume_from(producer)
			
	def consume_from(self,producer):
		assert isinstance(producer,Producer)
		self.__producer = producer
	
	def consume(self,max_wait=0):
		"""Pops an element from the current producer's queue.  Waits at most max_wait seconds for the queue to have an element before raising Empty"""
		try: a = self.__producer.get_next_element(max_wait)
		except AttributeError:
			time.sleep(max_wait)
			raise Empty
		return a
