Created
June 13, 2010 20:24
-
-
Save rmax/436974 to your computer and use it in GitHub Desktop.
This file contains 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
""" | |
Restarts the application if any file changed. | |
Borrowed from Tornado and Django. | |
Usage: | |
import txautoreload | |
txautoreload.start() | |
""" | |
import types | |
import os | |
import sys | |
from twisted.internet import reactor, task | |
from twisted.python import log | |
__all__ = ['start'] | |
RELOAD_ATTEMPTED = False | |
WIN32 = sys.platform == 'win32' | |
def start(clock=None, check_time=0.5): | |
"""Restarts the process automatically when a module is modified""" | |
clock = clock or reactor | |
modify_times = {} | |
scheduler = task.LoopingCall(reload_on_update, clock, modify_times) | |
scheduler.clock = clock | |
scheduler.start(check_time) | |
def reload_on_update(clock, modify_times): | |
global RELOAD_ATTEMPTED | |
if RELOAD_ATTEMPTED: | |
# We already tried to reload and it didn't work, so don't try again. | |
return | |
changed, path = code_changed(modify_times) | |
if changed: | |
_reload_attempted = True | |
log.msg("%s modified; restarting application" % path) | |
clock.stop() | |
try: | |
os.execv(sys.executable, [sys.executable] + sys.argv) | |
except OSError, e: | |
# Mac OS X versions prior to 10.6 do not support execv in a | |
# process that contains multiple threads. Instead of | |
# re-executing in the current process, start a new one and | |
# cause the current process to exit. This isn't ideal since | |
# new process is detached from the parent terminal and thus | |
# cannot easily be killed with ctrl-C, but it's better than | |
# not being able to autoreload at all. | |
# Unfortunately the errno returned in this case does not apper | |
# to be consistent, so we can't easily check for this error | |
# specifically. | |
os.spawnv(os.P_NOWAIT, sys.executable, [sys.executable] + sys.argv) | |
sys.exit(0) | |
def code_changed(modify_times): | |
# Some modules play games with sys.modules (e.g. email/__init__.py | |
# in the standard library), and occasionally this can cause strange | |
# failures in getattr. Just ignore anything that's not and ordinary | |
# module. | |
for module in sys.modules.values(): | |
if not isinstance(module, types.ModuleType): | |
continue | |
path = getattr(module, "__file__", None) | |
if not path: | |
continue | |
if path.endswith(".pyc") or path.endswith(".pyo"): | |
path = path[:-1] | |
try: | |
stat = os.stat(path) | |
except: | |
# e.g. TypeError | |
continue | |
modified = stat.st_mtime | |
if WIN32: | |
modified -= stat.st_ctime | |
if not path in modify_times: | |
modify_times[path] = modified | |
continue | |
if modified != modify_times[path]: | |
return True, path | |
return False, None |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment