blob: 62794d5bec96db9cbaa06ead829e19a47f725e5e [file] [log] [blame]
Tomi Pieviläinen40996e02012-03-03 11:43:08 +02001# -*- coding: utf-8 -*-
niemeyerdebf3cd2005-02-23 18:33:30 +00002"""
niemeyer1d784c52005-02-24 22:27:26 +00003Copyright (c) 2003-2005 Gustavo Niemeyer <gustavo@niemeyer.net>
niemeyerdebf3cd2005-02-23 18:33:30 +00004
Tomi Pieviläinen40996e02012-03-03 11:43:08 +02005This module offers extensions to the standard Python
niemeyerdebf3cd2005-02-23 18:33:30 +00006datetime module.
7"""
Tomi Pieviläinen40996e02012-03-03 11:43:08 +02008__author__ = "Tomi Pieviläinen <tomi.pievilainen@iki.fi>"
9__license__ = "Simplified BSD"
niemeyerdebf3cd2005-02-23 18:33:30 +000010
11import datetime
Gustavo Niemeyerac37a802011-03-24 14:16:39 -030012import _thread
niemeyerdebf3cd2005-02-23 18:33:30 +000013import signal
14import time
15
gl@clarisys.frfe750f22011-11-02 15:42:29 +010016class sched(object):
niemeyerdebf3cd2005-02-23 18:33:30 +000017
18 def __init__(self, rrule,
19 tolerance=None, last=None,
20 execute=None, args=None, kwargs=None):
21 self._rrule = rrule
22 if tolerance:
23 self._tolerance = datetime.timedelta(seconds=tolerance)
24 else:
25 self._tolerance = None
26 self._last = last
27 self._execute = execute
28 self._args = args or ()
29 self._kwargs = kwargs or {}
30
31 def last(self):
32 return self._last
33
34 def next(self, now=None):
35 if not now:
36 now = datetime.datetime.now()
37 return self._rrule.after(now)
38
39 def check(self, now=None, readonly=False):
40 if not now:
41 now = datetime.datetime.now()
42 item = self._rrule.before(now, inc=True)
43 if (item is None or item == self._last or
44 (self._tolerance and item+self._tolerance < now)):
45 return None
46 if not readonly:
47 self._last = item
48 if self._execute:
49 self._execute(*self._args, **self._kwargs)
50 return item
51
52
gl@clarisys.frfe750f22011-11-02 15:42:29 +010053class schedset(object):
niemeyerdebf3cd2005-02-23 18:33:30 +000054 def __init__(self):
55 self._scheds = []
56
57 def add(self, sched):
58 self._scheds.append(sched)
59
60 def next(self, now=None):
61 if not now:
62 now = datetime.datetime.now()
63 res = None
64 for sched in self._scheds:
65 next = sched.next(now)
66 if next and (not res or next < res):
67 res = next
68 return res
69
70 def check(self, now=None, readonly=False):
71 if not now:
72 now = datetime.datetime.now()
73 res = False
74 for sched in self._scheds:
75 if sched.check(now, readonly):
76 res = True
77 return res
78
79
gl@clarisys.frfe750f22011-11-02 15:42:29 +010080class schedthread(object):
niemeyerdebf3cd2005-02-23 18:33:30 +000081
82 def __init__(self, sched, lock=None):
83 self._sched = sched
84 self._lock = lock
85 self._running = False
86
87 def running(self):
88 return self._running
89
90 def run(self):
91 self._running = True
Gustavo Niemeyerac37a802011-03-24 14:16:39 -030092 _thread.start_new_thread(self._loop, ())
niemeyerdebf3cd2005-02-23 18:33:30 +000093
94 def stop(self):
95 self._running = False
96
97 def _loop(self):
98 while self._running:
99 if self._lock:
100 self._lock.acquire()
101 now = datetime.datetime.now()
102 self._sched.check(now)
103 if self._lock:
104 self._lock.release()
105 seconds = _seconds_left(self._sched.next(now))
106 if seconds is None:
107 self._running = False
108 break
109 if self._running:
110 time.sleep(seconds)
111
112
gl@clarisys.frfe750f22011-11-02 15:42:29 +0100113class schedalarm(object):
niemeyerdebf3cd2005-02-23 18:33:30 +0000114
115 def __init__(self, sched, lock=None):
116 self._sched = sched
117 self._lock = lock
118 self._running = False
119
120 def running(self):
121 return self._running
122
123 def run(self):
124 self._running = True
125 signal.signal(signal.SIGALRM, self._handler)
126 self._handler(None, None)
127
128 def stop(self):
129 self._running = False
130
131 def _handler(self, sig, frame):
132 while self._running:
133 if self._lock:
134 self._lock.acquire()
135 now = datetime.datetime.now()
136 self._sched.check(now)
137 if self._lock:
138 self._lock.release()
139 if self._running:
140 seconds = _seconds_left(self._sched.next(now))
141 if seconds:
142 signal.alarm(seconds)
143 break
144 elif seconds is None:
145 self._running = False
146 break
147
148
149def _seconds_left(next):
150 if not next:
151 return None
152 now = datetime.datetime.now()
153 delta = next-now
154 seconds = delta.days*86400+delta.seconds
155 if seconds < 0:
156 seconds = 0
157 return seconds
158