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