WebStatus: yes create public_html/ at startup, otherwise we get internal server error...
[buildbot.git] / buildbot / dnotify.py
blobff0d9e68611f4874ecd90303eb438124675ab7e7
1 #! /usr/bin/python
3 # spiv wants this
5 import fcntl, signal
7 class DNotify_Handler:
8 def __init__(self):
9 self.watchers = {}
10 self.installed = 0
11 def install(self):
12 if self.installed:
13 return
14 signal.signal(signal.SIGIO, self.fire)
15 self.installed = 1
16 def uninstall(self):
17 if not self.installed:
18 return
19 signal.signal(signal.SIGIO, signal.SIG_DFL)
20 self.installed = 0
21 def add(self, watcher):
22 self.watchers[watcher.fd.fileno()] = watcher
23 self.install()
24 def remove(self, watcher):
25 if self.watchers.has_key(watcher.fd.fileno()):
26 del(self.watchers[watcher.fd.fileno()])
27 if not self.watchers:
28 self.uninstall()
29 def fire(self, signum, frame):
30 # this is the signal handler
31 # without siginfo_t, we must fire them all
32 for watcher in self.watchers.values():
33 watcher.callback()
35 class DNotify:
36 DN_ACCESS = fcntl.DN_ACCESS # a file in the directory was read
37 DN_MODIFY = fcntl.DN_MODIFY # a file was modified (write,truncate)
38 DN_CREATE = fcntl.DN_CREATE # a file was created
39 DN_DELETE = fcntl.DN_DELETE # a file was unlinked
40 DN_RENAME = fcntl.DN_RENAME # a file was renamed
41 DN_ATTRIB = fcntl.DN_ATTRIB # a file had attributes changed (chmod,chown)
43 handler = [None]
45 def __init__(self, dirname, callback=None,
46 flags=[DN_MODIFY,DN_CREATE,DN_DELETE,DN_RENAME]):
48 """This object watches a directory for changes. The .callback
49 attribute should be set to a function to be run every time something
50 happens to it. Be aware that it will be called more times than you
51 expect."""
53 if callback:
54 self.callback = callback
55 else:
56 self.callback = self.fire
57 self.dirname = dirname
58 self.flags = reduce(lambda x, y: x | y, flags) | fcntl.DN_MULTISHOT
59 self.fd = open(dirname, "r")
60 # ideally we would move the notification to something like SIGRTMIN,
61 # (to free up SIGIO) and use sigaction to have the signal handler
62 # receive a structure with the fd number. But python doesn't offer
63 # either.
64 if not self.handler[0]:
65 self.handler[0] = DNotify_Handler()
66 self.handler[0].add(self)
67 fcntl.fcntl(self.fd, fcntl.F_NOTIFY, self.flags)
68 def remove(self):
69 self.handler[0].remove(self)
70 self.fd.close()
71 def fire(self):
72 print self.dirname, "changed!"
74 def test_dnotify1():
75 d = DNotify(".")
76 while 1:
77 signal.pause()
79 def test_dnotify2():
80 # create ./foo/, create/delete files in ./ and ./foo/ while this is
81 # running. Notice how both notifiers are fired when anything changes;
82 # this is an unfortunate side-effect of the lack of extended sigaction
83 # support in Python.
84 count = [0]
85 d1 = DNotify(".")
86 def fire1(count=count, d1=d1):
87 print "./ changed!", count[0]
88 count[0] += 1
89 if count[0] > 5:
90 d1.remove()
91 del(d1)
92 # change the callback, since we can't define it until after we have the
93 # dnotify object. Hmm, unless we give the dnotify to the callback.
94 d1.callback = fire1
95 def fire2(): print "foo/ changed!"
96 d2 = DNotify("foo", fire2)
97 while 1:
98 signal.pause()
101 if __name__ == '__main__':
102 test_dnotify2()