Listeners
============

Receiving Messages
-------------------

A listener is a Python callable object (i.e. a function, method or 
instance with a __call__ method). A listener subscribes to topics that
it wants to listen to. The only restrictions on listeners are: 

- A listener must satisfy the TMAS of a topic 
  otherwise it will be rejected when requesting to subscribe, leading 
  to a ListenerInadequate exception.
 
- A listener **must not** raise any exceptions since neither pubsub 
  nor the sender of a message know anything about the listener, let 
  alone what exceptions it can raise and what to do with them. See
  :ref:`exception-handling`.
  
A listener should not make any assumptions about: 

- The order of calls of listeners subscribed to same or other topics
- Where the message originates 

Listeners that are subscribed are held by pubsub in a registry. 
However, Listeners are stored in this registry in such a way that when the 
application no longer uses the listener (reference count goes to 
zero), it is removed from the registry. In other words, the 
listener is stored in the registry by weak reference only. This 
prevents pubsub from artificially keeping listeners alive when 
the application no longer needs them. 

A listener can be given the Topic object when receiving a message. 

Listener lifetime 
------------------

Listeners are subscribed in pubsub via weak reference only. 
What this means concretely is that a listener is automatically
unsubscribed as soon as there is no application code that 
refers to it, ie as soon as it is garbage collected. This is 
particularly useful when listeners are methods of objects that can 
be destroyed during the lifetime of an application (this is 
not the only case): you do not need to add code to track when 
the listener is no longer in use, pubsub does it for you. 

A consequence of this is that you cannot subscribe temporaries, 
as would be created when wrapping a listener (for, say, currying). 
For instance, :: 

	class wrapper: 
		def __init__(self, fn): 
			...
	def listener(): pass
	pub.subscribe(wrapper(listener), "topic")
	
This will not subscribe anything: the wrapper instance is a 
temporary, so when pub.subscribe() returns, the temporary is 
garbage collected and unsubscribed! The correct way of doing 
this would be ::

	wrappedListener = wrapper(listener)
	pub.subscribe(wrappedListener, "topic")


Listener exceptions
--------------------

A sender has no way of knowing which listeners are subscribed to 
the topic of the message it is about to send. As a result, it 
cannot make any assumptions about the exception handling of the 
listeners: it must assume that listeners let no exception escape
their scope. If an exception does escape a listener, it interrupts
the sendMessage() call. 

Pubsub supports defining a "listener exception" handler: an 
object that adhere to the IListenerExcHandler protocol to gracefully
handle the exception. This typically consists of printing an 
exception message to the appropriate part of your application GUI. 


Automatic parameters
----------------------

The dafault values of listeners are examined at subscription
time to determine if there are special values that the listener 
should get. One such special value is the topic object corresponding
to the full topic message::

	def listener(topic=pub.AUTO_TOPIC_OBJ): 
		print "real topic is", topic.getName()
	pub.subscribe(listener, "some_topic")
	pub.sendMessage("some_topic") # no data 
		
Here the MDS of "some_topic" is empty: pubsub infers from the
listener signature that it should add the pub.TopicObj object
to the listener call when a "some_topic" message is sent, and 
does not include it in the MDS of "some_topic" (so that each 
listener can decide whether it needs such object). 



