Changed how script repeats are internally handled; now counting the number of calls rather than trying to count down to zero in the ExtendedLoopingCall.

This commit is contained in:
Griatch 2014-02-15 15:40:40 +01:00
parent cdde9fb955
commit 5a06ac4a8b

View file

@ -17,20 +17,20 @@ from src.utils import logger
__all__ = ["Script", "DoNothing", "CheckSessions", __all__ = ["Script", "DoNothing", "CheckSessions",
"ValidateScripts", "ValidateChannelHandler"] "ValidateScripts", "ValidateChannelHandler"]
_GA = object.__getattribute__
_SESSIONS = None _SESSIONS = None
# attr-cache size in MB # attr-cache size in MB
_ATTRIBUTE_CACHE_MAXSIZE = settings.ATTRIBUTE_CACHE_MAXSIZE _ATTRIBUTE_CACHE_MAXSIZE = settings.ATTRIBUTE_CACHE_MAXSIZE
class ExtendedLoopingCall(LoopingCall): class ExtendedLoopingCall(LoopingCall):
""" """
LoopingCall that can start at a delay different LoopingCall that can start at a delay different
than self.interval. than self.interval.
""" """
start_delay = None start_delay = None
repeats = None callcount = 0
def start(self, interval, now=True, start_delay=None, repeats=None): def start(self, interval, now=True, start_delay=None, count_start=0):
""" """
Start running function every interval seconds. Start running function every interval seconds.
@ -40,8 +40,12 @@ class ExtendedLoopingCall(LoopingCall):
start_delay: The number of seconds before starting. start_delay: The number of seconds before starting.
If None, wait interval seconds. Only If None, wait interval seconds. Only
valid is now is False. valid is now is False.
repeats: Number of times for loopingcall to repeat before repeat_start: the task will track how many times it has run.
stopping. If None or 0, will loop forever. this will change where it starts counting from.
Note that as opposed to Twisted's inbuild
counter, this will count also if force_repeat()
was called (so it will not just count the number
of interval seconds since start).
""" """
assert not self.running, ("Tried to start an already running " assert not self.running, ("Tried to start an already running "
"ExtendedLoopingCall.") "ExtendedLoopingCall.")
@ -54,16 +58,11 @@ class ExtendedLoopingCall(LoopingCall):
self._expectNextCallAt = self.starttime self._expectNextCallAt = self.starttime
self.interval = interval self.interval = interval
self._runAtStart = now self._runAtStart = now
self.callcount = max(0, count_start)
if repeats and repeats > 0:
self.repeats = int(repeats)
if now: if now:
self() self()
else: else:
if self.repeats is not None:
# need to compensate for the first reschedule
self.repeats += 1
if start_delay is not None and start_delay >= 0: if start_delay is not None and start_delay >= 0:
# we set start_delay after the _reshedule call to make # we set start_delay after the _reshedule call to make
# next_call_time() find it until next reshedule. # next_call_time() find it until next reshedule.
@ -73,9 +72,13 @@ class ExtendedLoopingCall(LoopingCall):
self.start_delay = start_delay self.start_delay = start_delay
else: else:
self._reschedule() self._reschedule()
return d return d
def __call__(self):
"tick one step"
self.callcount += 1
super(ExtendedLoopingCall, self).__call__()
def _reschedule(self): def _reschedule(self):
""" """
Handle call rescheduling including Handle call rescheduling including
@ -83,12 +86,7 @@ class ExtendedLoopingCall(LoopingCall):
number of repeats is reached. number of repeats is reached.
""" """
self.start_delay = None self.start_delay = None
if self.repeats is not None:
self.repeats -= 1
super(ExtendedLoopingCall, self)._reschedule() super(ExtendedLoopingCall, self)._reschedule()
# for self.repeats <= 0, don't kill loop here; the callback
# won't have time to be called in some situations. Kill
# externally by checking task.repeats <= 0.
def force_repeat(self): def force_repeat(self):
"Force-fire the callback" "Force-fire the callback"
@ -136,18 +134,17 @@ class ScriptBase(TypeClass):
if self.db._paused_time: if self.db._paused_time:
# the script was paused; restarting # the script was paused; restarting
repeats = self.db._paused_repeats or self.dbobj.db_repeats callcount = self.db._paused_callcount or 0
self.ndb._task.start(self.dbobj.db_interval, self.ndb._task.start(self.dbobj.db_interval,
now=False, now=False,
start_delay=self.db._paused_time, start_delay=self.db._paused_time,
repeats=repeats) count_start=callcount)
del self.db._paused_time del self.db._paused_time
del self.db._paused_repeats del self.db._paused_repeats
else: else:
# starting script anew # starting script anew
self.ndb._task.start(self.dbobj.db_interval, self.ndb._task.start(self.dbobj.db_interval,
now=not self.dbobj.db_start_delay, now=not self.dbobj.db_start_delay)
repeats=self.dbobj.db_repeats)
def _stop_task(self): def _stop_task(self):
"stop task runner" "stop task runner"
@ -178,8 +175,9 @@ class ScriptBase(TypeClass):
self.at_repeat() self.at_repeat()
# check repeats # check repeats
repeats = self.ndb._task.repeats callcount = self.ndb._task.callcount
if repeats is not None and repeats <= 0: maxcount = self.dbobj.db_repeats
if maxcount > 0 and maxcount <= callcount:
print "stopping script!" print "stopping script!"
self.stop() self.stop()
@ -212,7 +210,7 @@ class ScriptBase(TypeClass):
"Get the number of returning repeats. Returns None if unlimited repeats." "Get the number of returning repeats. Returns None if unlimited repeats."
task = self.ndb._task task = self.ndb._task
if task: if task:
return task.repeats return max(0, self.dbobj.db_repeats - task.callcount)
def start(self, force_restart=False): def start(self, force_restart=False):
""" """
@ -237,7 +235,7 @@ class ScriptBase(TypeClass):
if obj: if obj:
# check so the scripted object is valid and initalized # check so the scripted object is valid and initalized
try: try:
object.__getattribute__(obj.dbobj, 'cmdset') _GA(obj.dbobj, 'cmdset')
except AttributeError: except AttributeError:
# this means the object is not initialized. # this means the object is not initialized.
logger.log_trace() logger.log_trace()
@ -248,17 +246,16 @@ class ScriptBase(TypeClass):
if self.unpause(): if self.unpause():
return 1 return 1
# try to start the script from scratch # start the script from scratch
try:
self.dbobj.is_active = True self.dbobj.is_active = True
try:
self.at_start() self.at_start()
except Exception:
logger.log_trace()
if self.dbobj.db_interval > 0: if self.dbobj.db_interval > 0:
self._start_task() self._start_task()
return 1 return 1
except Exception:
logger.log_trace()
self.dbobj.is_active = False
return 0
def stop(self, kill=False): def stop(self, kill=False):
""" """
@ -286,20 +283,23 @@ class ScriptBase(TypeClass):
def pause(self): def pause(self):
""" """
This stops a running script and stores its active state. This stops a running script and stores its active state.
It WILL NOT call that at_stop() hook.
""" """
if not self.db._paused_time:
# only allow pause if not already paused
task = self.ndb._task task = self.ndb._task
print "pausing", self.key, self.time_until_next_repeat()
if task: if task:
self.db._paused_time = task.next_call_time() self.db._paused_time = task.next_call_time()
self.db._paused_repeats = task.repeats self.db._paused_callcount = task.callcount
self._stop_task() self._stop_task()
self.dbobj.is_active = False
def unpause(self): def unpause(self):
""" """
Restart a paused script. This WILL call at_start(). Restart a paused script. This WILL call the at_start() hook.
""" """
#print "unpausing", self.key, self.db._paused_time
if self.db._paused_time: if self.db._paused_time:
# only unpause if previously paused
self.dbobj.is_active = True self.dbobj.is_active = True
try: try:
@ -424,7 +424,8 @@ class Script(ScriptBase):
at_start() - Called every time the script is started, which for at_start() - Called every time the script is started, which for
persistent scripts is at least once every server start. persistent scripts is at least once every server start.
Note that this is unaffected by self.delay_start, which Note that this is unaffected by self.delay_start, which
only delays the first call to at_repeat(). only delays the first call to at_repeat(). It will also
be called after a pause, to allow for setting up the script.
at_repeat() - Called every self.interval seconds. It will be called at_repeat() - Called every self.interval seconds. It will be called
immediately upon launch unless self.delay_start is True, immediately upon launch unless self.delay_start is True,
which will delay the first call of this method by which will delay the first call of this method by