1
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
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:
11
# The above copyright notice and this permission notice shall be
12
# included in all copies or substantial portions of the Software.
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.
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.
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.
35
from twisted.internet import error
36
from twisted.python.runtime import seconds as runtimeSeconds
37
from twisted.python import reflect
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.
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.
59
@return: The time which should be considered the current time.
64
def callLater(self, when, what, *a, **kw):
66
See L{twisted.internet.interfaces.IReactorTime.callLater}.
69
DelayedCall(self.seconds() + when,
74
self.calls.sort(lambda a, b: cmp(a.getTime(), b.getTime()))
78
def advance(self, amount):
80
Move time on this clock forward by the given amount and run whatever
81
pending calls should be run.
83
@type amount: C{float}
84
@param amount: The number of seconds which to advance this clock's
87
self.rightNow += amount
88
while self.calls and self.calls[0].getTime() <= self.seconds():
89
call = self.calls.pop(0)
91
call.func(*call.args, **call.kw)
94
def pump(self, timings):
96
Advance incrementally by the given set of times.
98
@type timings: iterable of C{float}
100
for amount in timings:
107
# enable .debug to record creator call stack, and it will be logged if
108
# an exception occurs while the function is being run
112
def __init__(self, time, func, args, kw, cancel, reset,
113
seconds=runtimeSeconds):
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
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
137
self.creator = traceback.format_stack()[:-2]
140
"""Return the time at which this call will fire
143
@return: The number of seconds after the epoch at which this call is
144
scheduled to be made.
146
return self.time + self.delayed_time
149
"""Unschedule this call
151
@raise AlreadyCancelled: Raised if this call has already been
154
@raise AlreadyCalled: Raised if this call has already been made.
157
raise error.AlreadyCancelled
159
raise error.AlreadyCalled
164
self._str = str(self)
165
del self.func, self.args, self.kw
167
def reset(self, secondsFromNow):
168
"""Reschedule this call for a different time
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.
174
@raise AlreadyCancelled: Raised if this call has been cancelled.
175
@raise AlreadyCalled: Raised if this call has already been made.
178
raise error.AlreadyCancelled
180
raise error.AlreadyCalled
182
newTime = self.seconds() + secondsFromNow
183
if newTime < self.time:
184
self.delayed_time = 0
188
self.delayed_time = newTime - self.time
190
def delay(self, secondsLater):
191
"""Reschedule this call for a later time
193
@type secondsLater: C{float}
194
@param secondsLater: The number of seconds after the originally
195
scheduled time for which to reschedule this call.
197
@raise AlreadyCancelled: Raised if this call has been cancelled.
198
@raise AlreadyCalled: Raised if this call has already been made.
201
raise error.AlreadyCancelled
203
raise error.AlreadyCalled
205
self.delayed_time += secondsLater
206
if self.delayed_time < 0:
207
self.activate_delay()
210
def activate_delay(self):
211
self.time += self.delayed_time
212
self.delayed_time = 0
215
"""Determine whether this call is still pending
218
@return: True if this call has not yet been made or cancelled,
221
return not (self.cancelled or self.called)
223
def __le__(self, other):
224
return self.time <= other.time
227
if self._str is not None:
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
235
func = reflect.safe_repr(self.func)
240
L = ["<DelayedCall %s [%ss] called=%s cancelled=%s" % (
241
id(self), self.time - now, self.called, self.cancelled)]
243
L.extend((" ", func, "("))
245
L.append(", ".join([reflect.safe_repr(e) for e in self.args]))
249
L.append(", ".join(['%s=%s' % (k, reflect.safe_repr(v)) for (k, v) in self.kw.iteritems()]))
253
L.append("\n\ntraceback at creation: \n\n%s" % (' '.join(self.creator)))