Python PubSub Website

Site Contents

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 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).