Commits

Giacomo Bagnoli committed 802c795

Use uWSGI file monitoring instead of pyinotify

  • Participants
  • Parent commits 278b377

Comments (0)

Files changed (5)

pasteuwsgi/__init__.py

 #!/usr/bin/env python
 
-version = (0, 1, 8)
+version = (0, 2, 0)
 extraversion = ""
 version_string = "Paste uWSGI version %d.%d.%d" % version
 

pasteuwsgi/monitor.py

-
-import logging
-import os
-import pyinotify
-import signal
-
-from threading import Timer
-
-class OnWriteHandler(pyinotify.ProcessEvent):
-
-    def my_init(self, pidfile, log):
-        self.log = log
-        self.sig = signal.SIGHUP
-        self.pidfile = pidfile
-        self.pid = None
-        self.interval = 2.0
-        self.timer = None
-        self.extensions = [".py", ".ini"]
-        log.debug("Created Watcher")
-
-    def process_IN_CREATE(self, event):
-        self.process_IN_MODIFY(event)
-
-    def process_IN_DELETE(self, event):
-        self.process_IN_MODIFY(event)
-
-    def process_IN_MODIFY(self, event):
-        if self.timer:
-            self.log.info("%s changed, delaying reload", event.pathname)
-            self.timer.cancel()
-        else:
-            self.log.info("%s changed, reloading in %d sec",
-                          event.pathname, self.interval)
-
-        self.timer = Timer(self.interval, reload_uwsgi,
-                           [self.log, event, self.pidfile, self.sig])
-        self.timer.start()
-
-
-def reload_uwsgi(log, event, pidfile, sig):
-    try:
-        with open(pidfile, 'r') as f:
-            pid = int(f.read().strip())
-    except:
-        log.exception("Cannot read pidfile, reloading aborted")
-        return
-
-    os.kill(pid, sig)
-    log.debug("Killing pid %s with signal %s", pid, sig)
-    log.warn("=" * 20 + " RELOADING uWSGI " + "=" * 20)
-
-
-def quick_check(notifier):
-    assert notifier._timeout is not None, \
-            'Notifier must be constructed with a short timeout'
-    while notifier.check_events():
-        notifier.read_events()
-        notifier.process_events()
-
-
-def filter_path(pathname):
-    extensions = (".py", ".ini")
-    if all(not pathname.endswith(ext) for ext in extensions):
-        return False
-    return True
-
-
-def watch(paths, pidfile):
-    logger = logging.getLogger(__name__)
-    wm = pyinotify.WatchManager()
-    handler = OnWriteHandler(pidfile=pidfile, log=logger)
-    notifier = pyinotify.Notifier(wm, handler, timeout=10)
-    for path in paths:
-        logger.info("Starting file watcher on path %s", path)
-        wm.add_watch(path, pyinotify.ALL_EVENTS, rec=True, auto_add=True,
-                     exclude_filter=filter_path)
-    return notifier
-
-
-if __name__ == '__main__':
-
-    def fakereload(self, event):
-        print "%s changed" % (event.pathname)
-
-    paths = ["/tmp"]
-    OnWriteHandler.reload = fakereload
-
-    logging.basicConfig(level=logging.DEBUG)
-    notifier = watch(paths)
-    notifier.loop()

pasteuwsgi/serve.py

 import ConfigParser
 import os
 import tempfile
-import time
 import logging
 import shlex
 import subprocess
 from paste.script import command
 
 import pasteuwsgi
-from pasteuwsgi.monitor import watch
 
 
 class ServeCommand(command.Command):
                       dest="uwsgi_opts",
                       help="Additional options to uwsgi")
 
-    def create_wsgi_script(self, ini_file, dest=None):
+    def create_wsgi_script(self, ini, paths):
         template_file = os.path.join(os.path.dirname(__file__), "wsgi.py")
+
+        paths = "['" + "','".join(paths) + "']"
         with open(template_file) as f:
-            template = f.read().format(ini=ini_file)
+            template_content = f.read()
+            wsgi_content = template_content.format(ini=ini, paths=paths)
 
-        if dest and isinstance(dest, "basestring"):
-            wsgi_name = dest
-            dest = open(dest)
-        elif not dest:
-            wsgi_name = tempfile.mkstemp(suffix=".wsgi")[1]
-            dest = open(wsgi_name, "w")
-        else:
-            wsgi_name = None
 
-        dest.write(template)
-        dest.close()
+        wsgi_name = tempfile.mkstemp(suffix=".wsgi")[1]
+        with open(wsgi_name, "w") as f:
+            f.write(wsgi_content)
+
         return wsgi_name
 
     def command(self):
         if not os.path.isfile(uwsgibin):
             uwsgibin = "/usr/bin/uwsgi"
 
-        wsgi_name = self.create_wsgi_script(ini_file)
+        if self.options.reload:
+            self.log.info("Setting up file monitor")
+            pth = os.path.join(venv_site_packages, "easy-install.pth")
+            if os.path.isfile(pth):
+                with open(pth) as f:
+                    paths = [os.path.realpath(p.strip())
+                                for p in f
+                                if ";" not in p and os.path.exists(p.strip())]
+            else:
+                paths = []
+            paths.append(venv_site_packages)
+            paths.append(ini_file)
+        else:
+            paths = []
+
+        for path in paths:
+            self.log.debug("Added %s to watched paths", path)
+
+        wsgi_name = self.create_wsgi_script(ini_file, paths)
         pidfile = tempfile.NamedTemporaryFile(suffix=".pid", delete=False)
         pidfilename = pidfile.name
 
         self.log.info("Starting uwsgi: %s", cmd)
         try:
             uwsgi = subprocess.Popen(shlex.split(cmd))
-            self.log.info("Started uwsgi with pid %s", uwsgi.pid)
-            if self.options.reload:
-                self.log.info("Setting up file monitor")
-                pth = os.path.join(venv_site_packages, "easy-install.pth")
-                if os.path.isfile(pth):
-                    with open(pth) as f:
-                        paths = [os.path.realpath(p.strip())
-                                 for p in f
-                                 if ";" not in p and os.path.exists(p.strip())]
-                else:
-                    paths = []
-                paths.append(venv_site_packages)
-                paths.append(ini_file)
-                notifier = watch(paths, pidfilename)
-                notifier.loop()
-
             uwsgi.wait()
 
         except KeyboardInterrupt:

pasteuwsgi/wsgi.py

 #!/usr/bin/env python
 from paste.deploy import loadapp
 from paste.script.util.logging_config import fileConfig
+import logging
+
+import uwsgi
+
+def reload_app(signum):
+    logging.info("File changed, reloading")
+    uwsgi.reload()
+
+uwsgi.register_signal(17, "workers", reload_app)
+for path in {paths}:
+    uwsgi.add_file_monitor(17, path)
+
 
 ini_file = "{ini}"
 fileConfig(ini_file)
     long_description=open('README').read(),
     install_requires = [
         "PasteScript>=1.3",
-        "pyinotify"
+        "uwsgi"
     ],
     test_suite = 'nose.collector',
     tests_require = [ "Nose" ],