~landscape/zope3/ztk-1.1.3

« back to all changes in this revision

Viewing changes to src/twisted/words/xish/utility.py

  • Committer: Andreas Hasenack
  • Date: 2009-07-20 17:49:16 UTC
  • Revision ID: andreas@canonical.com-20090720174916-g2tn6qmietz2hn0u
Revert twisted removal, it breaks several dozen tests [trivial]

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.words.test.test_xishutil -*-
 
2
#
 
3
# Copyright (c) 2001-2005 Twisted Matrix Laboratories.
 
4
# See LICENSE for details.
 
5
 
 
6
def _isStr(s):
 
7
    """ Internal method to determine if an object is a string """
 
8
    return isinstance(s, type('')) or isinstance(s, type(u''))
 
9
 
 
10
class _MethodWrapper(object):
 
11
    """ Internal class for tracking method calls """
 
12
    def __init__(self, method, *args, **kwargs):
 
13
        self.method = method
 
14
        self.args = args
 
15
        self.kwargs = kwargs
 
16
 
 
17
    def __call__(self, *args, **kwargs):
 
18
        nargs = self.args + args
 
19
        nkwargs = self.kwargs.copy()
 
20
        nkwargs.update(kwargs)
 
21
        self.method(*nargs, **nkwargs)        
 
22
 
 
23
class CallbackList:
 
24
    def __init__(self):
 
25
        self.callbacks = {}
 
26
 
 
27
    def addCallback(self, onetime, method, *args, **kwargs):
 
28
        if not method in self.callbacks:
 
29
            self.callbacks[method] = (_MethodWrapper(method, *args, **kwargs), onetime)
 
30
 
 
31
    def removeCallback(self, method):
 
32
        if method in self.callbacks:
 
33
            del self.callbacks[method]
 
34
 
 
35
    def callback(self, *args, **kwargs):
 
36
        for key, (methodwrapper, onetime) in self.callbacks.items():
 
37
            methodwrapper(*args, **kwargs)
 
38
            if onetime:
 
39
                del self.callbacks[key]
 
40
 
 
41
from twisted.words.xish import xpath, domish
 
42
 
 
43
class EventDispatcher:
 
44
    """ Event dispatching service.
 
45
 
 
46
    The C{EventDispatcher} allows observers to be registered for certain events
 
47
    that are dispatched. There are two types of events: XPath events and Named
 
48
    events.
 
49
    
 
50
    Every dispatch is triggered by calling L{dispatch} with a data object and,
 
51
    for named events, the name of the event.
 
52
    
 
53
    When an XPath type event is dispatched, the associated object is assumed
 
54
    to be a L{domish.Element} object, which is matched against all registered
 
55
    XPath queries. For every match, the respective observer will be called with
 
56
    the data object.
 
57
 
 
58
    A named event will simply call each registered observer for that particular
 
59
    event name, with the data object. Unlike XPath type events, the data object
 
60
    is not restricted to L{domish.Element}, but can be anything.
 
61
 
 
62
    When registering observers, the event that is to be observed is specified
 
63
    using an L{xpath.XPathQuery} object or a string. In the latter case, the
 
64
    string can also contain the string representation of an XPath expression.
 
65
    To distinguish these from named events, each named event should start with
 
66
    a special prefix that is stored in C{self.prefix}. It defaults to
 
67
    C{//event/}.
 
68
 
 
69
    Observers registered using L{addObserver} are persistent: after the
 
70
    observer has been triggered by a dispatch, it remains registered for a
 
71
    possible next dispatch. If instead L{addOnetimeObserver} was used to
 
72
    observe an event, the observer is removed from the list of observers after
 
73
    the first observed event.
 
74
 
 
75
    Obsevers can also prioritized, by providing an optional C{priority}
 
76
    parameter to the L{addObserver} and L{addOnetimeObserver} methods. Higher
 
77
    priority observers are then called before lower priority observers.
 
78
 
 
79
    Finally, observers can be unregistered by using L{removeObserver}.
 
80
    
 
81
    """
 
82
 
 
83
    def __init__(self, eventprefix = "//event/"):
 
84
        self.prefix = eventprefix
 
85
        self._eventObservers = {}
 
86
        self._xpathObservers = {}
 
87
        self._orderedEventObserverKeys = []
 
88
        self._orderedXpathObserverKeys = []
 
89
        self._dispatchDepth = 0  # Flag indicating levels of dispatching in progress
 
90
        self._updateQueue = [] # Queued updates for observer ops
 
91
 
 
92
    def _isEvent(self, event):
 
93
        return _isStr(event) and self.prefix == event[0:len(self.prefix)]
 
94
 
 
95
    def addOnetimeObserver(self, event, observerfn, priority=0, *args, **kwargs):
 
96
        """ Register a one-time observer for an event.
 
97
 
 
98
        Like L{addObserver}, but is only triggered at most once. See there
 
99
        for a description of the parameters.
 
100
        """
 
101
        self._addObserver(True, event, observerfn, priority, *args, **kwargs)
 
102
 
 
103
    def addObserver(self, event, observerfn, priority=0, *args, **kwargs):
 
104
        """ Register an observer for an event.
 
105
 
 
106
        Each observer will be registered with a certain priority. Higher
 
107
        priority observers get called before lower priority observers.
 
108
 
 
109
        @param event: Name or XPath query for the event to be monitored.
 
110
        @type event: L{str} or L{xpath.XPathQuery}.
 
111
        @param observerfn: Function to be called when the specified event
 
112
                           has been triggered. This function takes
 
113
                           one parameter: the data object that triggered
 
114
                           the event. When specified, the C{*args} and
 
115
                           C{**kwargs} parameters to addObserver are being used
 
116
                           as additional parameters to the registered observer
 
117
                           function.
 
118
        @param priority: (Optional) priority of this observer in relation to
 
119
                         other observer that match the same event. Defaults to
 
120
                         C{0}.
 
121
        @type priority: L{int}
 
122
        """
 
123
        self._addObserver(False, event, observerfn, priority, *args, **kwargs)
 
124
 
 
125
    def _addObserver(self, onetime, event, observerfn, priority, *args, **kwargs):
 
126
        # If this is happening in the middle of the dispatch, queue
 
127
        # it up for processing after the dispatch completes
 
128
        if self._dispatchDepth > 0:
 
129
            self._updateQueue.append(lambda:self.addObserver(event, observerfn, priority, *args, **kwargs))
 
130
            return
 
131
 
 
132
        observers = None
 
133
 
 
134
        if _isStr(event):
 
135
            if self.prefix == event[0:len(self.prefix)]:
 
136
                # Treat as event
 
137
                observers = self._eventObservers
 
138
            else:
 
139
                # Treat as xpath
 
140
                event = xpath.internQuery(event)
 
141
                observers = self._xpathObservers
 
142
        else:
 
143
            # Treat as xpath
 
144
            observers = self._xpathObservers
 
145
 
 
146
        key = (priority, event)
 
147
        if not key in observers:
 
148
            cbl = CallbackList()
 
149
            cbl.addCallback(onetime, observerfn, *args, **kwargs)
 
150
            observers[key] = cbl
 
151
        else:
 
152
            observers[key].addCallback(onetime, observerfn, *args, **kwargs)
 
153
 
 
154
        # Update the priority ordered list of xpath keys --
 
155
        # This really oughta be rethought for efficiency
 
156
        self._orderedEventObserverKeys = self._eventObservers.keys()
 
157
        self._orderedEventObserverKeys.sort()
 
158
        self._orderedEventObserverKeys.reverse()
 
159
        self._orderedXpathObserverKeys = self._xpathObservers.keys()
 
160
        self._orderedXpathObserverKeys.sort()
 
161
        self._orderedXpathObserverKeys.reverse()
 
162
 
 
163
    def removeObserver(self, event, observerfn):
 
164
        """ Remove function as observer for an event.
 
165
 
 
166
        The observer function is removed for all priority levels for the
 
167
        specified event.
 
168
        
 
169
        @param event: Event for which the observer function was registered.
 
170
        @type event: L{str} or L{xpath.XPathQuery}
 
171
        @param observerfn: Observer function to be unregistered.
 
172
        """
 
173
 
 
174
        # If this is happening in the middle of the dispatch, queue
 
175
        # it up for processing after the dispatch completes
 
176
        if self._dispatchDepth > 0:
 
177
            self._updateQueue.append(lambda:self.removeObserver(event, observerfn))
 
178
            return
 
179
 
 
180
        observers = None
 
181
 
 
182
        if _isStr(event):
 
183
            if self.prefix == event[0:len(self.prefix)]:
 
184
                observers = self._eventObservers
 
185
            else:
 
186
                event = xpath.internQuery(event)
 
187
                observers = self._xpathObservers
 
188
        else:
 
189
            observers = self._xpathObservers
 
190
 
 
191
        for priority, query in observers:
 
192
            if event == query:
 
193
                observers[(priority, query)].removeCallback(observerfn)
 
194
 
 
195
        # Update the priority ordered list of xpath keys --
 
196
        # This really oughta be rethought for efficiency
 
197
        self._orderedEventObserverKeys = self._eventObservers.keys()
 
198
        self._orderedEventObserverKeys.sort()
 
199
        self._orderedEventObserverKeys.reverse()
 
200
        self._orderedXpathObserverKeys = self._xpathObservers.keys()
 
201
        self._orderedXpathObserverKeys.sort()
 
202
        self._orderedXpathObserverKeys.reverse()
 
203
 
 
204
    def dispatch(self, object, event = None):
 
205
        """ Dispatch an event.
 
206
        
 
207
        When C{event} is C{None}, an XPath type event is triggered, and
 
208
        C{object} is assumed to be an instance of {domish.Element}. Otherwise,
 
209
        C{event} holds the name of the named event being triggered. In the
 
210
        latter case, C{object} can be anything.
 
211
 
 
212
        @param object: The object to be dispatched.
 
213
        @param event: Optional event name.
 
214
        @type event: L{str}
 
215
        """
 
216
 
 
217
        foundTarget = False
 
218
        
 
219
        # Aiyiyi! If this dispatch occurs within a dispatch
 
220
        # we need to preserve the original dispatching flag
 
221
        # and not mess up the updateQueue
 
222
        self._dispatchDepth = self._dispatchDepth + 1
 
223
            
 
224
        if event != None:
 
225
            for priority, query in self._orderedEventObserverKeys:
 
226
                if event == query:
 
227
                    self._eventObservers[(priority, event)].callback(object)
 
228
                    foundTarget = True
 
229
        else:
 
230
            for priority, query in self._orderedXpathObserverKeys:
 
231
                callbacklist = self._xpathObservers[(priority, query)]
 
232
                if query.matches(object):
 
233
                    callbacklist.callback(object)
 
234
                    foundTarget = True
 
235
 
 
236
        self._dispatchDepth = self._dispatchDepth -1
 
237
 
 
238
        # If this is a dispatch within a dispatch, don't
 
239
        # do anything with the updateQueue -- it needs to
 
240
        # wait until we've back all the way out of the stack
 
241
        if self._dispatchDepth == 0:
 
242
            # Deal with pending update operations
 
243
            for f in self._updateQueue:
 
244
                f()
 
245
            self._updateQueue = []
 
246
 
 
247
        return foundTarget