Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Side by Side Diff: third_party/twisted_8_1/twisted/words/xish/utility.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.words.test.test_xishutil -*-
2 #
3 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6 """
7 Event Dispatching and Callback utilities.
8 """
9
10 from twisted.python import log
11 from twisted.words.xish import xpath
12
13 class _MethodWrapper(object):
14 """
15 Internal class for tracking method calls.
16 """
17 def __init__(self, method, *args, **kwargs):
18 self.method = method
19 self.args = args
20 self.kwargs = kwargs
21
22
23 def __call__(self, *args, **kwargs):
24 nargs = self.args + args
25 nkwargs = self.kwargs.copy()
26 nkwargs.update(kwargs)
27 self.method(*nargs, **nkwargs)
28
29
30 class CallbackList:
31 """
32 Container for callbacks.
33
34 Event queries are linked to lists of callables. When a matching event
35 occurs, these callables are called in sequence. One-time callbacks
36 are removed from the list after the first time the event was triggered.
37
38 Arguments to callbacks are split spread across two sets. The first set,
39 callback specific, is passed to C{addCallback} and is used for all
40 subsequent event triggers. The second set is passed to C{callback} and is
41 event specific. Positional arguments in the second set come after the
42 positional arguments of the first set. Keyword arguments in the second set
43 override those in the first set.
44
45 @ivar callbacks: The registered callbacks as mapping from the callable to a
46 tuple of a wrapper for that callable that keeps the
47 callback specific arguments and a boolean that signifies
48 if it is to be called only once.
49 @type callbacks: C{dict}
50 """
51
52 def __init__(self):
53 self.callbacks = {}
54
55
56 def addCallback(self, onetime, method, *args, **kwargs):
57 """
58 Add callback.
59
60 The arguments passed are used as callback specific arguments.
61
62 @param onetime: If C{True}, this callback is called at most once.
63 @type onetime: C{bool}
64 @param method: The callback callable to be added.
65 @param args: Positional arguments to the callable.
66 @type args: C{list}
67 @param kwargs: Keyword arguments to the callable.
68 @type kwargs: C{dict}
69 """
70
71 if not method in self.callbacks:
72 self.callbacks[method] = (_MethodWrapper(method, *args, **kwargs),
73 onetime)
74
75
76 def removeCallback(self, method):
77 """
78 Remove callback.
79
80 @param method: The callable to be removed.
81 """
82
83 if method in self.callbacks:
84 del self.callbacks[method]
85
86
87 def callback(self, *args, **kwargs):
88 """
89 Call all registered callbacks.
90
91 The passed arguments are event specific and augment and override
92 the callback specific arguments as described above.
93
94 @note: Exceptions raised by callbacks are trapped and logged. They will
95 not propagate up to make sure other callbacks will still be
96 called, and the event dispatching allways succeeds.
97
98 @param args: Positional arguments to the callable.
99 @type args: C{list}
100 @param kwargs: Keyword arguments to the callable.
101 @type kwargs: C{dict}
102 """
103
104 for key, (methodwrapper, onetime) in self.callbacks.items():
105 try:
106 methodwrapper(*args, **kwargs)
107 except:
108 log.err()
109
110 if onetime:
111 del self.callbacks[key]
112
113
114 def isEmpty(self):
115 """
116 Return if list of registered callbacks is empty.
117
118 @rtype: C{bool}
119 """
120
121 return len(self.callbacks) == 0
122
123
124
125 class EventDispatcher:
126 """
127 Event dispatching service.
128
129 The C{EventDispatcher} allows observers to be registered for certain events
130 that are dispatched. There are two types of events: XPath events and Named
131 events.
132
133 Every dispatch is triggered by calling L{dispatch} with a data object and,
134 for named events, the name of the event.
135
136 When an XPath type event is dispatched, the associated object is assumed to
137 be an L{Element<twisted.words.xish.domish.Element>} instance, which is
138 matched against all registered XPath queries. For every match, the
139 respective observer will be called with the data object.
140
141 A named event will simply call each registered observer for that particular
142 event name, with the data object. Unlike XPath type events, the data object
143 is not restricted to L{Element<twisted.words.xish.domish.Element>}, but can
144 be anything.
145
146 When registering observers, the event that is to be observed is specified
147 using an L{xpath.XPathQuery} instance or a string. In the latter case, the
148 string can also contain the string representation of an XPath expression.
149 To distinguish these from named events, each named event should start with
150 a special prefix that is stored in C{self.prefix}. It defaults to
151 C{//event/}.
152
153 Observers registered using L{addObserver} are persistent: after the
154 observer has been triggered by a dispatch, it remains registered for a
155 possible next dispatch. If instead L{addOnetimeObserver} was used to
156 observe an event, the observer is removed from the list of observers after
157 the first observed event.
158
159 Obsevers can also prioritized, by providing an optional C{priority}
160 parameter to the L{addObserver} and L{addOnetimeObserver} methods. Higher
161 priority observers are then called before lower priority observers.
162
163 Finally, observers can be unregistered by using L{removeObserver}.
164 """
165
166 def __init__(self, eventprefix="//event/"):
167 self.prefix = eventprefix
168 self._eventObservers = {}
169 self._xpathObservers = {}
170 self._dispatchDepth = 0 # Flag indicating levels of dispatching
171 # in progress
172 self._updateQueue = [] # Queued updates for observer ops
173
174
175 def _getEventAndObservers(self, event):
176 if isinstance(event, xpath.XPathQuery):
177 # Treat as xpath
178 observers = self._xpathObservers
179 else:
180 if self.prefix == event[:len(self.prefix)]:
181 # Treat as event
182 observers = self._eventObservers
183 else:
184 # Treat as xpath
185 event = xpath.internQuery(event)
186 observers = self._xpathObservers
187
188 return event, observers
189
190
191 def addOnetimeObserver(self, event, observerfn, priority=0, *args, **kwargs) :
192 """
193 Register a one-time observer for an event.
194
195 Like L{addObserver}, but is only triggered at most once. See there
196 for a description of the parameters.
197 """
198 self._addObserver(True, event, observerfn, priority, *args, **kwargs)
199
200
201 def addObserver(self, event, observerfn, priority=0, *args, **kwargs):
202 """
203 Register an observer for an event.
204
205 Each observer will be registered with a certain priority. Higher
206 priority observers get called before lower priority observers.
207
208 @param event: Name or XPath query for the event to be monitored.
209 @type event: C{str} or L{xpath.XPathQuery}.
210 @param observerfn: Function to be called when the specified event
211 has been triggered. This callable takes
212 one parameter: the data object that triggered
213 the event. When specified, the C{*args} and
214 C{**kwargs} parameters to addObserver are being used
215 as additional parameters to the registered observer
216 callable.
217 @param priority: (Optional) priority of this observer in relation to
218 other observer that match the same event. Defaults to
219 C{0}.
220 @type priority: C{int}
221 """
222 self._addObserver(False, event, observerfn, priority, *args, **kwargs)
223
224
225 def _addObserver(self, onetime, event, observerfn, priority, *args, **kwargs ):
226 # If this is happening in the middle of the dispatch, queue
227 # it up for processing after the dispatch completes
228 if self._dispatchDepth > 0:
229 self._updateQueue.append(lambda:self._addObserver(onetime, event, ob serverfn, priority, *args, **kwargs))
230 return
231
232 event, observers = self._getEventAndObservers(event)
233
234 if priority not in observers:
235 cbl = CallbackList()
236 observers[priority] = {event: cbl}
237 else:
238 priorityObservers = observers[priority]
239 if event not in priorityObservers:
240 cbl = CallbackList()
241 observers[priority][event] = cbl
242 else:
243 cbl = priorityObservers[event]
244
245 cbl.addCallback(onetime, observerfn, *args, **kwargs)
246
247
248 def removeObserver(self, event, observerfn):
249 """
250 Remove callable as observer for an event.
251
252 The observer callable is removed for all priority levels for the
253 specified event.
254
255 @param event: Event for which the observer callable was registered.
256 @type event: C{str} or L{xpath.XPathQuery}
257 @param observerfn: Observer callable to be unregistered.
258 """
259
260 # If this is happening in the middle of the dispatch, queue
261 # it up for processing after the dispatch completes
262 if self._dispatchDepth > 0:
263 self._updateQueue.append(lambda:self.removeObserver(event, observerf n))
264 return
265
266 event, observers = self._getEventAndObservers(event)
267
268 emptyLists = []
269 for priority, priorityObservers in observers.iteritems():
270 for query, callbacklist in priorityObservers.iteritems():
271 if event == query:
272 callbacklist.removeCallback(observerfn)
273 if callbacklist.isEmpty():
274 emptyLists.append((priority, query))
275
276 for priority, query in emptyLists:
277 del observers[priority][query]
278
279
280 def dispatch(self, obj, event=None):
281 """
282 Dispatch an event.
283
284 When C{event} is C{None}, an XPath type event is triggered, and
285 C{obj} is assumed to be an instance of
286 L{Element<twisted.words.xish.domish.Element>}. Otherwise, C{event}
287 holds the name of the named event being triggered. In the latter case,
288 C{obj} can be anything.
289
290 @param obj: The object to be dispatched.
291 @param event: Optional event name.
292 @type event: C{str}
293 """
294
295 foundTarget = False
296
297 self._dispatchDepth += 1
298
299 if event != None:
300 # Named event
301 observers = self._eventObservers
302 match = lambda query, obj: query == event
303 else:
304 # XPath event
305 observers = self._xpathObservers
306 match = lambda query, obj: query.matches(obj)
307
308 priorities = observers.keys()
309 priorities.sort()
310 priorities.reverse()
311
312 emptyLists = []
313 for priority in priorities:
314 for query, callbacklist in observers[priority].iteritems():
315 if match(query, obj):
316 callbacklist.callback(obj)
317 foundTarget = True
318 if callbacklist.isEmpty():
319 emptyLists.append((priority, query))
320
321 for priority, query in emptyLists:
322 del observers[priority][query]
323
324 self._dispatchDepth -= 1
325
326 # If this is a dispatch within a dispatch, don't
327 # do anything with the updateQueue -- it needs to
328 # wait until we've back all the way out of the stack
329 if self._dispatchDepth == 0:
330 # Deal with pending update operations
331 for f in self._updateQueue:
332 f()
333 self._updateQueue = []
334
335 return foundTarget
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/words/xish/domish.py ('k') | third_party/twisted_8_1/twisted/words/xish/xmlstream.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698