summaryrefslogtreecommitdiff
path: root/sandbox/scheduler.py
blob: c9986003e6bbd50a67b8789139cecf95638a6318 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
"""
Copyright (c) 2003-2005  Gustavo Niemeyer <gustavo@niemeyer.net>

This module offers extensions to the standard python 2.3+
datetime module.
"""
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
__license__ = "PSF License"

import datetime
import thread
import signal
import time

class sched:

    def __init__(self, rrule,
                 tolerance=None, last=None,
                 execute=None, args=None, kwargs=None):
        self._rrule = rrule
        if tolerance:
            self._tolerance = datetime.timedelta(seconds=tolerance)
        else:
            self._tolerance = None
        self._last = last
        self._execute = execute
        self._args = args or ()
        self._kwargs = kwargs or {}

    def last(self):
        return self._last

    def next(self, now=None):
        if not now:
            now = datetime.datetime.now()
        return self._rrule.after(now)

    def check(self, now=None, readonly=False):
        if not now:
            now = datetime.datetime.now()
        item = self._rrule.before(now, inc=True)
        if (item is None or item == self._last or
            (self._tolerance and item+self._tolerance < now)):
            return None
        if not readonly:
            self._last = item
            if self._execute:
                self._execute(*self._args, **self._kwargs)
        return item


class schedset:
    def __init__(self):
        self._scheds = []

    def add(self, sched):
        self._scheds.append(sched)

    def next(self, now=None):
        if not now:
            now = datetime.datetime.now()
        res = None
        for sched in self._scheds:
            next = sched.next(now)
            if next and (not res or next < res):
                res = next
        return res

    def check(self, now=None, readonly=False):
        if not now:
            now = datetime.datetime.now()
        res = False
        for sched in self._scheds:
            if sched.check(now, readonly):
                res = True
        return res


class schedthread:
    
    def __init__(self, sched, lock=None):
        self._sched = sched
        self._lock = lock
        self._running = False

    def running(self):
        return self._running

    def run(self):
        self._running = True
        thread.start_new_thread(self._loop, ())
        
    def stop(self):
        self._running = False

    def _loop(self):
        while self._running:
            if self._lock:
                self._lock.acquire()
            now = datetime.datetime.now()
            self._sched.check(now)
            if self._lock:
                self._lock.release()
            seconds = _seconds_left(self._sched.next(now))
            if seconds is None:
                self._running = False
                break
            if self._running:
                time.sleep(seconds)


class schedalarm:
    
    def __init__(self, sched, lock=None):
        self._sched = sched
        self._lock = lock
        self._running = False

    def running(self):
        return self._running

    def run(self):
        self._running = True
        signal.signal(signal.SIGALRM, self._handler)
        self._handler(None, None)
        
    def stop(self):
        self._running = False

    def _handler(self, sig, frame):
        while self._running:
            if self._lock:
                self._lock.acquire()
            now = datetime.datetime.now()
            self._sched.check(now)
            if self._lock:
                self._lock.release()
            if self._running:
                seconds = _seconds_left(self._sched.next(now))
                if seconds:
                    signal.alarm(seconds)
                    break
                elif seconds is None:
                    self._running = False
                    break


def _seconds_left(next):
    if not next:
        return None
    now = datetime.datetime.now()
    delta = next-now
    seconds = delta.days*86400+delta.seconds
    if seconds < 0:
        seconds = 0
    return seconds