import time from twisted.internet import task class TimeoutError(Exception): pass class PollComplete(Exception): pass class PollMixin: _poll_should_ignore_these_errors = [] def poll(self, check_f, pollinterval=0.01, timeout=1000): # Return a Deferred, then call check_f periodically until it returns # True, at which point the Deferred will fire.. If check_f raises an # exception, the Deferred will errback. If the check_f does not # indicate success within timeout= seconds, the Deferred will # errback. If timeout=None, no timeout will be enforced, and the loop # will poll forever (or really until Trial times out). cutoff = None if timeout is not None: cutoff = time.time() + timeout lc = task.LoopingCall(self._poll, check_f, cutoff) d = lc.start(pollinterval) def _convert_done(f): f.trap(PollComplete) return None d.addErrback(_convert_done) return d def _poll(self, check_f, cutoff): if cutoff is not None and time.time() > cutoff: raise TimeoutError("PollMixin never saw %s return True" % check_f) if check_f(): raise PollComplete() # since PollMixin is mostly used for unit tests, quit if we see any # logged errors. This should give us a nice fast failure, instead of # waiting for a timeout. Tests which use flushLoggedErrors() will # need to warn us by putting the error types they'll be ignoring in # self._poll_should_ignore_these_errors if hasattr(self, "_observer") and hasattr(self._observer, "getErrors"): errs = [] for e in self._observer.getErrors(): if not e.check(*self._poll_should_ignore_these_errors): errs.append(e) if errs: print errs self.fail("Errors snooped, terminating early")