You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
2.9 KiB
113 lines
2.9 KiB
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()
|
|
|