File: notification.py

package info (click to toggle)
wxpython3.0 3.0.2.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 482,760 kB
  • ctags: 518,293
  • sloc: cpp: 2,127,226; python: 294,045; makefile: 51,942; ansic: 19,033; sh: 3,013; xml: 1,629; perl: 17
file content (331 lines) | stat: -rw-r--r-- 13,079 bytes parent folder | download | duplicates (3)
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)