class AbstractEntry(object): def currentsystime(): return time.time() currentsystime = staticmethod(currentsystime) def currentuptime(): return uptime.secs() currentuptime = staticmethod(currentuptime) def __init__(self, schedule, target, args): super(AbstractEntry, self).__init__() self._schedule = schedule self._target = target self._args = args self._setupflags() self._statelock = Lock() self.computewhen() def runswhen(self): """ Return up-time value at which entry is scheduled to run. This value is calculated and updated when 'computewhen()' is invoked; value returned is always the value configured by most recent computewhen() call. """ return self._targetuptime def computewhen(self): """ Calculate, or recalculate, target run time value in terms of system up time. NOTE - this method uses dynamic 'runuptime()' method to get up to date run time target value. """ self._targetuptime = self.runuptime() def runuptime(self): """ Dynamically generated target up-time value. This value is used interally by 'computewhen()' in order to configure the entry's run time target. NOTE - this method uses dynamic 'rundelay()' method to get offset from current up-time and calculate target up-time. """ return self.currentuptime() + self.rundelay() def rundelay(self, currenttime=None): """ Return delay, in seconds, between now and when entry runs. """ raise NotImplementedError('abstract rundelay() must be overridden') def _setupflags(self): self._cancelled = Flag(False) self._executed = Flag(False) self._executing = Flag(False) self._failed = Flag(False) self._scheduled = Flag(True) def scheduled(self): self._statelock.acquire() try: return self._scheduled.isSet() finally: self._statelock.release() def cancelled(self): self._statelock.acquire() try: return self._cancelled.isSet() finally: self._statelock.release() def executing(self): self._statelock.acquire() try: return self._executing.isSet() finally: self._statelock.release() def executing_caller(self): """ Returns True if caller is currently running as entry's execution """ self._statelock.acquire() try: executing = self._executing.isSet() return executing and self._schedule is current_thread() finally: self._statelock.release() def isdue(self): return self.rundelay() <= 0 def executable(self): """ Return True if entry may still be executed. An entry may be executed if it is not currently executing and has not yet been executed. """ self._statelock.acquire() try: return self._executable() finally: self._statelock.release() def overdue(self): """ A entry is overdue iff it may still be executed and the current uptime is greater than the target uptime at which the entry was scheduled to execute. """ # Order chosen to avoid race-condition. self._statelock.acquire() try: return self._overdue() finally: self._statelock.release() def executed(self): self._statelock.acquire() try: return self._executed.isSet() finally: self._statelock.release() def expired(self): self._statelock.acquire() try: return self._expired() finally: self._statelock.release() ## # No semi-private test-method can implement locking. # This allows indescriminate use of methods versus # direct invocation of flag instances without # making it easy to accidentally block on double-lock. def _expired(self): return self._executed() or self._cancelled() def _executable(self): return not (self._executing() or self._expired()) def _overdue(self): return (self.currentuptime() > self.runuptime()) and self._executable() def runsystime(self): """ Return time at which entry is supposed to run. """ return self.rundelay() + self.currentsystime() def execute(self): self._statelock.acquire() try: if not self._executable(): raise EActionExpired('Scheduled action %s has expired' % self) else: self._executing.set() finally: self._statelock.release() try: try: value = self._target(*self._args) except: self._failed.set() message = 'Entry Exception running: "%s%s"' msglog.log('broadway', msglog.types.DB, message % (self._target, self._args)) raise else: self._failed.clear() finally: self._statelock.acquire() # Ordering chosen to prefer overlap than gap. self._executed.set() self._executing.clear() self._scheduled.clear() self._statelock.release() return value def cancel(self): self._statelock.acquire() try: self._cancelled.set() if self._scheduled(): try: self._schedule.remove_entry(self) except IndexError: message = 'IndexError removing %s (at %#x)' self._schedule.debugout(message, 1, self, id(self)) self._scheduled.clear() finally: self._statelock.release() self._schedule.debugout('%s cancelled', 1, self) def __bigstr__(self): status = ['%s' % type(self).__name__] status.append(self._get_actionstr()) if self._executable(): execution = 'pending' if self._executed(): status.append('executed') if self._failed(): status.append('failed') else: status.append('succeeded') elif self._executing(): status.append('executing') elif self._cancelled(): status.append('cancelled') elif self._overdue(): status.append('overdue') runtime = self.runuptime() status.append('due %0.2f' % runtime) rundelay = runtime - self.currentuptime() if rundelay < 0: sign = '-' else: sign = '+' status.append('(now %s %0.2f sec)' % (sign, abs(rundelay))) return ' '.join(status) def __littlestr__(self): state = 'Pending' if self._executed: state = 'Executed' if self._failed: state += ', Failed' else: state += ', Succeeded' elif self._cancelled: state = 'Cancelled' elif self._overdue(): state = 'Overdue' return ('Scheduled Action: %s%s due %s -> %s' % (self._target, self._args, self.runswhen(), state)) __str__ = __bigstr__ def _get_actionstr(self): action = self._target description = [str(action)] try: if inspect.ismethod(action): if action.im_self is not None: description.insert(0, type(action.im_self).__name__) description[-1] = action.im_func.__name__ elif inspect.isfunction(action): description[-1] = action.__name__ elif inspect.isclass(action): description.insert(0, action.__name__) description.pop() except: pass return '%s%s' % ('.'.join(description), self._args)
class AbstractEntry(object): def currentsystime(): return time.time() currentsystime = staticmethod(currentsystime) def currentuptime(): return uptime.secs() currentuptime = staticmethod(currentuptime) def __init__(self, schedule, target, args): super(AbstractEntry, self).__init__() self._schedule = schedule self._target = target self._args = args self._setupflags() self._statelock = Lock() self.computewhen() def runswhen(self): """ Return up-time value at which entry is scheduled to run. This value is calculated and updated when 'computewhen()' is invoked; value returned is always the value configured by most recent computewhen() call. """ return self._targetuptime def computewhen(self): """ Calculate, or recalculate, target run time value in terms of system up time. NOTE - this method uses dynamic 'runuptime()' method to get up to date run time target value. """ self._targetuptime = self.runuptime() def runuptime(self): """ Dynamically generated target up-time value. This value is used interally by 'computewhen()' in order to configure the entry's run time target. NOTE - this method uses dynamic 'rundelay()' method to get offset from current up-time and calculate target up-time. """ return self.currentuptime() + self.rundelay() def rundelay(self, currenttime=None): """ Return delay, in seconds, between now and when entry runs. """ raise NotImplementedError("abstract rundelay() must be overridden") def _setupflags(self): self._cancelled = Flag(False) self._executed = Flag(False) self._executing = Flag(False) self._failed = Flag(False) self._scheduled = Flag(True) def scheduled(self): self._statelock.acquire() try: return self._scheduled.isSet() finally: self._statelock.release() def cancelled(self): self._statelock.acquire() try: return self._cancelled.isSet() finally: self._statelock.release() def executing(self): self._statelock.acquire() try: return self._executing.isSet() finally: self._statelock.release() def executing_caller(self): """ Returns True if caller is currently running as entry's execution """ self._statelock.acquire() try: executing = self._executing.isSet() return executing and self._schedule is current_thread() finally: self._statelock.release() def isdue(self): return self.rundelay() <= 0 def executable(self): """ Return True if entry may still be executed. An entry may be executed if it is not currently executing and has not yet been executed. """ self._statelock.acquire() try: return self._executable() finally: self._statelock.release() def overdue(self): """ A entry is overdue iff it may still be executed and the current uptime is greater than the target uptime at which the entry was scheduled to execute. """ # Order chosen to avoid race-condition. self._statelock.acquire() try: return self._overdue() finally: self._statelock.release() def executed(self): self._statelock.acquire() try: return self._executed.isSet() finally: self._statelock.release() def expired(self): self._statelock.acquire() try: return self._expired() finally: self._statelock.release() ## # No semi-private test-method can implement locking. # This allows indescriminate use of methods versus # direct invocation of flag instances without # making it easy to accidentally block on double-lock. def _expired(self): return self._executed() or self._cancelled() def _executable(self): return not (self._executing() or self._expired()) def _overdue(self): return (self.currentuptime() > self.runuptime()) and self._executable() def runsystime(self): """ Return time at which entry is supposed to run. """ return self.rundelay() + self.currentsystime() def execute(self): self._statelock.acquire() try: if not self._executable(): raise EActionExpired("Scheduled action %s has expired" % self) else: self._executing.set() finally: self._statelock.release() try: try: value = self._target(*self._args) except: self._failed.set() message = 'Entry Exception running: "%s%s"' msglog.log("broadway", msglog.types.DB, message % (self._target, self._args)) raise else: self._failed.clear() finally: self._statelock.acquire() # Ordering chosen to prefer overlap than gap. self._executed.set() self._executing.clear() self._scheduled.clear() self._statelock.release() return value def cancel(self): self._statelock.acquire() try: self._cancelled.set() if self._scheduled(): try: self._schedule.remove_entry(self) except IndexError: message = "IndexError removing %s (at %#x)" self._schedule.debugout(message, 1, self, id(self)) self._scheduled.clear() finally: self._statelock.release() self._schedule.debugout("%s cancelled", 1, self) def __bigstr__(self): status = ["%s" % type(self).__name__] status.append(self._get_actionstr()) if self._executable(): execution = "pending" if self._executed(): status.append("executed") if self._failed(): status.append("failed") else: status.append("succeeded") elif self._executing(): status.append("executing") elif self._cancelled(): status.append("cancelled") elif self._overdue(): status.append("overdue") runtime = self.runuptime() status.append("due %0.2f" % runtime) rundelay = runtime - self.currentuptime() if rundelay < 0: sign = "-" else: sign = "+" status.append("(now %s %0.2f sec)" % (sign, abs(rundelay))) return " ".join(status) def __littlestr__(self): state = "Pending" if self._executed: state = "Executed" if self._failed: state += ", Failed" else: state += ", Succeeded" elif self._cancelled: state = "Cancelled" elif self._overdue(): state = "Overdue" return "Scheduled Action: %s%s due %s -> %s" % (self._target, self._args, self.runswhen(), state) __str__ = __bigstr__ def _get_actionstr(self): action = self._target description = [str(action)] try: if inspect.ismethod(action): if action.im_self is not None: description.insert(0, type(action.im_self).__name__) description[-1] = action.im_func.__name__ elif inspect.isfunction(action): description[-1] = action.__name__ elif inspect.isclass(action): description.insert(0, action.__name__) description.pop() except: pass return "%s%s" % (".".join(description), self._args)
def _setupflags(self): self._cancelled = Flag(False) self._executed = Flag(False) self._executing = Flag(False) self._failed = Flag(False) self._scheduled = Flag(True)