blob: 6216fe61a7c9e980720a30ae4a55270ac4f7f20d [file] [log] [blame]
niemeyerdebf3cd2005-02-23 18:33:30 +00001"""
niemeyer1d784c52005-02-24 22:27:26 +00002Copyright (c) 2003-2005 Gustavo Niemeyer <gustavo@niemeyer.net>
niemeyerdebf3cd2005-02-23 18:33:30 +00003
4This module offers extensions to the standard python 2.3+
5datetime module.
6"""
niemeyer1d784c52005-02-24 22:27:26 +00007__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
niemeyerdebf3cd2005-02-23 18:33:30 +00008__license__ = "PSF License"
9
10import datetime
Gustavo Niemeyerac37a802011-03-24 14:16:39 -030011import _thread
niemeyerdebf3cd2005-02-23 18:33:30 +000012import signal
13import time
14
15class sched:
16
17 def __init__(self, rrule,
18 tolerance=None, last=None,
19 execute=None, args=None, kwargs=None):
20 self._rrule = rrule
21 if tolerance:
22 self._tolerance = datetime.timedelta(seconds=tolerance)
23 else:
24 self._tolerance = None
25 self._last = last
26 self._execute = execute
27 self._args = args or ()
28 self._kwargs = kwargs or {}
29
30 def last(self):
31 return self._last
32
33 def next(self, now=None):
34 if not now:
35 now = datetime.datetime.now()
36 return self._rrule.after(now)
37
38 def check(self, now=None, readonly=False):
39 if not now:
40 now = datetime.datetime.now()
41 item = self._rrule.before(now, inc=True)
42 if (item is None or item == self._last or
43 (self._tolerance and item+self._tolerance < now)):
44 return None
45 if not readonly:
46 self._last = item
47 if self._execute:
48 self._execute(*self._args, **self._kwargs)
49 return item
50
51
52class schedset:
53 def __init__(self):
54 self._scheds = []
55
56 def add(self, sched):
57 self._scheds.append(sched)
58
59 def next(self, now=None):
60 if not now:
61 now = datetime.datetime.now()
62 res = None
63 for sched in self._scheds:
64 next = sched.next(now)
65 if next and (not res or next < res):
66 res = next
67 return res
68
69 def check(self, now=None, readonly=False):
70 if not now:
71 now = datetime.datetime.now()
72 res = False
73 for sched in self._scheds:
74 if sched.check(now, readonly):
75 res = True
76 return res
77
78
79class schedthread:
80
81 def __init__(self, sched, lock=None):
82 self._sched = sched
83 self._lock = lock
84 self._running = False
85
86 def running(self):
87 return self._running
88
89 def run(self):
90 self._running = True
Gustavo Niemeyerac37a802011-03-24 14:16:39 -030091 _thread.start_new_thread(self._loop, ())
niemeyerdebf3cd2005-02-23 18:33:30 +000092
93 def stop(self):
94 self._running = False
95
96 def _loop(self):
97 while self._running:
98 if self._lock:
99 self._lock.acquire()
100 now = datetime.datetime.now()
101 self._sched.check(now)
102 if self._lock:
103 self._lock.release()
104 seconds = _seconds_left(self._sched.next(now))
105 if seconds is None:
106 self._running = False
107 break
108 if self._running:
109 time.sleep(seconds)
110
111
112class schedalarm:
113
114 def __init__(self, sched, lock=None):
115 self._sched = sched
116 self._lock = lock
117 self._running = False
118
119 def running(self):
120 return self._running
121
122 def run(self):
123 self._running = True
124 signal.signal(signal.SIGALRM, self._handler)
125 self._handler(None, None)
126
127 def stop(self):
128 self._running = False
129
130 def _handler(self, sig, frame):
131 while self._running:
132 if self._lock:
133 self._lock.acquire()
134 now = datetime.datetime.now()
135 self._sched.check(now)
136 if self._lock:
137 self._lock.release()
138 if self._running:
139 seconds = _seconds_left(self._sched.next(now))
140 if seconds:
141 signal.alarm(seconds)
142 break
143 elif seconds is None:
144 self._running = False
145 break
146
147
148def _seconds_left(next):
149 if not next:
150 return None
151 now = datetime.datetime.now()
152 delta = next-now
153 seconds = delta.days*86400+delta.seconds
154 if seconds < 0:
155 seconds = 0
156 return seconds
157