Package pytilities :: Package event :: Module dispatcher_
[hide private]
[frames] | no frames]

Source Code for Module pytilities.event.dispatcher_

  1  # Copyright (C) 2010 Tim Diels <limyreth@users.sourceforge.net> 
  2  #  
  3  # This file is part of pytilities. 
  4  #  
  5  # pytilities is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  #  
 10  # pytilities is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  #  
 15  # You should have received a copy of the GNU General Public License 
 16  # along with pytilities.  If not, see <http://www.gnu.org/licenses/>. 
 17  # 
 18   
 19  # Note: this file name isn't standard as epydoc and our testing couldn't 
 20  # understand it if we named it dispatcher.py; it would clash in __init__.py 
 21  # with the dispatcher decorator 
 22   
 23  __docformat__ = 'reStructuredText' 
 24   
 25  from pytilities.delegation import ( 
 26      delegator_factory, delegated) 
27 28 -class UnsupportedEventError(Exception):
29 """Tried to access an unregistered event""" 30
31 - def __init__(self, event_name):
32 Exception.__init__(self, "Unsupported event: %s" % event_name)
33
34 @delegator_factory() 35 -class Dispatcher(object):
36 37 """ 38 Utility class for dispatching events to handlers. 39 40 Events have to be registered before they can be dispatched or have handlers 41 added to them. 42 43 Handlers can have an owner associated with them, usually you'll use the 44 reference of the listener. This allows you to remove all the handlers of a 45 specific owner, which should save you some work. 46 47 Instance methods: 48 49 - `add_handler`: Add handler for an event 50 - `remove_handlers`: Remove all or some handlers 51 - `remove_handler`: Remove a handler 52 - `dispatch`: Dispatch an event 53 - `register_events`: Register events 54 - `has_event`: Check whether event is supported 55 56 Instance properties: 57 58 - `events`: Read-only, set of all supported events 59 60 Instance decorators: 61 62 - `event`: Register decorated as a handler 63 64 Class invariants: 65 66 - For every (owner, event), there can be only 0 or 1 handlers 67 """ 68 69 # inspired by pyglet.event.EventDispatcher, yay pyglet 70 71 @staticmethod
72 - def __init_delegation_profiles(profiles):
73 profiles['default'] |= profiles['public']
74
75 - def __init__(self):
76 # dict of handlers: event_name -> set((handler, owner)) 77 self.__handlers = {} 78 self.__registered_events = set()
79 80 @delegated("public")
81 - def add_handler(self, event_name, handler, owner = None):
82 """ 83 Add handler for an event, optionally with an owner. 84 85 Parameters: 86 87 event_name :: string 88 name of the event to add the handler to 89 90 handler :: callable 91 the handler to call when the event is dispatched 92 93 owner = None 94 owner of the handler. Use this reference to easily remove all 95 your handlers from the dispatcher (e.g. remove all handlers 96 with the same owner) 97 98 Raises: 99 100 - `UnsupportedEventError` when `event_name` doesn't exist 101 """ 102 103 if not self.has_event(event_name): 104 raise UnsupportedEventError(event_name) 105 106 assert handler, "handler argument musn't be None" 107 108 assert (handler, owner) not in self.__handlers, ( 109 "You cannot add the same handler for the same owner and event " + 110 "more than once.") 111 112 self.__handlers.setdefault(event_name, set()) 113 self.__handlers[event_name].add((handler, owner))
114 115 @delegated("public")
116 - def remove_handlers(self, event_name=None, owner=None):
117 """ 118 Remove all or some handlers of the dispatcher. 119 120 `event_name` and `owner` act as filters of what to remove. 121 122 If no handler matched the criterea, the method will return silently. 123 124 Parameters: 125 126 event_name :: string = None 127 the event of which to remove the handlers. `None` means any 128 129 owner = None 130 the owner of which to remove the handlers. `None` means any 131 """ 132 133 if event_name: 134 self.__remove_handlers(event_name, owner) 135 else: 136 for event in self.__registered_events: 137 self.__remove_handlers(event, owner)
138
139 - def __remove_handlers(self, event, owner):
140 """Remove handlers of single event, optionally with particular owner """ 141 handlers = self.__handlers[event] 142 handlers -= set((handler, o) for (handler, o) in handlers 143 if o is owner)
144 145 @delegated("public")
146 - def remove_handler(self, event_name, handler, owner = None):
147 """ 148 Remove a handler from an event. It is an error to try to remove a 149 handler from an event that doesn't have this handler attached to it. 150 151 Parameters: 152 153 event_name :: string 154 name of the event to which the handler belongs 155 156 handler :: callable 157 the handler that is attached to the event 158 159 owner = None 160 owner of the handler 161 162 Preconditions: 163 1. `handler` is attached to `event_name` 164 165 Raises: 166 - `UnsupportedEventError` when `event_name` doesn't exist 167 """ 168 169 if not self.has_event(event_name): 170 raise UnsupportedEventError(event_name) 171 172 assert handler, "handler argument musn't be None" 173 174 self.__handlers[event_name].remove((handler, owner))
175 176 @delegated()
177 - def dispatch(self, event_name, *args, **keyword_args):
178 """ 179 Dispatch an event to its handlers. 180 181 The handlers are executed in a random order. 182 183 Parameters: 184 185 event_name :: string 186 name of the event to dispatch 187 188 args 189 arguments to pass to the handlers 190 191 keyword_args 192 keyword arguments to pass to the handlers 193 194 Raises: 195 - `UnsupportedEventError` when `event_name` doesn't exist 196 """ 197 198 if not self.has_event(event_name): 199 raise UnsupportedEventError(event_name) 200 201 for handler in self.__handlers.get(event_name, ()): 202 handler[0](*args, **keyword_args)
203 204 @delegated("public")
205 - def event(self, event_name, owner = None):
206 """ 207 Register the decorated as a handler of `event_name` 208 209 Parameters: 210 211 event_name :: string 212 name of the event 213 214 owner = None 215 owner of the handler. Use this reference to easily remove all 216 your handlers from the dispatcher (e.g. remove all handlers 217 with the same owner) 218 """ 219 220 def decorator(handler): 221 self.add_handler(event_name, handler, owner) 222 return handler
223 224 return decorator
225 226 @delegated()
227 - def register_events(self, *event_names):
228 """ 229 Register events. 230 231 Parameters: 232 233 event_names :: (string...) 234 names of events to support 235 """ 236 # this allows us to have methods like has event, which allow for better 237 # duck typing (if I see something that sends out quack events, then I 238 # call that a duck) 239 self.__registered_events.update(event_names)
240 241 @delegated("public")
242 - def has_event(self, event_name):
243 """ 244 Checks if `event_name` is supported 245 246 Parameters: 247 248 event_name :: string 249 name of the event 250 251 Returns True if the dispatcher has the event 252 """ 253 return event_name in self.__registered_events
254 255 @delegated("public") 256 @property
257 - def events(self):
258 """ 259 Read-only, set of all supported events 260 261 Returns ::frozenset(string...) 262 """ 263 return frozenset(self.__registered_events)
264