From 9566a61a22e1da30fb8abfef4b25d83d0c465be9 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Thu, 21 Jul 2005 04:08:54 +0000 Subject: [PATCH] Added auto-reload to standalone server! Fixes #113. Thanks very much to Jason Huggins for the patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@266 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/management.py | 41 ++++++++++++++++--------------- django/utils/autoreload.py | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 django/utils/autoreload.py diff --git a/django/core/management.py b/django/core/management.py index 33783162ff..eed6686ed1 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -419,27 +419,30 @@ def runserver(port): "Starts a lightweight Web server for development." from django.core.servers.basehttp import run, WSGIServerException from django.core.handlers.wsgi import AdminMediaHandler, WSGIHandler - from django.conf.settings import SETTINGS_MODULE if not port.isdigit(): sys.stderr.write("Error: %r is not a valid port number.\n" % port) sys.exit(1) - print "Starting server on port %s with settings module %r." % (port, SETTINGS_MODULE) - print "Go to http://127.0.0.1:%s/ for Django." % port - print "Quit the server with CONTROL-C (Unix) or CTRL-BREAK (Windows)." - try: - run(int(port), AdminMediaHandler(WSGIHandler())) - except WSGIServerException, e: - # Use helpful error messages instead of ugly tracebacks. - ERRORS = { - 13: "You don't have permission to access that port.", - 98: "That port is already in use.", - } + def inner_run(): + from django.conf.settings import SETTINGS_MODULE + print "Starting server on port %s with settings module %r." % (port, SETTINGS_MODULE) + print "Go to http://127.0.0.1:%s/ for Django." % port + print "Quit the server with CONTROL-C (Unix) or CTRL-BREAK (Windows)." try: - error_text = ERRORS[e.args[0].args[0]] - except (AttributeError, KeyError): - error_text = str(e) - sys.stderr.write("Error: %s\n" % error_text) - sys.exit(1) - except KeyboardInterrupt: - sys.exit(0) + run(int(port), AdminMediaHandler(WSGIHandler())) + except WSGIServerException, e: + # Use helpful error messages instead of ugly tracebacks. + ERRORS = { + 13: "You don't have permission to access that port.", + 98: "That port is already in use.", + } + try: + error_text = ERRORS[e.args[0].args[0]] + except (AttributeError, KeyError): + error_text = str(e) + sys.stderr.write("Error: %s\n" % error_text) + sys.exit(1) + except KeyboardInterrupt: + sys.exit(0) + from django.utils import autoreload + autoreload.main(inner_run) runserver.args = '[optional port number]' diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py new file mode 100644 index 0000000000..140c83147c --- /dev/null +++ b/django/utils/autoreload.py @@ -0,0 +1,50 @@ +# Autoreloading launcher. +# Borrowed from Peter Hunt and the CherryPy project (http://www.cherrypy.org). +# Some taken from Ian Bicking's Paste (http://pythonpaste.org/). + +import os, sys, thread, time + +RUN_RELOADER = True +reloadFiles = [] + +def reloader_thread(): + mtimes = {} + while RUN_RELOADER: + for filename in filter(lambda v: v, map(lambda m: getattr(m, "__file__", None), sys.modules.values())) + reloadFiles: + if filename.endswith(".pyc"): + filename = filename[:-1] + mtime = os.stat(filename).st_mtime + if filename not in mtimes: + mtimes[filename] = mtime + continue + if mtime > mtimes[filename]: + sys.exit(3) # force reload + time.sleep(1) + +def restart_with_reloader(): + while True: + args = [sys.executable] + sys.argv + if sys.platform == "win32": + args = ['"%s"' % arg for arg in args] + new_environ = os.environ.copy() + new_environ["RUN_MAIN"] = 'true' + exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ) + if exit_code != 3: + return exit_code + +def main(main_func, args=None, kwargs=None): + if os.environ.get("RUN_MAIN") == "true": + if args is None: + args = () + if kwargs is None: + kwargs = {} + thread.start_new_thread(main_func, args, kwargs) + try: + reloader_thread() + except KeyboardInterrupt: + pass + else: + try: + sys.exit(restart_with_reloader()) + except KeyboardInterrupt: + pass