Created
April 20, 2011 00:30
-
-
Save jordanorelli/930067 to your computer and use it in GitHub Desktop.
file change monitor for Django
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
# 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 be 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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This tool works great for so many years now, but after upgrade to Python3 we got this error:
I don't know why the dict changes while iteration, but it can be fixed with:
for module in sys.modules.copy().values():