1
# -*- test-case-name: twisted.test.test_xishutil -*-
3
# Twisted, the Framework of Your Internet
4
# Copyright (C) 2001 Matthew W. Lefkowitz
6
# This library is free software; you can redistribute it and/or
7
# modify it under the terms of version 2.1 of the GNU Lesser General Public
8
# License as published by the Free Software Foundation.
10
# This library 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 GNU
13
# Lesser General Public License for more details.
15
# You should have received a copy of the GNU Lesser General Public
16
# License along with this library; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
"""Internal method to determine if an object is a string """
21
return isinstance(s, type('')) or isinstance(s, type(u''))
23
class _MethodWrapper(object):
24
"""Internal class for tracking method calls """
25
def __init__(self, method, *args, **kwargs):
30
def __call__(self, *args, **kwargs):
31
nargs = self.args + args
32
nkwargs = self.kwargs.copy()
33
nkwargs.update(kwargs)
34
self.method(*nargs, **nkwargs)
40
def addCallback(self, onetime, method, *args, **kwargs):
41
if not method in self.callbacks:
42
self.callbacks[method] = (_MethodWrapper(method, *args, **kwargs), onetime)
44
def removeCallback(self, method):
45
if method in self.callbacks:
46
del self.callbacks[method]
48
def callback(self, *args, **kwargs):
49
for key, (methodwrapper, onetime) in self.callbacks.items():
50
methodwrapper(*args, **kwargs)
52
del self.callbacks[key]
54
from twisted.xish import xpath
56
class EventDispatcher:
57
def __init__(self, eventprefix = "//event/"):
58
self.prefix = eventprefix
59
self._eventObservers = {}
60
self._xpathObservers = {}
61
self._dispatchDepth = 0 # Flag indicating levels of dispatching in progress
62
self._updateQueue = [] # Queued updates for observer ops
64
def _isEvent(self, event):
65
return _isStr(event) and self.prefix == event[0:len(self.prefix)]
67
def addOnetimeObserver(self, event, observerfn, *args, **kwargs):
68
self._addObserver(True, event, observerfn, *args, **kwargs)
70
def addObserver(self, event, observerfn, *args, **kwargs):
71
self._addObserver(False, event, observerfn, *args, **kwargs)
73
# AddObserver takes several different types of arguments
74
# - xpath (string or object form)
75
# - event designator (string that starts with a known prefix)
76
def _addObserver(self, onetime, event, observerfn, *args, **kwargs):
77
# If this is happening in the middle of the dispatch, queue
78
# it up for processing after the dispatch completes
79
if self._dispatchDepth > 0:
80
self._updateQueue.append(lambda:self.addObserver(event, observerfn, *args, **kwargs))
86
if self.prefix == event[0:len(self.prefix)]:
88
observers = self._eventObservers
91
event = xpath.intern(event)
92
observers = self._xpathObservers
95
observers = self._xpathObservers
97
if not event in observers:
99
cbl.addCallback(onetime, observerfn, *args, **kwargs)
100
observers[event] = cbl
102
observers[event].addCallback(onetime, observerfn, *args, **kwargs)
105
def removeObserver(self, event, observerfn):
106
# If this is happening in the middle of the dispatch, queue
107
# it up for processing after the dispatch completes
108
if self._dispatchDepth > 0:
109
self._updateQueue.append(lambda:self.removeObserver(event, observerfn))
115
if self.prefix == event[0:len(self.prefix)]:
116
observers = self._eventObservers
118
event = xpath.intern(event)
119
observers = self._xpathObservers
121
observers = self._xpathObservers
123
assert event in observers
124
observers[event].removeCallback(observerfn)
127
def dispatch(self, object, event = None):
128
# Aiyiyi! If this dispatch occurs within a dispatch
129
# we need to preserve the original dispatching flag
130
# and not mess up the updateQueue
131
self._dispatchDepth = self._dispatchDepth + 1
134
if event in self._eventObservers:
135
self._eventObservers[event].callback(object)
137
for (query, callbacklist) in self._xpathObservers.iteritems():
138
if query.matches(object):
139
callbacklist.callback(object)
141
self._dispatchDepth = self._dispatchDepth -1
143
# If this is a dispatch within a dispatch, don't
144
# do anything with the updateQueue -- it needs to
145
# wait until we've back all the way out of the stack
146
if self._dispatchDepth == 0:
147
# Deal with pending update operations
148
for f in self._updateQueue:
150
self._updateQueue = []