~ubuntu-branches/ubuntu/precise/gozerbot/precise

« back to all changes in this revision

Viewing changes to build/lib/gozerbot/periodical.py

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Malcolm
  • Date: 2008-06-02 19:26:39 UTC
  • mfrom: (1.1.3 upstream) (3.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080602192639-3rn65nx4q1sgd6sy
Tags: 0.8.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# JobInterval scheduler
2
 
# (c) Wijnand 'tehmaze' Modderman - http://tehmaze.com
3
 
# BSD License
4
 
 
5
 
from gozerbot.generic import calledfrom, lockdec, rlog, strtotime, \
6
 
handle_exception
7
 
import gozerbot.thr as thr
8
 
import datetime
9
 
import sys
10
 
import time
11
 
import thread
12
 
import types
13
 
 
14
 
plock    = thread.allocate_lock()
15
 
locked   = lockdec(plock)
16
 
pidcount = 0
17
 
 
18
 
class JobError(Exception):
19
 
 
20
 
    """ job error exception """
21
 
 
22
 
    pass
23
 
 
24
 
class Job(object):
25
 
 
26
 
    """ job to be scheduled """
27
 
 
28
 
    group = ''
29
 
    pid   = -1
30
 
 
31
 
    def __init__(self):
32
 
        global pidcount
33
 
        pidcount += 1
34
 
        self.pid = pidcount
35
 
 
36
 
    def id(self):
37
 
        """ return job id """
38
 
        return self.pid
39
 
 
40
 
    def member(self, group):
41
 
        """ check for group membership """
42
 
        return self.group == group
43
 
 
44
 
class JobAt(Job):
45
 
 
46
 
    """ job to run at a specific time/interval/repeat """
47
 
 
48
 
    def __init__(self, start, interval, repeat, func, *args, **kw):
49
 
        Job.__init__(self)
50
 
        self.func = func
51
 
        self.args = args
52
 
        self.kw = kw
53
 
        self.repeat = repeat
54
 
        self.description = ""
55
 
        self.counts = 0
56
 
        if type(start) in [types.IntType, types.FloatType]:
57
 
            self.next = float(start)
58
 
        elif type(start) in [types.StringType, types.UnicodeType]:
59
 
            d = strtotime(start)
60
 
            if d and d > time.time():
61
 
                self.next = d
62
 
            else:
63
 
                raise JobError("invalid date/time")
64
 
        if type(interval) in [types.IntType]:
65
 
            d = datetime.timedelta(days=interval)
66
 
            self.delta = d.seconds
67
 
        else:
68
 
            self.delta = interval
69
 
 
70
 
    def __repr__(self):
71
 
        return '<JobAt instance next=%s, interval=%s, repeat=%d, function=%s>' % (str(self.next),
72
 
            str(self.delta), self.repeat, str(self.func))
73
 
 
74
 
    def check(self):
75
 
        """ run check to see if job needs to be scheduled """
76
 
        if self.next <= time.time():
77
 
            rlog(-15, 'periodical', 'running %s' % str(self.func))
78
 
            self.func(*self.args, **self.kw)
79
 
            self.next += self.delta
80
 
            self.counts += 1
81
 
            if self.repeat > 0 and self.counts >= self.repeat:
82
 
                return False # remove this job
83
 
        return True
84
 
 
85
 
class JobInterval(Job):
86
 
 
87
 
    """ job to be scheduled at certain interval """
88
 
 
89
 
    def __init__(self, interval, repeat, func, *args, **kw):
90
 
        Job.__init__(self)
91
 
        self.func = func
92
 
        self.args = args
93
 
        self.kw = kw
94
 
        self.repeat = int(repeat)
95
 
        self.counts = 0
96
 
        self.interval = float(interval)
97
 
        self.description = ""
98
 
        self.next = time.time() + self.interval
99
 
        self.group = None
100
 
        rlog(5, 'periodical', 'scheduled next run of %s in %d seconds' % \
101
 
(str(self.func), self.interval))
102
 
 
103
 
    def __repr__(self):
104
 
        return '<JobInterval instance next=%s, interval=%s, repeat=%d, group=%s, \
105
 
function=%s>' % (str(self.next), str(self.interval), self.repeat, self.group,
106
 
str(self.func))
107
 
 
108
 
    def check(self):
109
 
        """ run check to see if job needs to be scheduled """
110
 
        if self.next <= time.time():
111
 
            rlog(-15, 'periodical', 'running %s' % (str(self.func)))
112
 
            self.next = time.time() + self.interval
113
 
            try:
114
 
                self.func(*self.args, **self.kw)
115
 
            except Exception, ex:
116
 
                handle_exception()
117
 
            self.counts += 1
118
 
            if self.repeat > 0 and self.counts >= self.repeat:
119
 
                return False # remove this job
120
 
        return True
121
 
 
122
 
class Periodical(object):
123
 
 
124
 
    """ periodical scheduler """
125
 
 
126
 
    SLEEPTIME = 1 # smallest interval possible
127
 
 
128
 
    def __init__(self):
129
 
        self.jobs = []
130
 
        self.run = True
131
 
        thr.start_new_thread(self.checkloop, ())
132
 
 
133
 
    def addjob(self, sleeptime, repeat, function, description="" , *args, **kw): 
134
 
        job = JobInterval(sleeptime, repeat, function, *args, **kw)
135
 
        job.group = calledfrom(sys._getframe())
136
 
        job.description = str(description)
137
 
        self.jobs.append(job)
138
 
        return job.pid
139
 
 
140
 
    def changeinterval(self, pid, interval):
141
 
        for i in periodical.jobs:
142
 
            if i.pid == pid:
143
 
                i.interval = interval
144
 
                i.next = time.time() + interval
145
 
 
146
 
    def checkloop(self):
147
 
        """ main loop """
148
 
        while self.run:
149
 
            for job in self.jobs:
150
 
                if job.next <= time.time():
151
 
                    thr.start_new_thread(self.runjob, (job, ))
152
 
                    time.sleep(0.1)
153
 
            time.sleep(self.SLEEPTIME)
154
 
 
155
 
    def runjob(self, job):
156
 
        """ kill job is not to be runned """
157
 
        if not job.check():
158
 
            self.killjob(job.id())
159
 
 
160
 
    def kill(self):
161
 
        ''' kill all jobs invoked by another module '''
162
 
        group = calledfrom(sys._getframe())
163
 
        self.killgroup(group)
164
 
 
165
 
    def killgroup(self, group):
166
 
        ''' kill all jobs with the same group '''
167
 
        @locked
168
 
        def shoot():
169
 
            """ knock down all jobs belonging to group """
170
 
            deljobs = [job for job in self.jobs if job.member(group)]
171
 
            for job in deljobs:
172
 
                self.jobs.remove(job)
173
 
            rlog(10, 'periodical', 'killed %d jobs for %s' % (len(deljobs), \
174
 
group))
175
 
            del deljobs
176
 
        shoot() # *pow* you're dead ;)
177
 
 
178
 
    def killjob(self, jobId):
179
 
        ''' kill one job by its id '''
180
 
        @locked
181
 
        def shoot():
182
 
            deljobs = [x for x in self.jobs if x.id() == jobId]
183
 
            numjobs = len(deljobs)
184
 
            for job in deljobs:
185
 
                self.jobs.remove(job)
186
 
            del deljobs
187
 
            return numjobs
188
 
        return shoot() # *pow* you're dead ;)
189
 
 
190
 
periodical = Periodical()
191
 
 
192
 
def interval(sleeptime, repeat=0):
193
 
    """ interval decorator """
194
 
    group = calledfrom(sys._getframe())
195
 
    def decorator(function):
196
 
        decorator.func_dict = function.func_dict
197
 
        def wrapper(*args, **kw):
198
 
            job = JobInterval(sleeptime, repeat, function, *args, **kw)
199
 
            job.group = group
200
 
            periodical.jobs.append(job)
201
 
            rlog(5, 'periodical', 'new interval job %d with sleeptime %d' % \
202
 
(job.id(), sleeptime))
203
 
        return wrapper
204
 
    return decorator
205
 
 
206
 
def at(start, interval=1, repeat=1):
207
 
    """ at decorator """
208
 
    group = calledfrom(sys._getframe())
209
 
    def decorator(function):
210
 
        decorator.func_dict = function.func_dict
211
 
        def wrapper(*args, **kw):
212
 
            job = JobAt(start, interval, repeat, function, *args, **kw)
213
 
            job.group = group
214
 
            periodical.jobs.append(job)
215
 
        wrapper.func_dict = function.func_dict
216
 
        return wrapper
217
 
    return decorator
218
 
 
219
 
def minutely(function):
220
 
    """ minute decorator """
221
 
    minutely.func_dict = function.func_dict
222
 
    group = calledfrom(sys._getframe())
223
 
    def wrapper(*args, **kw):
224
 
        job = JobInterval(60, 0, function, *args, **kw)
225
 
        job.group = group
226
 
        periodical.jobs.append(job)
227
 
        rlog(5, 'periodical', 'new interval job %d running minutely' % \
228
 
job.id())
229
 
    return wrapper
230
 
 
231
 
def hourly(function):
232
 
    """ hour decorator """
233
 
    rlog(0, 'periodical', '@hourly(%s)' % str(function))
234
 
    hourly.func_dict = function.func_dict
235
 
    group = calledfrom(sys._getframe())
236
 
    def wrapper(*args, **kw):
237
 
        job = JobInterval(3600, 0, function, *args, **kw)
238
 
        job.group = group
239
 
        rlog(5, 'periodical', 'new interval job %d running hourly' % job.id())
240
 
        periodical.jobs.append(job)
241
 
    return wrapper
242
 
 
243
 
def daily(function):
244
 
    """ day decorator """
245
 
    rlog(0, 'periodical', '@daily(%s)' % str(function))
246
 
    daily.func_dict = function.func_dict
247
 
    group = calledfrom(sys._getframe())
248
 
    def wrapper(*args, **kw):
249
 
        job = JobInterval(86400, 0, function, *args, **kw)
250
 
        job.group =  group
251
 
        periodical.jobs.append(job)
252
 
        rlog(5, 'periodical', 'new interval job %d running daily' % job.id())
253
 
    return wrapper