Debugging an application¶
Types of Errors¶
While developing an application that uses pubsub, calls to pubsub functions and methods may raise an exception. These are discussed in:
Notification: Tracking pubsub activity¶
Pubsub can call a specified handler every time it performs a certain task:
subscribe: whenever a listener subscribes to a topic
unsubscribe: whenever a listener unsubscribes from a topic
deadListener: whenever pubsub finds out that a listener has died
send: whenever the user calls sendMessage()
newTopic: whenever the user defines a new topic
delTopic: whenever the user undefines a topic
A notification handler must adhere to the pub.INotificationHandler:
import pubsub.utils
class MyNotifHandler(INotificationHandler):
def onSendMessage(...):
...
pub.addNotificationHandler( MyNotifHandler() )
A simple handler class is available already in pubsub.utils
:
notification.NotifyByPubsubMessage
.
This handler takes each notification received and generates a pubsub
message of a “pubsub.” topic named after the operation, such as “pubsub.subscribe”.
To use notification via this notifier, you must register one or more
listeners for the “pubsub.*” topics of interest.
A utility function is available from pubsub.utils for the most common case:
from pubsub.utils import notification
notification.useNotifyByPubsubMessage()
Naughty Listeners: Trap Exceptions¶
A sender has no way of knowing what can go wrong during message handling by the
subscribed listeners. As a result, a listener must not raise any exceptions (or
rather, must not let any exceptions escape): if an exception does escape a
listener, it interrupts the pub.sendMessage()
call such that some listeners may
not be sent the message. Putting a try/except clause around every sendMessage is
typically not practical.
Since exceptions are common during application development (bugs due to invalid arguments, failed assertions, etc), pubsub provdes a hook to register a ‘listener exception’ handler: whenever a listener raises an exception, pubsub then sends it to the handler, and continues with the send operation until all listeners have received the message. The handler might print it to a log file, output a message in a status bar, show an error box, etc. The handling itself is very application-specific, hence this strategy.
The handler must adhere to the pub.IListenerExcHandler
protocol. An instance
of the handler can be given to pub.setListenerExcHandler()
.
Listen for messages from all topics¶
Pubsub defines a specia topic named pub.ALL_TOPICS. A listener that subscribes to this topic will receives all messages of every topic. By default, the listener will not receive any data since pub.ALL_TOPICS is the parent of all root topics: its MDS must be empty.
However, any listener that is a callable with a “catch-all” **kwargs parameter will be given all message data. Moreover, pubsub sends the topic object automatically with the message data if it finds that listener accepts a keyword argument with a default value of pub.AUTO_TOPIC. Together, these can be used to obtain complete information about all messages:
>>> def snoop(topicObj=pub.AUTO_TOPIC, **mesgData):
>>> print 'topic "%s": %s' % (topicObj.getName(), mesgData)
>>>
>>> pub.subscribe(snoop, pub.ALL_TOPICS)
(<pubsub.core.listenerimpl.Listener instance at 0x01A040A8>, True)
>>> pub.sendMessage('some.topic.name', a=1, b=2)
topic "some.topic.name": {'a': 1, 'b': 2}
Using the pub.Listener class¶
Every callable that is subscribed via pub.subscribe() is wrapped in a pub.Listener instance returned by this function. This class has several useful functions such as name(), typeName(), module(), and isDead(). For example:
>>> def snoop(topicObj=pub.AUTO_TOPIC, **mesgData):
>>> pass
>>>
>>> pubListener, first = pub.subscribe(snoop, pub.ALL_TOPICS)
>>> assert first == true # since first time subscribed
>>> assert pubListener.isDead() == false
>>> assert pubListener.wantsTopicObjOnCall() == true
>>> assert pubListener.wantsAllMessageData() == true
>>> print pubListener.name()
snoop_2752
>>> print pubListener.name()
snoop
Doing something with every topic¶
Derive from pub.ITopicTreeVisitor and give instance to an instance of pub.TopicTreeTraverser, then call traverse() method. For example, assume a callable ‘listener’ has been subscribed to several topics. An easy way to verify all topics subscribed to use this:
>>> class MyVisitor(pub.ITopicTreeVisitor):
>>> def __init__(self, listener):
>>> self.subscribed = []
>>> self.listener = listener
>>> def _onTopic(self, topicObj):
>>> if (topicObj.hasListener(self.listener))
>>> self.subscribed.append(topicObj.getName())
>>>
>>> tester = new MyVisitor(listener)
>>> traverser = pub.TopicTreeTraverser( tester )
>>> traverser.traverse(pub.getDefaultTopicTreeRoot())
>>> print tester.subscribed
['topic-name', 'topic-name2', ...]
Printing Topic Tree¶
See pubsub.utils.printTreeDocs().