"""
Python Sock tools: cron_timer.py - a cron-like timer for use in daemon processes
Copyright (C) 2016 GarethNelson
This file is part of python-sock-tools
python-sock-tools is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
python-sock-tools is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with python-sock-tools. If not, see <http://www.gnu.org/licenses/>.
This module provides a cron-like timer intended for use in daemon processes. Essentially, it lets you schedule events for every X seconds.
A lot of emphasis is placed on trying to get timings as accurate as possible while still using greenlets instead of full size threads.
To use it, setup the CronTimer class and configure your timed events then in your main loop call start_timer() and end_timer() before and after your other code.
If you do not call end_timer() your scheduled events will not run.
"""
import eventlet
eventlet.monkey_patch()
import time
[docs]class CronTimer:
"""Cron-like timer for daemon processes
This class implements the cron-like timer for daemon processes. Events are scheduled to occur at a set frequency by registering them along with the time delay between function invocations.
In other words, if you wanted to schedule a function to run every hour you'd use a time delay of 3600.
Keyword args:
scheduled_events (dict): Maps time delays to functions
"""
def __init__(self,scheduled_events={}):
self.scheduled_events = self.get_default_events()
self.scheduled_events.update(scheduled_events)
self.delays = self.get_sorted_delays()
self.pool = eventlet.GreenPool(1000)
self.diff = 0
self.start_time = time.time()
self.end_time = self.start_time
self.last_delays = {}
self.update_events()
[docs] def get_default_events(self):
"""Override this to add your own default events
This method is provided for the use of child classes and simply returns an events dict mapping time delays to lists of functions.
The default implementation returns an empty dict.
Note that you should NOT put the same callback in 2 different delays.
Returns:
dict: maps time delays to lists of functions
"""
return {}
[docs] def get_sorted_delays(self):
"""Returns a sorted list of delays
This method sorts the current scheduled events and returns them in a list starting with smallest first.
Returns:
list: list of time delays starting from the smallest first
"""
delays = self.scheduled_events.keys()
delays.sort()
return delays
[docs] def update_events(self):
"""Updates all internal state
Call this method after updating the scheduled_events dict
"""
self.delays = self.get_sorted_delays()
for d in self.delays:
self.last_delays[d] = self.start_time
[docs] def start_timer(self):
"""Starts the main loop timer
To keep the timing accurate the main loop iterations are timed and the actual delay is calculated taking the main loop time into account
"""
self.start_time = time.time()
[docs] def end_timer(self):
"""Ends the main loop timer and runs scheduled events
This method should be called at the end of your main loop in order to run the scheduled events.
"""
self.end_time = time.time()
self.diff = self.end_time - self.start_time
for delay in self.delays:
if (self.end_time - self.last_delays[delay]) >= delay:
for cb in self.scheduled_events[delay]:
cb()
self.last_delays[delay] = time.time()
if __name__=='__main__':
import random
def five_a():
print 'Every 5 seconds: A, time is %s' % time.ctime()
def five_b():
print 'Every 5 seconds: B, time is %s' % time.ctime()
def fifteen():
print 'Every fifteen seconds! time is %s' % time.ctime()
# a simple test
cron = CronTimer(scheduled_events={5:[five_a,five_b],15:[fifteen]})
while True:
cron.start_timer()
eventlet.greenthread.sleep(random.randint(1,10)/10.0) # simulate doing stuff with variable timeframes
cron.end_timer()