Matthew Schinckel avatar Matthew Schinckel committed afe7be1

Initial import

Comments (0)

Files changed (5)

Empty file added.

Empty file added.

+WSGI Monitor
+===============
+
+Please note that I did not write this code: I only packaged it up to make
+it easier to install. This code is all taken from the mod_wsgi_ wiki page on
+SourceCodeReloading_, and was written by Graham Dumpleton.
+
+Since ``mod_wsgi`` is released under the `Apache License 2.0`_, I have assumed that
+this code is also available under that license, as it is part of the same
+repository.
+
+
+Installation
+-------------
+
+::
+
+    pip install wsgi-monitor
+
+or download the package, and install with::
+
+    python setup.py install
+
+Hopefully, you are using this inside a virtualenv.
+
+
+Usage
+------
+
+mod_passenger_ / Django_
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you use ``mod_passenger``, then add something like the following to the bottom of your
+``passenger_wsgi.py`` file::
+
+    if settings.DEBUG:
+        import wsgi_monitor
+        wsgi_monitor.start(interval=1.0)
+
+
+This particular version assumes that ``settings.DEBUG`` is the flag that
+determines if we are running in development mode, and reload should
+be on. This works great with Django, but your system may require something
+different. If you use this with another framework or web server, please
+contribute by forking this on bitbucket (`wsgi-monitor`_) and updating the docs.
+
+
+
+.. _`mod_wsgi`: http://www.modwsgi.org
+.. _`SourceCodeReloading`: http://code.google.com/p/modwsgi/wiki/ReloadingSourceCode
+.. _`Apache License 2.0`: http://www.apache.org/licenses/LICENSE-2.0
+.. _`mod_passenger`: http://www.modrails.com/
+.. _`Django`: http://www.djangoproject.com/
+.. _`wsgi-monitor`: http://bitbucket.org/schinckel/wsgi-monitor
+from distutils.core import setup
+
+setup(
+    name = "wsgi-monitor",
+    version = "1.0",
+    description = "Auto-reload WSGI server when files change.",
+    url = "http://bitbucket.org/schinckel/wsgi-monitor",
+    author = "Matthew Schinckel",
+    author_email = "matt@schinckel.net",
+    py_modules = [
+        "wsgi_monitor",
+    ],
+    classifiers = [
+        'Programming Language :: Python',
+        'License :: OSI Approved :: Apache Software License',
+        'Operating System :: OS Independent',
+        'Framework :: Django',
+    ],
+)
+import os
+import sys
+import time
+import signal
+import threading
+import atexit
+import Queue
+
+_interval = 1.0
+_times = {}
+_files = []
+
+_running = False
+_queue = Queue.Queue()
+_lock = threading.Lock()
+
+def _restart(path):
+    _queue.put(True)
+    prefix = 'monitor (pid=%d):' % os.getpid()
+    print >> sys.stderr, '%s Change detected to \'%s\'.' % (prefix, path)
+    print >> sys.stderr, '%s Triggering process restart.' % prefix
+    os.kill(os.getpid(), signal.SIGINT)
+
+def _modified(path):
+    try:
+        # If path doesn't denote a file and were previously
+        # tracking it, then it has been removed or the file type
+        # has changed so force a restart. If not previously
+        # tracking the file then we can ignore it as probably
+        # pseudo reference such as when file extracted from a
+        # collection of modules contained in a zip file.
+
+        if not os.path.isfile(path):
+            return path in _times
+
+        # Check for when file last modified.
+
+        mtime = os.stat(path).st_mtime
+        if path not in _times:
+            _times[path] = mtime
+
+        # Force restart when modification time has changed, even
+        # if time now older, as that could indicate older file
+        # has been restored.
+
+        if mtime != _times[path]:
+            return True
+    except:
+        # If any exception occured, likely that file has been
+        # been removed just before stat(), so force a restart.
+
+        return True
+
+    return False
+
+def _monitor():
+    while 1:
+        # Check modification times on all files in sys.modules.
+
+        for module in sys.modules.values():
+            if not hasattr(module, '__file__'):
+                continue
+            path = getattr(module, '__file__')
+            if not path:
+                continue
+            if os.path.splitext(path)[1] in ['.pyc', '.pyo', '.pyd']:
+                path = path[:-1]
+            if _modified(path):
+                return _restart(path)
+
+        # Check modification times on files which have
+        # specifically been registered for monitoring.
+
+        for path in _files:
+            if _modified(path):
+                return _restart(path)
+
+        # Go to sleep for specified interval.
+
+        try:
+            return _queue.get(timeout=_interval)
+        except:
+            pass
+
+_thread = threading.Thread(target=_monitor)
+_thread.setDaemon(True)
+
+def _exiting():
+    try:
+        _queue.put(True)
+    except:
+        pass
+    _thread.join()
+
+atexit.register(_exiting)
+
+def track(path):
+    if not path in _files:
+        _files.append(path)
+
+def start(interval=1.0):
+    global _interval
+    if interval < _interval:
+        _interval = interval
+
+    global _running
+    _lock.acquire()
+    if not _running:
+        prefix = 'monitor (pid=%d):' % os.getpid()
+        print >> sys.stderr, '%s Starting change monitor.' % prefix
+        _running = True
+        _thread.start()
+    _lock.release()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.