Вы можете просто использовать обычный синтаксис передачи аргументов Python, чтобы указать свой crontab. Например, предположим, что мы определяем класс Event, как показано ниже:
from datetime import datetime, timedelta
import time
# Some utility classes / functions first
class AllMatch(set):
"""Universal set - match everything"""
def __contains__(self, item): return True
allMatch = AllMatch()
def conv_to_set(obj): # Allow single integer to be provided
if isinstance(obj, (int,long)):
return set([obj]) # Single item
if not isinstance(obj, set):
obj = set(obj)
return obj
# The actual Event class
class Event(object):
def __init__(self, action, min=allMatch, hour=allMatch,
day=allMatch, month=allMatch, dow=allMatch,
args=(), kwargs={}):
self.mins = conv_to_set(min)
self.hours= conv_to_set(hour)
self.days = conv_to_set(day)
self.months = conv_to_set(month)
self.dow = conv_to_set(dow)
self.action = action
self.args = args
self.kwargs = kwargs
def matchtime(self, t):
"""Return True if this event should trigger at the specified datetime"""
return ((t.minute in self.mins) and
(t.hour in self.hours) and
(t.day in self.days) and
(t.month in self.months) and
(t.weekday() in self.dow))
def check(self, t):
if self.matchtime(t):
self.action(*self.args, **self.kwargs)
(Примечание: не полностью проверено)
Тогда ваш CronTab может быть указан в обычном синтаксисе python:
c = CronTab(
Event(perform_backup, 0, 2, dow=6 ),
Event(purge_temps, 0, range(9,18,2), dow=range(0,5))
)
Таким образом, вы получаете все возможности механики аргументов Python (смешивая позиционные и ключевые аргументы и можете использовать символические имена для названий недель и месяцев)
Класс CronTab будет определен как просто спящий с шагом в минуту и вызывающий check () для каждого события. (Возможно, есть некоторые тонкости с летним временем / часовыми поясами, которые следует с осторожностью относиться). Вот быстрая реализация:
class CronTab(object):
def __init__(self, *events):
self.events = events
def run(self):
t=datetime(*datetime.now().timetuple()[:5])
while 1:
for e in self.events:
e.check(t)
t += timedelta(minutes=1)
while datetime.now() < t:
time.sleep((t - datetime.now()).seconds)
Несколько вещей, на которые следует обратить внимание: дни / месяцы Python имеют нулевое индексирование (в отличие от cron), и этот диапазон исключает последний элемент, поэтому синтаксис, такой как «1-5», становится range (0,5) - то есть [0,1 2,3,4]. Если вы предпочитаете синтаксис cron, его синтаксический анализ не должен быть слишком сложным.