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