Added module watcher for easier development (automatic module reload)

Fri, 28 Jul 2017 16:05:08 +0200

author
mdd
date
Fri, 28 Jul 2017 16:05:08 +0200
changeset 30
9188235e3bbf
parent 29
c96f20e95029
child 31
92035ebc4743

Added module watcher for easier development (automatic module reload)

printrun-src/printrun/module_watcher.py file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/printrun-src/printrun/module_watcher.py	Fri Jul 28 16:05:08 2017 +0200
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+# Author: Chris Eberle <eberle1080@gmail.com>
+# Watch for any changes in a module or package, and reload it automatically
+
+import pyinotify
+import imp
+import os
+
+class ModuleWatcher(pyinotify.ProcessEvent):
+    """
+    Automatically reload any modules or packages as they change
+    """
+
+    def __init__(self):
+        "El constructor"
+
+        self.wm = pyinotify.WatchManager()
+        self.notifier = None
+        self.mod_map = {}
+
+    def _watch_file(self, file_name, module):
+        "Add a watch for a specific file, and map said file to a module name"
+
+        file_name = os.path.realpath(file_name)
+        self.mod_map[file_name] = module
+        self.wm.add_watch(file_name, pyinotify.EventsCodes.IN_MODIFY)
+        print 'Watching', file_name
+
+    def watch_module(self, name):
+        "Load a module, determine which files it uses, and watch them"
+
+        if imp.is_builtin(name) != 0:
+            # Pretty pointless to watch built-in modules
+            return
+
+        (fd, pathname, description) = imp.find_module(name)
+
+        try:
+            mod = imp.load_module(name, fd, pathname, description)
+            if fd:
+                self._watch_file(fd.name, name)
+            else:
+                for root, dirs, files in os.walk(pathname):
+                    for filename in files:
+                        fpath = os.path.join(root, filename)
+                        if fpath.endswith('.py'):
+                            self._watch_file(fpath, name)
+        finally:
+            if fd:
+                fd.close()
+
+    def start_watching(self):
+        "Start the pyinotify watch thread"
+
+        if self.notifier is None:
+            self.notifier = pyinotify.ThreadedNotifier(self.wm, self)
+        self.notifier.start()
+
+    def stop_watching(self):
+        "Stop the pyinotify watch thread"
+
+        if self.notifier is not None:
+            self.notifier.stop()
+
+    def process_IN_MODIFY(self, event):
+        "A file of interest has changed"
+
+        # Is it a file I know about?
+        if event.path not in self.mod_map:
+            return
+
+        # Find out which module is using that file
+        modname = self.mod_map[event.path]
+
+        # Reload the module
+        (fd, pathname, description) = imp.find_module(modname)
+        try:
+            print "Reloading changed file: %s" % pathname
+            imp.load_module(modname, fd, pathname, description)
+        finally:
+            if fd:
+                fd.close()
+
+        #print 'Reload', modname
+
+if __name__ == '__main__':
+    # Test everything
+
+    import sys
+
+    mw = ModuleWatcher()
+    mw.watch_module('module1')
+    mw.watch_module('module2')
+    mw.start_watching()
+
+    try:
+        raw_input('Press ENTER to exit')
+    finally:
+        mw.stop_watching()
+        sys.exit(0)

mercurial