~ack/landscape-client/sources.list-preserve-old-permissions

« back to all changes in this revision

Viewing changes to landscape/tests/clock.py

  • Committer: Christopher Armstrong
  • Date: 2008-06-10 10:56:01 UTC
  • Revision ID: radix@twistedmatrix.com-20080610105601-l9qfvqjf88e7j8b6
Import landscape-client into public branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
 
2
 
 
3
# Permission is hereby granted, free of charge, to any person obtaining
 
4
# a copy of this software and associated documentation files (the
 
5
# "Software"), to deal in the Software without restriction, including
 
6
# without limitation the rights to use, copy, modify, merge, publish,
 
7
# distribute, sublicense, and/or sell copies of the Software, and to
 
8
# permit persons to whom the Software is furnished to do so, subject to
 
9
# the following conditions:
 
10
 
 
11
# The above copyright notice and this permission notice shall be
 
12
# included in all copies or substantial portions of the Software.
 
13
 
 
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
15
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
16
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
17
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
18
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
19
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
20
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
21
 
 
22
"""
 
23
Copies of certain classes from Twisted 2.5, so that we can use the
 
24
functionality they provide with Twisted 2.2 and up. These should
 
25
really only be used from the test suite for now.
 
26
 
 
27
Currently:
 
28
 
 
29
 * L{twisted.internet.task.Clock}, which is new in Twisted 2.5.
 
30
 * L{twisted.internet.base.DelayedCall}, which didn't grow its
 
31
   C{seconds} argument until after Twisted 2.2.
 
32
"""
 
33
 
 
34
 
 
35
from twisted.internet import error
 
36
from twisted.python.runtime import seconds as runtimeSeconds
 
37
from twisted.python import reflect
 
38
import traceback
 
39
 
 
40
 
 
41
class Clock:
 
42
    """
 
43
    Provide a deterministic, easily-controlled implementation of
 
44
    L{IReactorTime.callLater}.  This is commonly useful for writing
 
45
    deterministic unit tests for code which schedules events using this API.
 
46
    """
 
47
    rightNow = 0.0
 
48
 
 
49
    def __init__(self):
 
50
        self.calls = []
 
51
 
 
52
    def seconds(self):
 
53
        """
 
54
        Pretend to be time.time().  This is used internally when an operation
 
55
        such as L{IDelayedCall.reset} needs to determine a a time value
 
56
        relative to the current time.
 
57
 
 
58
        @rtype: C{float}
 
59
        @return: The time which should be considered the current time.
 
60
        """
 
61
        return self.rightNow
 
62
 
 
63
 
 
64
    def callLater(self, when, what, *a, **kw):
 
65
        """
 
66
        See L{twisted.internet.interfaces.IReactorTime.callLater}.
 
67
        """
 
68
        self.calls.append(
 
69
            DelayedCall(self.seconds() + when,
 
70
                             what, a, kw,
 
71
                             self.calls.remove,
 
72
                             lambda c: None,
 
73
                             self.seconds))
 
74
        self.calls.sort(lambda a, b: cmp(a.getTime(), b.getTime()))
 
75
        return self.calls[-1]
 
76
 
 
77
 
 
78
    def advance(self, amount):
 
79
        """
 
80
        Move time on this clock forward by the given amount and run whatever
 
81
        pending calls should be run.
 
82
 
 
83
        @type amount: C{float}
 
84
        @param amount: The number of seconds which to advance this clock's
 
85
        time.
 
86
        """
 
87
        self.rightNow += amount
 
88
        while self.calls and self.calls[0].getTime() <= self.seconds():
 
89
            call = self.calls.pop(0)
 
90
            call.called = 1
 
91
            call.func(*call.args, **call.kw)
 
92
 
 
93
 
 
94
    def pump(self, timings):
 
95
        """
 
96
        Advance incrementally by the given set of times.
 
97
 
 
98
        @type timings: iterable of C{float}
 
99
        """
 
100
        for amount in timings:
 
101
            self.advance(amount)
 
102
 
 
103
 
 
104
 
 
105
class DelayedCall:
 
106
 
 
107
    # enable .debug to record creator call stack, and it will be logged if
 
108
    # an exception occurs while the function is being run
 
109
    debug = False
 
110
    _str = None
 
111
 
 
112
    def __init__(self, time, func, args, kw, cancel, reset,
 
113
                 seconds=runtimeSeconds):
 
114
        """
 
115
        @param time: Seconds from the epoch at which to call C{func}.
 
116
        @param func: The callable to call.
 
117
        @param args: The positional arguments to pass to the callable.
 
118
        @param kw: The keyword arguments to pass to the callable.
 
119
        @param cancel: A callable which will be called with this
 
120
            DelayedCall before cancellation.
 
121
        @param reset: A callable which will be called with this
 
122
            DelayedCall after changing this DelayedCall's scheduled
 
123
            execution time. The callable should adjust any necessary
 
124
            scheduling details to ensure this DelayedCall is invoked
 
125
            at the new appropriate time.
 
126
        @param seconds: If provided, a no-argument callable which will be
 
127
            used to determine the current time any time that information is
 
128
            needed.
 
129
        """
 
130
        self.time, self.func, self.args, self.kw = time, func, args, kw
 
131
        self.resetter = reset
 
132
        self.canceller = cancel
 
133
        self.seconds = seconds
 
134
        self.cancelled = self.called = 0
 
135
        self.delayed_time = 0
 
136
        if self.debug:
 
137
            self.creator = traceback.format_stack()[:-2]
 
138
 
 
139
    def getTime(self):
 
140
        """Return the time at which this call will fire
 
141
 
 
142
        @rtype: C{float}
 
143
        @return: The number of seconds after the epoch at which this call is
 
144
        scheduled to be made.
 
145
        """
 
146
        return self.time + self.delayed_time
 
147
 
 
148
    def cancel(self):
 
149
        """Unschedule this call
 
150
 
 
151
        @raise AlreadyCancelled: Raised if this call has already been
 
152
        unscheduled.
 
153
 
 
154
        @raise AlreadyCalled: Raised if this call has already been made.
 
155
        """
 
156
        if self.cancelled:
 
157
            raise error.AlreadyCancelled
 
158
        elif self.called:
 
159
            raise error.AlreadyCalled
 
160
        else:
 
161
            self.canceller(self)
 
162
            self.cancelled = 1
 
163
            if self.debug:
 
164
                self._str = str(self)
 
165
            del self.func, self.args, self.kw
 
166
 
 
167
    def reset(self, secondsFromNow):
 
168
        """Reschedule this call for a different time
 
169
 
 
170
        @type secondsFromNow: C{float}
 
171
        @param secondsFromNow: The number of seconds from the time of the
 
172
        C{reset} call at which this call will be scheduled.
 
173
 
 
174
        @raise AlreadyCancelled: Raised if this call has been cancelled.
 
175
        @raise AlreadyCalled: Raised if this call has already been made.
 
176
        """
 
177
        if self.cancelled:
 
178
            raise error.AlreadyCancelled
 
179
        elif self.called:
 
180
            raise error.AlreadyCalled
 
181
        else:
 
182
            newTime = self.seconds() + secondsFromNow
 
183
            if newTime < self.time:
 
184
                self.delayed_time = 0
 
185
                self.time = newTime
 
186
                self.resetter(self)
 
187
            else:
 
188
                self.delayed_time = newTime - self.time
 
189
 
 
190
    def delay(self, secondsLater):
 
191
        """Reschedule this call for a later time
 
192
 
 
193
        @type secondsLater: C{float}
 
194
        @param secondsLater: The number of seconds after the originally
 
195
        scheduled time for which to reschedule this call.
 
196
 
 
197
        @raise AlreadyCancelled: Raised if this call has been cancelled.
 
198
        @raise AlreadyCalled: Raised if this call has already been made.
 
199
        """
 
200
        if self.cancelled:
 
201
            raise error.AlreadyCancelled
 
202
        elif self.called:
 
203
            raise error.AlreadyCalled
 
204
        else:
 
205
            self.delayed_time += secondsLater
 
206
            if self.delayed_time < 0:
 
207
                self.activate_delay()
 
208
                self.resetter(self)
 
209
 
 
210
    def activate_delay(self):
 
211
        self.time += self.delayed_time
 
212
        self.delayed_time = 0
 
213
 
 
214
    def active(self):
 
215
        """Determine whether this call is still pending
 
216
 
 
217
        @rtype: C{bool}
 
218
        @return: True if this call has not yet been made or cancelled,
 
219
        False otherwise.
 
220
        """
 
221
        return not (self.cancelled or self.called)
 
222
 
 
223
    def __le__(self, other):
 
224
        return self.time <= other.time
 
225
 
 
226
    def __str__(self):
 
227
        if self._str is not None:
 
228
            return self._str
 
229
        if hasattr(self, 'func'):
 
230
            if hasattr(self.func, 'func_name'):
 
231
                func = self.func.func_name
 
232
                if hasattr(self.func, 'im_class'):
 
233
                    func = self.func.im_class.__name__ + '.' + func
 
234
            else:
 
235
                func = reflect.safe_repr(self.func)
 
236
        else:
 
237
            func = None
 
238
 
 
239
        now = self.seconds()
 
240
        L = ["<DelayedCall %s [%ss] called=%s cancelled=%s" % (
 
241
                id(self), self.time - now, self.called, self.cancelled)]
 
242
        if func is not None:
 
243
            L.extend((" ", func, "("))
 
244
            if self.args:
 
245
                L.append(", ".join([reflect.safe_repr(e) for e in self.args]))
 
246
                if self.kw:
 
247
                    L.append(", ")
 
248
            if self.kw:
 
249
                L.append(", ".join(['%s=%s' % (k, reflect.safe_repr(v)) for (k, v) in self.kw.iteritems()]))
 
250
            L.append(")")
 
251
 
 
252
        if self.debug:
 
253
            L.append("\n\ntraceback at creation: \n\n%s" % ('    '.join(self.creator)))
 
254
        L.append('>')
 
255
 
 
256
        return "".join(L)