]> git.neil.brown.name Git - plato.git/commitdiff
Add dnotify.py
authorNeilBrown <neilb@suse.de>
Sat, 21 Apr 2012 10:17:10 +0000 (20:17 +1000)
committerNeilBrown <neilb@suse.de>
Sat, 21 Apr 2012 10:17:10 +0000 (20:17 +1000)
This is a library to make it easy to python scripts to notice when
files changes.

Signed-off-by: NeilBrown <neilb@suse.de>
lib/dnotify.py [new file with mode: 0644]

diff --git a/lib/dnotify.py b/lib/dnotify.py
new file mode 100644 (file)
index 0000000..8ec6cb8
--- /dev/null
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+
+# class to allow watching multiple files and
+# calling a callback when any change (size or mtime)
+#
+# We take exclusive use of SIGIO and maintain a global list of
+# watched files.
+# As we cannot get siginfo in python, we check every file
+# every time we get a signal.
+# We report change if size, mtime, or ino of the file (given by name)
+
+# Copyright (C) 2011 Neil Brown <neilb@suse.de>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License along
+#    with this program; if not, write to the Free Software Foundation, Inc.,
+#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os, fcntl, signal
+
+dirlist = []
+def notified(sig, stack):
+    for d in dirlist:
+        fcntl.fcntl(d.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME|
+                                           fcntl.DN_CREATE|fcntl.DN_DELETE))
+        d.check()
+
+class dir():
+    def __init__(self, dname):
+        self.dname = dname
+        self.fd = os.open(dname, 0)
+        self.files = []
+        self.callbacks = []
+        fcntl.fcntl(self.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME|
+                                              fcntl.DN_CREATE|fcntl.DN_DELETE))
+        if not dirlist:
+            signal.signal(signal.SIGIO, notified)
+        dirlist.append(self)
+
+    def watch(self, fname, callback):
+        f = file(os.path.join(self.dname, fname), callback)
+        self.files.append(f)
+        return f
+
+    def watchall(self, callback):
+        self.callbacks.append(callback)
+
+    def check(self):
+        newlist = []
+        for c in self.callbacks:
+            if c():
+                newlist.append(c)
+        self.callbacks = newlist
+
+        for f in self.files:
+            f.check()
+
+    def cancel(self, victim):
+        if victim in self.files:
+            self.files.remove(victim)
+
+class file():
+    def __init__(self, fname, callback):
+        self.name = fname
+        try:
+            stat = os.stat(self.name)
+        except OSError:
+            self.ino = 0
+            self.size = 0
+            self.mtime = 0
+        else:
+            self.ino = stat.st_ino
+            self.size = stat.st_size
+            self.mtime = stat.st_mtime
+        self.callback = callback
+
+    def check(self):
+        try:
+            stat = os.stat(self.name)
+        except OSError:
+            if self.ino == 0:
+                return False
+            self.size = 0
+            self.mtime = 0
+            self.ino = 0
+        else:
+            if stat.st_size == self.size and stat.st_mtime == self.mtime \
+                   and stat.st_ino == self.ino:
+                return False
+            self.size = stat.st_size
+            self.mtime = stat.st_mtime
+            self.ino = stat.st_ino
+
+        self.callback(self)
+        return True
+
+    def cancel(self):
+        global dirlist
+        for d in dirlist:
+            d.cancel(self)
+
+
+if __name__ == "__main__" :
+    import os
+
+    def ping(f): print "got ", f.name
+    def something():
+        print "some change happened"
+        return True
+    try:
+        os.mkdir("/tmp/n")
+    except OSError:
+        pass
+    d = dir("/tmp/n")
+    d.watchall(something)
+    a = d.watch("a", ping)
+    b = d.watch("b", ping)
+    c = d.watch("c", ping)
+
+    print "Please modify files 'a', 'b', and 'c' in '/tmp/n'"
+    print "Ctrl-C when done"
+    try:
+        while True:
+            signal.pause()
+    except KeyboardInterrupt:
+        print "Goodbye"