1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 __docformat__ = 'reStructuredText'
20
21 from pytilities.delegation import (
22 delegator_factory, delegated)
25 """Tried to access an unregistered event"""
26
28 Exception.__init__(self, "Unsupported event: %s" % event_name)
29
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
66
67 @staticmethod
69 profiles['default'] |= profiles['public']
70
72
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")
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
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")
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()
224 """
225 Register events.
226
227 Parameters:
228
229 event_names :: (string...)
230 names of events to support
231 """
232
233
234
235 self.__registered_events.update(event_names)
236
237 @delegated("public")
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
254 """
255 Read-only, set of all supported events
256
257 Returns ::frozenset(string...)
258 """
259 return frozenset(self.__registered_events)
260