1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
|
"""
Provide an interface class for handling pubsub notification messages,
and an example class (though very useful in practice) showing how to
use it.
Notification messages are generated by pubsub
- if a handler has been configured via pub.addNotificationHandler()
- when pubsub does certain tasks, such as when a listener subscribes to
or unsubscribes from a topic
Derive from this class to handle notification events from
various parts of pubsub. E.g. when a listener subscribes,
unsubscribes, or dies, a notification handler, if you
specified one via pub.addNotificationHandler(), is given the
relevant information.
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE_BSD_Simple.txt for details.
"""
from ..core import callables
from ..core.notificationmgr import INotificationHandler
class IgnoreNotificationsMixin(INotificationHandler):
"""
Derive your Notifications handler from this class if your handler
just wants to be notified of one or two types of pubsub events.
Then just override the desired methods. The rest of the notifications
will automatically be ignored.
"""
def notifySubscribe(self, pubListener, topicObj, newSub):
pass
def notifyUnsubscribe(self, pubListener, topicObj):
pass
def notifyDeadListener(self, pubListener, topicObj):
pass
def notifySend(self, stage, topicObj, pubListener=None):
pass
def notifyNewTopic(self, topicObj, description, required, argsDocs):
pass
def notifyDelTopic(self, topicName):
pass
class NotifyByWriteFile(INotificationHandler):
"""
Print a message to stdout when a notification is received.
"""
defaultPrefix = 'PUBSUB:'
def __init__(self, fileObj = None, prefix = None):
"""Will write to stdout unless fileObj given. Will use
defaultPrefix as prefix for each line output, unless prefix
specified. """
self.__pre = prefix or self.defaultPrefix
if fileObj is None:
import sys
self.__fileObj = sys.stdout
else:
self.__fileObj = fileObj
def changeFile(self, fileObj):
self.__fileObj = fileObj
def notifySubscribe(self, pubListener, topicObj, newSub):
if newSub:
msg = '%s Subscribed listener "%s" to topic "%s"\n'
else:
msg = '%s Subscription of "%s" to topic "%s" redundant\n'
msg = msg % (self.__pre, pubListener, topicObj.getName())
self.__fileObj.write(msg)
def notifyUnsubscribe(self, pubListener, topicObj):
msg = '%s Unsubscribed listener "%s" from topic "%s"\n'
msg = msg % (self.__pre, pubListener, topicObj.getName())
self.__fileObj.write(msg)
def notifyDeadListener(self, pubListener, topicObj):
msg = '%s Listener "%s" of Topic "%s" has died\n' \
% (self.__pre, pubListener, topicObj.getName())
# a bug apparently: sometimes on exit, the stream gets closed before
# and leads to a TypeError involving NoneType
self.__fileObj.write(msg)
def notifySend(self, stage, topicObj, pubListener=None):
if stage == 'in':
msg = '%s Sending message of topic "%s" to listener %s\n' % (self.__pre, topicObj.getName(), pubListener)
elif stage == 'pre':
msg = '%s Start sending message of topic "%s"\n' % (self.__pre, topicObj.getName())
else:
msg = '%s Done sending message of topic "%s"\n' % (self.__pre, topicObj.getName())
self.__fileObj.write(msg)
def notifyNewTopic(self, topicObj, description, required, argsDocs):
msg = '%s New topic "%s" created\n' % (self.__pre, topicObj.getName())
self.__fileObj.write(msg)
def notifyDelTopic(self, topicName):
msg = '%s Topic "%s" destroyed\n' % (self.__pre, topicName)
self.__fileObj.write(msg)
class NotifyByPubsubMessage(INotificationHandler):
"""
Handle pubsub notification messages by generating
messages of a 'pubsub.' subtopic. Also provides
an example of how to create a notification handler.
Use it by calling::
import pubsub.utils
pubsub.utils.useNotifyByPubsubMessage()
...
pub.setNotificationFlags(...) # optional
E.g. whenever a listener is unsubscribed, a 'pubsub.unsubscribe'
message is generated. If you have subscribed a listener of
this topic, your listener will be notified of what listener
unsubscribed from what topic.
"""
topicRoot = 'pubsub'
topics = dict(
send = '%s.sendMessage' % topicRoot,
subscribe = '%s.subscribe' % topicRoot,
unsubscribe = '%s.unsubscribe' % topicRoot,
newTopic = '%s.newTopic' % topicRoot,
delTopic = '%s.delTopic' % topicRoot,
deadListener = '%s.deadListener' % topicRoot)
def __init__(self, topicMgr=None):
self._pubTopic = None
self.__sending = False # used to guard against infinite loop
if topicMgr is not None:
self.createNotificationTopics(topicMgr)
def createNotificationTopics(self, topicMgr):
"""Create the notification topics. The root of the topics created
is self.topicRoot. The topicMgr is (usually) pub.topicMgr."""
# see if the special topics have already been defined
try:
topicMgr.getTopic(self.topicRoot)
except ValueError:
# no, so create them
self._pubTopic = topicMgr.getOrCreateTopic(self.topicRoot)
self._pubTopic.setDescription('root of all pubsub-specific topics')
_createTopics(self.topics, topicMgr)
def notifySubscribe(self, pubListener, topicObj, newSub):
if (self._pubTopic is None) or self.__sending:
return
pubTopic = self._pubTopic.getSubtopic('subscribe')
if topicObj is not pubTopic:
kwargs = dict(listener=pubListener, topic=topicObj, newSub=newSub)
self.__doNotification(pubTopic, kwargs)
def notifyUnsubscribe(self, pubListener, topicObj):
if (self._pubTopic is None) or self.__sending:
return
pubTopic = self._pubTopic.getSubtopic('unsubscribe')
if topicObj is not pubTopic:
kwargs = dict(
topic = topicObj,
listenerRaw = pubListener.getCallable(),
listener = pubListener)
self.__doNotification(pubTopic, kwargs)
def notifyDeadListener(self, pubListener, topicObj):
if (self._pubTopic is None) or self.__sending:
return
pubTopic = self._pubTopic.getSubtopic('deadListener')
kwargs = dict(topic=topicObj, listener=pubListener)
self.__doNotification(pubTopic, kwargs)
def notifySend(self, stage, topicObj, pubListener=None):
"""Stage must be 'pre' or 'post'. Note that any pubsub sendMessage
operation resulting from this notification (which sends a message;
listener could handle by sending another message!) will NOT themselves
lead to a send notification. """
if (self._pubTopic is None) or self.__sending:
return
sendMsgTopic = self._pubTopic.getSubtopic('sendMessage')
if stage == 'pre' and (topicObj is sendMsgTopic):
msg = 'Not allowed to send messages of topic %s' % topicObj.getName()
raise ValueError(msg)
self.__doNotification(sendMsgTopic, dict(topic=topicObj, stage=stage))
def notifyNewTopic(self, topicObj, desc, required, argsDocs):
if (self._pubTopic is None) or self.__sending:
return
pubTopic = self._pubTopic.getSubtopic('newTopic')
kwargs = dict(topic=topicObj, description=desc, required=required, args=argsDocs)
self.__doNotification(pubTopic, kwargs)
def notifyDelTopic(self, topicName):
if (self._pubTopic is None) or self.__sending:
return
pubTopic = self._pubTopic.getSubtopic('delTopic')
self.__doNotification(pubTopic, dict(name=topicName) )
def __doNotification(self, pubTopic, kwargs):
self.__sending = True
try:
pubTopic.publish( **kwargs )
finally:
self.__sending = False
def _createTopics(topicMap, topicMgr):
"""
Create notification topics. These are used when
some of the notification flags have been set to True (see
pub.setNotificationFlags(). The topicMap is a dict where key is
the notification type, and value is the topic name to create.
Notification type is a string in ('send', 'subscribe',
'unsubscribe', 'newTopic', 'delTopic', 'deadListener'.
"""
def newTopic(_name, _desc, _required=None, **argsDocs):
topic = topicMgr.getOrCreateTopic(_name)
topic.setDescription(_desc)
topic.setMsgArgSpec(argsDocs, _required)
newTopic(
_name = topicMap['subscribe'],
_desc = 'whenever a listener is subscribed to a topic',
topic = 'topic that listener has subscribed to',
listener = 'instance of pub.Listener containing listener',
newSub = 'false if listener was already subscribed, true otherwise')
newTopic(
_name = topicMap['unsubscribe'],
_desc = 'whenever a listener is unsubscribed from a topic',
topic = 'instance of Topic that listener has been unsubscribed from',
listener = 'instance of pub.Listener unsubscribed; None if listener not found',
listenerRaw = 'listener unsubscribed')
newTopic(
_name = topicMap['send'],
_desc = 'sent at beginning and end of sendMessage()',
topic = 'instance of topic for message being sent',
stage = 'stage of send operation: "pre" or "post" or "in"',
listener = 'which listener being sent to')
newTopic(
_name = topicMap['newTopic'],
_desc = 'whenever a new topic is defined',
topic = 'instance of Topic created',
description = 'description of topic (use)',
args = 'the argument names/descriptions for arguments that listeners must accept',
required = 'which args are required (all others are optional)')
newTopic(
_name = topicMap['delTopic'],
_desc = 'whenever a topic is deleted',
name = 'full name of the Topic instance that was destroyed')
newTopic(
_name = topicMap['deadListener'],
_desc = 'whenever a listener dies without having unsubscribed',
topic = 'instance of Topic that listener was subscribed to',
listener = 'instance of pub.Listener containing dead listener')
def useNotifyByPubsubMessage(publisher=None, all=True, **kwargs):
"""Will cause all of pubsub's notifications of pubsub "actions" (such as
new topic created, message sent, listener subscribed, etc) to be sent
out as messages. Topic will be 'pubsub' subtopics, such as
'pubsub.newTopic', 'pubsub.delTopic', 'pubsub.sendMessage', etc.
The 'all' and kwargs args are the same as pubsub's setNotificationFlags(),
except that 'all' defaults to True.
The publisher is rarely needed:
* The publisher must be specfied if pubsub is not installed
on the system search path (ie from pubsub import ... would fail or
import wrong pubsub -- such as if pubsub is within wxPython's
wx.lib package). Then pbuModule is the pub module to use::
from wx.lib.pubsub import pub
from wx.lib.pubsub.utils import notification
notification.useNotifyByPubsubMessage()
"""
if publisher is None:
from .. import pub
publisher = pub.getDefaultPublisher()
topicMgr = publisher.getTopicMgr()
notifHandler = NotifyByPubsubMessage( topicMgr )
publisher.addNotificationHandler(notifHandler)
publisher.setNotificationFlags(all=all, **kwargs)
def useNotifyByWriteFile(fileObj=None, prefix=None,
publisher=None, all=True, **kwargs):
"""Will cause all pubsub notifications of pubsub "actions" (such as
new topic created, message sent, listener died etc) to be written to
specified file (or stdout if none given). The fileObj need only
provide a 'write(string)' method.
The first two arguments are the same as those of NotifyByWriteFile
constructor. The 'all' and kwargs arguments are those of pubsub's
setNotificationFlags(), except that 'all' defaults to True. See
useNotifyByPubsubMessage() for an explanation of pubModule (typically
only if pubsub inside wxPython's wx.lib)"""
notifHandler = NotifyByWriteFile(fileObj, prefix)
if publisher is None:
from .. import pub
publisher = pub.getDefaultPublisher()
publisher.addNotificationHandler(notifHandler)
publisher.setNotificationFlags(all=all, **kwargs)
|