1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 __docformat__ = 'reStructuredText'
24
25 from pytilities.delegation import (
26 delegator_factory, delegated)
29 """Tried to access an unregistered event"""
30
32 Exception.__init__(self, "Unsupported event: %s" % event_name)
33
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
70
71 @staticmethod
73 profiles['default'] |= profiles['public']
74
76
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")
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
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")
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()
228 """
229 Register events.
230
231 Parameters:
232
233 event_names :: (string...)
234 names of events to support
235 """
236
237
238
239 self.__registered_events.update(event_names)
240
241 @delegated("public")
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
258 """
259 Read-only, set of all supported events
260
261 Returns ::frozenset(string...)
262 """
263 return frozenset(self.__registered_events)
264