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