more NEWS items
[buildbot.git] / buildbot / changes / maildir.py
blob6f991361c652a03566422ae9c30e6da490558fa0
1 #! /usr/bin/python
3 # This is a class which watches a maildir for new messages. It uses the
4 # linux dirwatcher API (if available) to look for new files. The
5 # .messageReceived method is invoked with the filename of the new message,
6 # relative to the 'new' directory of the maildir.
8 # this is an abstract base class. It must be subclassed by something to
9 # provide a delay function (which polls in the case that DNotify isn't
10 # available) and a way to safely schedule code to run after a signal handler
11 # has fired. See maildirgtk.py and maildirtwisted.py for forms that use the
12 # event loops provided by Gtk+ and Twisted.
14 try:
15 from dnotify import DNotify
16 have_dnotify = 1
17 except:
18 have_dnotify = 0
19 import os
21 class Maildir:
22 """This is a class which watches a maildir for new messages. Once
23 started, it will run its .messageReceived method when a message is
24 available.
25 """
26 def __init__(self, basedir=None):
27 """Create the Maildir watcher. BASEDIR is the maildir directory (the
28 one which contains new/ and tmp/)
29 """
30 self.basedir = basedir
31 self.files = []
32 self.pollinterval = 10 # only used if we don't have DNotify
33 self.running = 0
34 self.dnotify = None
36 def setBasedir(self, basedir):
37 self.basedir = basedir
39 def start(self):
40 """You must run start to receive any messages."""
41 assert self.basedir
42 self.newdir = os.path.join(self.basedir, "new")
43 if self.running:
44 return
45 self.running = 1
46 if not os.path.isdir(self.basedir) or not os.path.isdir(self.newdir):
47 raise "invalid maildir '%s'" % self.basedir
48 # we must hold an fd open on the directory, so we can get notified
49 # when it changes.
50 global have_dnotify
51 if have_dnotify:
52 try:
53 self.dnotify = DNotify(self.newdir, self.dnotify_callback,
54 [DNotify.DN_CREATE])
55 except (IOError, OverflowError):
56 # IOError is probably linux<2.4.19, which doesn't support
57 # dnotify. OverflowError will occur on some 64-bit machines
58 # because of a python bug
59 print "DNotify failed, falling back to polling"
60 have_dnotify = 0
62 self.poll()
64 def startTimeout(self):
65 raise NotImplemented
66 def stopTimeout(self):
67 raise NotImplemented
68 def dnotify_callback(self):
69 print "callback"
70 self.poll()
71 raise NotImplemented
73 def stop(self):
74 if self.dnotify:
75 self.dnotify.remove()
76 self.dnotify = None
77 else:
78 self.stopTimeout()
79 self.running = 0
81 def poll(self):
82 assert self.basedir
83 # see what's new
84 for f in self.files:
85 if not os.path.isfile(os.path.join(self.newdir, f)):
86 self.files.remove(f)
87 newfiles = []
88 for f in os.listdir(self.newdir):
89 if not f in self.files:
90 newfiles.append(f)
91 self.files.extend(newfiles)
92 # TODO: sort by ctime, then filename, since safecat uses a rather
93 # fine-grained timestamp in the filename
94 for n in newfiles:
95 # TODO: consider catching exceptions in messageReceived
96 self.messageReceived(n)
97 if not have_dnotify:
98 self.startTimeout()
100 def messageReceived(self, filename):
101 """Called when a new file is noticed. Override it in subclasses.
102 Will receive path relative to maildir/new."""
103 print filename
106 def test1():
107 m = Maildir("ddir")
108 m.start()
109 import signal
110 while 1:
111 signal.pause()
113 if __name__ == '__main__':
114 test1()