Skip to content

Instantly share code, notes, and snippets.

@rmax
Created June 13, 2010 20:24
Show Gist options
  • Save rmax/436974 to your computer and use it in GitHub Desktop.
Save rmax/436974 to your computer and use it in GitHub Desktop.
"""
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