Package elisa :: Package core :: Package bus :: Module bus
[hide private]
[frames] | no frames]

Source Code for Module elisa.core.bus.bus

  1  # Elisa - Home multimedia server 
  2  # Copyright (C) 2006-2008 Fluendo, S.A. (www.fluendo.com). 
  3  # All rights reserved. 
  4  # 
  5  # This file is available under one of two license agreements. 
  6  # 
  7  # This file is licensed under the GPL version 3. 
  8  # See "LICENSE.GPL" in the root of this distribution including a special 
  9  # exception to use Elisa with Fluendo's plugins. 
 10  # 
 11  # The GPL part of Elisa is also available under a commercial licensing 
 12  # agreement from Fluendo. 
 13  # See "LICENSE.Elisa" in the root directory of this distribution package 
 14  # for details on that license. 
 15   
 16  __maintainer__ = 'Philippe Normand <philippe@fluendo.com>' 
 17  __maintainer2__ = 'Florian Boucault <florian@fluendo.com>' 
 18   
 19   
 20  import threading 
 21   
 22  from elisa.core import log 
 23  from elisa.core.bus import bus_message 
 24  from elisa.core.utils import threadsafe_list 
 25  from twisted.internet import reactor, threads, defer 
 26   
27 -class Bus(log.Loggable):
28 """ 29 Python objects can register callbacks with the bus and be called when 30 L{elisa.core.bus.bus_message.Message}s are sent by other objects. 31 32 Here is a simple example:: 33 34 bus = Bus() 35 36 def my_cb(message, sender): 37 print 'Got message %r from %r' % (message, sender) 38 39 bus.register(my_cb) 40 41 bus.send_message(Message()) 42 43 44 Messages dispatching empties the message queue and call the registered 45 callbacks. Messages filtering is also supported, just pass a Message 46 type class to bus.register(), like this:: 47 48 bus.register(my_cb, Message) 49 50 You can filter on multiple Message types by supplying a list to 51 bus.register():: 52 53 bus.register(my_cb, (FooMessage, DataMessage)) 54 55 @ivar callbacks: registered callbacks 56 @type callbacks: dict, keys are callable objects and values are 57 Message types lists 58 """ 59
60 - def __init__(self):
61 """Initialize the Message queue and the callbacks dictionary. 62 """ 63 log.Loggable.__init__(self) 64 self.debug("Creating") 65 66 self._queue = threadsafe_list.ThreadsafeList() 67 68 self._running = False 69 70 # callbacks dictionary mapping callables to Message type lists 71 self._callbacks = {} 72 73 # threading lock for operations on self._callbacks 74 self._callbacks_lock = threading.Lock()
75
76 - def _dispatch_queued_msgs(self):
77 # dispatch pending messages 78 while self._queue: 79 message, sender = self._queue.pop() 80 reactor.callFromThread(self._dispatch, message, sender)
81
82 - def start(self):
83 """ 84 Start message dispatching: once started, messages sent over the bus 85 are guaranteed to be dispatched. 86 87 Queued messages are dispatched inside a separate thread. 88 """ 89 self.info("Starting") 90 self._running = True 91 self._dispatch_queued_msgs()
92
93 - def stop(self):
94 """ 95 Stop message dispatching: messages sent over the bus will not be 96 dispatched automatically anymore, they will be locally queued. 97 """ 98 self.info("Stopping") 99 self._running = False
100
101 - def send_message(self, message, sender=None):
102 """Send a message over the bus. The message is automatically 103 dispatched (inside a thread) if the L{Bus} is 104 running. Otherwise the message is locally queued until the 105 L{Bus} starts. 106 107 MT safe. 108 109 @param message: the message to send 110 @type message: L{elisa.core.bus.bus_message.Message} 111 @param sender: the sender object. None by default. Will be passed to 112 receiver callbacks. 113 @type sender: object 114 """ 115 assert isinstance(message, bus_message.Message), message 116 self.debug("Sending message %r", message) 117 118 if self._running: 119 reactor.callFromThread(self._dispatch, message, sender) 120 else: 121 self._queue.append((message, sender))
122
123 - def register(self, callback, *message_types):
124 """ 125 Register a new callback with the bus. The given callback will be 126 called when a message of one of the given types is dispatched on the 127 bus. 128 129 MT safe. 130 131 @param callback: the callback to register 132 @type callback: callable 133 @param message_types: Message types to filter on 134 @type message_types: type or list of types 135 """ 136 if not message_types: 137 message_types = (bus_message.Message,) 138 139 self.debug("Registering callback %r for %r message types", 140 callback, message_types) 141 142 try: 143 self._callbacks_lock.acquire() 144 self._callbacks[callback] = message_types 145 finally: 146 self._callbacks_lock.release()
147
148 - def unregister(self, callback):
149 """Unregister a callback from the bus. 150 151 MT safe. 152 153 @param callback: the callback to register 154 @type callback: callable 155 """ 156 try: 157 self._callbacks_lock.acquire() 158 if callback in self._callbacks: 159 del self._callbacks[callback] 160 finally: 161 self._callbacks_lock.release()
162
163 - def _dispatch(self, message, sender):
164 """Dispatch messages to registered callbacks and empty the 165 message queue. 166 """ 167 dispatched = False 168 try: 169 self._callbacks_lock.acquire() 170 for callback, mfilter in self._callbacks.iteritems(): 171 if isinstance(message, mfilter): 172 try: 173 cb_name = "%s.%s" % (callback.im_class.__name__, 174 callback.__name__) 175 except AttributeError: 176 cb_name = callback.__name__ 177 self.debug("Dispatching message %r to %r", message, cb_name) 178 callback(message, sender) 179 dispatched = True 180 finally: 181 self._callbacks_lock.release() 182 183 if not dispatched: 184 self.debug("Undispatched message: %r", message) 185 return dispatched
186
187 -def bus_listener(bus, *message_types):
188 """ Utility decorator to simply register a function or method on 189 the message bus. 190 """ 191 def decorator(func): 192 bus.register(func, *message_types)
193 194 return decorator 195